| 1 | //===---- TargetInfo.cpp - Encapsulate target details -----------*- 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 | // These classes wrap the information about a call or function |
| 10 | // definition used to handle ABI compliancy. |
| 11 | // |
| 12 | //===----------------------------------------------------------------------===// |
| 13 | |
| 14 | #include "TargetInfo.h" |
| 15 | #include "ABIInfo.h" |
| 16 | #include "ABIInfoImpl.h" |
| 17 | #include "CodeGenFunction.h" |
| 18 | #include "clang/Basic/CodeGenOptions.h" |
| 19 | #include "clang/CodeGen/CGFunctionInfo.h" |
| 20 | #include "llvm/ADT/StringExtras.h" |
| 21 | #include "llvm/ADT/Twine.h" |
| 22 | #include "llvm/IR/Function.h" |
| 23 | #include "llvm/IR/Type.h" |
| 24 | #include "llvm/Support/raw_ostream.h" |
| 25 | |
| 26 | using namespace clang; |
| 27 | using namespace CodeGen; |
| 28 | |
| 29 | LLVM_DUMP_METHOD void ABIArgInfo::dump() const { |
| 30 | raw_ostream &OS = llvm::errs(); |
| 31 | OS << "(ABIArgInfo Kind=" ; |
| 32 | switch (TheKind) { |
| 33 | case Direct: |
| 34 | OS << "Direct Type=" ; |
| 35 | if (llvm::Type *Ty = getCoerceToType()) |
| 36 | Ty->print(O&: OS); |
| 37 | else |
| 38 | OS << "null" ; |
| 39 | break; |
| 40 | case Extend: |
| 41 | OS << "Extend" ; |
| 42 | break; |
| 43 | case Ignore: |
| 44 | OS << "Ignore" ; |
| 45 | break; |
| 46 | case InAlloca: |
| 47 | OS << "InAlloca Offset=" << getInAllocaFieldIndex(); |
| 48 | break; |
| 49 | case Indirect: |
| 50 | OS << "Indirect Align=" << getIndirectAlign().getQuantity() |
| 51 | << " ByVal=" << getIndirectByVal() |
| 52 | << " Realign=" << getIndirectRealign(); |
| 53 | break; |
| 54 | case IndirectAliased: |
| 55 | OS << "Indirect Align=" << getIndirectAlign().getQuantity() |
| 56 | << " AadrSpace=" << getIndirectAddrSpace() |
| 57 | << " Realign=" << getIndirectRealign(); |
| 58 | break; |
| 59 | case Expand: |
| 60 | OS << "Expand" ; |
| 61 | break; |
| 62 | case CoerceAndExpand: |
| 63 | OS << "CoerceAndExpand Type=" ; |
| 64 | getCoerceAndExpandType()->print(O&: OS); |
| 65 | break; |
| 66 | } |
| 67 | OS << ")\n" ; |
| 68 | } |
| 69 | |
| 70 | TargetCodeGenInfo::TargetCodeGenInfo(std::unique_ptr<ABIInfo> Info) |
| 71 | : Info(std::move(Info)) {} |
| 72 | |
| 73 | TargetCodeGenInfo::~TargetCodeGenInfo() = default; |
| 74 | |
| 75 | // If someone can figure out a general rule for this, that would be great. |
| 76 | // It's probably just doomed to be platform-dependent, though. |
| 77 | unsigned TargetCodeGenInfo::getSizeOfUnwindException() const { |
| 78 | // Verified for: |
| 79 | // x86-64 FreeBSD, Linux, Darwin |
| 80 | // x86-32 FreeBSD, Linux, Darwin |
| 81 | // PowerPC Linux |
| 82 | // ARM Darwin (*not* EABI) |
| 83 | // AArch64 Linux |
| 84 | return 32; |
| 85 | } |
| 86 | |
| 87 | bool TargetCodeGenInfo::isNoProtoCallVariadic(const CallArgList &args, |
| 88 | const FunctionNoProtoType *fnType) const { |
| 89 | // The following conventions are known to require this to be false: |
| 90 | // x86_stdcall |
| 91 | // MIPS |
| 92 | // For everything else, we just prefer false unless we opt out. |
| 93 | return false; |
| 94 | } |
| 95 | |
| 96 | void |
| 97 | TargetCodeGenInfo::getDependentLibraryOption(llvm::StringRef Lib, |
| 98 | llvm::SmallString<24> &Opt) const { |
| 99 | // This assumes the user is passing a library name like "rt" instead of a |
| 100 | // filename like "librt.a/so", and that they don't care whether it's static or |
| 101 | // dynamic. |
| 102 | Opt = "-l" ; |
| 103 | Opt += Lib; |
| 104 | } |
| 105 | |
| 106 | unsigned TargetCodeGenInfo::getOpenCLKernelCallingConv() const { |
| 107 | // OpenCL kernels are called via an explicit runtime API with arguments |
| 108 | // set with clSetKernelArg(), not as normal sub-functions. |
| 109 | // Return SPIR_KERNEL by default as the kernel calling convention to |
| 110 | // ensure the fingerprint is fixed such way that each OpenCL argument |
| 111 | // gets one matching argument in the produced kernel function argument |
| 112 | // list to enable feasible implementation of clSetKernelArg() with |
| 113 | // aggregates etc. In case we would use the default C calling conv here, |
| 114 | // clSetKernelArg() might break depending on the target-specific |
| 115 | // conventions; different targets might split structs passed as values |
| 116 | // to multiple function arguments etc. |
| 117 | return llvm::CallingConv::SPIR_KERNEL; |
| 118 | } |
| 119 | |
| 120 | void TargetCodeGenInfo::setOCLKernelStubCallingConvention( |
| 121 | const FunctionType *&FT) const { |
| 122 | FT = getABIInfo().getContext().adjustFunctionType( |
| 123 | Fn: FT, EInfo: FT->getExtInfo().withCallingConv(cc: CC_C)); |
| 124 | } |
| 125 | |
| 126 | llvm::Constant *TargetCodeGenInfo::getNullPointer(const CodeGen::CodeGenModule &CGM, |
| 127 | llvm::PointerType *T, QualType QT) const { |
| 128 | return llvm::ConstantPointerNull::get(T); |
| 129 | } |
| 130 | |
| 131 | LangAS TargetCodeGenInfo::getGlobalVarAddressSpace(CodeGenModule &CGM, |
| 132 | const VarDecl *D) const { |
| 133 | assert(!CGM.getLangOpts().OpenCL && |
| 134 | !(CGM.getLangOpts().CUDA && CGM.getLangOpts().CUDAIsDevice) && |
| 135 | "Address space agnostic languages only" ); |
| 136 | return D ? D->getType().getAddressSpace() : LangAS::Default; |
| 137 | } |
| 138 | |
| 139 | llvm::Value *TargetCodeGenInfo::performAddrSpaceCast( |
| 140 | CodeGen::CodeGenFunction &CGF, llvm::Value *Src, LangAS SrcAddr, |
| 141 | llvm::Type *DestTy, bool isNonNull) const { |
| 142 | // Since target may map different address spaces in AST to the same address |
| 143 | // space, an address space conversion may end up as a bitcast. |
| 144 | if (auto *C = dyn_cast<llvm::Constant>(Val: Src)) |
| 145 | return performAddrSpaceCast(CGM&: CGF.CGM, V: C, SrcAddr, DestTy); |
| 146 | // Try to preserve the source's name to make IR more readable. |
| 147 | return CGF.Builder.CreateAddrSpaceCast( |
| 148 | V: Src, DestTy, Name: Src->hasName() ? Src->getName() + ".ascast" : "" ); |
| 149 | } |
| 150 | |
| 151 | llvm::Constant * |
| 152 | TargetCodeGenInfo::performAddrSpaceCast(CodeGenModule &CGM, llvm::Constant *Src, |
| 153 | LangAS SrcAddr, |
| 154 | llvm::Type *DestTy) const { |
| 155 | // Since target may map different address spaces in AST to the same address |
| 156 | // space, an address space conversion may end up as a bitcast. |
| 157 | return llvm::ConstantExpr::getPointerCast(C: Src, Ty: DestTy); |
| 158 | } |
| 159 | |
| 160 | llvm::SyncScope::ID |
| 161 | TargetCodeGenInfo::getLLVMSyncScopeID(const LangOptions &LangOpts, |
| 162 | SyncScope Scope, |
| 163 | llvm::AtomicOrdering Ordering, |
| 164 | llvm::LLVMContext &Ctx) const { |
| 165 | return Ctx.getOrInsertSyncScopeID(SSN: "" ); /* default sync scope */ |
| 166 | } |
| 167 | |
| 168 | void TargetCodeGenInfo::addStackProbeTargetAttributes( |
| 169 | const Decl *D, llvm::GlobalValue *GV, CodeGen::CodeGenModule &CGM) const { |
| 170 | if (llvm::Function *Fn = dyn_cast_or_null<llvm::Function>(Val: GV)) { |
| 171 | if (CGM.getCodeGenOpts().StackProbeSize != 4096) |
| 172 | Fn->addFnAttr(Kind: "stack-probe-size" , |
| 173 | Val: llvm::utostr(X: CGM.getCodeGenOpts().StackProbeSize)); |
| 174 | if (CGM.getCodeGenOpts().NoStackArgProbe) |
| 175 | Fn->addFnAttr(Kind: "no-stack-arg-probe" ); |
| 176 | } |
| 177 | } |
| 178 | |
| 179 | /// Create an OpenCL kernel for an enqueued block. |
| 180 | /// |
| 181 | /// The kernel has the same function type as the block invoke function. Its |
| 182 | /// name is the name of the block invoke function postfixed with "_kernel". |
| 183 | /// It simply calls the block invoke function then returns. |
| 184 | llvm::Value *TargetCodeGenInfo::createEnqueuedBlockKernel( |
| 185 | CodeGenFunction &CGF, llvm::Function *Invoke, llvm::Type *BlockTy) const { |
| 186 | auto *InvokeFT = Invoke->getFunctionType(); |
| 187 | auto &C = CGF.getLLVMContext(); |
| 188 | std::string Name = Invoke->getName().str() + "_kernel" ; |
| 189 | auto *FT = llvm::FunctionType::get(Result: llvm::Type::getVoidTy(C), |
| 190 | Params: InvokeFT->params(), isVarArg: false); |
| 191 | auto *F = llvm::Function::Create(Ty: FT, Linkage: llvm::GlobalValue::ExternalLinkage, N: Name, |
| 192 | M: &CGF.CGM.getModule()); |
| 193 | llvm::CallingConv::ID KernelCC = |
| 194 | CGF.getTypes().ClangCallConvToLLVMCallConv(CC: CallingConv::CC_DeviceKernel); |
| 195 | F->setCallingConv(KernelCC); |
| 196 | |
| 197 | llvm::AttrBuilder KernelAttrs(C); |
| 198 | |
| 199 | // FIXME: This is missing setTargetAttributes |
| 200 | CGF.CGM.addDefaultFunctionDefinitionAttributes(attrs&: KernelAttrs); |
| 201 | F->addFnAttrs(Attrs: KernelAttrs); |
| 202 | |
| 203 | auto IP = CGF.Builder.saveIP(); |
| 204 | auto *BB = llvm::BasicBlock::Create(Context&: C, Name: "entry" , Parent: F); |
| 205 | auto &Builder = CGF.Builder; |
| 206 | Builder.SetInsertPoint(BB); |
| 207 | llvm::SmallVector<llvm::Value *, 2> Args(llvm::make_pointer_range(Range: F->args())); |
| 208 | llvm::CallInst *Call = Builder.CreateCall(Callee: Invoke, Args); |
| 209 | Call->setCallingConv(Invoke->getCallingConv()); |
| 210 | |
| 211 | Builder.CreateRetVoid(); |
| 212 | Builder.restoreIP(IP); |
| 213 | return F; |
| 214 | } |
| 215 | |
| 216 | void TargetCodeGenInfo::setBranchProtectionFnAttributes( |
| 217 | const TargetInfo::BranchProtectionInfo &BPI, llvm::Function &F) { |
| 218 | // Called on already created and initialized function where attributes already |
| 219 | // set from command line attributes but some might need to be removed as the |
| 220 | // actual BPI is different. |
| 221 | if (BPI.SignReturnAddr != LangOptions::SignReturnAddressScopeKind::None) { |
| 222 | F.addFnAttr(Kind: "sign-return-address" , Val: BPI.getSignReturnAddrStr()); |
| 223 | F.addFnAttr(Kind: "sign-return-address-key" , Val: BPI.getSignKeyStr()); |
| 224 | } else { |
| 225 | if (F.hasFnAttribute(Kind: "sign-return-address" )) |
| 226 | F.removeFnAttr(Kind: "sign-return-address" ); |
| 227 | if (F.hasFnAttribute(Kind: "sign-return-address-key" )) |
| 228 | F.removeFnAttr(Kind: "sign-return-address-key" ); |
| 229 | } |
| 230 | |
| 231 | auto AddRemoveAttributeAsSet = [&](bool Set, const StringRef &ModAttr) { |
| 232 | if (Set) |
| 233 | F.addFnAttr(Kind: ModAttr); |
| 234 | else if (F.hasFnAttribute(Kind: ModAttr)) |
| 235 | F.removeFnAttr(Kind: ModAttr); |
| 236 | }; |
| 237 | |
| 238 | AddRemoveAttributeAsSet(BPI.BranchTargetEnforcement, |
| 239 | "branch-target-enforcement" ); |
| 240 | AddRemoveAttributeAsSet(BPI.BranchProtectionPAuthLR, |
| 241 | "branch-protection-pauth-lr" ); |
| 242 | AddRemoveAttributeAsSet(BPI.GuardedControlStack, "guarded-control-stack" ); |
| 243 | } |
| 244 | |
| 245 | void TargetCodeGenInfo::initBranchProtectionFnAttributes( |
| 246 | const TargetInfo::BranchProtectionInfo &BPI, llvm::AttrBuilder &FuncAttrs) { |
| 247 | // Only used for initializing attributes in the AttrBuilder, which will not |
| 248 | // contain any of these attributes so no need to remove anything. |
| 249 | if (BPI.SignReturnAddr != LangOptions::SignReturnAddressScopeKind::None) { |
| 250 | FuncAttrs.addAttribute(A: "sign-return-address" , V: BPI.getSignReturnAddrStr()); |
| 251 | FuncAttrs.addAttribute(A: "sign-return-address-key" , V: BPI.getSignKeyStr()); |
| 252 | } |
| 253 | if (BPI.BranchTargetEnforcement) |
| 254 | FuncAttrs.addAttribute(A: "branch-target-enforcement" ); |
| 255 | if (BPI.BranchProtectionPAuthLR) |
| 256 | FuncAttrs.addAttribute(A: "branch-protection-pauth-lr" ); |
| 257 | if (BPI.GuardedControlStack) |
| 258 | FuncAttrs.addAttribute(A: "guarded-control-stack" ); |
| 259 | } |
| 260 | |
| 261 | void TargetCodeGenInfo::setPointerAuthFnAttributes( |
| 262 | const PointerAuthOptions &Opts, llvm::Function &F) { |
| 263 | auto UpdateAttr = [&F](bool AttrShouldExist, StringRef AttrName) { |
| 264 | if (AttrShouldExist && !F.hasFnAttribute(Kind: AttrName)) |
| 265 | F.addFnAttr(Kind: AttrName); |
| 266 | if (!AttrShouldExist && F.hasFnAttribute(Kind: AttrName)) |
| 267 | F.removeFnAttr(Kind: AttrName); |
| 268 | }; |
| 269 | UpdateAttr(Opts.ReturnAddresses, "ptrauth-returns" ); |
| 270 | UpdateAttr((bool)Opts.FunctionPointers, "ptrauth-calls" ); |
| 271 | UpdateAttr(Opts.AuthTraps, "ptrauth-auth-traps" ); |
| 272 | UpdateAttr(Opts.IndirectGotos, "ptrauth-indirect-gotos" ); |
| 273 | UpdateAttr(Opts.AArch64JumpTableHardening, "aarch64-jump-table-hardening" ); |
| 274 | } |
| 275 | |
| 276 | void TargetCodeGenInfo::initPointerAuthFnAttributes( |
| 277 | const PointerAuthOptions &Opts, llvm::AttrBuilder &FuncAttrs) { |
| 278 | if (Opts.ReturnAddresses) |
| 279 | FuncAttrs.addAttribute(A: "ptrauth-returns" ); |
| 280 | if (Opts.FunctionPointers) |
| 281 | FuncAttrs.addAttribute(A: "ptrauth-calls" ); |
| 282 | if (Opts.AuthTraps) |
| 283 | FuncAttrs.addAttribute(A: "ptrauth-auth-traps" ); |
| 284 | if (Opts.IndirectGotos) |
| 285 | FuncAttrs.addAttribute(A: "ptrauth-indirect-gotos" ); |
| 286 | if (Opts.AArch64JumpTableHardening) |
| 287 | FuncAttrs.addAttribute(A: "aarch64-jump-table-hardening" ); |
| 288 | } |
| 289 | |
| 290 | namespace { |
| 291 | class DefaultTargetCodeGenInfo : public TargetCodeGenInfo { |
| 292 | public: |
| 293 | DefaultTargetCodeGenInfo(CodeGen::CodeGenTypes &CGT) |
| 294 | : TargetCodeGenInfo(std::make_unique<DefaultABIInfo>(args&: CGT)) {} |
| 295 | }; |
| 296 | } // namespace |
| 297 | |
| 298 | std::unique_ptr<TargetCodeGenInfo> |
| 299 | CodeGen::createDefaultTargetCodeGenInfo(CodeGenModule &CGM) { |
| 300 | return std::make_unique<DefaultTargetCodeGenInfo>(args&: CGM.getTypes()); |
| 301 | } |
| 302 | |