#include "llvm/ADT/Optional.h"
#include <cstdint>
+#include <bitset>
namespace llvm {
class MachineInstr;
+class MachineFunction;
class MachineOperand;
class MachineRegisterInfo;
class RegisterBankInfo;
class TargetInstrInfo;
class TargetRegisterInfo;
+/// Container class for CodeGen predicate results.
+/// This is convenient because std::bitset does not have a constructor
+/// with an initializer list of set bits.
+///
+/// Each InstructionSelector subclass should define a PredicateBitset class with:
+/// const unsigned MAX_SUBTARGET_PREDICATES = 192;
+/// using PredicateBitset = PredicateBitsetImpl<MAX_SUBTARGET_PREDICATES>;
+/// and updating the constant to suit the target. Tablegen provides a suitable
+/// definition for the predicates in use in <Target>GenGlobalISel.inc when
+/// GET_GLOBALISEL_PREDICATE_BITSET is defined.
+template <std::size_t MaxPredicates>
+class PredicateBitsetImpl : public std::bitset<MaxPredicates> {
+public:
+ // Cannot inherit constructors because it's not supported by VC++..
+ PredicateBitsetImpl() = default;
+
+ PredicateBitsetImpl(const std::bitset<MaxPredicates> &B)
+ : std::bitset<MaxPredicates>(B) {}
+
+ PredicateBitsetImpl(std::initializer_list<unsigned> Init) {
+ for (auto I : Init)
+ std::bitset<MaxPredicates>::set(I);
+ }
+};
+
/// Provides the logic to select generic machine instructions.
class InstructionSelector {
public:
virtual ~InstructionSelector() {}
+ /// This is executed before selecting a function.
+ virtual void beginFunction(const MachineFunction &MF) {}
+
/// Select the (possibly generic) instruction \p I to only use target-specific
/// opcodes. It is OK to insert multiple instructions, but they cannot be
/// generic pre-isel instructions.
namespace {
+#define GET_GLOBALISEL_PREDICATE_BITSET
+#include "AArch64GenGlobalISel.inc"
+#undef GET_GLOBALISEL_PREDICATE_BITSET
+
class AArch64InstructionSelector : public InstructionSelector {
public:
AArch64InstructionSelector(const AArch64TargetMachine &TM,
const AArch64Subtarget &STI,
const AArch64RegisterBankInfo &RBI);
+ void beginFunction(const MachineFunction &MF) override;
bool select(MachineInstr &I) const override;
private:
const AArch64InstrInfo &TII;
const AArch64RegisterInfo &TRI;
const AArch64RegisterBankInfo &RBI;
+ bool ForCodeSize;
+
+ PredicateBitset AvailableFeatures;
+ PredicateBitset
+ computeAvailableFeatures(const MachineFunction *MF,
+ const AArch64Subtarget *Subtarget) const;
// We declare the temporaries used by selectImpl() in the class to minimize the
// cost of constructing placeholder values.
const AArch64TargetMachine &TM, const AArch64Subtarget &STI,
const AArch64RegisterBankInfo &RBI)
: InstructionSelector(), TM(TM), STI(STI), TII(*STI.getInstrInfo()),
- TRI(*STI.getRegisterInfo()), RBI(RBI)
+ TRI(*STI.getRegisterInfo()), RBI(RBI), ForCodeSize(), AvailableFeatures()
#define GET_GLOBALISEL_TEMPORARIES_INIT
#include "AArch64GenGlobalISel.inc"
#undef GET_GLOBALISEL_TEMPORARIES_INIT
return true;
}
+void AArch64InstructionSelector::beginFunction(
+ const MachineFunction &MF) {
+ ForCodeSize = MF.getFunction()->optForSize();
+ AvailableFeatures = computeAvailableFeatures(&MF, &STI);
+}
+
bool AArch64InstructionSelector::select(MachineInstr &I) const {
assert(I.getParent() && "Instruction should be in a basic block!");
assert(I.getParent()->getParent() && "Instruction should be in a function!");
/// encoding when possible in order to reduce code size.
FunctionPass *createX86EvexToVexInsts();
-InstructionSelector *createX86InstructionSelector(X86Subtarget &,
+InstructionSelector *createX86InstructionSelector(const X86TargetMachine &TM,
+ X86Subtarget &,
X86RegisterBankInfo &);
void initializeEvexToVexInstPassPass(PassRegistry &);
namespace {
+#define GET_GLOBALISEL_PREDICATE_BITSET
+#include "X86GenGlobalISel.inc"
+#undef GET_GLOBALISEL_PREDICATE_BITSET
+
class X86InstructionSelector : public InstructionSelector {
public:
- X86InstructionSelector(const X86Subtarget &STI,
+ X86InstructionSelector(const X86TargetMachine &TM, const X86Subtarget &STI,
const X86RegisterBankInfo &RBI);
+ void beginFunction(const MachineFunction &MF) override;
bool select(MachineInstr &I) const override;
private:
bool selectTrunc(MachineInstr &I, MachineRegisterInfo &MRI,
MachineFunction &MF) const;
+ const X86TargetMachine &TM;
const X86Subtarget &STI;
const X86InstrInfo &TII;
const X86RegisterInfo &TRI;
const X86RegisterBankInfo &RBI;
+ bool OptForSize;
+ bool OptForMinSize;
+
+ PredicateBitset AvailableFeatures;
+ PredicateBitset computeAvailableFeatures(const MachineFunction *MF,
+ const X86Subtarget *Subtarget) const;
#define GET_GLOBALISEL_TEMPORARIES_DECL
#include "X86GenGlobalISel.inc"
#include "X86GenGlobalISel.inc"
#undef GET_GLOBALISEL_IMPL
-X86InstructionSelector::X86InstructionSelector(const X86Subtarget &STI,
+X86InstructionSelector::X86InstructionSelector(const X86TargetMachine &TM,
+ const X86Subtarget &STI,
const X86RegisterBankInfo &RBI)
- : InstructionSelector(), STI(STI), TII(*STI.getInstrInfo()),
- TRI(*STI.getRegisterInfo()), RBI(RBI)
+ : InstructionSelector(), TM(TM), STI(STI), TII(*STI.getInstrInfo()),
+ TRI(*STI.getRegisterInfo()), RBI(RBI), OptForSize(false),
+ OptForMinSize(false), AvailableFeatures()
#define GET_GLOBALISEL_TEMPORARIES_INIT
#include "X86GenGlobalISel.inc"
#undef GET_GLOBALISEL_TEMPORARIES_INIT
return true;
}
+void X86InstructionSelector::beginFunction(const MachineFunction &MF) {
+ OptForSize = MF.getFunction()->optForSize();
+ OptForMinSize = MF.getFunction()->optForMinSize();
+ AvailableFeatures = computeAvailableFeatures(&MF, &STI);
+}
+
bool X86InstructionSelector::select(MachineInstr &I) const {
assert(I.getParent() && "Instruction should be in a basic block!");
assert(I.getParent()->getParent() && "Instruction should be in a function!");
}
InstructionSelector *
-llvm::createX86InstructionSelector(X86Subtarget &Subtarget,
+llvm::createX86InstructionSelector(const X86TargetMachine &TM,
+ X86Subtarget &Subtarget,
X86RegisterBankInfo &RBI) {
- return new X86InstructionSelector(Subtarget, RBI);
+ return new X86InstructionSelector(TM, Subtarget, RBI);
}
auto *RBI = new X86RegisterBankInfo(*I->getRegisterInfo());
GISel->RegBankInfo.reset(RBI);
- GISel->InstSelector.reset(createX86InstructionSelector(*I, *RBI));
+ GISel->InstSelector.reset(createX86InstructionSelector(*this, *I, *RBI));
#endif
I->setGISelAccessor(*GISel);
}
def Z : OperandWithDefaultOps <i32, (ops R0)>;
def m1Z : OperandWithDefaultOps <i32, (ops (i32 -1), R0)>;
-//===- Test the function definition boilerplate. --------------------------===//
+def HasA : Predicate<"Subtarget->hasA()">;
+def HasB : Predicate<"Subtarget->hasB()">;
+
+//===- Test the function boilerplate. -------------------------------------===//
+
+// CHECK-LABEL: enum SubtargetFeatureBits : uint8_t {
+// CHECK-NEXT: Feature_HasABit = 0,
+// CHECK-NEXT: Feature_HasBBit = 1,
+// CHECK-NEXT: };
+
+// CHECK-LABEL: static const char *SubtargetFeatureNames[] = {
+// CHECK-NEXT: "Feature_HasA",
+// CHECK-NEXT: "Feature_HasB",
+// CHECK-NEXT: nullptr
+// CHECK-NEXT: };
+
+// CHECK-LABEL: PredicateBitset MyTargetInstructionSelector::
+// CHECK-NEXT: computeAvailableFeatures(const MachineFunction *MF, const MyTargetSubtarget *Subtarget) const {
+// CHECK-NEXT: PredicateBitset Features;
+// CHECK-NEXT: if (Subtarget->hasA())
+// CHECK-NEXT: Features[Feature_HasABit] = 1;
+// CHECK-NEXT: if (Subtarget->hasB())
+// CHECK-NEXT: Features[Feature_HasBBit] = 1;
+// CHECK-NEXT: return Features;
+// CHECK-NEXT: }
// CHECK: bool MyTargetInstructionSelector::selectImpl(MachineInstr &I) const {
// CHECK: MachineFunction &MF = *I.getParent()->getParent();
//===- Test a nested instruction match. -----------------------------------===//
// CHECK-LABEL: if ([&]() {
+// CHECK-NEXT: PredicateBitset ExpectedFeatures = {Feature_HasABit};
+// CHECK-NEXT: if ((AvailableFeatures & ExpectedFeatures) != ExpectedFeatures)
+// CHECK-NEXT: return false;
// CHECK-NEXT: MachineInstr &MI0 = I;
// CHECK-NEXT: if (MI0.getNumOperands() < 3)
// CHECK-NEXT: return false;
// We also get a second rule by commutativity.
// CHECK-LABEL: if ([&]() {
+// CHECK-NEXT: PredicateBitset ExpectedFeatures = {Feature_HasABit};
+// CHECK-NEXT: if ((AvailableFeatures & ExpectedFeatures) != ExpectedFeatures)
+// CHECK-NEXT: return false;
// CHECK-NEXT: MachineInstr &MI0 = I;
// CHECK-NEXT: if (MI0.getNumOperands() < 3)
// CHECK-NEXT: return false;
def MULADD : I<(outs GPR32:$dst), (ins GPR32:$src1, GPR32:$src2, GPR32:$src3),
[(set GPR32:$dst,
- (mul (add GPR32:$src1, GPR32:$src2), GPR32:$src3))]>;
+ (mul (add GPR32:$src1, GPR32:$src2), GPR32:$src3))]>,
+ Requires<[HasA]>;
//===- Test another simple pattern with regclass operands. ----------------===//
// CHECK-LABEL: if ([&]() {
+// CHECK-NEXT: PredicateBitset ExpectedFeatures = {Feature_HasABit, Feature_HasBBit};
+// CHECK-NEXT: if ((AvailableFeatures & ExpectedFeatures) != ExpectedFeatures)
+// CHECK-NEXT: return false;
// CHECK-NEXT: MachineInstr &MI0 = I;
// CHECK-NEXT: if (MI0.getNumOperands() < 3)
// CHECK-NEXT: return false;
// CHECK-NEXT: }()) { return true; }
def MUL : I<(outs GPR32:$dst), (ins GPR32:$src2, GPR32:$src1),
- [(set GPR32:$dst, (mul GPR32:$src1, GPR32:$src2))]>;
+ [(set GPR32:$dst, (mul GPR32:$src1, GPR32:$src2))]>,
+ Requires<[HasA, HasB]>;
//===- Test a pattern with ComplexPattern operands. -----------------------===//
//
emitValidateOperandClass(Info, OS);
// Emit the available features compute function.
- SubtargetFeatureInfo::emitComputeAvailableFeatures(
+ SubtargetFeatureInfo::emitComputeAssemblerAvailableFeatures(
Info.Target.getName(), ClassName, "ComputeAvailableFeatures",
Info.SubtargetFeatures, OS);
o << "#endif // NDEBUG\n";
// Emit the available features compute function.
- SubtargetFeatureInfo::emitComputeAvailableFeatures(
+ SubtargetFeatureInfo::emitComputeAssemblerAvailableFeatures(
Target.getName(), "MCCodeEmitter", "computeAvailableFeatures",
SubtargetFeatures, o);
//===----------------------------------------------------------------------===//
#include "CodeGenDAGPatterns.h"
+#include "SubtargetFeatureInfo.h"
#include "llvm/ADT/Optional.h"
#include "llvm/ADT/SmallSet.h"
#include "llvm/ADT/Statistic.h"
return Explanation;
}
-static std::string explainRulePredicates(const ArrayRef<Init *> Predicates) {
- std::string Explanation = "";
- StringRef Separator = "";
- for (const auto *P : Predicates) {
- Explanation += Separator;
-
- if (const DefInit *PDef = dyn_cast<DefInit>(P)) {
- Explanation += PDef->getDef()->getName();
- } else
- Explanation += "<unknown>";
- }
- return Explanation;
-}
-
std::string explainOperator(Record *Operator) {
if (Operator->isSubClassOf("SDNode"))
return " (" + Operator->getValueAsString("Opcode") + ")";
/// ID for the next instruction variable defined with defineInsnVar()
unsigned NextInsnVarID;
+ std::vector<Record *> RequiredFeatures;
+
public:
RuleMatcher()
: Matchers(), Actions(), InsnVariableNames(), NextInsnVarID(0) {}
RuleMatcher &operator=(RuleMatcher &&Other) = default;
InstructionMatcher &addInstructionMatcher();
+ void addRequiredFeature(Record *Feature);
template <class Kind, class... Args> Kind &addAction(Args &&... args);
void emitCxxCapturedInsnList(raw_ostream &OS);
void emitCxxCaptureStmts(raw_ostream &OS, StringRef Expr);
- void emit(raw_ostream &OS);
+ void emit(raw_ostream &OS,
+ std::map<Record *, SubtargetFeatureInfo, LessRecordByID>
+ SubtargetFeatures);
/// Compare the priority of this object and B.
///
return *Matchers.back();
}
+void RuleMatcher::addRequiredFeature(Record *Feature) {
+ RequiredFeatures.push_back(Feature);
+}
+
template <class Kind, class... Args>
Kind &RuleMatcher::addAction(Args &&... args) {
Actions.emplace_back(llvm::make_unique<Kind>(std::forward<Args>(args)...));
Matchers.front()->emitCxxCaptureStmts(OS, *this, InsnVarName);
}
-void RuleMatcher::emit(raw_ostream &OS) {
+void RuleMatcher::emit(raw_ostream &OS,
+ std::map<Record *, SubtargetFeatureInfo, LessRecordByID>
+ SubtargetFeatures) {
if (Matchers.empty())
llvm_unreachable("Unexpected empty matcher!");
// %elt0(s32), %elt1(s32) = TGT_LOAD_PAIR %ptr
// on some targets but we don't need to make use of that yet.
assert(Matchers.size() == 1 && "Cannot handle multi-root matchers yet");
- OS << "if ([&]() {\n";
+
+ OS << "if (";
+ OS << "[&]() {\n";
+ if (!RequiredFeatures.empty()) {
+ OS << " PredicateBitset ExpectedFeatures = {";
+ StringRef Separator = "";
+ for (const auto &Predicate : RequiredFeatures) {
+ const auto &I = SubtargetFeatures.find(Predicate);
+ assert(I != SubtargetFeatures.end() && "Didn't import predicate?");
+ OS << Separator << I->second.getEnumBitName();
+ Separator = ", ";
+ }
+ OS << "};\n";
+ OS << "if ((AvailableFeatures & ExpectedFeatures) != ExpectedFeatures)\n"
+ << " return false;\n";
+ }
emitCxxCaptureStmts(OS, "I");
/// GIComplexPatternEquiv.
DenseMap<const Record *, const Record *> ComplexPatternEquivs;
+ // Map of predicates to their subtarget features.
+ std::map<Record *, SubtargetFeatureInfo, LessRecordByID> SubtargetFeatures;
+
void gatherNodeEquivs();
const CodeGenInstruction *findNodeEquiv(Record *N) const;
- Error importRulePredicates(RuleMatcher &M, ArrayRef<Init *> Predicates) const;
+ Error importRulePredicates(RuleMatcher &M, ArrayRef<Init *> Predicates);
Expected<InstructionMatcher &>
createAndImportSelDAGMatcher(InstructionMatcher &InsnMatcher,
const TreePatternNode *Src) const;
/// Analyze pattern \p P, returning a matcher for it if possible.
/// Otherwise, return an Error explaining why we don't support it.
Expected<RuleMatcher> runOnPattern(const PatternToMatch &P);
+
+ void declareSubtargetFeature(Record *Predicate);
};
void GlobalISelEmitter::gatherNodeEquivs() {
Error
GlobalISelEmitter::importRulePredicates(RuleMatcher &M,
- ArrayRef<Init *> Predicates) const {
- if (!Predicates.empty())
- return failedImport("Pattern has a rule predicate (" +
- explainRulePredicates(Predicates) + ")");
+ ArrayRef<Init *> Predicates) {
+ for (const Init *Predicate : Predicates) {
+ const DefInit *PredicateDef = static_cast<const DefInit *>(Predicate);
+ declareSubtargetFeature(PredicateDef->getDef());
+ M.addRequiredFeature(PredicateDef->getDef());
+ }
+
return Error::success();
}
for (const auto &Rule : Rules)
MaxTemporaries = std::max(MaxTemporaries, Rule.countTemporaryOperands());
+ OS << "#ifdef GET_GLOBALISEL_PREDICATE_BITSET\n"
+ << "const unsigned MAX_SUBTARGET_PREDICATES = " << SubtargetFeatures.size()
+ << ";\n"
+ << "using PredicateBitset = "
+ "PredicateBitsetImpl<MAX_SUBTARGET_PREDICATES>;\n"
+ << "#endif // ifdef GET_GLOBALISEL_PREDICATE_BITSET\n\n";
+
OS << "#ifdef GET_GLOBALISEL_TEMPORARIES_DECL\n";
for (unsigned I = 0; I < MaxTemporaries; ++I)
OS << " mutable MachineOperand TempOp" << I << ";\n";
OS << ", TempOp" << I << "(MachineOperand::CreatePlaceholder())\n";
OS << "#endif // ifdef GET_GLOBALISEL_TEMPORARIES_INIT\n\n";
- OS << "#ifdef GET_GLOBALISEL_IMPL\n"
- << "bool " << Target.getName()
+ OS << "#ifdef GET_GLOBALISEL_IMPL\n";
+ SubtargetFeatureInfo::emitSubtargetFeatureBitEnumeration(SubtargetFeatures,
+ OS);
+ SubtargetFeatureInfo::emitNameTable(SubtargetFeatures, OS);
+ SubtargetFeatureInfo::emitComputeAvailableFeatures(
+ Target.getName(), "InstructionSelector", "computeAvailableFeatures",
+ SubtargetFeatures, OS);
+
+ OS << "bool " << Target.getName()
<< "InstructionSelector::selectImpl(MachineInstr &I) const {\n"
<< " MachineFunction &MF = *I.getParent()->getParent();\n"
<< " const MachineRegisterInfo &MRI = MF.getRegInfo();\n";
for (auto &Rule : Rules) {
- Rule.emit(OS);
+ Rule.emit(OS, SubtargetFeatures);
++NumPatternEmitted;
}
<< "#endif // ifdef GET_GLOBALISEL_IMPL\n";
}
+void GlobalISelEmitter::declareSubtargetFeature(Record *Predicate) {
+ if (SubtargetFeatures.count(Predicate) == 0)
+ SubtargetFeatures.emplace(
+ Predicate, SubtargetFeatureInfo(Predicate, SubtargetFeatures.size()));
+}
+
} // end anonymous namespace
//===----------------------------------------------------------------------===//
OS << "};\n\n";
}
+void SubtargetFeatureInfo::emitSubtargetFeatureBitEnumeration(
+ std::map<Record *, SubtargetFeatureInfo, LessRecordByID> &SubtargetFeatures,
+ raw_ostream &OS) {
+ OS << "// Bits for subtarget features that participate in "
+ << "instruction matching.\n";
+ OS << "enum SubtargetFeatureBits : "
+ << getMinimalTypeForRange(SubtargetFeatures.size()) << " {\n";
+ for (const auto &SF : SubtargetFeatures) {
+ const SubtargetFeatureInfo &SFI = SF.second;
+ OS << " " << SFI.getEnumBitName() << " = " << SFI.Index << ",\n";
+ }
+ OS << "};\n\n";
+}
+
void SubtargetFeatureInfo::emitNameTable(
std::map<Record *, SubtargetFeatureInfo, LessRecordByID> &SubtargetFeatures,
raw_ostream &OS) {
StringRef TargetName, StringRef ClassName, StringRef FuncName,
std::map<Record *, SubtargetFeatureInfo, LessRecordByID> &SubtargetFeatures,
raw_ostream &OS) {
+ OS << "PredicateBitset " << TargetName << ClassName << "::\n"
+ << FuncName << "(const MachineFunction *MF, const " << TargetName
+ << "Subtarget *Subtarget) const {\n";
+ OS << " PredicateBitset Features;\n";
+ for (const auto &SF : SubtargetFeatures) {
+ const SubtargetFeatureInfo &SFI = SF.second;
+
+ OS << " if (" << SFI.TheDef->getValueAsString("CondString") << ")\n";
+ OS << " Features[" << SFI.getEnumBitName() << "] = 1;\n";
+ }
+ OS << " return Features;\n";
+ OS << "}\n\n";
+}
+
+void SubtargetFeatureInfo::emitComputeAssemblerAvailableFeatures(
+ StringRef TargetName, StringRef ClassName, StringRef FuncName,
+ std::map<Record *, SubtargetFeatureInfo, LessRecordByID> &SubtargetFeatures,
+ raw_ostream &OS) {
OS << "uint64_t " << TargetName << ClassName << "::\n"
<< FuncName << "(const FeatureBitset& FB) const {\n";
OS << " uint64_t Features = 0;\n";
return "Feature_" + TheDef->getName().str();
}
+ /// \brief The name of the enumerated constant identifying the bitnumber for
+ /// this feature.
+ std::string getEnumBitName() const {
+ return "Feature_" + TheDef->getName().str() + "Bit";
+ }
+
void dump() const;
static std::vector<std::pair<Record *, SubtargetFeatureInfo>>
getAll(const RecordKeeper &Records);
/// Emit the subtarget feature flag definitions.
+ ///
+ /// This version emits the bit value for the feature and is therefore limited
+ /// to 64 feature bits.
static void emitSubtargetFeatureFlagEnumeration(
std::map<Record *, SubtargetFeatureInfo, LessRecordByID>
&SubtargetFeatures,
raw_ostream &OS);
+ /// Emit the subtarget feature flag definitions.
+ ///
+ /// This version emits the bit index for the feature and can therefore support
+ /// more than 64 feature bits.
+ static void emitSubtargetFeatureBitEnumeration(
+ std::map<Record *, SubtargetFeatureInfo, LessRecordByID>
+ &SubtargetFeatures,
+ raw_ostream &OS);
+
static void emitNameTable(std::map<Record *, SubtargetFeatureInfo,
LessRecordByID> &SubtargetFeatures,
raw_ostream &OS);
/// Emit the function to compute the list of available features given a
/// subtarget.
///
+ /// This version is used for subtarget features defined using Predicate<>
+ /// and supports more than 64 feature bits.
+ ///
/// \param TargetName The name of the target as used in class prefixes (e.g.
/// <TargetName>Subtarget)
/// \param ClassName The name of the class (without the <Target> prefix)
std::map<Record *, SubtargetFeatureInfo, LessRecordByID>
&SubtargetFeatures,
raw_ostream &OS);
+
+ /// Emit the function to compute the list of available features given a
+ /// subtarget.
+ ///
+ /// This version is used for subtarget features defined using
+ /// AssemblerPredicate<> and supports up to 64 feature bits.
+ ///
+ /// \param TargetName The name of the target as used in class prefixes (e.g.
+ /// <TargetName>Subtarget)
+ /// \param ClassName The name of the class (without the <Target> prefix)
+ /// that will contain the generated functions.
+ /// \param FuncName The name of the function to emit.
+ /// \param SubtargetFeatures A map of TableGen records to the
+ /// SubtargetFeatureInfo equivalent.
+ static void emitComputeAssemblerAvailableFeatures(
+ StringRef TargetName, StringRef ClassName, StringRef FuncName,
+ std::map<Record *, SubtargetFeatureInfo, LessRecordByID>
+ &SubtargetFeatures,
+ raw_ostream &OS);
};
} // end namespace llvm
uint64_t MaxIndex = Size;
if (MaxIndex > 0)
MaxIndex--;
+ assert(MaxIndex <= 64 && "Too many bits");
return getMinimalTypeForRange(1ULL << MaxIndex);
}