77#define DEBUG_TYPE "safe-stack"
81STATISTIC(NumFunctions,
"Total number of functions");
82STATISTIC(NumUnsafeStackFunctions,
"Number of functions with unsafe stack");
84 "Number of functions that use setjmp or exceptions");
87STATISTIC(NumUnsafeStaticAllocas,
"Number of unsafe static allocas");
88STATISTIC(NumUnsafeDynamicAllocas,
"Number of unsafe dynamic allocas");
89STATISTIC(NumUnsafeByValArguments,
"Number of unsafe byval arguments");
90STATISTIC(NumUnsafeStackRestorePoints,
"Number of setjmps and landingpads");
101 cl::desc(
"enable safe stack coloring"),
123 Value *UnsafeStackPtr =
nullptr;
131 static constexpr Align StackAlignment = Align::Constant<16>();
172 Value *StaticTop,
bool NeedDynamicTop);
177 void moveDynamicAllocasToUnsafeStack(
Function &F,
Value *UnsafeStackPtr,
181 bool IsSafeStackAlloca(
const Value *AllocaPtr,
uint64_t AllocaSize);
188 bool ShouldInlinePointerAddress(
CallInst &CI);
189 void TryInlinePointerAddress();
194 :
F(
F), TL(TL),
DL(
DL), DTU(DTU), SE(SE),
196 IntPtrTy(
DL.getIntPtrType(
F.getContext())),
198 Int8Ty(
Type::getInt8Ty(
F.getContext())) {}
205constexpr Align SafeStack::StackAlignment;
213 Size *=
C->getZExtValue();
220 const SCEV *AddrExpr = SE.getSCEV(
Addr);
221 const auto *
Base = dyn_cast<SCEVUnknown>(SE.getPointerBase(AddrExpr));
222 if (!
Base ||
Base->getValue() != AllocaPtr) {
224 dbgs() <<
"[SafeStack] "
225 << (isa<AllocaInst>(AllocaPtr) ?
"Alloca " :
"ByValArgument ")
226 << *AllocaPtr <<
"\n"
227 <<
"SCEV " << *AddrExpr <<
" not directly based on alloca\n");
231 const SCEV *Expr = SE.removePointerBase(AddrExpr);
239 bool Safe = AllocaRange.
contains(AccessRange);
242 dbgs() <<
"[SafeStack] "
243 << (isa<AllocaInst>(AllocaPtr) ?
"Alloca " :
"ByValArgument ")
244 << *AllocaPtr <<
"\n"
245 <<
" Access " << *
Addr <<
"\n"
247 <<
" U: " << SE.getUnsignedRange(Expr)
248 <<
", S: " << SE.getSignedRange(Expr) <<
"\n"
249 <<
" Range " << AccessRange <<
"\n"
250 <<
" AllocaRange " << AllocaRange <<
"\n"
251 <<
" " << (Safe ?
"safe" :
"unsafe") <<
"\n");
257 const Value *AllocaPtr,
259 if (
auto MTI = dyn_cast<MemTransferInst>(
MI)) {
260 if (MTI->getRawSource() != U && MTI->getRawDest() != U)
263 if (
MI->getRawDest() != U)
267 const auto *
Len = dyn_cast<ConstantInt>(
MI->getLength());
269 if (!Len)
return false;
270 return IsAccessSafe(U,
Len->getZExtValue(), AllocaPtr, AllocaSize);
276bool SafeStack::IsSafeStackAlloca(
const Value *AllocaPtr,
uint64_t AllocaSize) {
285 while (!WorkList.
empty()) {
287 for (
const Use &UI :
V->uses()) {
288 auto I = cast<const Instruction>(UI.getUser());
291 switch (
I->getOpcode()) {
292 case Instruction::Load:
293 if (!IsAccessSafe(UI,
DL.getTypeStoreSize(
I->getType()), AllocaPtr,
298 case Instruction::VAArg:
301 case Instruction::Store:
302 if (V ==
I->getOperand(0)) {
305 <<
"[SafeStack] Unsafe alloca: " << *AllocaPtr
306 <<
"\n store of address: " << *
I <<
"\n");
310 if (!IsAccessSafe(UI,
DL.getTypeStoreSize(
I->getOperand(0)->getType()),
311 AllocaPtr, AllocaSize))
315 case Instruction::Ret:
319 case Instruction::Call:
320 case Instruction::Invoke: {
323 if (
I->isLifetimeStartOrEnd())
327 if (!IsMemIntrinsicSafe(
MI, UI, AllocaPtr, AllocaSize)) {
329 <<
"[SafeStack] Unsafe alloca: " << *AllocaPtr
330 <<
"\n unsafe memintrinsic: " << *
I <<
"\n");
344 for (
const auto *
A =
B;
A !=
E; ++
A)
349 <<
"\n unsafe call: " << *
I <<
"\n");
357 WorkList.
push_back(cast<const Instruction>(
I));
367 Value *StackGuardVar = TL.getIRStackGuard(IRB);
370 if (!StackGuardVar) {
371 TL.insertSSPDeclarations(*M);
375 return IRB.
CreateLoad(StackPtrTy, StackGuardVar,
"StackGuard");
385 if (
auto AI = dyn_cast<AllocaInst>(&
I)) {
389 if (IsSafeStackAlloca(AI,
Size))
393 ++NumUnsafeStaticAllocas;
396 ++NumUnsafeDynamicAllocas;
399 }
else if (
auto RI = dyn_cast<ReturnInst>(&
I)) {
400 if (
CallInst *CI =
I.getParent()->getTerminatingMustTailCall())
404 }
else if (
auto CI = dyn_cast<CallInst>(&
I)) {
406 if (CI->getCalledFunction() && CI->canReturnTwice())
408 }
else if (
auto LP = dyn_cast<LandingPadInst>(&
I)) {
411 }
else if (
auto II = dyn_cast<IntrinsicInst>(&
I)) {
412 if (II->getIntrinsicID() == Intrinsic::gcroot)
414 "gcroot intrinsic not compatible with safestack attribute");
418 if (!Arg.hasByValAttr())
421 if (IsSafeStackAlloca(&Arg,
Size))
424 ++NumUnsafeByValArguments;
432 Value *StaticTop,
bool NeedDynamicTop) {
433 assert(StaticTop &&
"The stack top isn't set.");
435 if (StackRestorePoints.
empty())
445 if (NeedDynamicTop) {
449 "unsafe_stack_dynamic_ptr");
455 ++NumUnsafeStackRestorePoints;
459 DynamicTop ? IRB.
CreateLoad(StackPtrTy, DynamicTop) : StaticTop;
475 FailureProb.getNumerator());
481 F.getParent()->getOrInsertFunction(
"__stack_chk_fail", IRB.
getVoidTy());
482 IRBFail.CreateCall(StackChkFail, {});
488Value *SafeStack::moveStaticAllocasToUnsafeStack(
492 if (StaticAllocas.
empty() && ByValArguments.
empty())
497 StackLifetime SSC(
F, StaticAllocas, StackLifetime::LivenessType::May);
502 for (
const auto *
I : SSC.getMarkers()) {
503 auto *
Op = dyn_cast<Instruction>(
I->getOperand(1));
506 if (
Op &&
Op->use_empty())
507 Op->eraseFromParent();
512 if (StackGuardSlot) {
515 SSL.addObject(StackGuardSlot, getStaticAllocaAllocationSize(StackGuardSlot),
516 Align, SSC.getFullLiveRange());
519 for (
Argument *Arg : ByValArguments) {
520 Type *Ty = Arg->getParamByValType();
527 if (
auto A = Arg->getParamAlign())
529 SSL.addObject(Arg,
Size,
Align, SSC.getFullLiveRange());
542 ClColoring ? SSC.getLiveRange(AI) : NoColoringRange);
546 Align FrameAlignment = SSL.getFrameAlignment();
550 if (FrameAlignment > StackAlignment) {
562 if (StackGuardSlot) {
563 unsigned Offset = SSL.getObjectOffset(StackGuardSlot);
574 for (
Argument *Arg : ByValArguments) {
575 unsigned Offset = SSL.getObjectOffset(Arg);
577 Type *Ty = Arg->getParamByValType();
586 Arg->getName() +
".unsafe-byval");
591 Arg->replaceAllUsesWith(NewArg);
599 unsigned Offset = SSL.getObjectOffset(AI);
606 std::string
Name = std::string(AI->
getName()) +
".unsafe";
612 if (
auto *
PHI = dyn_cast<PHINode>(
User))
613 InsertBefore =
PHI->getIncomingBlock(U)->getTerminator();
618 Value *
Off = IRBUser.CreateGEP(Int8Ty, BasePointer,
622 if (
auto *
PHI = dyn_cast<PHINode>(
User))
625 PHI->setIncomingValueForBlock(
PHI->getIncomingBlock(U), Replacement);
636 unsigned FrameSize =
alignTo(SSL.getFrameSize(), StackAlignment);
640 Data.push_back(MDB.createString(
"unsafe-stack-size"));
643 F.setMetadata(LLVMContext::MD_annotation, MD);
650 "unsafe_stack_static_top");
655void SafeStack::moveDynamicAllocasToUnsafeStack(
665 if (ArraySize->
getType() != IntPtrTy)
677 auto Align = std::max(std::max(
DL.getPrefTypeAlign(Ty), AI->
getAlign()),
691 if (AI->
hasName() && isa<Instruction>(NewAI))
699 if (!DynamicAllocas.empty()) {
702 auto *II = dyn_cast<IntrinsicInst>(&
I);
706 if (II->getIntrinsicID() == Intrinsic::stacksave) {
710 II->replaceAllUsesWith(LI);
711 II->eraseFromParent();
712 }
else if (II->getIntrinsicID() == Intrinsic::stackrestore) {
717 II->eraseFromParent();
723bool SafeStack::ShouldInlinePointerAddress(
CallInst &CI) {
725 if (CI.
hasFnAttr(Attribute::AlwaysInline) &&
728 if (
Callee->isInterposable() ||
Callee->hasFnAttribute(Attribute::NoInline) ||
734void SafeStack::TryInlinePointerAddress() {
735 auto *CI = dyn_cast<CallInst>(UnsafeStackPtr);
743 if (!Callee ||
Callee->isDeclaration())
746 if (!ShouldInlinePointerAddress(*CI))
753bool SafeStack::run() {
754 assert(
F.hasFnAttribute(Attribute::SafeStack) &&
755 "Can't run SafeStack on a function without the attribute");
756 assert(!
F.isDeclaration() &&
"Can't run SafeStack on a function declaration");
774 findInsts(
F, StaticAllocas, DynamicAllocas, ByValArguments, Returns,
777 if (StaticAllocas.
empty() && DynamicAllocas.
empty() &&
778 ByValArguments.
empty() && StackRestorePoints.
empty())
781 if (!StaticAllocas.
empty() || !DynamicAllocas.
empty() ||
782 !ByValArguments.
empty())
783 ++NumUnsafeStackFunctions;
785 if (!StackRestorePoints.
empty())
786 ++NumUnsafeStackRestorePointsFunctions;
788 IRBuilder<> IRB(&
F.front(),
F.begin()->getFirstInsertionPt());
793 DILocation::get(SP->
getContext(), SP->getScopeLine(), 0, SP));
796 "__safestack_pointer_address", StackPtrTy->getPointerTo(0));
799 UnsafeStackPtr = TL.getSafeStackPointerLocation(IRB);
805 IRB.
CreateLoad(StackPtrTy, UnsafeStackPtr,
false,
"unsafe_stack_ptr");
810 if (
F.hasFnAttribute(Attribute::StackProtect) ||
811 F.hasFnAttribute(Attribute::StackProtectStrong) ||
812 F.hasFnAttribute(Attribute::StackProtectReq)) {
819 checkStackGuard(IRBRet,
F, *RI, StackGuardSlot, StackGuard);
825 Value *StaticTop = moveStaticAllocasToUnsafeStack(
826 IRB,
F, StaticAllocas, ByValArguments, BasePointer, StackGuardSlot);
834 AllocaInst *DynamicTop = createStackRestorePoints(
835 IRB,
F, StackRestorePoints, StaticTop, !DynamicAllocas.
empty());
838 moveDynamicAllocasToUnsafeStack(
F, UnsafeStackPtr, DynamicTop,
847 TryInlinePointerAddress();
873 if (!
F.hasFnAttribute(Attribute::SafeStack)) {
875 " for this function\n");
879 if (
F.isDeclaration()) {
881 " is not available\n");
886 auto *TL =
TM->getSubtargetImpl(
F)->getTargetLowering();
890 auto *
DL = &
F.getParent()->getDataLayout();
891 auto &TLI = getAnalysis<TargetLibraryInfoWrapperPass>().getTLI(
F);
892 auto &ACT = getAnalysis<AssumptionCacheTracker>().getAssumptionCache(
F);
899 bool ShouldPreserveDominatorTree;
900 std::optional<DominatorTree> LazilyComputedDomTree;
905 if (
auto *DTWP = getAnalysisIfAvailable<DominatorTreeWrapperPass>()) {
906 DT = &DTWP->getDomTree();
907 ShouldPreserveDominatorTree =
true;
910 LazilyComputedDomTree.emplace(
F);
911 DT = &*LazilyComputedDomTree;
912 ShouldPreserveDominatorTree =
false;
922 return SafeStack(
F, *TL, *
DL, ShouldPreserveDominatorTree ? &DTU :
nullptr,
930char SafeStackLegacyPass::ID = 0;
933 "Safe Stack instrumentation pass",
false,
false)
MachineBasicBlock MachineBasicBlock::iterator DebugLoc DL
This file implements a class to represent arbitrary precision integral constant values and operations...
This file contains the simple types necessary to represent the attributes associated with functions a...
static GCRegistry::Add< OcamlGC > B("ocaml", "ocaml 3.10-compatible GC")
static GCRegistry::Add< ErlangGC > A("erlang", "erlang-compatible garbage collector")
static GCRegistry::Add< CoreCLRGC > E("coreclr", "CoreCLR-compatible GC")
This file contains the declarations for the subclasses of Constant, which represent the different fla...
Select target instructions out of generic instructions
Module.h This file contains the declarations for the Module class.
const char LLVMTargetMachineRef TM
#define INITIALIZE_PASS_DEPENDENCY(depName)
#define INITIALIZE_PASS_END(passName, arg, name, cfg, analysis)
#define INITIALIZE_PASS_BEGIN(passName, arg, name, cfg, analysis)
assert(ImpDefSCC.getReg()==AMDGPU::SCC &&ImpDefSCC.isDef())
static cl::opt< bool > SafeStackUsePointerAddress("safestack-use-pointer-address", cl::init(false), cl::Hidden)
Use __safestack_pointer_address even if the platform has a faster way of access safe stack pointer.
static cl::opt< bool > ClColoring("safe-stack-coloring", cl::desc("enable safe stack coloring"), cl::Hidden, cl::init(true))
Safe Stack instrumentation pass
This file defines the SmallPtrSet class.
This file defines the SmallVector class.
static Value * getStackGuard(const TargetLoweringBase *TLI, Module *M, IRBuilder<> &B, bool *SupportsSelectionDAGSP=nullptr)
Create a stack guard loading and populate whether SelectionDAG SSP is supported.
This file defines the 'Statistic' class, which is designed to be an easy way to expose various metric...
#define STATISTIC(VARNAME, DESC)
This file describes how to lower LLVM code to machine code.
Target-Independent Code Generator Pass Configuration Options pass.
This defines the Use class.
Class for arbitrary precision integers.
an instruction to allocate memory on the stack
bool isStaticAlloca() const
Return true if this alloca is in the entry block of the function and is a constant size.
Align getAlign() const
Return the alignment of the memory that is being allocated by the instruction.
PointerType * getType() const
Overload to return most specific pointer type.
Type * getAllocatedType() const
Return the type that is being allocated by the instruction.
bool isArrayAllocation() const
Return true if there is an allocation size parameter to the allocation instruction that is not 1.
const Value * getArraySize() const
Get the number of elements allocated.
Represent the analysis usage information of a pass.
AnalysisUsage & addRequired()
AnalysisUsage & addPreserved()
Add the specified Pass class to the set of analyses preserved by this pass.
This class represents an incoming formal argument to a Function.
ArrayRef - Represent a constant reference to an array (0 or more elements consecutively in memory),...
bool empty() const
empty - Check if the array is empty.
An immutable pass that tracks lazily created AssumptionCache objects.
static BranchProbability getBranchProbStackProtector(bool IsLikely)
Base class for all callable instructions (InvokeInst and CallInst) Holds everything related to callin...
bool doesNotCapture(unsigned OpNo) const
Determine whether this data operand is not captured.
Function * getCalledFunction() const
Returns the function called, or null if this is an indirect function invocation or the function signa...
bool doesNotAccessMemory(unsigned OpNo) const
bool hasFnAttr(Attribute::AttrKind Kind) const
Determine whether this call has the given attribute.
User::op_iterator arg_begin()
Return the iterator pointing to the beginning of the argument list.
bool isNoInline() const
Return true if the call should not be inlined.
User::op_iterator arg_end()
Return the iterator pointing to the end of the argument list.
This class represents a function call, abstracting a target machine's calling convention.
static Constant * get(Type *Ty, uint64_t V, bool IsSigned=false)
If Ty is a vector type, return a Constant with a splat of the given value.
This class represents a range of values.
ConstantRange add(const ConstantRange &Other) const
Return a new range representing the possible values resulting from an addition of a value in this ran...
bool contains(const APInt &Val) const
Return true if the specified value is in the set.
This class represents an Operation in the Expression.
A parsed version of the target data layout string in and methods for querying it.
Legacy analysis pass which computes a DominatorTree.
Concrete subclass of DominatorTreeBase that is used to compute a normal dominator tree.
A handy container for a FunctionType+Callee-pointer pair, which can be passed around as a single enti...
FunctionPass class - This class is used to implement most global optimizations.
virtual bool runOnFunction(Function &F)=0
runOnFunction - Virtual method overriden by subclasses to do the per-function processing of the pass.
AllocaInst * CreateAlloca(Type *Ty, unsigned AddrSpace, Value *ArraySize=nullptr, const Twine &Name="")
Value * CreatePointerCast(Value *V, Type *DestTy, const Twine &Name="")
Value * CreateIntToPtr(Value *V, Type *DestTy, const Twine &Name="")
void SetCurrentDebugLocation(DebugLoc L)
Set location information used by debugging information.
Value * CreateICmpNE(Value *LHS, Value *RHS, const Twine &Name="")
Value * CreateSub(Value *LHS, Value *RHS, const Twine &Name="", bool HasNUW=false, bool HasNSW=false)
Value * CreateBitCast(Value *V, Type *DestTy, const Twine &Name="")
LoadInst * CreateLoad(Type *Ty, Value *Ptr, const char *Name)
Provided to resolve 'CreateLoad(Ty, Ptr, "...")' correctly, instead of converting the string to 'bool...
Value * CreateAnd(Value *LHS, Value *RHS, const Twine &Name="")
StoreInst * CreateStore(Value *Val, Value *Ptr, bool isVolatile=false)
Value * CreatePtrToInt(Value *V, Type *DestTy, const Twine &Name="")
Value * CreateIntCast(Value *V, Type *DestTy, bool isSigned, const Twine &Name="")
void SetInsertPoint(BasicBlock *TheBB)
This specifies that created instructions should be appended to the end of the specified block.
Type * getVoidTy()
Fetch the type representing void.
CallInst * CreateCall(FunctionType *FTy, Value *Callee, ArrayRef< Value * > Args=std::nullopt, const Twine &Name="", MDNode *FPMathTag=nullptr)
Value * CreateGEP(Type *Ty, Value *Ptr, ArrayRef< Value * > IdxList, const Twine &Name="", bool IsInBounds=false)
CallInst * CreateMemCpy(Value *Dst, MaybeAlign DstAlign, Value *Src, MaybeAlign SrcAlign, uint64_t Size, bool isVolatile=false, MDNode *TBAATag=nullptr, MDNode *TBAAStructTag=nullptr, MDNode *ScopeTag=nullptr, MDNode *NoAliasTag=nullptr)
Create and insert a memcpy between the specified pointers.
Value * CreateMul(Value *LHS, Value *RHS, const Twine &Name="", bool HasNUW=false, bool HasNSW=false)
This provides a uniform API for creating instructions and inserting them into a basic block: either a...
This class captures the data input to the InlineFunction call, and records the auxiliary results prod...
InstListType::iterator eraseFromParent()
This method unlinks 'this' from the containing basic block and deletes it.
A wrapper class for inspecting calls to intrinsic functions.
MDNode * createBranchWeights(uint32_t TrueWeight, uint32_t FalseWeight)
Return metadata containing two branch weights.
static MDTuple * get(LLVMContext &Context, ArrayRef< Metadata * > MDs)
This is the common base class for memset/memcpy/memmove.
A Module instance is used to store all the information related to an LLVM module.
static PassRegistry * getPassRegistry()
getPassRegistry - Access the global registry object, which is automatically initialized at applicatio...
virtual void getAnalysisUsage(AnalysisUsage &) const
getAnalysisUsage - This function should be overriden by passes that need analysis information to do t...
This class represents an analyzed expression in the program.
Type * getType() const
Return the LLVM type of this SCEV expression.
The main scalar evolution driver.
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.
This class consists of common code factored out of the SmallVector class to reduce code duplication b...
void push_back(const T &Elt)
This is a 'vector' (really, a variable-sized array), optimized for the case when the array is small.
This class represents a set of interesting instructions where an alloca is live.
Compute live ranges of allocas.
This base class for TargetLowering contains the SelectionDAG-independent parts that can be used from ...
Primary interface to the complete machine description for the target machine.
Target-Independent Code Generator Pass Configuration Options.
The instances of the Type class are immutable: once they are created, they are never changed.
A Use represents the edge between a Value definition and its users.
LLVM Value Representation.
Type * getType() const
All values are typed, get the type of this value.
void replaceAllUsesWith(Value *V)
Change all uses of this to point to a new Value.
LLVMContext & getContext() const
All values hold a context through their type.
StringRef getName() const
Return a constant reference to the value's name.
void takeName(Value *V)
Transfer the name from V to this value.
NodeTy * getNextNode()
Get the next node, or nullptr for the list tail.
Compute the layout of an unsafe stack frame.
unsigned ID
LLVM IR allows to use arbitrary numbers as calling convention identifiers.
@ C
The default llvm calling convention, compatible with C.
Function * getDeclaration(Module *M, ID id, ArrayRef< Type * > Tys=std::nullopt)
Create or insert an LLVM Function declaration for an intrinsic, and return it.
initializer< Ty > init(const Ty &Val)
PointerTypeMap run(const Module &M)
Compute the PointerTypeMap for the module M.
This is an optimization pass for GlobalISel generic memory operations.
FunctionPass * createSafeStackPass()
This pass splits the stack into a safe stack and an unsafe stack to protect against stack-based overf...
iterator_range< early_inc_iterator_impl< detail::IterOfRange< RangeT > > > make_early_inc_range(RangeT &&Range)
Make a range that does early increment to allow mutation of the underlying range without disrupting i...
InlineResult isInlineViable(Function &Callee)
Minimal filter to detect invalid constructs for inlining.
raw_ostream & dbgs()
dbgs() - This returns a reference to a raw_ostream for debugging messages.
void report_fatal_error(Error Err, bool gen_crash_diag=true)
Report a serious error, calling any installed error handler.
uint64_t alignTo(uint64_t Size, Align A)
Returns a multiple of A needed to store Size bytes.
void initializeSafeStackLegacyPassPass(PassRegistry &)
InlineResult InlineFunction(CallBase &CB, InlineFunctionInfo &IFI, bool MergeAttributes=false, AAResults *CalleeAAR=nullptr, bool InsertLifetime=true, Function *ForwardVarArgsTo=nullptr)
This function inlines the called function into the basic block of the caller.
void replaceDbgValueForAlloca(AllocaInst *AI, Value *NewAllocaAddress, DIBuilder &Builder, int Offset=0)
Replaces multiple llvm.dbg.value instructions when the alloca it describes is replaced with a new val...
constexpr unsigned BitWidth
Instruction * SplitBlockAndInsertIfThen(Value *Cond, BasicBlock::iterator SplitBefore, bool Unreachable, MDNode *BranchWeights=nullptr, DomTreeUpdater *DTU=nullptr, LoopInfo *LI=nullptr, BasicBlock *ThenBlock=nullptr)
Split the containing block at the specified instruction - everything before SplitBefore stays in the ...
bool replaceDbgDeclare(Value *Address, Value *NewAddress, DIBuilder &Builder, uint8_t DIExprFlags, int Offset)
Replaces llvm.dbg.declare instruction when the address it describes is replaced with a new value.
This struct is a compact representation of a valid (non-zero power of two) alignment.
uint64_t value() const
This is a hole in the type system and should not be abused.
This struct is a compact representation of a valid (power of two) or undefined (0) alignment.