| 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::getDeviceKernelCallingConv() const { |
| 107 | if (getABIInfo().getContext().getLangOpts().OpenCL) { |
| 108 | // Device kernels are called via an explicit runtime API with arguments, |
| 109 | // such as set with clSetKernelArg() for OpenCL, not as normal |
| 110 | // sub-functions. Return SPIR_KERNEL by default as the kernel calling |
| 111 | // convention to ensure the fingerprint is fixed such way that each kernel |
| 112 | // argument gets one matching argument in the produced kernel function |
| 113 | // argument list to enable feasible implementation of clSetKernelArg() with |
| 114 | // aggregates etc. In case we would use the default C calling conv here, |
| 115 | // clSetKernelArg() might break depending on the target-specific |
| 116 | // conventions; different targets might split structs passed as values |
| 117 | // to multiple function arguments etc. |
| 118 | return llvm::CallingConv::SPIR_KERNEL; |
| 119 | } |
| 120 | llvm_unreachable("Unknown kernel calling convention" ); |
| 121 | } |
| 122 | |
| 123 | void TargetCodeGenInfo::setOCLKernelStubCallingConvention( |
| 124 | const FunctionType *&FT) const { |
| 125 | FT = getABIInfo().getContext().adjustFunctionType( |
| 126 | Fn: FT, EInfo: FT->getExtInfo().withCallingConv(cc: CC_C)); |
| 127 | } |
| 128 | |
| 129 | llvm::Constant *TargetCodeGenInfo::getNullPointer(const CodeGen::CodeGenModule &CGM, |
| 130 | llvm::PointerType *T, QualType QT) const { |
| 131 | return llvm::ConstantPointerNull::get(T); |
| 132 | } |
| 133 | |
| 134 | LangAS TargetCodeGenInfo::getGlobalVarAddressSpace(CodeGenModule &CGM, |
| 135 | const VarDecl *D) const { |
| 136 | assert(!CGM.getLangOpts().OpenCL && |
| 137 | !(CGM.getLangOpts().CUDA && CGM.getLangOpts().CUDAIsDevice) && |
| 138 | "Address space agnostic languages only" ); |
| 139 | return D ? D->getType().getAddressSpace() : LangAS::Default; |
| 140 | } |
| 141 | |
| 142 | llvm::Value *TargetCodeGenInfo::performAddrSpaceCast( |
| 143 | CodeGen::CodeGenFunction &CGF, llvm::Value *Src, LangAS SrcAddr, |
| 144 | llvm::Type *DestTy, bool isNonNull) const { |
| 145 | // Since target may map different address spaces in AST to the same address |
| 146 | // space, an address space conversion may end up as a bitcast. |
| 147 | if (auto *C = dyn_cast<llvm::Constant>(Val: Src)) |
| 148 | return performAddrSpaceCast(CGM&: CGF.CGM, V: C, SrcAddr, DestTy); |
| 149 | // Try to preserve the source's name to make IR more readable. |
| 150 | return CGF.Builder.CreateAddrSpaceCast( |
| 151 | V: Src, DestTy, Name: Src->hasName() ? Src->getName() + ".ascast" : "" ); |
| 152 | } |
| 153 | |
| 154 | llvm::Constant * |
| 155 | TargetCodeGenInfo::performAddrSpaceCast(CodeGenModule &CGM, llvm::Constant *Src, |
| 156 | LangAS SrcAddr, |
| 157 | llvm::Type *DestTy) const { |
| 158 | // Since target may map different address spaces in AST to the same address |
| 159 | // space, an address space conversion may end up as a bitcast. |
| 160 | return llvm::ConstantExpr::getPointerCast(C: Src, Ty: DestTy); |
| 161 | } |
| 162 | |
| 163 | llvm::SyncScope::ID |
| 164 | TargetCodeGenInfo::getLLVMSyncScopeID(const LangOptions &LangOpts, |
| 165 | SyncScope Scope, |
| 166 | llvm::AtomicOrdering Ordering, |
| 167 | llvm::LLVMContext &Ctx) const { |
| 168 | return Ctx.getOrInsertSyncScopeID(SSN: "" ); /* default sync scope */ |
| 169 | } |
| 170 | |
| 171 | void TargetCodeGenInfo::addStackProbeTargetAttributes( |
| 172 | const Decl *D, llvm::GlobalValue *GV, CodeGen::CodeGenModule &CGM) const { |
| 173 | if (llvm::Function *Fn = dyn_cast_or_null<llvm::Function>(Val: GV)) { |
| 174 | if (CGM.getCodeGenOpts().StackProbeSize != 4096) |
| 175 | Fn->addFnAttr(Kind: "stack-probe-size" , |
| 176 | Val: llvm::utostr(X: CGM.getCodeGenOpts().StackProbeSize)); |
| 177 | if (CGM.getCodeGenOpts().NoStackArgProbe) |
| 178 | Fn->addFnAttr(Kind: "no-stack-arg-probe" ); |
| 179 | } |
| 180 | } |
| 181 | |
| 182 | /// Create an OpenCL kernel for an enqueued block. |
| 183 | /// |
| 184 | /// The kernel has the same function type as the block invoke function. Its |
| 185 | /// name is the name of the block invoke function postfixed with "_kernel". |
| 186 | /// It simply calls the block invoke function then returns. |
| 187 | llvm::Value *TargetCodeGenInfo::createEnqueuedBlockKernel( |
| 188 | CodeGenFunction &CGF, llvm::Function *Invoke, llvm::Type *BlockTy) const { |
| 189 | auto *InvokeFT = Invoke->getFunctionType(); |
| 190 | auto &C = CGF.getLLVMContext(); |
| 191 | std::string Name = Invoke->getName().str() + "_kernel" ; |
| 192 | auto *FT = llvm::FunctionType::get(Result: llvm::Type::getVoidTy(C), |
| 193 | Params: InvokeFT->params(), isVarArg: false); |
| 194 | auto *F = llvm::Function::Create(Ty: FT, Linkage: llvm::GlobalValue::ExternalLinkage, N: Name, |
| 195 | M: &CGF.CGM.getModule()); |
| 196 | llvm::CallingConv::ID KernelCC = |
| 197 | CGF.getTypes().ClangCallConvToLLVMCallConv(CC: CallingConv::CC_DeviceKernel); |
| 198 | F->setCallingConv(KernelCC); |
| 199 | |
| 200 | llvm::AttrBuilder KernelAttrs(C); |
| 201 | |
| 202 | // FIXME: This is missing setTargetAttributes |
| 203 | CGF.CGM.addDefaultFunctionDefinitionAttributes(attrs&: KernelAttrs); |
| 204 | F->addFnAttrs(Attrs: KernelAttrs); |
| 205 | |
| 206 | auto IP = CGF.Builder.saveIP(); |
| 207 | auto *BB = llvm::BasicBlock::Create(Context&: C, Name: "entry" , Parent: F); |
| 208 | auto &Builder = CGF.Builder; |
| 209 | Builder.SetInsertPoint(BB); |
| 210 | llvm::SmallVector<llvm::Value *, 2> Args(llvm::make_pointer_range(Range: F->args())); |
| 211 | llvm::CallInst *Call = Builder.CreateCall(Callee: Invoke, Args); |
| 212 | Call->setCallingConv(Invoke->getCallingConv()); |
| 213 | |
| 214 | Builder.CreateRetVoid(); |
| 215 | Builder.restoreIP(IP); |
| 216 | return F; |
| 217 | } |
| 218 | |
| 219 | void TargetCodeGenInfo::setBranchProtectionFnAttributes( |
| 220 | const TargetInfo::BranchProtectionInfo &BPI, llvm::Function &F) { |
| 221 | // Called on already created and initialized function where attributes already |
| 222 | // set from command line attributes but some might need to be removed as the |
| 223 | // actual BPI is different. |
| 224 | if (BPI.SignReturnAddr != LangOptions::SignReturnAddressScopeKind::None) { |
| 225 | F.addFnAttr(Kind: "sign-return-address" , Val: BPI.getSignReturnAddrStr()); |
| 226 | F.addFnAttr(Kind: "sign-return-address-key" , Val: BPI.getSignKeyStr()); |
| 227 | } else { |
| 228 | if (F.hasFnAttribute(Kind: "sign-return-address" )) |
| 229 | F.removeFnAttr(Kind: "sign-return-address" ); |
| 230 | if (F.hasFnAttribute(Kind: "sign-return-address-key" )) |
| 231 | F.removeFnAttr(Kind: "sign-return-address-key" ); |
| 232 | } |
| 233 | |
| 234 | auto AddRemoveAttributeAsSet = [&](bool Set, const StringRef &ModAttr) { |
| 235 | if (Set) |
| 236 | F.addFnAttr(Kind: ModAttr); |
| 237 | else if (F.hasFnAttribute(Kind: ModAttr)) |
| 238 | F.removeFnAttr(Kind: ModAttr); |
| 239 | }; |
| 240 | |
| 241 | AddRemoveAttributeAsSet(BPI.BranchTargetEnforcement, |
| 242 | "branch-target-enforcement" ); |
| 243 | AddRemoveAttributeAsSet(BPI.BranchProtectionPAuthLR, |
| 244 | "branch-protection-pauth-lr" ); |
| 245 | AddRemoveAttributeAsSet(BPI.GuardedControlStack, "guarded-control-stack" ); |
| 246 | } |
| 247 | |
| 248 | void TargetCodeGenInfo::initBranchProtectionFnAttributes( |
| 249 | const TargetInfo::BranchProtectionInfo &BPI, llvm::AttrBuilder &FuncAttrs) { |
| 250 | // Only used for initializing attributes in the AttrBuilder, which will not |
| 251 | // contain any of these attributes so no need to remove anything. |
| 252 | if (BPI.SignReturnAddr != LangOptions::SignReturnAddressScopeKind::None) { |
| 253 | FuncAttrs.addAttribute(A: "sign-return-address" , V: BPI.getSignReturnAddrStr()); |
| 254 | FuncAttrs.addAttribute(A: "sign-return-address-key" , V: BPI.getSignKeyStr()); |
| 255 | } |
| 256 | if (BPI.BranchTargetEnforcement) |
| 257 | FuncAttrs.addAttribute(A: "branch-target-enforcement" ); |
| 258 | if (BPI.BranchProtectionPAuthLR) |
| 259 | FuncAttrs.addAttribute(A: "branch-protection-pauth-lr" ); |
| 260 | if (BPI.GuardedControlStack) |
| 261 | FuncAttrs.addAttribute(A: "guarded-control-stack" ); |
| 262 | } |
| 263 | |
| 264 | void TargetCodeGenInfo::setPointerAuthFnAttributes( |
| 265 | const PointerAuthOptions &Opts, llvm::Function &F) { |
| 266 | auto UpdateAttr = [&F](bool AttrShouldExist, StringRef AttrName) { |
| 267 | if (AttrShouldExist && !F.hasFnAttribute(Kind: AttrName)) |
| 268 | F.addFnAttr(Kind: AttrName); |
| 269 | if (!AttrShouldExist && F.hasFnAttribute(Kind: AttrName)) |
| 270 | F.removeFnAttr(Kind: AttrName); |
| 271 | }; |
| 272 | UpdateAttr(Opts.ReturnAddresses, "ptrauth-returns" ); |
| 273 | UpdateAttr((bool)Opts.FunctionPointers, "ptrauth-calls" ); |
| 274 | UpdateAttr(Opts.AuthTraps, "ptrauth-auth-traps" ); |
| 275 | UpdateAttr(Opts.IndirectGotos, "ptrauth-indirect-gotos" ); |
| 276 | UpdateAttr(Opts.AArch64JumpTableHardening, "aarch64-jump-table-hardening" ); |
| 277 | } |
| 278 | |
| 279 | void TargetCodeGenInfo::initPointerAuthFnAttributes( |
| 280 | const PointerAuthOptions &Opts, llvm::AttrBuilder &FuncAttrs) { |
| 281 | if (Opts.ReturnAddresses) |
| 282 | FuncAttrs.addAttribute(A: "ptrauth-returns" ); |
| 283 | if (Opts.FunctionPointers) |
| 284 | FuncAttrs.addAttribute(A: "ptrauth-calls" ); |
| 285 | if (Opts.AuthTraps) |
| 286 | FuncAttrs.addAttribute(A: "ptrauth-auth-traps" ); |
| 287 | if (Opts.IndirectGotos) |
| 288 | FuncAttrs.addAttribute(A: "ptrauth-indirect-gotos" ); |
| 289 | if (Opts.AArch64JumpTableHardening) |
| 290 | FuncAttrs.addAttribute(A: "aarch64-jump-table-hardening" ); |
| 291 | } |
| 292 | |
| 293 | namespace { |
| 294 | class DefaultTargetCodeGenInfo : public TargetCodeGenInfo { |
| 295 | public: |
| 296 | DefaultTargetCodeGenInfo(CodeGen::CodeGenTypes &CGT) |
| 297 | : TargetCodeGenInfo(std::make_unique<DefaultABIInfo>(args&: CGT)) {} |
| 298 | }; |
| 299 | } // namespace |
| 300 | |
| 301 | std::unique_ptr<TargetCodeGenInfo> |
| 302 | CodeGen::createDefaultTargetCodeGenInfo(CodeGenModule &CGM) { |
| 303 | return std::make_unique<DefaultTargetCodeGenInfo>(args&: CGM.getTypes()); |
| 304 | } |
| 305 | |