39#define DEBUG_TYPE "arm64eccalllowering"
41STATISTIC(Arm64ECCallsLowered,
"Number of Arm64EC calls lowered");
50enum ThunkArgTranslation :
uint8_t {
59 ThunkArgTranslation Translation;
62class AArch64Arm64ECCallLowering :
public ModulePass {
78 int cfguard_module_flag = 0;
83 Constant *DispatchFnGlobal =
nullptr;
106 ThunkArgInfo canonicalizeThunkType(
Type *
T,
Align Alignment,
bool Ret,
112void AArch64Arm64ECCallLowering::getThunkType(
116 Out << (
TT == Arm64ECThunkType::Entry ?
"$ientry_thunk$cdecl$"
117 :
"$iexit_thunk$cdecl$");
128 if (TT == Arm64ECThunkType::Exit)
132 bool HasSretPtr =
false;
133 getThunkRetType(FT, AttrList, Out, Arm64RetTy, X64RetTy, Arm64ArgTypes,
134 X64ArgTypes, ArgTranslations, HasSretPtr);
136 getThunkArgTypes(FT, AttrList, TT, Out, Arm64ArgTypes, X64ArgTypes,
137 ArgTranslations, HasSretPtr);
139 Arm64Ty = FunctionType::get(Arm64RetTy, Arm64ArgTypes,
false);
141 X64Ty = FunctionType::get(X64RetTy, X64ArgTypes,
false);
144void AArch64Arm64ECCallLowering::getThunkArgTypes(
151 if (FT->isVarArg()) {
174 for (
int i = HasSretPtr ? 1 : 0; i < 4; i++) {
177 ArgTranslations.
push_back(ThunkArgTranslation::Direct);
183 ArgTranslations.
push_back(ThunkArgTranslation::Direct);
186 if (TT != Arm64ECThunkType::Entry) {
190 ArgTranslations.
push_back(ThunkArgTranslation::Direct);
199 if (
I == FT->getNumParams()) {
204 for (
unsigned E = FT->getNumParams();
I != E; ++
I) {
208 uint64_t ArgSizeBytes = AttrList.getParamArm64ECArgSizeBytes(
I);
214 auto [Arm64Ty, X64Ty, ArgTranslation] =
215 canonicalizeThunkType(FT->getParamType(
I), ParamAlign,
216 false, ArgSizeBytes, Out);
219 ArgTranslations.
push_back(ArgTranslation);
223void AArch64Arm64ECCallLowering::getThunkRetType(
228 Type *
T = FT->getReturnType();
232 uint64_t ArgSizeBytes = AttrList.getRetArm64ECArgSizeBytes();
234 int64_t ArgSizeBytes = 0;
237 if (FT->getNumParams()) {
241 if (FT->getNumParams() > 1) {
245 SRetAttr1 = AttrList.
getParamAttr(1, Attribute::StructRet);
246 InRegAttr1 = AttrList.
getParamAttr(1, Attribute::InReg);
269 canonicalizeThunkType(SRetType, SRetAlign,
true, ArgSizeBytes,
273 Arm64ArgTypes.
push_back(FT->getParamType(0));
274 X64ArgTypes.
push_back(FT->getParamType(0));
275 ArgTranslations.
push_back(ThunkArgTranslation::Direct);
288 canonicalizeThunkType(
T,
Align(),
true, ArgSizeBytes, Out);
289 Arm64RetTy =
info.Arm64Ty;
290 X64RetTy =
info.X64Ty;
300ThunkArgInfo AArch64Arm64ECCallLowering::canonicalizeThunkType(
304 auto direct = [](
Type *
T) {
305 return ThunkArgInfo{
T,
T, ThunkArgTranslation::Direct};
308 auto bitcast = [
this](
Type *Arm64Ty,
uint64_t SizeInBytes) {
309 return ThunkArgInfo{Arm64Ty,
311 ThunkArgTranslation::Bitcast};
314 auto pointerIndirection = [
this](
Type *Arm64Ty) {
315 return ThunkArgInfo{Arm64Ty, PtrTy,
316 ThunkArgTranslation::PointerIndirection};
325 if (
T->isFloatTy()) {
330 if (
T->isDoubleTy()) {
335 if (
T->isFloatingPointTy()) {
337 "for ARM64EC thunks");
340 auto &
DL =
M->getDataLayout();
342 if (
auto *StructTy = dyn_cast<StructType>(
T))
343 if (StructTy->getNumElements() == 1)
344 T = StructTy->getElementType(0);
346 if (
T->isArrayTy()) {
347 Type *ElementTy =
T->getArrayElementType();
348 uint64_t ElementCnt =
T->getArrayNumElements();
349 uint64_t ElementSizePerBytes =
DL.getTypeSizeInBits(ElementTy) / 8;
350 uint64_t TotalSizeBytes = ElementCnt * ElementSizePerBytes;
360 Out << TotalSizeBytes;
361 if (Alignment.
value() >= 16 && !Ret)
362 Out <<
"a" << Alignment.
value();
363 if (TotalSizeBytes <= 8) {
366 return bitcast(
T, TotalSizeBytes);
369 return pointerIndirection(
T);
371 }
else if (
T->isFloatingPointTy()) {
373 "Only 16, 32, and 64 bit floating points are supported "
374 "for ARM64EC thunks");
378 if ((
T->isIntegerTy() ||
T->isPointerTy()) &&
DL.getTypeSizeInBits(
T) <= 64) {
380 return direct(I64Ty);
389 if (Alignment.
value() >= 16 && !Ret)
390 Out <<
"a" << Alignment.
value();
397 return pointerIndirection(
T);
409 getThunkType(FT, Attrs, Arm64ECThunkType::Exit, ExitThunkStream, Arm64Ty,
410 X64Ty, ArgTranslations);
411 if (
Function *
F =
M->getFunction(ExitThunkName))
417 F->setSection(
".wowthk$aa");
418 F->setComdat(
M->getOrInsertComdat(ExitThunkName));
420 F->addFnAttr(
"frame-pointer",
"all");
424 if (FT->getNumParams()) {
425 auto SRet =
Attrs.getParamAttr(0, Attribute::StructRet);
426 auto InReg =
Attrs.getParamAttr(0, Attribute::InReg);
427 if (SRet.isValid() && !InReg.isValid())
428 F->addParamAttr(1, SRet);
435 M->getOrInsertGlobal(
"__os_arm64x_dispatch_call_no_redirect", PtrTy);
437 auto &
DL =
M->getDataLayout();
441 auto X64TyOffset = 1;
442 Args.push_back(
F->arg_begin());
445 if (
RetTy != X64Ty->getReturnType()) {
449 if (
DL.getTypeStoreSize(
RetTy) > 8) {
457 make_range(X64Ty->param_begin() + X64TyOffset, X64Ty->param_end()),
473 if (ArgTranslation != ThunkArgTranslation::Direct) {
474 Value *Mem = IRB.CreateAlloca(Arg.getType());
475 IRB.CreateStore(&Arg, Mem);
476 if (ArgTranslation == ThunkArgTranslation::Bitcast) {
478 Args.push_back(IRB.CreateLoad(IntTy, Mem));
480 assert(ArgTranslation == ThunkArgTranslation::PointerIndirection);
484 Args.push_back(&Arg);
494 if (
RetTy != X64Ty->getReturnType()) {
497 if (
DL.getTypeStoreSize(
RetTy) > 8) {
498 RetVal = IRB.CreateLoad(
RetTy, Args[1]);
501 IRB.CreateStore(Call, CastAlloca);
502 RetVal = IRB.CreateLoad(
RetTy, CastAlloca);
506 if (
RetTy->isVoidTy())
509 IRB.CreateRet(RetVal);
520 getThunkType(
F->getFunctionType(),
F->getAttributes(),
521 Arm64ECThunkType::Entry, EntryThunkStream, Arm64Ty, X64Ty,
523 if (
Function *
F =
M->getFunction(EntryThunkName))
529 Thunk->setSection(
".wowthk$aa");
530 Thunk->setComdat(
M->getOrInsertComdat(EntryThunkName));
532 Thunk->addFnAttr(
"frame-pointer",
"all");
538 Type *X64RetType = X64Ty->getReturnType();
540 bool TransformDirectToSRet = X64RetType->
isVoidTy() && !
RetTy->isVoidTy();
541 unsigned ThunkArgOffset = TransformDirectToSRet ? 2 : 1;
542 unsigned PassthroughArgSize =
543 (
F->isVarArg() ? 5 :
Thunk->arg_size()) - ThunkArgOffset;
544 assert(ArgTranslations.
size() == (
F->isVarArg() ? 5 : PassthroughArgSize));
548 for (
unsigned i = 0; i != PassthroughArgSize; ++i) {
549 Value *Arg =
Thunk->getArg(i + ThunkArgOffset);
550 Type *ArgTy = Arm64Ty->getParamType(i);
551 ThunkArgTranslation ArgTranslation = ArgTranslations[i];
552 if (ArgTranslation != ThunkArgTranslation::Direct) {
554 if (ArgTranslation == ThunkArgTranslation::Bitcast) {
555 Value *CastAlloca = IRB.CreateAlloca(ArgTy);
556 IRB.CreateStore(Arg, CastAlloca);
557 Arg = IRB.CreateLoad(ArgTy, CastAlloca);
559 assert(ArgTranslation == ThunkArgTranslation::PointerIndirection);
560 Arg = IRB.CreateLoad(ArgTy, Arg);
574 Thunk->addParamAttr(5, Attribute::InReg);
576 Arg = IRB.CreatePtrAdd(Arg, IRB.getInt64(0x20));
580 Args.push_back(IRB.getInt64(0));
587 auto SRetAttr =
F->getAttributes().getParamAttr(0, Attribute::StructRet);
588 auto InRegAttr =
F->getAttributes().getParamAttr(0, Attribute::InReg);
589 if (SRetAttr.isValid() && !InRegAttr.isValid()) {
590 Thunk->addParamAttr(1, SRetAttr);
591 Call->addParamAttr(0, SRetAttr);
595 if (TransformDirectToSRet) {
596 IRB.CreateStore(RetVal,
Thunk->getArg(1));
597 }
else if (X64RetType !=
RetTy) {
598 Value *CastAlloca = IRB.CreateAlloca(X64RetType);
599 IRB.CreateStore(Call, CastAlloca);
600 RetVal = IRB.CreateLoad(X64RetType, CastAlloca);
610 IRB.CreateRet(RetVal);
631 getThunkType(
F->getFunctionType(),
F->getAttributes(),
632 Arm64ECThunkType::GuestExit, NullThunkName, Arm64Ty, X64Ty,
635 assert(MangledName &&
"Can't guest exit to function that's already native");
636 std::string ThunkName = *MangledName;
637 if (ThunkName[0] ==
'?' && ThunkName.find(
"@") != std::string::npos) {
638 ThunkName.insert(ThunkName.find(
"@"),
"$exit_thunk");
640 ThunkName.append(
"$exit_thunk");
644 GuestExit->setComdat(
M->getOrInsertComdat(ThunkName));
647 "arm64ec_unmangled_name",
651 "arm64ec_ecmangled_name",
654 F->setMetadata(
"arm64ec_hasguestexit",
MDNode::get(
M->getContext(), {}));
660 if (cfguard_module_flag == 2 && !
F->hasFnAttribute(
"guard_nocf"))
661 GuardFn = GuardFnCFGlobal;
663 GuardFn = GuardFnGlobal;
664 LoadInst *GuardCheckLoad =
B.CreateLoad(PtrTy, GuardFn);
668 Function *
Thunk = buildExitThunk(
F->getFunctionType(),
F->getAttributes());
670 GuardFnType, GuardCheckLoad, {
F,
Thunk});
679 if (
Call->getType()->isVoidTy())
684 auto SRetAttr =
F->getAttributes().getParamAttr(0, Attribute::StructRet);
685 auto InRegAttr =
F->getAttributes().getParamAttr(0, Attribute::InReg);
686 if (SRetAttr.isValid() && !InRegAttr.isValid()) {
688 Call->addParamAttr(0, SRetAttr);
695AArch64Arm64ECCallLowering::buildPatchableThunk(
GlobalAlias *UnmangledAlias,
701 getThunkType(
F->getFunctionType(),
F->getAttributes(),
702 Arm64ECThunkType::GuestExit, NullThunkName, Arm64Ty, X64Ty,
704 std::string ThunkName(MangledAlias->
getName());
705 if (ThunkName[0] ==
'?' && ThunkName.find(
"@") != std::string::npos) {
706 ThunkName.insert(ThunkName.find(
"@"),
"$hybpatch_thunk");
708 ThunkName.append(
"$hybpatch_thunk");
713 GuestExit->setComdat(
M->getOrInsertComdat(ThunkName));
719 LoadInst *DispatchLoad =
B.CreateLoad(PtrTy, DispatchFnGlobal);
723 buildExitThunk(
F->getFunctionType(),
F->getAttributes());
725 B.CreateCall(DispatchFnType, DispatchLoad,
726 {UnmangledAlias, ExitThunk, UnmangledAlias->
getAliasee()});
735 if (
Call->getType()->isVoidTy())
740 auto SRetAttr =
F->getAttributes().getParamAttr(0, Attribute::StructRet);
741 auto InRegAttr =
F->getAttributes().getParamAttr(0, Attribute::InReg);
742 if (SRetAttr.isValid() && !InRegAttr.isValid()) {
744 Call->addParamAttr(0, SRetAttr);
752void AArch64Arm64ECCallLowering::lowerCall(
CallBase *CB) {
764 if (cfguard_module_flag == 2 && !CB->
hasFnAttr(
"guard_nocf"))
765 GuardFn = GuardFnCFGlobal;
767 GuardFn = GuardFnGlobal;
768 LoadInst *GuardCheckLoad =
B.CreateLoad(PtrTy, GuardFn);
774 B.CreateCall(GuardFnType, GuardCheckLoad, {CalledOperand,
Thunk},
783bool AArch64Arm64ECCallLowering::runOnModule(
Module &
Mod) {
791 mdconst::extract_or_null<ConstantInt>(
M->getModuleFlag(
"cfguard")))
792 cfguard_module_flag = MD->getZExtValue();
794 PtrTy = PointerType::getUnqual(
M->getContext());
798 GuardFnType = FunctionType::get(PtrTy, {PtrTy, PtrTy},
false);
799 DispatchFnType = FunctionType::get(PtrTy, {PtrTy, PtrTy, PtrTy},
false);
800 GuardFnCFGlobal =
M->getOrInsertGlobal(
"__os_arm64x_check_icall_cfg", PtrTy);
801 GuardFnGlobal =
M->getOrInsertGlobal(
"__os_arm64x_check_icall", PtrTy);
802 DispatchFnGlobal =
M->getOrInsertGlobal(
"__os_arm64x_dispatch_call", PtrTy);
809 auto F = dyn_cast_or_null<Function>(
A.getAliaseeObject());
812 if (std::optional<std::string> MangledName =
814 F->addMetadata(
"arm64ec_unmangled_name",
817 A.setName(MangledName.value());
825 if (
F.hasPersonalityFn()) {
827 cast<GlobalValue>(
F.getPersonalityFn()->stripPointerCasts());
829 if (std::optional<std::string> MangledName =
831 PersFn->
setName(MangledName.value());
836 if (!
F.hasFnAttribute(Attribute::HybridPatchable) ||
837 F.isDeclarationForLinker() ||
F.hasLocalLinkage() ||
843 if (std::optional<std::string> MangledName =
845 std::string OrigName(
F.getName());
857 MangledName.value(), &
F);
858 F.replaceUsesWithIf(AM,
859 [](
Use &U) {
return isa<GlobalAlias>(
U.getUser()); });
860 F.replaceAllUsesWith(
A);
861 F.setMetadata(
"arm64ec_exp_name",
864 "EXP+" + MangledName.value())));
868 if (
F.hasDLLExportStorageClass()) {
880 if (!
F.isDeclarationForLinker() &&
892 if (!
F.isDeclarationForLinker() &&
893 (!
F.hasLocalLinkage() ||
F.hasAddressTaken()) &&
897 F.setComdat(
Mod.getOrInsertComdat(
F.getName()));
899 {&
F, buildEntryThunk(&
F), Arm64ECThunkType::Entry});
903 auto GA = dyn_cast<GlobalAlias>(O);
904 auto F = dyn_cast<Function>(GA ? GA->getAliasee() : O);
906 {
O, buildExitThunk(
F->getFunctionType(),
F->getAttributes()),
907 Arm64ECThunkType::Exit});
908 if (!GA && !
F->hasDLLImportStorageClass())
910 {buildGuestExitThunk(
F),
F, Arm64ECThunkType::GuestExit});
917 if (!ThunkMapping.
empty()) {
919 for (ThunkInfo &Thunk : ThunkMapping) {
926 ThunkMappingArrayElems.
size()),
927 ThunkMappingArrayElems);
930 "llvm.arm64ec.symbolmap");
936bool AArch64Arm64ECCallLowering::processFunction(
948 if (!
F.hasLocalLinkage() ||
F.hasAddressTaken()) {
949 if (std::optional<std::string> MangledName =
951 F.addMetadata(
"arm64ec_unmangled_name",
954 if (
F.hasComdat() &&
F.getComdat()->getName() ==
F.getName()) {
955 Comdat *MangledComdat =
M->getOrInsertComdat(MangledName.value());
959 User->setComdat(MangledComdat);
961 F.setName(MangledName.value());
971 auto *CB = dyn_cast<CallBase>(&
I);
983 F->isIntrinsic() || !
F->isDeclarationForLinker())
993 if (
I != FnsMap.
end()) {
995 DirectCalledFns.
insert(
I->first);
1001 ++Arm64ECCallsLowered;
1005 if (IndirectCalls.
empty())
1014char AArch64Arm64ECCallLowering::ID = 0;
1016 "AArch64Arm64ECCallLowering",
false,
false)
1019 return new AArch64Arm64ECCallLowering;
static cl::opt< bool > LowerDirectToIndirect("arm64ec-lower-direct-to-indirect", cl::Hidden, cl::init(true))
std::optional< std::string > getArm64ECMangledFunctionName(GlobalValue &GV)
static cl::opt< bool > GenerateThunks("arm64ec-generate-thunks", cl::Hidden, cl::init(true))
OperandBundleDefT< Value * > OperandBundleDef
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
MachineBasicBlock MachineBasicBlock::iterator DebugLoc DL
static GCRegistry::Add< OcamlGC > B("ocaml", "ocaml 3.10-compatible GC")
static GCRegistry::Add< ErlangGC > A("erlang", "erlang-compatible garbage collector")
Module.h This file contains the declarations for the Module class.
static bool processFunction(Function &F, NVPTXTargetMachine &TM)
if(auto Err=PB.parsePassPipeline(MPM, Passes)) return wrap(std MPM run * Mod
#define INITIALIZE_PASS(passName, arg, name, cfg, analysis)
This file implements a set that has insertion order iteration characteristics.
This file defines the SmallString class.
This file defines the SmallVector class.
This file defines the 'Statistic' class, which is designed to be an easy way to expose various metric...
#define STATISTIC(VARNAME, DESC)
static SymbolRef::Type getType(const Symbol *Sym)
Class for arbitrary precision integers.
static LLVM_ABI ArrayType * get(Type *ElementType, uint64_t NumElements)
This static method is the primary way to construct an ArrayType.
Attribute getParamAttr(unsigned ArgNo, Attribute::AttrKind Kind) const
Return the attribute object that exists at the arg index.
LLVM_ABI MaybeAlign getParamAlignment(unsigned ArgNo) const
Return the alignment for the specified function parameter.
bool isValid() const
Return true if the attribute is any kind of attribute.
LLVM_ABI Type * getValueAsType() const
Return the attribute's value as a Type.
LLVM Basic Block Representation.
static BasicBlock * Create(LLVMContext &Context, const Twine &Name="", Function *Parent=nullptr, BasicBlock *InsertBefore=nullptr)
Creates a new BasicBlock.
Base class for all callable instructions (InvokeInst and CallInst) Holds everything related to callin...
bool isInlineAsm() const
Check if this call is an inline asm statement.
void setCallingConv(CallingConv::ID CC)
std::optional< OperandBundleUse > getOperandBundle(StringRef Name) const
Return an operand bundle by name, if present.
Function * getCalledFunction() const
Returns the function called, or null if this is an indirect function invocation or the function signa...
bool hasFnAttr(Attribute::AttrKind Kind) const
Determine whether this call has the given attribute.
CallingConv::ID getCallingConv() const
Value * getCalledOperand() const
FunctionType * getFunctionType() const
void setCalledOperand(Value *V)
AttributeList getAttributes() const
Return the attributes for this call.
This class represents a function call, abstracting a target machine's calling convention.
static LLVM_ABI Constant * get(ArrayType *T, ArrayRef< Constant * > V)
static Constant * getAnon(ArrayRef< Constant * > V, bool Packed=false)
Return an anonymous struct that has the specified elements.
This is an important base class in LLVM.
iterator find(const_arg_type_t< KeyT > Val)
static Function * Create(FunctionType *Ty, LinkageTypes Linkage, unsigned AddrSpace, const Twine &N="", Module *M=nullptr)
LLVM_ABI void setAliasee(Constant *Aliasee)
These methods retrieve and set alias target.
const Constant * getAliasee() const
static LLVM_ABI GlobalAlias * create(Type *Ty, unsigned AddressSpace, LinkageTypes Linkage, const Twine &Name, Constant *Aliasee, Module *Parent)
If a parent module is specified, the alias is automatically inserted into the end of the specified mo...
@ DLLExportStorageClass
Function to be accessible from DLL.
@ WeakODRLinkage
Same, but only replaced by something equivalent.
@ ExternalLinkage
Externally visible function.
@ LinkOnceODRLinkage
Same, but only replaced by something equivalent.
Type * getValueType() const
This provides a uniform API for creating instructions and inserting them into a basic block: either a...
An instruction for reading from memory.
static MDTuple * get(LLVMContext &Context, ArrayRef< Metadata * > MDs)
static LLVM_ABI MDString * get(LLVMContext &Context, StringRef Str)
ModulePass class - This class is used to implement unstructured interprocedural optimizations and ana...
virtual bool runOnModule(Module &M)=0
runOnModule - Virtual method overriden by subclasses to process the module being operated on.
A Module instance is used to store all the information related to an LLVM module.
A container for an operand bundle being viewed as a set of values rather than a set of uses.
A vector that has set insertion semantics.
bool insert(const value_type &X)
Insert a new element into the SetVector.
SmallString - A SmallString is just a SmallVector with methods and accessors that make it work better...
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.
The instances of the Type class are immutable: once they are created, they are never changed.
bool isPointerTy() const
True if this is an instance of PointerType.
bool isFloatTy() const
Return true if this is 'float', a 32-bit IEEE fp type.
static LLVM_ABI IntegerType * getIntNTy(LLVMContext &C, unsigned N)
static LLVM_ABI IntegerType * getInt64Ty(LLVMContext &C)
static LLVM_ABI Type * getVoidTy(LLVMContext &C)
bool isHalfTy() const
Return true if this is 'half', a 16-bit IEEE fp type.
bool isDoubleTy() const
Return true if this is 'double', a 64-bit IEEE fp type.
bool isFunctionTy() const
True if this is an instance of FunctionType.
bool isVoidTy() const
Return true if this is 'void'.
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.
LLVM_ABI void setName(const Twine &Name)
Change the name of the value.
LLVM_ABI StringRef getName() const
Return a constant reference to the value's name.
A raw_ostream that discards all output.
This class implements an extremely fast bulk output stream that can only output to a stream.
A raw_ostream that writes to an SmallVector or SmallString.
constexpr char Args[]
Key for Kernel::Metadata::mArgs.
constexpr char Attrs[]
Key for Kernel::Metadata::mAttrs.
unsigned ID
LLVM IR allows to use arbitrary numbers as calling convention identifiers.
@ ARM64EC_Thunk_Native
Calling convention used in the ARM64EC ABI to implement calls between ARM64 code and thunks.
@ CFGuard_Check
Special calling convention on Windows for calling the Control Guard Check ICall funtion.
@ ARM64EC_Thunk_X64
Calling convention used in the ARM64EC ABI to implement calls between x64 code and thunks.
initializer< Ty > init(const Ty &Val)
This is an optimization pass for GlobalISel generic memory operations.
LLVM_ABI std::optional< std::string > getArm64ECMangledFunctionName(StringRef Name)
Returns the ARM64EC mangled function name unless the input is already mangled.
detail::zippy< detail::zip_first, T, U, Args... > zip_equal(T &&t, U &&u, Args &&...args)
zip iterator that assumes that all iteratees have the same length.
iterator_range< T > make_range(T x, T y)
Convenience function for iterating over sub-ranges.
ModulePass * createAArch64Arm64ECCallLoweringPass()
LLVM_ABI void report_fatal_error(Error Err, bool gen_crash_diag=true)
SmallVector< ValueTypeFromRangeType< R >, Size > to_vector(R &&Range)
Given a range of type R, iterate the entire range and return a SmallVector with elements of the vecto...
constexpr std::string_view HybridPatchableTargetSuffix
iterator_range< pointer_iterator< WrappedIteratorT > > make_pointer_range(RangeT &&Range)
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.
Align valueOrOne() const
For convenience, returns a valid alignment or 1 if undefined.