diff --git a/clang/include/clang/Basic/UnsignedOrNone.h b/clang/include/clang/Basic/UnsignedOrNone.h index 659fd8c6487d2..e0104c8b34474 100644 --- a/clang/include/clang/Basic/UnsignedOrNone.h +++ b/clang/include/clang/Basic/UnsignedOrNone.h @@ -14,39 +14,22 @@ #ifndef LLVM_CLANG_BASIC_UNSIGNED_OR_NONE_H #define LLVM_CLANG_BASIC_UNSIGNED_OR_NONE_H -#include -#include +#include "llvm/ADT/ValueOrSentinel.h" namespace clang { -struct UnsignedOrNone { - constexpr UnsignedOrNone(std::nullopt_t) : Rep(0) {} - UnsignedOrNone(unsigned Val) : Rep(Val + 1) { assert(operator bool()); } - UnsignedOrNone(int) = delete; - - constexpr static UnsignedOrNone fromInternalRepresentation(unsigned Rep) { - return {std::nullopt, Rep}; - } - constexpr unsigned toInternalRepresentation() const { return Rep; } - - explicit constexpr operator bool() const { return Rep != 0; } - unsigned operator*() const { - assert(operator bool()); - return Rep - 1; - } - - friend constexpr bool operator==(UnsignedOrNone LHS, UnsignedOrNone RHS) { - return LHS.Rep == RHS.Rep; +namespace detail { +struct AdjustAddOne { + constexpr static unsigned toRepresentation(unsigned Value) { + return Value + 1; } - friend constexpr bool operator!=(UnsignedOrNone LHS, UnsignedOrNone RHS) { - return LHS.Rep != RHS.Rep; + constexpr static unsigned fromRepresentation(unsigned Value) { + return Value - 1; } - -private: - constexpr UnsignedOrNone(std::nullopt_t, unsigned Rep) : Rep(Rep) {}; - - unsigned Rep; }; +} // namespace detail + +using UnsignedOrNone = llvm::ValueOrSentinel; } // namespace clang diff --git a/llvm/include/llvm/ADT/ValueOrSentinel.h b/llvm/include/llvm/ADT/ValueOrSentinel.h new file mode 100644 index 0000000000000..6078a6f4b8e63 --- /dev/null +++ b/llvm/include/llvm/ADT/ValueOrSentinel.h @@ -0,0 +1,86 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This file defines the ValueOrSentinel class, which is a type akin to a +/// std::optional, but uses a sentinel rather than an additional "valid" flag. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_ADT_VALUEORSENTINEL_H +#define LLVM_ADT_VALUEORSENTINEL_H + +#include +#include +#include +#include + +namespace llvm { + +namespace detail { +/// An adjustment allows changing how the value is stored. An example use case +/// is to use zero as a sentinel value. +template struct DefaultValueAdjustment { + constexpr static T toRepresentation(T Value) { return Value; } + constexpr static T fromRepresentation(T Value) { return Value; } +}; +} // namespace detail + +template > +class ValueOrSentinel { +public: + constexpr ValueOrSentinel() = default; + constexpr ValueOrSentinel(std::nullopt_t) {}; + constexpr ValueOrSentinel(T Value) : Value(Adjust::toRepresentation(Value)) { + assert(this->Value != Sentinel && + "Value is sentinel (use default constructor)"); + }; + + constexpr ValueOrSentinel &operator=(T NewValue) { + Value = Adjust::toRepresentation(NewValue); + assert(Value != Sentinel && "NewValue is sentinel (use .clear())"); + return *this; + } + + constexpr bool operator==(ValueOrSentinel Other) const { + return Value == Other.Value; + } + constexpr bool operator!=(ValueOrSentinel Other) const { + return !(*this == Other); + } + + T value() const { + assert(has_value() && ".value() called on sentinel"); + return Adjust::fromRepresentation(Value); + } + T operator*() const { return value(); } + + explicit operator T() const { return value(); } + explicit constexpr operator bool() const { return has_value(); } + + constexpr void clear() { Value = Sentinel; } + constexpr bool has_value() const { return Value != Sentinel; } + + constexpr static ValueOrSentinel fromInternalRepresentation(T Value) { + return {std::nullopt, Value}; + } + constexpr T toInternalRepresentation() const { return Value; } + +protected: + ValueOrSentinel(std::nullopt_t, T Value) : Value(Value) {}; + + T Value{Sentinel}; +}; + +template +using ValueOrSentinelIntMax = ValueOrSentinel::max()>; + +} // namespace llvm + +#endif diff --git a/llvm/lib/Target/AArch64/AArch64FrameLowering.cpp b/llvm/lib/Target/AArch64/AArch64FrameLowering.cpp index 175b5e04d82ff..06e1b8788284d 100644 --- a/llvm/lib/Target/AArch64/AArch64FrameLowering.cpp +++ b/llvm/lib/Target/AArch64/AArch64FrameLowering.cpp @@ -3479,7 +3479,7 @@ bool AArch64FrameLowering::assignCalleeSavedSpillSlots( } Register LastReg = 0; - int HazardSlotIndex = std::numeric_limits::max(); + ValueOrSentinelIntMax HazardSlotIndex; for (auto &CS : CSI) { MCRegister Reg = CS.getReg(); const TargetRegisterClass *RC = RegInfo->getMinimalPhysRegClass(Reg); @@ -3488,16 +3488,16 @@ bool AArch64FrameLowering::assignCalleeSavedSpillSlots( if (AFI->hasStackHazardSlotIndex() && (!LastReg || !AArch64InstrInfo::isFpOrNEON(LastReg)) && AArch64InstrInfo::isFpOrNEON(Reg)) { - assert(HazardSlotIndex == std::numeric_limits::max() && + assert(!HazardSlotIndex.has_value() && "Unexpected register order for hazard slot"); HazardSlotIndex = MFI.CreateStackObject(StackHazardSize, Align(8), true); - LLVM_DEBUG(dbgs() << "Created CSR Hazard at slot " << HazardSlotIndex + LLVM_DEBUG(dbgs() << "Created CSR Hazard at slot " << *HazardSlotIndex << "\n"); - AFI->setStackHazardCSRSlotIndex(HazardSlotIndex); - if ((unsigned)HazardSlotIndex < MinCSFrameIndex) - MinCSFrameIndex = HazardSlotIndex; - if ((unsigned)HazardSlotIndex > MaxCSFrameIndex) - MaxCSFrameIndex = HazardSlotIndex; + AFI->setStackHazardCSRSlotIndex(*HazardSlotIndex); + if (static_cast(*HazardSlotIndex) < MinCSFrameIndex) + MinCSFrameIndex = *HazardSlotIndex; + if (static_cast(*HazardSlotIndex) > MaxCSFrameIndex) + MaxCSFrameIndex = *HazardSlotIndex; } unsigned Size = RegInfo->getSpillSize(*RC); @@ -3524,16 +3524,15 @@ bool AArch64FrameLowering::assignCalleeSavedSpillSlots( } // Add hazard slot in the case where no FPR CSRs are present. - if (AFI->hasStackHazardSlotIndex() && - HazardSlotIndex == std::numeric_limits::max()) { + if (AFI->hasStackHazardSlotIndex() && !HazardSlotIndex.has_value()) { HazardSlotIndex = MFI.CreateStackObject(StackHazardSize, Align(8), true); - LLVM_DEBUG(dbgs() << "Created CSR Hazard at slot " << HazardSlotIndex + LLVM_DEBUG(dbgs() << "Created CSR Hazard at slot " << *HazardSlotIndex << "\n"); - AFI->setStackHazardCSRSlotIndex(HazardSlotIndex); - if ((unsigned)HazardSlotIndex < MinCSFrameIndex) - MinCSFrameIndex = HazardSlotIndex; - if ((unsigned)HazardSlotIndex > MaxCSFrameIndex) - MaxCSFrameIndex = HazardSlotIndex; + AFI->setStackHazardCSRSlotIndex(*HazardSlotIndex); + if (static_cast(*HazardSlotIndex) < MinCSFrameIndex) + MinCSFrameIndex = *HazardSlotIndex; + if (static_cast(*HazardSlotIndex) > MaxCSFrameIndex) + MaxCSFrameIndex = *HazardSlotIndex; } return true; diff --git a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp index 5ffaf2c49b4c0..9153c470004df 100644 --- a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp +++ b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp @@ -3061,10 +3061,10 @@ AArch64TargetLowering::EmitInitTPIDR2Object(MachineInstr &MI, BuildMI(*BB, MI, MI.getDebugLoc(), TII->get(AArch64::STPXi)) .addReg(MI.getOperand(0).getReg()) .addReg(MI.getOperand(1).getReg()) - .addFrameIndex(TPIDR2.FrameIndex) + .addFrameIndex(*TPIDR2.FrameIndex) .addImm(0); } else - MFI.RemoveStackObject(TPIDR2.FrameIndex); + MFI.RemoveStackObject(*TPIDR2.FrameIndex); BB->remove_instr(&MI); return BB; @@ -9399,7 +9399,7 @@ AArch64TargetLowering::LowerCall(CallLoweringInfo &CLI, if (RequiresLazySave) { TPIDR2Object &TPIDR2 = FuncInfo->getTPIDR2Obj(); SDValue TPIDR2ObjAddr = DAG.getFrameIndex( - TPIDR2.FrameIndex, + *TPIDR2.FrameIndex, DAG.getTargetLoweringInfo().getFrameIndexTy(DAG.getDataLayout())); Chain = DAG.getNode( ISD::INTRINSIC_VOID, DL, MVT::Other, Chain, @@ -9956,7 +9956,7 @@ AArch64TargetLowering::LowerCall(CallLoweringInfo &CLI, // RESTORE_ZA pseudo. SDValue Glue; SDValue TPIDR2Block = DAG.getFrameIndex( - TPIDR2.FrameIndex, + *TPIDR2.FrameIndex, DAG.getTargetLoweringInfo().getFrameIndexTy(DAG.getDataLayout())); Result = DAG.getCopyToReg(Result, DL, AArch64::X0, TPIDR2Block, Glue); Result = diff --git a/llvm/lib/Target/AArch64/AArch64MachineFunctionInfo.h b/llvm/lib/Target/AArch64/AArch64MachineFunctionInfo.h index 993cff112ba84..43034202acd6e 100644 --- a/llvm/lib/Target/AArch64/AArch64MachineFunctionInfo.h +++ b/llvm/lib/Target/AArch64/AArch64MachineFunctionInfo.h @@ -18,6 +18,7 @@ #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/SmallPtrSet.h" #include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/ValueOrSentinel.h" #include "llvm/CodeGen/CallingConvLower.h" #include "llvm/CodeGen/MIRYamlMapping.h" #include "llvm/CodeGen/MachineFrameInfo.h" @@ -38,7 +39,7 @@ class AArch64Subtarget; class MachineInstr; struct TPIDR2Object { - int FrameIndex = std::numeric_limits::max(); + ValueOrSentinelIntMax FrameIndex; unsigned Uses = 0; }; @@ -114,8 +115,8 @@ class AArch64FunctionInfo final : public MachineFunctionInfo { /// The stack slots used to add space between FPR and GPR accesses when using /// hazard padding. StackHazardCSRSlotIndex is added between GPR and FPR CSRs. /// StackHazardSlotIndex is added between (sorted) stack objects. - int StackHazardSlotIndex = std::numeric_limits::max(); - int StackHazardCSRSlotIndex = std::numeric_limits::max(); + ValueOrSentinelIntMax StackHazardSlotIndex; + ValueOrSentinelIntMax StackHazardCSRSlotIndex; /// True if this function has a subset of CSRs that is handled explicitly via /// copies. @@ -205,7 +206,7 @@ class AArch64FunctionInfo final : public MachineFunctionInfo { bool HasSwiftAsyncContext = false; /// The stack slot where the Swift asynchronous context is stored. - int SwiftAsyncContextFrameIdx = std::numeric_limits::max(); + ValueOrSentinelIntMax SwiftAsyncContextFrameIdx; bool IsMTETagged = false; @@ -372,16 +373,16 @@ class AArch64FunctionInfo final : public MachineFunctionInfo { MaxOffset = std::max(Offset + ObjSize, MaxOffset); } - if (SwiftAsyncContextFrameIdx != std::numeric_limits::max()) { + if (SwiftAsyncContextFrameIdx.has_value()) { int64_t Offset = MFI.getObjectOffset(getSwiftAsyncContextFrameIdx()); int64_t ObjSize = MFI.getObjectSize(getSwiftAsyncContextFrameIdx()); MinOffset = std::min(Offset, MinOffset); MaxOffset = std::max(Offset + ObjSize, MaxOffset); } - if (StackHazardCSRSlotIndex != std::numeric_limits::max()) { - int64_t Offset = MFI.getObjectOffset(StackHazardCSRSlotIndex); - int64_t ObjSize = MFI.getObjectSize(StackHazardCSRSlotIndex); + if (StackHazardCSRSlotIndex.has_value()) { + int64_t Offset = MFI.getObjectOffset(*StackHazardCSRSlotIndex); + int64_t ObjSize = MFI.getObjectSize(*StackHazardCSRSlotIndex); MinOffset = std::min(Offset, MinOffset); MaxOffset = std::max(Offset + ObjSize, MaxOffset); } @@ -447,16 +448,16 @@ class AArch64FunctionInfo final : public MachineFunctionInfo { void setVarArgsFPRSize(unsigned Size) { VarArgsFPRSize = Size; } bool hasStackHazardSlotIndex() const { - return StackHazardSlotIndex != std::numeric_limits::max(); + return StackHazardSlotIndex.has_value(); } - int getStackHazardSlotIndex() const { return StackHazardSlotIndex; } + int getStackHazardSlotIndex() const { return *StackHazardSlotIndex; } void setStackHazardSlotIndex(int Index) { - assert(StackHazardSlotIndex == std::numeric_limits::max()); + assert(!StackHazardSlotIndex.has_value()); StackHazardSlotIndex = Index; } - int getStackHazardCSRSlotIndex() const { return StackHazardCSRSlotIndex; } + int getStackHazardCSRSlotIndex() const { return *StackHazardCSRSlotIndex; } void setStackHazardCSRSlotIndex(int Index) { - assert(StackHazardCSRSlotIndex == std::numeric_limits::max()); + assert(!StackHazardCSRSlotIndex.has_value()); StackHazardCSRSlotIndex = Index; } @@ -574,7 +575,9 @@ class AArch64FunctionInfo final : public MachineFunctionInfo { void setSwiftAsyncContextFrameIdx(int FI) { SwiftAsyncContextFrameIdx = FI; } - int getSwiftAsyncContextFrameIdx() const { return SwiftAsyncContextFrameIdx; } + int getSwiftAsyncContextFrameIdx() const { + return *SwiftAsyncContextFrameIdx; + } bool needsDwarfUnwindInfo(const MachineFunction &MF) const; bool needsAsyncDwarfUnwindInfo(const MachineFunction &MF) const; diff --git a/llvm/unittests/ADT/CMakeLists.txt b/llvm/unittests/ADT/CMakeLists.txt index dafd73518aedb..af79a1307f050 100644 --- a/llvm/unittests/ADT/CMakeLists.txt +++ b/llvm/unittests/ADT/CMakeLists.txt @@ -54,6 +54,7 @@ add_llvm_unittest(ADTTests LazyAtomicPointerTest.cpp MappedIteratorTest.cpp MapVectorTest.cpp + ValueOrSentinelTest.cpp PackedVectorTest.cpp PagedVectorTest.cpp PointerEmbeddedIntTest.cpp diff --git a/llvm/unittests/ADT/ValueOrSentinelTest.cpp b/llvm/unittests/ADT/ValueOrSentinelTest.cpp new file mode 100644 index 0000000000000..858fb5319e2d8 --- /dev/null +++ b/llvm/unittests/ADT/ValueOrSentinelTest.cpp @@ -0,0 +1,62 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/ADT/ValueOrSentinel.h" +#include "gtest/gtest.h" + +using namespace llvm; + +namespace { + +TEST(ValueOrSentinelTest, Basic) { + // Default constructor should equal sentinel. + ValueOrSentinelIntMax Value; + EXPECT_FALSE(Value.has_value()); + EXPECT_FALSE(bool(Value)); + + // Assignment operator. + Value = 1000; + EXPECT_TRUE(Value.has_value()); + + // .value(), operator*, implicit constructor, explicit conversion + EXPECT_EQ(Value, 1000); + EXPECT_EQ(Value.value(), 1000); + EXPECT_EQ(*Value, 1000); + EXPECT_EQ(int(Value), 1000); + + // .clear() should set value to sentinel + Value.clear(); + EXPECT_FALSE(Value.has_value()); + EXPECT_FALSE(bool(Value)); + + // construction from value, comparison operators + ValueOrSentinelIntMax OtherValue(99); + EXPECT_TRUE(OtherValue.has_value()); + EXPECT_TRUE(bool(OtherValue)); + EXPECT_EQ(OtherValue, 99); + EXPECT_NE(Value, OtherValue); + + Value = OtherValue; + EXPECT_EQ(Value, OtherValue); +} + +TEST(ValueOrSentinelTest, PointerType) { + ValueOrSentinel Value; + EXPECT_FALSE(Value.has_value()); + + int A = 10; + Value = &A; + EXPECT_TRUE(Value.has_value()); + + EXPECT_EQ(*Value.value(), 10); + + Value.clear(); + EXPECT_FALSE(Value.has_value()); +} + +} // end anonymous namespace