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