Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 75 additions & 0 deletions llvm/include/llvm/ADT/ValueWithSentinel.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
//===----------------------------------------------------------------------===//
//
// 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 ValueWithSentinel class, which is a type akin to a
/// std::optional, but uses a sentinel rather than an additional "valid" flag.
///
//===----------------------------------------------------------------------===//

#ifndef LLVM_ADT_VALUEWITHSENTINEL_H
#define LLVM_ADT_VALUEWITHSENTINEL_H

#include <cassert>
#include <limits>
#include <utility>

namespace llvm {

template <typename T, T Sentinel> class ValueWithSentinel {
public:
ValueWithSentinel() = default;

ValueWithSentinel(T Value) : Value(std::move(Value)) {
assert(Value != Sentinel && "Value is sentinel (use default constructor)");
};

ValueWithSentinel &operator=(T const &NewValue) {
assert(NewValue != Sentinel && "Assigned to sentinel (use .clear())");
Value = NewValue;
return *this;
}

bool operator==(ValueWithSentinel const &Other) const {
return Value == Other.Value;
}

bool operator!=(ValueWithSentinel const &Other) const {
return !(*this == Other);
}

T &operator*() {
assert(has_value() && "Invalid value");
return Value;
}
const T &operator*() const {
return const_cast<ValueWithSentinel &>(*this).operator*();
}

T *operator->() { return &operator*(); }
T const *operator->() const { return &operator*(); }

T &value() { return operator*(); }
T const &value() const { return operator*(); }

bool has_value() const { return Value != Sentinel; }
explicit operator bool() const { return has_value(); }

void clear() { Value = Sentinel; }

private:
T Value{Sentinel};
};

template <typename T>
using ValueWithSentinelNumericMax =
ValueWithSentinel<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();
ValueWithSentinelNumericMax<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 ((unsigned)*HazardSlotIndex < MinCSFrameIndex)
MinCSFrameIndex = *HazardSlotIndex;
if ((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 ((unsigned)*HazardSlotIndex < MinCSFrameIndex)
MinCSFrameIndex = *HazardSlotIndex;
if ((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/ValueWithSentinel.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();
ValueWithSentinelNumericMax<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();
ValueWithSentinelNumericMax<int> StackHazardSlotIndex;
ValueWithSentinelNumericMax<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();
ValueWithSentinelNumericMax<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 @@ -93,6 +93,7 @@ add_llvm_unittest(ADTTests
TwineTest.cpp
TypeSwitchTest.cpp
TypeTraitsTest.cpp
ValueWithSentinelTest.cpp
)

target_link_libraries(ADTTests PRIVATE LLVMTestingSupport)
Expand Down
37 changes: 37 additions & 0 deletions llvm/unittests/ADT/ValueWithSentinelTest.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
//===----------------------------------------------------------------------===//
//
// 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/ValueWithSentinel.h"
#include "gtest/gtest.h"

using namespace llvm;

namespace {

TEST(ValueWithSentinelTest, Basic) {
ValueWithSentinelNumericMax<int> Value;
EXPECT_FALSE(Value.has_value());

Value = 1000;
EXPECT_TRUE(Value.has_value());

EXPECT_EQ(Value, 1000);
EXPECT_EQ(Value.value(), 1000);

Value.clear();
EXPECT_FALSE(Value.has_value());

ValueWithSentinelNumericMax<int> OtherValue(99);
EXPECT_TRUE(OtherValue.has_value());
EXPECT_NE(Value, OtherValue);

Value = OtherValue;
EXPECT_EQ(Value, OtherValue);
}

} // end anonymous namespace
Loading