LLVM 23.0.0git
AArch64Arm64ECCallLowering.cpp
Go to the documentation of this file.
1//===-- AArch64Arm64ECCallLowering.cpp - Lower Arm64EC calls ----*- C++ -*-===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8///
9/// \file
10/// This file contains the IR transform to lower external or indirect calls for
11/// the ARM64EC calling convention. Such calls must go through the runtime, so
12/// we can translate the calling convention for calls into the emulator.
13///
14/// This subsumes Control Flow Guard handling.
15///
16//===----------------------------------------------------------------------===//
17
18#include "AArch64.h"
19#include "llvm/ADT/SetVector.h"
22#include "llvm/ADT/Statistic.h"
23#include "llvm/IR/CallingConv.h"
25#include "llvm/IR/GlobalAlias.h"
26#include "llvm/IR/IRBuilder.h"
27#include "llvm/IR/Instruction.h"
28#include "llvm/IR/Mangler.h"
29#include "llvm/IR/Module.h"
30#include "llvm/Object/COFF.h"
31#include "llvm/Pass.h"
34
35using namespace llvm;
36using namespace llvm::COFF;
37
39
40#define DEBUG_TYPE "arm64eccalllowering"
41
42STATISTIC(Arm64ECCallsLowered, "Number of Arm64EC calls lowered");
43
44static cl::opt<bool> LowerDirectToIndirect("arm64ec-lower-direct-to-indirect",
45 cl::Hidden, cl::init(true));
46static cl::opt<bool> GenerateThunks("arm64ec-generate-thunks", cl::Hidden,
47 cl::init(true));
48
49namespace {
50
51enum ThunkArgTranslation : uint8_t {
52 Direct,
53 Bitcast,
54 PointerIndirection,
55};
56
57struct ThunkArgInfo {
58 Type *Arm64Ty;
59 Type *X64Ty;
60 ThunkArgTranslation Translation;
61};
62
63class AArch64Arm64ECCallLowering : public ModulePass {
64public:
65 static char ID;
66 AArch64Arm64ECCallLowering() : ModulePass(ID) {}
67
68 Function *buildExitThunk(FunctionType *FnTy, AttributeList Attrs);
69 Function *buildEntryThunk(Function *F);
70 void lowerCall(CallBase *CB);
71 Function *buildGuestExitThunk(Function *F);
72 Function *buildPatchableThunk(GlobalAlias *UnmangledAlias,
73 GlobalAlias *MangledAlias);
74 bool processFunction(Function &F, SetVector<GlobalValue *> &DirectCalledFns,
75 DenseMap<GlobalAlias *, GlobalAlias *> &FnsMap);
76 bool runOnModule(Module &M) override;
77
78private:
79 ControlFlowGuardMode CFGuardModuleFlag = ControlFlowGuardMode::Disabled;
80 FunctionType *GuardFnType = nullptr;
81 FunctionType *DispatchFnType = nullptr;
82 Constant *GuardFnCFGlobal = nullptr;
83 Constant *GuardFnGlobal = nullptr;
84 Constant *DispatchFnGlobal = nullptr;
85 Module *M = nullptr;
86
87 Type *PtrTy;
88 Type *I64Ty;
89 Type *VoidTy;
90
91 void getThunkType(FunctionType *FT, AttributeList AttrList,
92 Arm64ECThunkType TT, raw_ostream &Out,
93 FunctionType *&Arm64Ty, FunctionType *&X64Ty,
94 SmallVector<ThunkArgTranslation> &ArgTranslations);
95 void getThunkRetType(FunctionType *FT, AttributeList AttrList,
96 raw_ostream &Out, Type *&Arm64RetTy, Type *&X64RetTy,
97 SmallVectorImpl<Type *> &Arm64ArgTypes,
98 SmallVectorImpl<Type *> &X64ArgTypes,
99 SmallVector<ThunkArgTranslation> &ArgTranslations,
100 bool &HasSretPtr);
101 void getThunkArgTypes(FunctionType *FT, AttributeList AttrList,
102 Arm64ECThunkType TT, raw_ostream &Out,
103 SmallVectorImpl<Type *> &Arm64ArgTypes,
104 SmallVectorImpl<Type *> &X64ArgTypes,
105 SmallVectorImpl<ThunkArgTranslation> &ArgTranslations,
106 bool HasSretPtr);
107 ThunkArgInfo canonicalizeThunkType(Type *T, Align Alignment, bool Ret,
108 uint64_t ArgSizeBytes, raw_ostream &Out);
109};
110
111} // end anonymous namespace
112
113void AArch64Arm64ECCallLowering::getThunkType(
114 FunctionType *FT, AttributeList AttrList, Arm64ECThunkType TT,
115 raw_ostream &Out, FunctionType *&Arm64Ty, FunctionType *&X64Ty,
116 SmallVector<ThunkArgTranslation> &ArgTranslations) {
117 Out << (TT == Arm64ECThunkType::Entry ? "$ientry_thunk$cdecl$"
118 : "$iexit_thunk$cdecl$");
119
120 Type *Arm64RetTy;
121 Type *X64RetTy;
122
123 SmallVector<Type *> Arm64ArgTypes;
124 SmallVector<Type *> X64ArgTypes;
125
126 // The first argument to a thunk is the called function, stored in x9.
127 // For exit thunks, we pass the called function down to the emulator;
128 // for entry/guest exit thunks, we just call the Arm64 function directly.
129 if (TT == Arm64ECThunkType::Exit)
130 Arm64ArgTypes.push_back(PtrTy);
131 X64ArgTypes.push_back(PtrTy);
132
133 bool HasSretPtr = false;
134 getThunkRetType(FT, AttrList, Out, Arm64RetTy, X64RetTy, Arm64ArgTypes,
135 X64ArgTypes, ArgTranslations, HasSretPtr);
136
137 getThunkArgTypes(FT, AttrList, TT, Out, Arm64ArgTypes, X64ArgTypes,
138 ArgTranslations, HasSretPtr);
139
140 Arm64Ty = FunctionType::get(Arm64RetTy, Arm64ArgTypes, false);
141
142 X64Ty = FunctionType::get(X64RetTy, X64ArgTypes, false);
143}
144
145void AArch64Arm64ECCallLowering::getThunkArgTypes(
146 FunctionType *FT, AttributeList AttrList, Arm64ECThunkType TT,
147 raw_ostream &Out, SmallVectorImpl<Type *> &Arm64ArgTypes,
148 SmallVectorImpl<Type *> &X64ArgTypes,
149 SmallVectorImpl<ThunkArgTranslation> &ArgTranslations, bool HasSretPtr) {
150
151 Out << "$";
152 if (FT->isVarArg()) {
153 // We treat the variadic function's thunk as a normal function
154 // with the following type on the ARM side:
155 // rettype exitthunk(
156 // ptr x9, ptr x0, i64 x1, i64 x2, i64 x3, ptr x4, i64 x5)
157 //
158 // that can coverage all types of variadic function.
159 // x9 is similar to normal exit thunk, store the called function.
160 // x0-x3 is the arguments be stored in registers.
161 // x4 is the address of the arguments on the stack.
162 // x5 is the size of the arguments on the stack.
163 //
164 // On the x64 side, it's the same except that x5 isn't set.
165 //
166 // If both the ARM and X64 sides are sret, there are only three
167 // arguments in registers.
168 //
169 // If the X64 side is sret, but the ARM side isn't, we pass an extra value
170 // to/from the X64 side, and let SelectionDAG transform it into a memory
171 // location.
172 Out << "varargs";
173
174 // x0-x3
175 for (int i = HasSretPtr ? 1 : 0; i < 4; i++) {
176 Arm64ArgTypes.push_back(I64Ty);
177 X64ArgTypes.push_back(I64Ty);
178 ArgTranslations.push_back(ThunkArgTranslation::Direct);
179 }
180
181 // x4
182 Arm64ArgTypes.push_back(PtrTy);
183 X64ArgTypes.push_back(PtrTy);
184 ArgTranslations.push_back(ThunkArgTranslation::Direct);
185 // x5
186 Arm64ArgTypes.push_back(I64Ty);
187 if (TT != Arm64ECThunkType::Entry) {
188 // FIXME: x5 isn't actually used by the x64 side; revisit once we
189 // have proper isel for varargs
190 X64ArgTypes.push_back(I64Ty);
191 ArgTranslations.push_back(ThunkArgTranslation::Direct);
192 }
193 return;
194 }
195
196 unsigned I = 0;
197 if (HasSretPtr)
198 I++;
199
200 if (I == FT->getNumParams()) {
201 Out << "v";
202 return;
203 }
204
205 for (unsigned E = FT->getNumParams(); I != E; ++I) {
206#if 0
207 // FIXME: Need more information about argument size; see
208 // https://reviews.llvm.org/D132926
209 uint64_t ArgSizeBytes = AttrList.getParamArm64ECArgSizeBytes(I);
210 Align ParamAlign = AttrList.getParamAlignment(I).valueOrOne();
211#else
212 uint64_t ArgSizeBytes = 0;
213 Align ParamAlign = Align();
214#endif
215 auto [Arm64Ty, X64Ty, ArgTranslation] =
216 canonicalizeThunkType(FT->getParamType(I), ParamAlign,
217 /*Ret*/ false, ArgSizeBytes, Out);
218 Arm64ArgTypes.push_back(Arm64Ty);
219 X64ArgTypes.push_back(X64Ty);
220 ArgTranslations.push_back(ArgTranslation);
221 }
222}
223
224void AArch64Arm64ECCallLowering::getThunkRetType(
225 FunctionType *FT, AttributeList AttrList, raw_ostream &Out,
226 Type *&Arm64RetTy, Type *&X64RetTy, SmallVectorImpl<Type *> &Arm64ArgTypes,
227 SmallVectorImpl<Type *> &X64ArgTypes,
228 SmallVector<ThunkArgTranslation> &ArgTranslations, bool &HasSretPtr) {
229 Type *T = FT->getReturnType();
230#if 0
231 // FIXME: Need more information about argument size; see
232 // https://reviews.llvm.org/D132926
233 uint64_t ArgSizeBytes = AttrList.getRetArm64ECArgSizeBytes();
234#else
235 int64_t ArgSizeBytes = 0;
236#endif
237 if (T->isVoidTy()) {
238 if (FT->getNumParams()) {
239 Attribute SRetAttr0 = AttrList.getParamAttr(0, Attribute::StructRet);
240 Attribute InRegAttr0 = AttrList.getParamAttr(0, Attribute::InReg);
241 Attribute SRetAttr1, InRegAttr1;
242 if (FT->getNumParams() > 1) {
243 // Also check the second parameter (for class methods, the first
244 // parameter is "this", and the second parameter is the sret pointer.)
245 // It doesn't matter which one is sret.
246 SRetAttr1 = AttrList.getParamAttr(1, Attribute::StructRet);
247 InRegAttr1 = AttrList.getParamAttr(1, Attribute::InReg);
248 }
249 if ((SRetAttr0.isValid() && InRegAttr0.isValid()) ||
250 (SRetAttr1.isValid() && InRegAttr1.isValid())) {
251 // sret+inreg indicates a call that returns a C++ class value. This is
252 // actually equivalent to just passing and returning a void* pointer
253 // as the first or second argument. Translate it that way, instead of
254 // trying to model "inreg" in the thunk's calling convention; this
255 // simplfies the rest of the code, and matches MSVC mangling.
256 Out << "i8";
257 Arm64RetTy = I64Ty;
258 X64RetTy = I64Ty;
259 return;
260 }
261 if (SRetAttr0.isValid()) {
262 // FIXME: Sanity-check the sret type; if it's an integer or pointer,
263 // we'll get screwy mangling/codegen.
264 // FIXME: For large struct types, mangle as an integer argument and
265 // integer return, so we can reuse more thunks, instead of "m" syntax.
266 // (MSVC mangles this case as an integer return with no argument, but
267 // that's a miscompile.)
268 Type *SRetType = SRetAttr0.getValueAsType();
269 Align SRetAlign = AttrList.getParamAlignment(0).valueOrOne();
270 canonicalizeThunkType(SRetType, SRetAlign, /*Ret*/ true, ArgSizeBytes,
271 Out);
272 Arm64RetTy = VoidTy;
273 X64RetTy = VoidTy;
274 Arm64ArgTypes.push_back(FT->getParamType(0));
275 X64ArgTypes.push_back(FT->getParamType(0));
276 ArgTranslations.push_back(ThunkArgTranslation::Direct);
277 HasSretPtr = true;
278 return;
279 }
280 }
281
282 Out << "v";
283 Arm64RetTy = VoidTy;
284 X64RetTy = VoidTy;
285 return;
286 }
287
288 auto info =
289 canonicalizeThunkType(T, Align(), /*Ret*/ true, ArgSizeBytes, Out);
290 Arm64RetTy = info.Arm64Ty;
291 X64RetTy = info.X64Ty;
292 if (X64RetTy->isPointerTy()) {
293 // If the X64 type is canonicalized to a pointer, that means it's
294 // passed/returned indirectly. For a return value, that means it's an
295 // sret pointer.
296 X64ArgTypes.push_back(X64RetTy);
297 X64RetTy = VoidTy;
298 }
299}
300
301ThunkArgInfo AArch64Arm64ECCallLowering::canonicalizeThunkType(
302 Type *T, Align Alignment, bool Ret, uint64_t ArgSizeBytes,
303 raw_ostream &Out) {
304
305 auto direct = [](Type *T) {
306 return ThunkArgInfo{T, T, ThunkArgTranslation::Direct};
307 };
308
309 auto bitcast = [this](Type *Arm64Ty, uint64_t SizeInBytes) {
310 return ThunkArgInfo{Arm64Ty,
311 llvm::Type::getIntNTy(M->getContext(), SizeInBytes * 8),
312 ThunkArgTranslation::Bitcast};
313 };
314
315 auto pointerIndirection = [this](Type *Arm64Ty) {
316 return ThunkArgInfo{Arm64Ty, PtrTy,
317 ThunkArgTranslation::PointerIndirection};
318 };
319
320 if (T->isHalfTy()) {
321 // Prefix with `llvm` since MSVC doesn't specify `_Float16`
322 Out << "__llvm_h__";
323 return direct(T);
324 }
325
326 if (T->isBFloatTy()) {
327 // Prefix with `llvm` since MSVC doesn't specify `__bf16`
328 Out << "__llvm_bf16__";
329 return direct(T);
330 }
331
332 if (T->isFloatTy()) {
333 Out << "f";
334 return direct(T);
335 }
336
337 if (T->isDoubleTy()) {
338 Out << "d";
339 return direct(T);
340 }
341
342 if (T->isFP128Ty()) {
343 // Prefix with `llvm` since MSVC doesn't specify `_Float128`
344 Out << "__llvm_q__";
345 // On windows f128 is passed indirectly, and Clang/LLVM
346 // returns using sret for compatibility with GCC.
347 return pointerIndirection(T);
348 }
349
350 if (T->isFloatingPointTy()) {
352 "Only half, bfloat16, float, double, and fp128 are supported "
353 "for ARM64EC thunks");
354 }
355
356 auto &DL = M->getDataLayout();
357
358 if (auto *StructTy = dyn_cast<StructType>(T))
359 if (StructTy->getNumElements() == 1)
360 T = StructTy->getElementType(0);
361
362 if (T->isArrayTy()) {
363 Type *ElementTy = T->getArrayElementType();
364 uint64_t ElementCnt = T->getArrayNumElements();
365 uint64_t ElementSizePerBytes = DL.getTypeSizeInBits(ElementTy) / 8;
366 uint64_t TotalSizeBytes = ElementCnt * ElementSizePerBytes;
367 if (ElementTy->isHalfTy() || ElementTy->isBFloatTy() ||
368 ElementTy->isFloatTy() || ElementTy->isDoubleTy() ||
369 ElementTy->isFP128Ty()) {
370 if (ElementTy->isHalfTy())
371 // Prefix with `llvm` since MSVC doesn't specify `_Float16`
372 Out << "__llvm_H__";
373 else if (ElementTy->isBFloatTy())
374 // Prefix with `llvm` since MSVC doesn't specify `__bf16`
375 Out << "__llvm_BF16__";
376 else if (ElementTy->isFloatTy())
377 Out << "F";
378 else if (ElementTy->isDoubleTy())
379 Out << "D";
380 else if (ElementTy->isFP128Ty())
381 // Prefix with `llvm` since MSVC doesn't specify `_Float128`
382 Out << "__llvm_Q__";
383 Out << TotalSizeBytes;
384 if (Alignment.value() >= 16 && !Ret)
385 Out << "a" << Alignment.value();
386 if (TotalSizeBytes <= 8) {
387 // Arm64 returns small structs of float/double in float registers;
388 // X64 uses RAX.
389 return bitcast(T, TotalSizeBytes);
390 } else {
391 // Struct is passed directly on Arm64, but indirectly on X64.
392 return pointerIndirection(T);
393 }
394 } else if (ElementTy->isFloatingPointTy()) {
396 "Only half, bfloat16, float, double, and fp128 are supported "
397 "for ARM64EC thunks");
398 }
399 }
400
401 if ((T->isIntegerTy() || T->isPointerTy()) && DL.getTypeSizeInBits(T) <= 64) {
402 Out << "i8";
403 return direct(I64Ty);
404 }
405
406 unsigned TypeSize = ArgSizeBytes;
407 if (TypeSize == 0)
408 TypeSize = DL.getTypeSizeInBits(T) / 8;
409 Out << "m";
410 if (TypeSize != 4)
411 Out << TypeSize;
412 if (Alignment.value() >= 16 && !Ret)
413 Out << "a" << Alignment.value();
414 // FIXME: Try to canonicalize Arm64Ty more thoroughly?
415 if (TypeSize == 1 || TypeSize == 2 || TypeSize == 4 || TypeSize == 8) {
416 // Pass directly in an integer register
417 return bitcast(T, TypeSize);
418 } else {
419 // Passed directly on Arm64, but indirectly on X64.
420 return pointerIndirection(T);
421 }
422}
423
424// This function builds the "exit thunk", a function which translates
425// arguments and return values when calling x64 code from AArch64 code.
426Function *AArch64Arm64ECCallLowering::buildExitThunk(FunctionType *FT,
427 AttributeList Attrs) {
428 SmallString<256> ExitThunkName;
429 llvm::raw_svector_ostream ExitThunkStream(ExitThunkName);
430 FunctionType *Arm64Ty, *X64Ty;
431 SmallVector<ThunkArgTranslation> ArgTranslations;
432 getThunkType(FT, Attrs, Arm64ECThunkType::Exit, ExitThunkStream, Arm64Ty,
433 X64Ty, ArgTranslations);
434 if (Function *F = M->getFunction(ExitThunkName))
435 return F;
436
438 ExitThunkName, M);
439 F->setCallingConv(CallingConv::ARM64EC_Thunk_Native);
440 F->setSection(".wowthk$aa");
441 F->setComdat(M->getOrInsertComdat(ExitThunkName));
442 // Copy MSVC, and always set up a frame pointer. (Maybe this isn't necessary.)
443 F->addFnAttr("frame-pointer", "all");
444 // Only copy sret from the first argument. For C++ instance methods, clang can
445 // stick an sret marking on a later argument, but it doesn't actually affect
446 // the ABI, so we can omit it. This avoids triggering a verifier assertion.
447 if (FT->getNumParams()) {
448 auto SRet = Attrs.getParamAttr(0, Attribute::StructRet);
449 auto InReg = Attrs.getParamAttr(0, Attribute::InReg);
450 if (SRet.isValid() && !InReg.isValid())
451 F->addParamAttr(1, SRet);
452 }
453 // FIXME: Copy anything other than sret? Shouldn't be necessary for normal
454 // C ABI, but might show up in other cases.
455 BasicBlock *BB = BasicBlock::Create(M->getContext(), "", F);
456 IRBuilder<> IRB(BB);
457 Value *CalleePtr =
458 M->getOrInsertGlobal("__os_arm64x_dispatch_call_no_redirect", PtrTy);
459 Value *Callee = IRB.CreateLoad(PtrTy, CalleePtr);
460 auto &DL = M->getDataLayout();
462
463 // Pass the called function in x9.
464 auto X64TyOffset = 1;
465 Args.push_back(F->arg_begin());
466
467 Type *RetTy = Arm64Ty->getReturnType();
468 if (RetTy != X64Ty->getReturnType()) {
469 // If the return type is an array or struct, translate it. Values of size
470 // 8 or less go into RAX; bigger values go into memory, and we pass a
471 // pointer.
472 if (DL.getTypeStoreSize(RetTy) > 8) {
473 Args.push_back(IRB.CreateAlloca(RetTy));
474 X64TyOffset++;
475 }
476 }
477
478 for (auto [Arg, X64ArgType, ArgTranslation] : llvm::zip_equal(
479 make_range(F->arg_begin() + 1, F->arg_end()),
480 make_range(X64Ty->param_begin() + X64TyOffset, X64Ty->param_end()),
481 ArgTranslations)) {
482 // Translate arguments from AArch64 calling convention to x86 calling
483 // convention.
484 //
485 // For simple types, we don't need to do any translation: they're
486 // represented the same way. (Implicit sign extension is not part of
487 // either convention.)
488 //
489 // The big thing we have to worry about is struct types... but
490 // fortunately AArch64 clang is pretty friendly here: the cases that need
491 // translation are always passed as a struct or array. (If we run into
492 // some cases where this doesn't work, we can teach clang to mark it up
493 // with an attribute.)
494 //
495 // The first argument is the called function, stored in x9.
496 if (ArgTranslation != ThunkArgTranslation::Direct) {
497 Value *Mem = IRB.CreateAlloca(Arg.getType());
498 IRB.CreateStore(&Arg, Mem);
499 if (ArgTranslation == ThunkArgTranslation::Bitcast) {
500 Type *IntTy = IRB.getIntNTy(DL.getTypeStoreSizeInBits(Arg.getType()));
501 Args.push_back(IRB.CreateLoad(IntTy, Mem));
502 } else {
503 assert(ArgTranslation == ThunkArgTranslation::PointerIndirection);
504 Args.push_back(Mem);
505 }
506 } else {
507 Args.push_back(&Arg);
508 }
509 assert(Args.back()->getType() == X64ArgType);
510 }
511 // FIXME: Transfer necessary attributes? sret? anything else?
512
513 CallInst *Call = IRB.CreateCall(X64Ty, Callee, Args);
514 Call->setCallingConv(CallingConv::ARM64EC_Thunk_X64);
515
516 Value *RetVal = Call;
517 if (RetTy != X64Ty->getReturnType()) {
518 // If we rewrote the return type earlier, convert the return value to
519 // the proper type.
520 if (DL.getTypeStoreSize(RetTy) > 8) {
521 RetVal = IRB.CreateLoad(RetTy, Args[1]);
522 } else {
523 Value *CastAlloca = IRB.CreateAlloca(RetTy);
524 IRB.CreateStore(Call, CastAlloca);
525 RetVal = IRB.CreateLoad(RetTy, CastAlloca);
526 }
527 }
528
529 if (RetTy->isVoidTy())
530 IRB.CreateRetVoid();
531 else
532 IRB.CreateRet(RetVal);
533 return F;
534}
535
536// This function builds the "entry thunk", a function which translates
537// arguments and return values when calling AArch64 code from x64 code.
538Function *AArch64Arm64ECCallLowering::buildEntryThunk(Function *F) {
539 SmallString<256> EntryThunkName;
540 llvm::raw_svector_ostream EntryThunkStream(EntryThunkName);
541 FunctionType *Arm64Ty, *X64Ty;
542 SmallVector<ThunkArgTranslation> ArgTranslations;
543 getThunkType(F->getFunctionType(), F->getAttributes(),
544 Arm64ECThunkType::Entry, EntryThunkStream, Arm64Ty, X64Ty,
545 ArgTranslations);
546 if (Function *F = M->getFunction(EntryThunkName))
547 return F;
548
550 EntryThunkName, M);
551 Thunk->setCallingConv(CallingConv::ARM64EC_Thunk_X64);
552 Thunk->setSection(".wowthk$aa");
553 Thunk->setComdat(M->getOrInsertComdat(EntryThunkName));
554 // Copy MSVC, and always set up a frame pointer. (Maybe this isn't necessary.)
555 Thunk->addFnAttr("frame-pointer", "all");
556
557 BasicBlock *BB = BasicBlock::Create(M->getContext(), "", Thunk);
558 IRBuilder<> IRB(BB);
559
560 Type *RetTy = Arm64Ty->getReturnType();
561 Type *X64RetType = X64Ty->getReturnType();
562
563 bool TransformDirectToSRet = X64RetType->isVoidTy() && !RetTy->isVoidTy();
564 unsigned ThunkArgOffset = TransformDirectToSRet ? 2 : 1;
565 unsigned PassthroughArgSize =
566 (F->isVarArg() ? 5 : Thunk->arg_size()) - ThunkArgOffset;
567 assert(ArgTranslations.size() == (F->isVarArg() ? 5 : PassthroughArgSize));
568
569 // Translate arguments to call.
571 for (unsigned i = 0; i != PassthroughArgSize; ++i) {
572 Value *Arg = Thunk->getArg(i + ThunkArgOffset);
573 Type *ArgTy = Arm64Ty->getParamType(i);
574 ThunkArgTranslation ArgTranslation = ArgTranslations[i];
575 if (ArgTranslation != ThunkArgTranslation::Direct) {
576 // Translate array/struct arguments to the expected type.
577 if (ArgTranslation == ThunkArgTranslation::Bitcast) {
578 Value *CastAlloca = IRB.CreateAlloca(ArgTy);
579 IRB.CreateStore(Arg, CastAlloca);
580 Arg = IRB.CreateLoad(ArgTy, CastAlloca);
581 } else {
582 assert(ArgTranslation == ThunkArgTranslation::PointerIndirection);
583 Arg = IRB.CreateLoad(ArgTy, Arg);
584 }
585 }
586 assert(Arg->getType() == ArgTy);
587 Args.push_back(Arg);
588 }
589
590 if (F->isVarArg()) {
591 // The 5th argument to variadic entry thunks is used to model the x64 sp
592 // which is passed to the thunk in x4, this can be passed to the callee as
593 // the variadic argument start address after skipping over the 32 byte
594 // shadow store.
595
596 // The EC thunk CC will assign any argument marked as InReg to x4.
597 Thunk->addParamAttr(5, Attribute::InReg);
598 Value *Arg = Thunk->getArg(5);
599 Arg = IRB.CreatePtrAdd(Arg, IRB.getInt64(0x20));
600 Args.push_back(Arg);
601
602 // Pass in a zero variadic argument size (in x5).
603 Args.push_back(IRB.getInt64(0));
604 }
605
606 // Call the function passed to the thunk.
607 Value *Callee = Thunk->getArg(0);
608 CallInst *Call = IRB.CreateCall(Arm64Ty, Callee, Args);
609
610 auto SRetAttr = F->getAttributes().getParamAttr(0, Attribute::StructRet);
611 auto InRegAttr = F->getAttributes().getParamAttr(0, Attribute::InReg);
612 if (SRetAttr.isValid() && !InRegAttr.isValid()) {
613 Thunk->addParamAttr(1, SRetAttr);
614 Call->addParamAttr(0, SRetAttr);
615 }
616
617 Value *RetVal = Call;
618 if (TransformDirectToSRet) {
619 // The x64 side returns this value indirectly via a hidden pointer (sret).
620 // Mark the thunk's pointer arg with sret so that ISel saves it and copies
621 // it into x8 (RAX) on return, matching the x64 calling convention.
622 Thunk->addParamAttr(
623 1, Attribute::getWithStructRetType(M->getContext(), RetTy));
624 IRB.CreateStore(RetVal, Thunk->getArg(1));
625 } else if (X64RetType != RetTy) {
626 Value *CastAlloca = IRB.CreateAlloca(X64RetType);
627 IRB.CreateStore(Call, CastAlloca);
628 RetVal = IRB.CreateLoad(X64RetType, CastAlloca);
629 }
630
631 // Return to the caller. Note that the isel has code to translate this
632 // "ret" to a tail call to __os_arm64x_dispatch_ret. (Alternatively, we
633 // could emit a tail call here, but that would require a dedicated calling
634 // convention, which seems more complicated overall.)
635 if (X64RetType->isVoidTy())
636 IRB.CreateRetVoid();
637 else
638 IRB.CreateRet(RetVal);
639
640 return Thunk;
641}
642
643std::optional<std::string> getArm64ECMangledFunctionName(GlobalValue &GV) {
644 if (!GV.hasName()) {
645 GV.setName("__unnamed");
646 }
647
649}
650
651// Builds the "guest exit thunk", a helper to call a function which may or may
652// not be an exit thunk. (We optimistically assume non-dllimport function
653// declarations refer to functions defined in AArch64 code; if the linker
654// can't prove that, we use this routine instead.)
655Function *AArch64Arm64ECCallLowering::buildGuestExitThunk(Function *F) {
656 llvm::raw_null_ostream NullThunkName;
657 FunctionType *Arm64Ty, *X64Ty;
658 SmallVector<ThunkArgTranslation> ArgTranslations;
659 getThunkType(F->getFunctionType(), F->getAttributes(),
660 Arm64ECThunkType::GuestExit, NullThunkName, Arm64Ty, X64Ty,
661 ArgTranslations);
662 auto MangledName = getArm64ECMangledFunctionName(*F);
663 assert(MangledName && "Can't guest exit to function that's already native");
664 std::string ThunkName = *MangledName;
665 if (ThunkName[0] == '?' && ThunkName.find("@") != std::string::npos) {
666 ThunkName.insert(ThunkName.find("@"), "$exit_thunk");
667 } else {
668 ThunkName.append("$exit_thunk");
669 }
671 Function::Create(Arm64Ty, GlobalValue::WeakODRLinkage, 0, ThunkName, M);
672 GuestExit->setComdat(M->getOrInsertComdat(ThunkName));
673 GuestExit->setSection(".wowthk$aa");
674 GuestExit->addMetadata(
675 "arm64ec_unmangled_name",
677 MDString::get(M->getContext(), F->getName())));
678 GuestExit->setMetadata(
679 "arm64ec_ecmangled_name",
681 MDString::get(M->getContext(), *MangledName)));
682 F->setMetadata("arm64ec_hasguestexit", MDNode::get(M->getContext(), {}));
684 IRBuilder<> B(BB);
685
686 // Create new call instruction. The call check should always be a call,
687 // even if the original CallBase is an Invoke or CallBr instructio.
688 // This is treated as a direct call, so do not use GuardFnCFGlobal.
689 LoadInst *GuardCheckLoad = B.CreateLoad(PtrTy, GuardFnGlobal);
690 Function *Thunk = buildExitThunk(F->getFunctionType(), F->getAttributes());
691 CallInst *GuardCheck = B.CreateCall(
692 GuardFnType, GuardCheckLoad, {F, Thunk});
693 Value *GuardCheckDest = B.CreateExtractValue(GuardCheck, 0);
694 Value *GuardFinalDest = B.CreateExtractValue(GuardCheck, 1);
695
696 // Ensure that the first argument is passed in the correct register.
697 GuardCheck->setCallingConv(CallingConv::CFGuard_Check);
698
700 OperandBundleDef OB("cfguardtarget", GuardFinalDest);
701 CallInst *Call = B.CreateCall(Arm64Ty, GuardCheckDest, Args, OB);
703
704 if (Call->getType()->isVoidTy())
705 B.CreateRetVoid();
706 else
707 B.CreateRet(Call);
708
709 auto SRetAttr = F->getAttributes().getParamAttr(0, Attribute::StructRet);
710 auto InRegAttr = F->getAttributes().getParamAttr(0, Attribute::InReg);
711 if (SRetAttr.isValid() && !InRegAttr.isValid()) {
712 GuestExit->addParamAttr(0, SRetAttr);
713 Call->addParamAttr(0, SRetAttr);
714 }
715
716 return GuestExit;
717}
718
719Function *
720AArch64Arm64ECCallLowering::buildPatchableThunk(GlobalAlias *UnmangledAlias,
721 GlobalAlias *MangledAlias) {
722 llvm::raw_null_ostream NullThunkName;
723 FunctionType *Arm64Ty, *X64Ty;
724 Function *F = cast<Function>(MangledAlias->getAliasee());
725 SmallVector<ThunkArgTranslation> ArgTranslations;
726 getThunkType(F->getFunctionType(), F->getAttributes(),
727 Arm64ECThunkType::GuestExit, NullThunkName, Arm64Ty, X64Ty,
728 ArgTranslations);
729 std::string ThunkName(MangledAlias->getName());
730 if (ThunkName[0] == '?' && ThunkName.find("@") != std::string::npos) {
731 ThunkName.insert(ThunkName.find("@"), "$hybpatch_thunk");
732 } else {
733 ThunkName.append("$hybpatch_thunk");
734 }
735
737 Function::Create(Arm64Ty, GlobalValue::WeakODRLinkage, 0, ThunkName, M);
738 GuestExit->setComdat(M->getOrInsertComdat(ThunkName));
739 GuestExit->setSection(".wowthk$aa");
741 IRBuilder<> B(BB);
742
743 // Load the global symbol as a pointer to the check function.
744 LoadInst *DispatchLoad = B.CreateLoad(PtrTy, DispatchFnGlobal);
745
746 // Create new dispatch call instruction.
747 Function *ExitThunk =
748 buildExitThunk(F->getFunctionType(), F->getAttributes());
749 CallInst *Dispatch =
750 B.CreateCall(DispatchFnType, DispatchLoad,
751 {UnmangledAlias, ExitThunk, UnmangledAlias->getAliasee()});
752
753 // Ensure that the first arguments are passed in the correct registers.
754 Dispatch->setCallingConv(CallingConv::CFGuard_Check);
755
757 CallInst *Call = B.CreateCall(Arm64Ty, Dispatch, Args);
759
760 if (Call->getType()->isVoidTy())
761 B.CreateRetVoid();
762 else
763 B.CreateRet(Call);
764
765 auto SRetAttr = F->getAttributes().getParamAttr(0, Attribute::StructRet);
766 auto InRegAttr = F->getAttributes().getParamAttr(0, Attribute::InReg);
767 if (SRetAttr.isValid() && !InRegAttr.isValid()) {
768 GuestExit->addParamAttr(0, SRetAttr);
769 Call->addParamAttr(0, SRetAttr);
770 }
771
772 MangledAlias->setAliasee(GuestExit);
773 return GuestExit;
774}
775
776// Lower an indirect call with inline code.
777void AArch64Arm64ECCallLowering::lowerCall(CallBase *CB) {
778 IRBuilder<> B(CB);
779 Value *CalledOperand = CB->getCalledOperand();
780
781 // If the indirect call is called within catchpad or cleanuppad,
782 // we need to copy "funclet" bundle of the call.
784 if (auto Bundle = CB->getOperandBundle(LLVMContext::OB_funclet))
785 Bundles.push_back(OperandBundleDef(*Bundle));
786
787 // Load the global symbol as a pointer to the check function.
788 Value *GuardFn;
789 if ((CFGuardModuleFlag == ControlFlowGuardMode::Enabled) &&
790 !CB->hasFnAttr("guard_nocf"))
791 GuardFn = GuardFnCFGlobal;
792 else
793 GuardFn = GuardFnGlobal;
794 LoadInst *GuardCheckLoad = B.CreateLoad(PtrTy, GuardFn);
795
796 // Create new call instruction. The CFGuard check should always be a call,
797 // even if the original CallBase is an Invoke or CallBr instruction.
798 Function *Thunk = buildExitThunk(CB->getFunctionType(), CB->getAttributes());
799 CallInst *GuardCheck =
800 B.CreateCall(GuardFnType, GuardCheckLoad, {CalledOperand, Thunk},
801 Bundles);
802 Value *GuardCheckDest = B.CreateExtractValue(GuardCheck, 0);
803 Value *GuardFinalDest = B.CreateExtractValue(GuardCheck, 1);
804
805 // Ensure that the first argument is passed in the correct register.
806 GuardCheck->setCallingConv(CallingConv::CFGuard_Check);
807
808 // Update the call: set the callee, and add a bundle with the final
809 // destination,
810 CB->setCalledOperand(GuardCheckDest);
811 OperandBundleDef OB("cfguardtarget", GuardFinalDest);
813 OB, CB->getIterator());
814 NewCall->copyMetadata(*CB);
815 CB->replaceAllUsesWith(NewCall);
816 CB->eraseFromParent();
817}
818
819bool AArch64Arm64ECCallLowering::runOnModule(Module &Mod) {
820 if (!GenerateThunks)
821 return false;
822
823 M = &Mod;
824
825 // Check if this module has the cfguard flag and read its value.
826 CFGuardModuleFlag = M->getControlFlowGuardMode();
827
828 // Warn if the module flag requests an unsupported CFGuard mechanism.
829 if (CFGuardModuleFlag == ControlFlowGuardMode::Enabled) {
831 Mod.getModuleFlag("cfguard-mechanism"))) {
832 auto MechanismOverride =
833 static_cast<ControlFlowGuardMechanism>(CI->getZExtValue());
834 if (MechanismOverride != ControlFlowGuardMechanism::Automatic &&
835 MechanismOverride != ControlFlowGuardMechanism::Check)
836 Mod.getContext().diagnose(
837 DiagnosticInfoGeneric("only the Check Control Flow Guard mechanism "
838 "is supported for Arm64EC",
839 DS_Warning));
840 }
841 }
842
843 PtrTy = PointerType::getUnqual(M->getContext());
844 I64Ty = Type::getInt64Ty(M->getContext());
845 VoidTy = Type::getVoidTy(M->getContext());
846
847 GuardFnType =
848 FunctionType::get(StructType::get(PtrTy, PtrTy), {PtrTy, PtrTy}, false);
849 DispatchFnType = FunctionType::get(PtrTy, {PtrTy, PtrTy, PtrTy}, false);
850 GuardFnCFGlobal = M->getOrInsertGlobal("__os_arm64x_check_icall_cfg", PtrTy);
851 GuardFnGlobal = M->getOrInsertGlobal("__os_arm64x_check_icall", PtrTy);
852 DispatchFnGlobal = M->getOrInsertGlobal("__os_arm64x_dispatch_call", PtrTy);
853
854 // Mangle names of function aliases and add the alias name to
855 // arm64ec_unmangled_name metadata to ensure a weak anti-dependency symbol is
856 // emitted for the alias as well. Do this early, before handling
857 // hybrid_patchable functions, to avoid mangling their aliases.
858 for (GlobalAlias &A : Mod.aliases()) {
859 auto F = dyn_cast_or_null<Function>(A.getAliaseeObject());
860 if (!F)
861 continue;
862 if (std::optional<std::string> MangledName =
864 F->addMetadata("arm64ec_unmangled_name",
866 MDString::get(M->getContext(), A.getName())));
867 A.setName(MangledName.value());
868 }
869 }
870
871 DenseMap<GlobalAlias *, GlobalAlias *> FnsMap;
872 SetVector<GlobalAlias *> PatchableFns;
873
874 for (Function &F : Mod) {
875 if (F.hasPersonalityFn()) {
876 GlobalValue *PersFn =
877 cast<GlobalValue>(F.getPersonalityFn()->stripPointerCasts());
878 if (PersFn->getValueType() && PersFn->getValueType()->isFunctionTy()) {
879 if (std::optional<std::string> MangledName =
881 PersFn->setName(MangledName.value());
882 }
883 }
884 }
885
886 if (!F.hasFnAttribute(Attribute::HybridPatchable) ||
887 F.isDeclarationForLinker() || F.hasLocalLinkage() ||
888 F.getName().ends_with(HybridPatchableTargetSuffix))
889 continue;
890
891 // Rename hybrid patchable functions and change callers to use a global
892 // alias instead.
893 if (std::optional<std::string> MangledName =
895 std::string OrigName(F.getName());
896 F.setName(MangledName.value() + HybridPatchableTargetSuffix);
897
898 // The unmangled symbol is a weak alias to an undefined symbol with the
899 // "EXP+" prefix. This undefined symbol is resolved by the linker by
900 // creating an x86 thunk that jumps back to the actual EC target. Since we
901 // can't represent that in IR, we create an alias to the target instead.
902 // The "EXP+" symbol is set as metadata, which is then used by
903 // emitGlobalAlias to emit the right alias.
904 auto *A =
907 MangledName.value(), &F);
908 F.replaceUsesWithIf(AM,
909 [](Use &U) { return isa<GlobalAlias>(U.getUser()); });
910 F.replaceAllUsesWith(A);
911 F.setMetadata("arm64ec_exp_name",
914 "EXP+" + MangledName.value())));
915 A->setAliasee(&F);
916 AM->setAliasee(&F);
917
918 if (F.hasDLLExportStorageClass()) {
919 A->setDLLStorageClass(GlobalValue::DLLExportStorageClass);
920 F.setDLLStorageClass(GlobalValue::DefaultStorageClass);
921 }
922
923 FnsMap[A] = AM;
924 PatchableFns.insert(A);
925 }
926 }
927
928 SetVector<GlobalValue *> DirectCalledFns;
929 for (Function &F : Mod)
930 if (!F.isDeclarationForLinker() &&
931 F.getCallingConv() != CallingConv::ARM64EC_Thunk_Native &&
932 F.getCallingConv() != CallingConv::ARM64EC_Thunk_X64)
933 processFunction(F, DirectCalledFns, FnsMap);
934
935 struct ThunkInfo {
936 Constant *Src;
937 Constant *Dst;
939 };
940 SmallVector<ThunkInfo> ThunkMapping;
941 for (Function &F : Mod) {
942 if (!F.isDeclarationForLinker() &&
943 (!F.hasLocalLinkage() || F.hasAddressTaken()) &&
944 F.getCallingConv() != CallingConv::ARM64EC_Thunk_Native &&
945 F.getCallingConv() != CallingConv::ARM64EC_Thunk_X64) {
946 if (!F.hasComdat())
947 F.setComdat(Mod.getOrInsertComdat(F.getName()));
948 ThunkMapping.push_back(
949 {&F, buildEntryThunk(&F), Arm64ECThunkType::Entry});
950 }
951 }
952 for (GlobalValue *O : DirectCalledFns) {
953 auto GA = dyn_cast<GlobalAlias>(O);
954 auto F = dyn_cast<Function>(GA ? GA->getAliasee() : O);
955 ThunkMapping.push_back(
956 {O, buildExitThunk(F->getFunctionType(), F->getAttributes()),
957 Arm64ECThunkType::Exit});
958 if (!GA && !F->hasDLLImportStorageClass())
959 ThunkMapping.push_back(
960 {buildGuestExitThunk(F), F, Arm64ECThunkType::GuestExit});
961 }
962 for (GlobalAlias *A : PatchableFns) {
963 Function *Thunk = buildPatchableThunk(A, FnsMap[A]);
964 ThunkMapping.push_back({Thunk, A, Arm64ECThunkType::GuestExit});
965 }
966
967 if (!ThunkMapping.empty()) {
968 SmallVector<Constant *> ThunkMappingArrayElems;
969 for (ThunkInfo &Thunk : ThunkMapping) {
970 ThunkMappingArrayElems.push_back(ConstantStruct::getAnon(
971 {Thunk.Src, Thunk.Dst,
972 ConstantInt::get(M->getContext(), APInt(32, uint8_t(Thunk.Kind)))}));
973 }
974 Constant *ThunkMappingArray = ConstantArray::get(
975 llvm::ArrayType::get(ThunkMappingArrayElems[0]->getType(),
976 ThunkMappingArrayElems.size()),
977 ThunkMappingArrayElems);
978 new GlobalVariable(Mod, ThunkMappingArray->getType(), /*isConstant*/ false,
979 GlobalValue::ExternalLinkage, ThunkMappingArray,
980 "llvm.arm64ec.symbolmap");
981 }
982
983 return true;
984}
985
986bool AArch64Arm64ECCallLowering::processFunction(
987 Function &F, SetVector<GlobalValue *> &DirectCalledFns,
988 DenseMap<GlobalAlias *, GlobalAlias *> &FnsMap) {
989 SmallVector<CallBase *, 8> IndirectCalls;
990
991 // For ARM64EC targets, a function definition's name is mangled differently
992 // from the normal symbol. We currently have no representation of this sort
993 // of symbol in IR, so we change the name to the mangled name, then store
994 // the unmangled name as metadata. Later passes that need the unmangled
995 // name (emitting the definition) can grab it from the metadata.
996 //
997 // FIXME: Handle functions with weak linkage?
998 if (!F.hasLocalLinkage() || F.hasAddressTaken()) {
999 if (std::optional<std::string> MangledName =
1001 F.addMetadata("arm64ec_unmangled_name",
1002 *MDNode::get(M->getContext(),
1003 MDString::get(M->getContext(), F.getName())));
1004 if (F.hasComdat() && F.getComdat()->getName() == F.getName()) {
1005 Comdat *MangledComdat = M->getOrInsertComdat(MangledName.value());
1006 SmallVector<GlobalObject *> ComdatUsers =
1007 to_vector(F.getComdat()->getUsers());
1008 for (GlobalObject *User : ComdatUsers)
1009 User->setComdat(MangledComdat);
1010 }
1011 F.setName(MangledName.value());
1012 }
1013 }
1014
1015 // Iterate over the instructions to find all indirect call/invoke/callbr
1016 // instructions. Make a separate list of pointers to indirect
1017 // call/invoke/callbr instructions because the original instructions will be
1018 // deleted as the checks are added.
1019 for (BasicBlock &BB : F) {
1020 for (Instruction &I : BB) {
1021 auto *CB = dyn_cast<CallBase>(&I);
1022 if (!CB || CB->getCallingConv() == CallingConv::ARM64EC_Thunk_X64 ||
1023 CB->isInlineAsm())
1024 continue;
1025
1026 // We need to instrument any call that isn't directly calling an
1027 // ARM64 function.
1028 //
1029 // FIXME: getCalledFunction() fails if there's a bitcast (e.g.
1030 // unprototyped functions in C)
1031 if (Function *F = CB->getCalledFunction()) {
1032 if (!LowerDirectToIndirect || F->hasLocalLinkage() ||
1033 F->isIntrinsic() || !F->isDeclarationForLinker())
1034 continue;
1035
1036 DirectCalledFns.insert(F);
1037 continue;
1038 }
1039
1040 // Use mangled global alias for direct calls to patchable functions.
1041 if (GlobalAlias *A = dyn_cast<GlobalAlias>(CB->getCalledOperand())) {
1042 auto I = FnsMap.find(A);
1043 if (I != FnsMap.end()) {
1044 CB->setCalledOperand(I->second);
1045 DirectCalledFns.insert(I->first);
1046 continue;
1047 }
1048 }
1049
1050 IndirectCalls.push_back(CB);
1051 ++Arm64ECCallsLowered;
1052 }
1053 }
1054
1055 if (IndirectCalls.empty())
1056 return false;
1057
1058 for (CallBase *CB : IndirectCalls)
1059 lowerCall(CB);
1060
1061 return true;
1062}
1063
1064char AArch64Arm64ECCallLowering::ID = 0;
1065INITIALIZE_PASS(AArch64Arm64ECCallLowering, "Arm64ECCallLowering",
1066 "AArch64Arm64ECCallLowering", false, false)
1067
1069 return new AArch64Arm64ECCallLowering;
1070}
static cl::opt< bool > LowerDirectToIndirect("arm64ec-lower-direct-to-indirect", cl::Hidden, cl::init(true))
static cl::opt< bool > GenerateThunks("arm64ec-generate-thunks", cl::Hidden, cl::init(true))
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
MachineBasicBlock MachineBasicBlock::iterator DebugLoc DL
static GCRegistry::Add< ErlangGC > A("erlang", "erlang-compatible garbage collector")
static GCRegistry::Add< CoreCLRGC > E("coreclr", "CoreCLR-compatible GC")
static GCRegistry::Add< OcamlGC > B("ocaml", "ocaml 3.10-compatible GC")
Module.h This file contains the declarations for the Module class.
lazy value info
#define F(x, y, z)
Definition MD5.cpp:54
#define I(x, y, z)
Definition MD5.cpp:57
Machine Check Debug Module
#define T
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)
Definition PassSupport.h:56
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)
Definition Statistic.h:171
static SymbolRef::Type getType(const Symbol *Sym)
Definition TapiFile.cpp:39
static LLVM_ABI ArrayType * get(Type *ElementType, uint64_t NumElements)
This static method is the primary way to construct an ArrayType.
bool isValid() const
Return true if the attribute is any kind of attribute.
Definition Attributes.h:261
LLVM_ABI Type * getValueAsType() const
Return the attribute's value as a Type.
static BasicBlock * Create(LLVMContext &Context, const Twine &Name="", Function *Parent=nullptr, BasicBlock *InsertBefore=nullptr)
Creates a new BasicBlock.
Definition BasicBlock.h:206
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
static LLVM_ABI CallBase * addOperandBundle(CallBase *CB, uint32_t ID, OperandBundleDef OB, InsertPosition InsertPt=nullptr)
Create a clone of CB with operand bundle OB added.
Value * getCalledOperand() const
FunctionType * getFunctionType() const
void setCalledOperand(Value *V)
AttributeList getAttributes() const
Return the attributes for this call.
void addParamAttr(unsigned ArgNo, Attribute::AttrKind Kind)
Adds the attribute to the indicated argument.
void setTailCallKind(TailCallKind TCK)
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.
Definition Constants.h:643
iterator find(const_arg_type_t< KeyT > Val)
Definition DenseMap.h:225
iterator end()
Definition DenseMap.h:143
static Function * Create(FunctionType *Ty, LinkageTypes Linkage, unsigned AddrSpace, const Twine &N="", Module *M=nullptr)
Definition Function.h:168
LLVM_ABI void setAliasee(Constant *Aliasee)
These methods retrieve and set alias target.
Definition Globals.cpp:662
const Constant * getAliasee() const
Definition GlobalAlias.h:87
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...
Definition Globals.cpp:630
@ DLLExportStorageClass
Function to be accessible from DLL.
Definition GlobalValue.h:77
@ WeakODRLinkage
Same, but only replaced by something equivalent.
Definition GlobalValue.h:58
@ ExternalLinkage
Externally visible function.
Definition GlobalValue.h:53
@ LinkOnceODRLinkage
Same, but only replaced by something equivalent.
Definition GlobalValue.h:56
Type * getValueType() const
LLVM_ABI InstListType::iterator eraseFromParent()
This method unlinks 'this' from the containing basic block and deletes it.
static MDTuple * get(LLVMContext &Context, ArrayRef< Metadata * > MDs)
Definition Metadata.h:1554
static LLVM_ABI MDString * get(LLVMContext &Context, StringRef Str)
Definition Metadata.cpp:614
ModulePass class - This class is used to implement unstructured interprocedural optimizations and ana...
Definition Pass.h:255
LLVMContext & getContext() const
Get the global data context.
Definition Module.h:287
Function * getFunction(StringRef Name) const
Look up the specified function in the module symbol table.
Definition Module.cpp:235
ControlFlowGuardMode getControlFlowGuardMode() const
Gets the Control Flow Guard mode.
Definition Module.cpp:974
Comdat * getOrInsertComdat(StringRef Name)
Return the Comdat in the module with the specified name.
Definition Module.cpp:621
const DataLayout & getDataLayout() const
Get the data layout for the module's target platform.
Definition Module.h:280
GlobalVariable * getOrInsertGlobal(StringRef Name, Type *Ty, function_ref< GlobalVariable *()> CreateGlobalCallback)
Look up the specified global in the module symbol table.
Definition Module.cpp:262
A container for an operand bundle being viewed as a set of values rather than a set of uses.
bool insert(const value_type &X)
Insert a new element into the SetVector.
Definition SetVector.h:151
void push_back(const T &Elt)
This is a 'vector' (really, a variable-sized array), optimized for the case when the array is small.
static LLVM_ABI StructType * get(LLVMContext &Context, ArrayRef< Type * > Elements, bool isPacked=false)
This static method is the primary way to create a literal StructType.
Definition Type.cpp:477
bool isPointerTy() const
True if this is an instance of PointerType.
Definition Type.h:282
bool isFloatTy() const
Return true if this is 'float', a 32-bit IEEE fp type.
Definition Type.h:155
bool isBFloatTy() const
Return true if this is 'bfloat', a 16-bit bfloat type.
Definition Type.h:147
bool isFP128Ty() const
Return true if this is 'fp128'.
Definition Type.h:164
bool isHalfTy() const
Return true if this is 'half', a 16-bit IEEE fp type.
Definition Type.h:144
bool isDoubleTy() const
Return true if this is 'double', a 64-bit IEEE fp type.
Definition Type.h:158
bool isFloatingPointTy() const
Return true if this is one of the floating-point types.
Definition Type.h:186
bool isFunctionTy() const
True if this is an instance of FunctionType.
Definition Type.h:273
static LLVM_ABI IntegerType * getIntNTy(LLVMContext &C, unsigned N)
Definition Type.cpp:313
bool isVoidTy() const
Return true if this is 'void'.
Definition Type.h:141
Type * getType() const
All values are typed, get the type of this value.
Definition Value.h:255
LLVM_ABI void setName(const Twine &Name)
Change the name of the value.
Definition Value.cpp:394
LLVM_ABI void replaceAllUsesWith(Value *V)
Change all uses of this to point to a new Value.
Definition Value.cpp:553
bool hasName() const
Definition Value.h:261
LLVM_ABI StringRef getName() const
Return a constant reference to the value's name.
Definition Value.cpp:319
self_iterator getIterator()
Definition ilist_node.h:123
This class implements an extremely fast bulk output stream that can only output to a stream.
Definition raw_ostream.h:53
CallInst * Call
constexpr char Align[]
Key for Kernel::Arg::Metadata::mAlign.
constexpr char Args[]
Key for Kernel::Metadata::mArgs.
constexpr char Attrs[]
Key for Kernel::Metadata::mAttrs.
Arm64ECThunkType
Definition COFF.h:860
@ GuestExit
Definition COFF.h:861
@ BasicBlock
Various leaf nodes.
Definition ISDOpcodes.h:81
@ OB
OB - OneByte - Set if this instruction has a one byte opcode.
initializer< Ty > init(const Ty &Val)
std::enable_if_t< detail::IsValidPointer< X, Y >::value, X * > dyn_extract_or_null(Y &&MD)
Extract a Value from Metadata, if any, allowing null.
Definition Metadata.h:709
@ User
could "use" a pointer
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.
Definition Mangler.cpp:292
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.
Definition STLExtras.h:840
decltype(auto) dyn_cast(const From &Val)
dyn_cast<X> - Return the argument parameter cast to the specified type.
Definition Casting.h:643
iterator_range< T > make_range(T x, T y)
Convenience function for iterating over sub-ranges.
RelativeUniformCounterPtr ValuesPtrExpr VTableAddr Value
Definition InstrProf.h:143
ControlFlowGuardMode
Definition CodeGen.h:177
auto dyn_cast_or_null(const Y &Val)
Definition Casting.h:753
ModulePass * createAArch64Arm64ECCallLoweringPass()
LLVM_ABI void report_fatal_error(Error Err, bool gen_crash_diag=true)
Definition Error.cpp:163
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...
class LLVM_GSL_OWNER SmallVector
Forward declaration of SmallVector so that calculateSmallVectorDefaultInlinedElements can reference s...
bool isa(const From &Val)
isa<X> - Return true if the parameter to the template is an instance of one of the template type argu...
Definition Casting.h:547
constexpr std::string_view HybridPatchableTargetSuffix
Definition Mangler.h:37
IRBuilder(LLVMContext &, FolderTy, InserterTy, MDNode *, ArrayRef< OperandBundleDef >) -> IRBuilder< FolderTy, InserterTy >
OperandBundleDefT< Value * > OperandBundleDef
Definition AutoUpgrade.h:34
decltype(auto) cast(const From &Val)
cast<X> - Return the argument parameter cast to the specified type.
Definition Casting.h:559
iterator_range< pointer_iterator< WrappedIteratorT > > make_pointer_range(RangeT &&Range)
Definition iterator.h:368
ControlFlowGuardMechanism
Definition CodeGen.h:187
constexpr uint64_t value() const
This is a hole in the type system and should not be abused.
Definition Alignment.h:77