Skip to content
37 changes: 10 additions & 27 deletions clang/include/clang/Basic/UnsignedOrNone.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,39 +14,22 @@
#ifndef LLVM_CLANG_BASIC_UNSIGNED_OR_NONE_H
#define LLVM_CLANG_BASIC_UNSIGNED_OR_NONE_H

#include <cassert>
#include <optional>
#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<unsigned, 0, detail::AdjustAddOne>;

} // namespace clang

Expand Down
86 changes: 86 additions & 0 deletions llvm/include/llvm/ADT/ValueOrSentinel.h
Original file line number Diff line number Diff line change
@@ -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 <cassert>
#include <limits>
#include <optional>
#include <utility>

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 <typename T> struct DefaultValueAdjustment {
constexpr static T toRepresentation(T Value) { return Value; }
constexpr static T fromRepresentation(T Value) { return Value; }
};
} // namespace detail

template <typename T, T Sentinel,
typename Adjust = detail::DefaultValueAdjustment<T>>
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 <typename T>
using ValueOrSentinelIntMax = ValueOrSentinel<T, std::numeric_limits<T>::max()>;

} // namespace llvm

#endif
31 changes: 15 additions & 16 deletions llvm/lib/Target/AArch64/AArch64FrameLowering.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3479,7 +3479,7 @@ bool AArch64FrameLowering::assignCalleeSavedSpillSlots(
}

Register LastReg = 0;
int HazardSlotIndex = std::numeric_limits<int>::max();
ValueOrSentinelIntMax<int> HazardSlotIndex;
for (auto &CS : CSI) {
MCRegister Reg = CS.getReg();
const TargetRegisterClass *RC = RegInfo->getMinimalPhysRegClass(Reg);
Expand All @@ -3488,16 +3488,16 @@ bool AArch64FrameLowering::assignCalleeSavedSpillSlots(
if (AFI->hasStackHazardSlotIndex() &&
(!LastReg || !AArch64InstrInfo::isFpOrNEON(LastReg)) &&
AArch64InstrInfo::isFpOrNEON(Reg)) {
assert(HazardSlotIndex == std::numeric_limits<int>::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<unsigned>(*HazardSlotIndex) < MinCSFrameIndex)
MinCSFrameIndex = *HazardSlotIndex;
if (static_cast<unsigned>(*HazardSlotIndex) > MaxCSFrameIndex)
MaxCSFrameIndex = *HazardSlotIndex;
}

unsigned Size = RegInfo->getSpillSize(*RC);
Expand All @@ -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<int>::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<unsigned>(*HazardSlotIndex) < MinCSFrameIndex)
MinCSFrameIndex = *HazardSlotIndex;
if (static_cast<unsigned>(*HazardSlotIndex) > MaxCSFrameIndex)
MaxCSFrameIndex = *HazardSlotIndex;
}

return true;
Expand Down
8 changes: 4 additions & 4 deletions llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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 =
Expand Down
31 changes: 17 additions & 14 deletions llvm/lib/Target/AArch64/AArch64MachineFunctionInfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -38,7 +39,7 @@ class AArch64Subtarget;
class MachineInstr;

struct TPIDR2Object {
int FrameIndex = std::numeric_limits<int>::max();
ValueOrSentinelIntMax<int> FrameIndex;
unsigned Uses = 0;
};

Expand Down Expand Up @@ -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<int>::max();
int StackHazardCSRSlotIndex = std::numeric_limits<int>::max();
ValueOrSentinelIntMax<int> StackHazardSlotIndex;
ValueOrSentinelIntMax<int> StackHazardCSRSlotIndex;

/// True if this function has a subset of CSRs that is handled explicitly via
/// copies.
Expand Down Expand Up @@ -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<int>::max();
ValueOrSentinelIntMax<int> SwiftAsyncContextFrameIdx;

bool IsMTETagged = false;

Expand Down Expand Up @@ -372,16 +373,16 @@ class AArch64FunctionInfo final : public MachineFunctionInfo {
MaxOffset = std::max<int64_t>(Offset + ObjSize, MaxOffset);
}

if (SwiftAsyncContextFrameIdx != std::numeric_limits<int>::max()) {
if (SwiftAsyncContextFrameIdx.has_value()) {
int64_t Offset = MFI.getObjectOffset(getSwiftAsyncContextFrameIdx());
int64_t ObjSize = MFI.getObjectSize(getSwiftAsyncContextFrameIdx());
MinOffset = std::min<int64_t>(Offset, MinOffset);
MaxOffset = std::max<int64_t>(Offset + ObjSize, MaxOffset);
}

if (StackHazardCSRSlotIndex != std::numeric_limits<int>::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<int64_t>(Offset, MinOffset);
MaxOffset = std::max<int64_t>(Offset + ObjSize, MaxOffset);
}
Expand Down Expand Up @@ -447,16 +448,16 @@ class AArch64FunctionInfo final : public MachineFunctionInfo {
void setVarArgsFPRSize(unsigned Size) { VarArgsFPRSize = Size; }

bool hasStackHazardSlotIndex() const {
return StackHazardSlotIndex != std::numeric_limits<int>::max();
return StackHazardSlotIndex.has_value();
}
int getStackHazardSlotIndex() const { return StackHazardSlotIndex; }
int getStackHazardSlotIndex() const { return *StackHazardSlotIndex; }
void setStackHazardSlotIndex(int Index) {
assert(StackHazardSlotIndex == std::numeric_limits<int>::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<int>::max());
assert(!StackHazardCSRSlotIndex.has_value());
StackHazardCSRSlotIndex = Index;
}

Expand Down Expand Up @@ -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;
Expand Down
1 change: 1 addition & 0 deletions llvm/unittests/ADT/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ add_llvm_unittest(ADTTests
LazyAtomicPointerTest.cpp
MappedIteratorTest.cpp
MapVectorTest.cpp
ValueOrSentinelTest.cpp
PackedVectorTest.cpp
PagedVectorTest.cpp
PointerEmbeddedIntTest.cpp
Expand Down
62 changes: 62 additions & 0 deletions llvm/unittests/ADT/ValueOrSentinelTest.cpp
Original file line number Diff line number Diff line change
@@ -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<int> 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<int> 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<int *, nullptr> 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
Loading