1*0fca6ea1SDimitry Andric //===------ SemaAMDGPU.cpp ------- AMDGPU target-specific routines --------===// 2*0fca6ea1SDimitry Andric // 3*0fca6ea1SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4*0fca6ea1SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 5*0fca6ea1SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6*0fca6ea1SDimitry Andric // 7*0fca6ea1SDimitry Andric //===----------------------------------------------------------------------===// 8*0fca6ea1SDimitry Andric // 9*0fca6ea1SDimitry Andric // This file implements semantic analysis functions specific to AMDGPU. 10*0fca6ea1SDimitry Andric // 11*0fca6ea1SDimitry Andric //===----------------------------------------------------------------------===// 12*0fca6ea1SDimitry Andric 13*0fca6ea1SDimitry Andric #include "clang/Sema/SemaAMDGPU.h" 14*0fca6ea1SDimitry Andric #include "clang/Basic/DiagnosticSema.h" 15*0fca6ea1SDimitry Andric #include "clang/Basic/TargetBuiltins.h" 16*0fca6ea1SDimitry Andric #include "clang/Sema/Ownership.h" 17*0fca6ea1SDimitry Andric #include "clang/Sema/Sema.h" 18*0fca6ea1SDimitry Andric #include "llvm/Support/AtomicOrdering.h" 19*0fca6ea1SDimitry Andric #include <cstdint> 20*0fca6ea1SDimitry Andric 21*0fca6ea1SDimitry Andric namespace clang { 22*0fca6ea1SDimitry Andric 23*0fca6ea1SDimitry Andric SemaAMDGPU::SemaAMDGPU(Sema &S) : SemaBase(S) {} 24*0fca6ea1SDimitry Andric 25*0fca6ea1SDimitry Andric bool SemaAMDGPU::CheckAMDGCNBuiltinFunctionCall(unsigned BuiltinID, 26*0fca6ea1SDimitry Andric CallExpr *TheCall) { 27*0fca6ea1SDimitry Andric // position of memory order and scope arguments in the builtin 28*0fca6ea1SDimitry Andric unsigned OrderIndex, ScopeIndex; 29*0fca6ea1SDimitry Andric switch (BuiltinID) { 30*0fca6ea1SDimitry Andric case AMDGPU::BI__builtin_amdgcn_global_load_lds: { 31*0fca6ea1SDimitry Andric constexpr const int SizeIdx = 2; 32*0fca6ea1SDimitry Andric llvm::APSInt Size; 33*0fca6ea1SDimitry Andric Expr *ArgExpr = TheCall->getArg(SizeIdx); 34*0fca6ea1SDimitry Andric [[maybe_unused]] ExprResult R = 35*0fca6ea1SDimitry Andric SemaRef.VerifyIntegerConstantExpression(ArgExpr, &Size); 36*0fca6ea1SDimitry Andric assert(!R.isInvalid()); 37*0fca6ea1SDimitry Andric switch (Size.getSExtValue()) { 38*0fca6ea1SDimitry Andric case 1: 39*0fca6ea1SDimitry Andric case 2: 40*0fca6ea1SDimitry Andric case 4: 41*0fca6ea1SDimitry Andric return false; 42*0fca6ea1SDimitry Andric default: 43*0fca6ea1SDimitry Andric Diag(ArgExpr->getExprLoc(), 44*0fca6ea1SDimitry Andric diag::err_amdgcn_global_load_lds_size_invalid_value) 45*0fca6ea1SDimitry Andric << ArgExpr->getSourceRange(); 46*0fca6ea1SDimitry Andric Diag(ArgExpr->getExprLoc(), 47*0fca6ea1SDimitry Andric diag::note_amdgcn_global_load_lds_size_valid_value) 48*0fca6ea1SDimitry Andric << ArgExpr->getSourceRange(); 49*0fca6ea1SDimitry Andric return true; 50*0fca6ea1SDimitry Andric } 51*0fca6ea1SDimitry Andric } 52*0fca6ea1SDimitry Andric case AMDGPU::BI__builtin_amdgcn_get_fpenv: 53*0fca6ea1SDimitry Andric case AMDGPU::BI__builtin_amdgcn_set_fpenv: 54*0fca6ea1SDimitry Andric return false; 55*0fca6ea1SDimitry Andric case AMDGPU::BI__builtin_amdgcn_atomic_inc32: 56*0fca6ea1SDimitry Andric case AMDGPU::BI__builtin_amdgcn_atomic_inc64: 57*0fca6ea1SDimitry Andric case AMDGPU::BI__builtin_amdgcn_atomic_dec32: 58*0fca6ea1SDimitry Andric case AMDGPU::BI__builtin_amdgcn_atomic_dec64: 59*0fca6ea1SDimitry Andric OrderIndex = 2; 60*0fca6ea1SDimitry Andric ScopeIndex = 3; 61*0fca6ea1SDimitry Andric break; 62*0fca6ea1SDimitry Andric case AMDGPU::BI__builtin_amdgcn_fence: 63*0fca6ea1SDimitry Andric OrderIndex = 0; 64*0fca6ea1SDimitry Andric ScopeIndex = 1; 65*0fca6ea1SDimitry Andric break; 66*0fca6ea1SDimitry Andric default: 67*0fca6ea1SDimitry Andric return false; 68*0fca6ea1SDimitry Andric } 69*0fca6ea1SDimitry Andric 70*0fca6ea1SDimitry Andric ExprResult Arg = TheCall->getArg(OrderIndex); 71*0fca6ea1SDimitry Andric auto ArgExpr = Arg.get(); 72*0fca6ea1SDimitry Andric Expr::EvalResult ArgResult; 73*0fca6ea1SDimitry Andric 74*0fca6ea1SDimitry Andric if (!ArgExpr->EvaluateAsInt(ArgResult, getASTContext())) 75*0fca6ea1SDimitry Andric return Diag(ArgExpr->getExprLoc(), diag::err_typecheck_expect_int) 76*0fca6ea1SDimitry Andric << ArgExpr->getType(); 77*0fca6ea1SDimitry Andric auto Ord = ArgResult.Val.getInt().getZExtValue(); 78*0fca6ea1SDimitry Andric 79*0fca6ea1SDimitry Andric // Check validity of memory ordering as per C11 / C++11's memody model. 80*0fca6ea1SDimitry Andric // Only fence needs check. Atomic dec/inc allow all memory orders. 81*0fca6ea1SDimitry Andric if (!llvm::isValidAtomicOrderingCABI(Ord)) 82*0fca6ea1SDimitry Andric return Diag(ArgExpr->getBeginLoc(), 83*0fca6ea1SDimitry Andric diag::warn_atomic_op_has_invalid_memory_order) 84*0fca6ea1SDimitry Andric << 0 << ArgExpr->getSourceRange(); 85*0fca6ea1SDimitry Andric switch (static_cast<llvm::AtomicOrderingCABI>(Ord)) { 86*0fca6ea1SDimitry Andric case llvm::AtomicOrderingCABI::relaxed: 87*0fca6ea1SDimitry Andric case llvm::AtomicOrderingCABI::consume: 88*0fca6ea1SDimitry Andric if (BuiltinID == AMDGPU::BI__builtin_amdgcn_fence) 89*0fca6ea1SDimitry Andric return Diag(ArgExpr->getBeginLoc(), 90*0fca6ea1SDimitry Andric diag::warn_atomic_op_has_invalid_memory_order) 91*0fca6ea1SDimitry Andric << 0 << ArgExpr->getSourceRange(); 92*0fca6ea1SDimitry Andric break; 93*0fca6ea1SDimitry Andric case llvm::AtomicOrderingCABI::acquire: 94*0fca6ea1SDimitry Andric case llvm::AtomicOrderingCABI::release: 95*0fca6ea1SDimitry Andric case llvm::AtomicOrderingCABI::acq_rel: 96*0fca6ea1SDimitry Andric case llvm::AtomicOrderingCABI::seq_cst: 97*0fca6ea1SDimitry Andric break; 98*0fca6ea1SDimitry Andric } 99*0fca6ea1SDimitry Andric 100*0fca6ea1SDimitry Andric Arg = TheCall->getArg(ScopeIndex); 101*0fca6ea1SDimitry Andric ArgExpr = Arg.get(); 102*0fca6ea1SDimitry Andric Expr::EvalResult ArgResult1; 103*0fca6ea1SDimitry Andric // Check that sync scope is a constant literal 104*0fca6ea1SDimitry Andric if (!ArgExpr->EvaluateAsConstantExpr(ArgResult1, getASTContext())) 105*0fca6ea1SDimitry Andric return Diag(ArgExpr->getExprLoc(), diag::err_expr_not_string_literal) 106*0fca6ea1SDimitry Andric << ArgExpr->getType(); 107*0fca6ea1SDimitry Andric 108*0fca6ea1SDimitry Andric return false; 109*0fca6ea1SDimitry Andric } 110*0fca6ea1SDimitry Andric 111*0fca6ea1SDimitry Andric static bool 112*0fca6ea1SDimitry Andric checkAMDGPUFlatWorkGroupSizeArguments(Sema &S, Expr *MinExpr, Expr *MaxExpr, 113*0fca6ea1SDimitry Andric const AMDGPUFlatWorkGroupSizeAttr &Attr) { 114*0fca6ea1SDimitry Andric // Accept template arguments for now as they depend on something else. 115*0fca6ea1SDimitry Andric // We'll get to check them when they eventually get instantiated. 116*0fca6ea1SDimitry Andric if (MinExpr->isValueDependent() || MaxExpr->isValueDependent()) 117*0fca6ea1SDimitry Andric return false; 118*0fca6ea1SDimitry Andric 119*0fca6ea1SDimitry Andric uint32_t Min = 0; 120*0fca6ea1SDimitry Andric if (!S.checkUInt32Argument(Attr, MinExpr, Min, 0)) 121*0fca6ea1SDimitry Andric return true; 122*0fca6ea1SDimitry Andric 123*0fca6ea1SDimitry Andric uint32_t Max = 0; 124*0fca6ea1SDimitry Andric if (!S.checkUInt32Argument(Attr, MaxExpr, Max, 1)) 125*0fca6ea1SDimitry Andric return true; 126*0fca6ea1SDimitry Andric 127*0fca6ea1SDimitry Andric if (Min == 0 && Max != 0) { 128*0fca6ea1SDimitry Andric S.Diag(Attr.getLocation(), diag::err_attribute_argument_invalid) 129*0fca6ea1SDimitry Andric << &Attr << 0; 130*0fca6ea1SDimitry Andric return true; 131*0fca6ea1SDimitry Andric } 132*0fca6ea1SDimitry Andric if (Min > Max) { 133*0fca6ea1SDimitry Andric S.Diag(Attr.getLocation(), diag::err_attribute_argument_invalid) 134*0fca6ea1SDimitry Andric << &Attr << 1; 135*0fca6ea1SDimitry Andric return true; 136*0fca6ea1SDimitry Andric } 137*0fca6ea1SDimitry Andric 138*0fca6ea1SDimitry Andric return false; 139*0fca6ea1SDimitry Andric } 140*0fca6ea1SDimitry Andric 141*0fca6ea1SDimitry Andric AMDGPUFlatWorkGroupSizeAttr * 142*0fca6ea1SDimitry Andric SemaAMDGPU::CreateAMDGPUFlatWorkGroupSizeAttr(const AttributeCommonInfo &CI, 143*0fca6ea1SDimitry Andric Expr *MinExpr, Expr *MaxExpr) { 144*0fca6ea1SDimitry Andric ASTContext &Context = getASTContext(); 145*0fca6ea1SDimitry Andric AMDGPUFlatWorkGroupSizeAttr TmpAttr(Context, CI, MinExpr, MaxExpr); 146*0fca6ea1SDimitry Andric 147*0fca6ea1SDimitry Andric if (checkAMDGPUFlatWorkGroupSizeArguments(SemaRef, MinExpr, MaxExpr, TmpAttr)) 148*0fca6ea1SDimitry Andric return nullptr; 149*0fca6ea1SDimitry Andric return ::new (Context) 150*0fca6ea1SDimitry Andric AMDGPUFlatWorkGroupSizeAttr(Context, CI, MinExpr, MaxExpr); 151*0fca6ea1SDimitry Andric } 152*0fca6ea1SDimitry Andric 153*0fca6ea1SDimitry Andric void SemaAMDGPU::addAMDGPUFlatWorkGroupSizeAttr(Decl *D, 154*0fca6ea1SDimitry Andric const AttributeCommonInfo &CI, 155*0fca6ea1SDimitry Andric Expr *MinExpr, Expr *MaxExpr) { 156*0fca6ea1SDimitry Andric if (auto *Attr = CreateAMDGPUFlatWorkGroupSizeAttr(CI, MinExpr, MaxExpr)) 157*0fca6ea1SDimitry Andric D->addAttr(Attr); 158*0fca6ea1SDimitry Andric } 159*0fca6ea1SDimitry Andric 160*0fca6ea1SDimitry Andric void SemaAMDGPU::handleAMDGPUFlatWorkGroupSizeAttr(Decl *D, 161*0fca6ea1SDimitry Andric const ParsedAttr &AL) { 162*0fca6ea1SDimitry Andric Expr *MinExpr = AL.getArgAsExpr(0); 163*0fca6ea1SDimitry Andric Expr *MaxExpr = AL.getArgAsExpr(1); 164*0fca6ea1SDimitry Andric 165*0fca6ea1SDimitry Andric addAMDGPUFlatWorkGroupSizeAttr(D, AL, MinExpr, MaxExpr); 166*0fca6ea1SDimitry Andric } 167*0fca6ea1SDimitry Andric 168*0fca6ea1SDimitry Andric static bool checkAMDGPUWavesPerEUArguments(Sema &S, Expr *MinExpr, 169*0fca6ea1SDimitry Andric Expr *MaxExpr, 170*0fca6ea1SDimitry Andric const AMDGPUWavesPerEUAttr &Attr) { 171*0fca6ea1SDimitry Andric if (S.DiagnoseUnexpandedParameterPack(MinExpr) || 172*0fca6ea1SDimitry Andric (MaxExpr && S.DiagnoseUnexpandedParameterPack(MaxExpr))) 173*0fca6ea1SDimitry Andric return true; 174*0fca6ea1SDimitry Andric 175*0fca6ea1SDimitry Andric // Accept template arguments for now as they depend on something else. 176*0fca6ea1SDimitry Andric // We'll get to check them when they eventually get instantiated. 177*0fca6ea1SDimitry Andric if (MinExpr->isValueDependent() || (MaxExpr && MaxExpr->isValueDependent())) 178*0fca6ea1SDimitry Andric return false; 179*0fca6ea1SDimitry Andric 180*0fca6ea1SDimitry Andric uint32_t Min = 0; 181*0fca6ea1SDimitry Andric if (!S.checkUInt32Argument(Attr, MinExpr, Min, 0)) 182*0fca6ea1SDimitry Andric return true; 183*0fca6ea1SDimitry Andric 184*0fca6ea1SDimitry Andric uint32_t Max = 0; 185*0fca6ea1SDimitry Andric if (MaxExpr && !S.checkUInt32Argument(Attr, MaxExpr, Max, 1)) 186*0fca6ea1SDimitry Andric return true; 187*0fca6ea1SDimitry Andric 188*0fca6ea1SDimitry Andric if (Min == 0 && Max != 0) { 189*0fca6ea1SDimitry Andric S.Diag(Attr.getLocation(), diag::err_attribute_argument_invalid) 190*0fca6ea1SDimitry Andric << &Attr << 0; 191*0fca6ea1SDimitry Andric return true; 192*0fca6ea1SDimitry Andric } 193*0fca6ea1SDimitry Andric if (Max != 0 && Min > Max) { 194*0fca6ea1SDimitry Andric S.Diag(Attr.getLocation(), diag::err_attribute_argument_invalid) 195*0fca6ea1SDimitry Andric << &Attr << 1; 196*0fca6ea1SDimitry Andric return true; 197*0fca6ea1SDimitry Andric } 198*0fca6ea1SDimitry Andric 199*0fca6ea1SDimitry Andric return false; 200*0fca6ea1SDimitry Andric } 201*0fca6ea1SDimitry Andric 202*0fca6ea1SDimitry Andric AMDGPUWavesPerEUAttr * 203*0fca6ea1SDimitry Andric SemaAMDGPU::CreateAMDGPUWavesPerEUAttr(const AttributeCommonInfo &CI, 204*0fca6ea1SDimitry Andric Expr *MinExpr, Expr *MaxExpr) { 205*0fca6ea1SDimitry Andric ASTContext &Context = getASTContext(); 206*0fca6ea1SDimitry Andric AMDGPUWavesPerEUAttr TmpAttr(Context, CI, MinExpr, MaxExpr); 207*0fca6ea1SDimitry Andric 208*0fca6ea1SDimitry Andric if (checkAMDGPUWavesPerEUArguments(SemaRef, MinExpr, MaxExpr, TmpAttr)) 209*0fca6ea1SDimitry Andric return nullptr; 210*0fca6ea1SDimitry Andric 211*0fca6ea1SDimitry Andric return ::new (Context) AMDGPUWavesPerEUAttr(Context, CI, MinExpr, MaxExpr); 212*0fca6ea1SDimitry Andric } 213*0fca6ea1SDimitry Andric 214*0fca6ea1SDimitry Andric void SemaAMDGPU::addAMDGPUWavesPerEUAttr(Decl *D, const AttributeCommonInfo &CI, 215*0fca6ea1SDimitry Andric Expr *MinExpr, Expr *MaxExpr) { 216*0fca6ea1SDimitry Andric if (auto *Attr = CreateAMDGPUWavesPerEUAttr(CI, MinExpr, MaxExpr)) 217*0fca6ea1SDimitry Andric D->addAttr(Attr); 218*0fca6ea1SDimitry Andric } 219*0fca6ea1SDimitry Andric 220*0fca6ea1SDimitry Andric void SemaAMDGPU::handleAMDGPUWavesPerEUAttr(Decl *D, const ParsedAttr &AL) { 221*0fca6ea1SDimitry Andric if (!AL.checkAtLeastNumArgs(SemaRef, 1) || !AL.checkAtMostNumArgs(SemaRef, 2)) 222*0fca6ea1SDimitry Andric return; 223*0fca6ea1SDimitry Andric 224*0fca6ea1SDimitry Andric Expr *MinExpr = AL.getArgAsExpr(0); 225*0fca6ea1SDimitry Andric Expr *MaxExpr = (AL.getNumArgs() > 1) ? AL.getArgAsExpr(1) : nullptr; 226*0fca6ea1SDimitry Andric 227*0fca6ea1SDimitry Andric addAMDGPUWavesPerEUAttr(D, AL, MinExpr, MaxExpr); 228*0fca6ea1SDimitry Andric } 229*0fca6ea1SDimitry Andric 230*0fca6ea1SDimitry Andric void SemaAMDGPU::handleAMDGPUNumSGPRAttr(Decl *D, const ParsedAttr &AL) { 231*0fca6ea1SDimitry Andric uint32_t NumSGPR = 0; 232*0fca6ea1SDimitry Andric Expr *NumSGPRExpr = AL.getArgAsExpr(0); 233*0fca6ea1SDimitry Andric if (!SemaRef.checkUInt32Argument(AL, NumSGPRExpr, NumSGPR)) 234*0fca6ea1SDimitry Andric return; 235*0fca6ea1SDimitry Andric 236*0fca6ea1SDimitry Andric D->addAttr(::new (getASTContext()) 237*0fca6ea1SDimitry Andric AMDGPUNumSGPRAttr(getASTContext(), AL, NumSGPR)); 238*0fca6ea1SDimitry Andric } 239*0fca6ea1SDimitry Andric 240*0fca6ea1SDimitry Andric void SemaAMDGPU::handleAMDGPUNumVGPRAttr(Decl *D, const ParsedAttr &AL) { 241*0fca6ea1SDimitry Andric uint32_t NumVGPR = 0; 242*0fca6ea1SDimitry Andric Expr *NumVGPRExpr = AL.getArgAsExpr(0); 243*0fca6ea1SDimitry Andric if (!SemaRef.checkUInt32Argument(AL, NumVGPRExpr, NumVGPR)) 244*0fca6ea1SDimitry Andric return; 245*0fca6ea1SDimitry Andric 246*0fca6ea1SDimitry Andric D->addAttr(::new (getASTContext()) 247*0fca6ea1SDimitry Andric AMDGPUNumVGPRAttr(getASTContext(), AL, NumVGPR)); 248*0fca6ea1SDimitry Andric } 249*0fca6ea1SDimitry Andric 250*0fca6ea1SDimitry Andric static bool 251*0fca6ea1SDimitry Andric checkAMDGPUMaxNumWorkGroupsArguments(Sema &S, Expr *XExpr, Expr *YExpr, 252*0fca6ea1SDimitry Andric Expr *ZExpr, 253*0fca6ea1SDimitry Andric const AMDGPUMaxNumWorkGroupsAttr &Attr) { 254*0fca6ea1SDimitry Andric if (S.DiagnoseUnexpandedParameterPack(XExpr) || 255*0fca6ea1SDimitry Andric (YExpr && S.DiagnoseUnexpandedParameterPack(YExpr)) || 256*0fca6ea1SDimitry Andric (ZExpr && S.DiagnoseUnexpandedParameterPack(ZExpr))) 257*0fca6ea1SDimitry Andric return true; 258*0fca6ea1SDimitry Andric 259*0fca6ea1SDimitry Andric // Accept template arguments for now as they depend on something else. 260*0fca6ea1SDimitry Andric // We'll get to check them when they eventually get instantiated. 261*0fca6ea1SDimitry Andric if (XExpr->isValueDependent() || (YExpr && YExpr->isValueDependent()) || 262*0fca6ea1SDimitry Andric (ZExpr && ZExpr->isValueDependent())) 263*0fca6ea1SDimitry Andric return false; 264*0fca6ea1SDimitry Andric 265*0fca6ea1SDimitry Andric uint32_t NumWG = 0; 266*0fca6ea1SDimitry Andric Expr *Exprs[3] = {XExpr, YExpr, ZExpr}; 267*0fca6ea1SDimitry Andric for (int i = 0; i < 3; i++) { 268*0fca6ea1SDimitry Andric if (Exprs[i]) { 269*0fca6ea1SDimitry Andric if (!S.checkUInt32Argument(Attr, Exprs[i], NumWG, i, 270*0fca6ea1SDimitry Andric /*StrictlyUnsigned=*/true)) 271*0fca6ea1SDimitry Andric return true; 272*0fca6ea1SDimitry Andric if (NumWG == 0) { 273*0fca6ea1SDimitry Andric S.Diag(Attr.getLoc(), diag::err_attribute_argument_is_zero) 274*0fca6ea1SDimitry Andric << &Attr << Exprs[i]->getSourceRange(); 275*0fca6ea1SDimitry Andric return true; 276*0fca6ea1SDimitry Andric } 277*0fca6ea1SDimitry Andric } 278*0fca6ea1SDimitry Andric } 279*0fca6ea1SDimitry Andric 280*0fca6ea1SDimitry Andric return false; 281*0fca6ea1SDimitry Andric } 282*0fca6ea1SDimitry Andric 283*0fca6ea1SDimitry Andric AMDGPUMaxNumWorkGroupsAttr *SemaAMDGPU::CreateAMDGPUMaxNumWorkGroupsAttr( 284*0fca6ea1SDimitry Andric const AttributeCommonInfo &CI, Expr *XExpr, Expr *YExpr, Expr *ZExpr) { 285*0fca6ea1SDimitry Andric ASTContext &Context = getASTContext(); 286*0fca6ea1SDimitry Andric AMDGPUMaxNumWorkGroupsAttr TmpAttr(Context, CI, XExpr, YExpr, ZExpr); 287*0fca6ea1SDimitry Andric 288*0fca6ea1SDimitry Andric if (checkAMDGPUMaxNumWorkGroupsArguments(SemaRef, XExpr, YExpr, ZExpr, 289*0fca6ea1SDimitry Andric TmpAttr)) 290*0fca6ea1SDimitry Andric return nullptr; 291*0fca6ea1SDimitry Andric 292*0fca6ea1SDimitry Andric return ::new (Context) 293*0fca6ea1SDimitry Andric AMDGPUMaxNumWorkGroupsAttr(Context, CI, XExpr, YExpr, ZExpr); 294*0fca6ea1SDimitry Andric } 295*0fca6ea1SDimitry Andric 296*0fca6ea1SDimitry Andric void SemaAMDGPU::addAMDGPUMaxNumWorkGroupsAttr(Decl *D, 297*0fca6ea1SDimitry Andric const AttributeCommonInfo &CI, 298*0fca6ea1SDimitry Andric Expr *XExpr, Expr *YExpr, 299*0fca6ea1SDimitry Andric Expr *ZExpr) { 300*0fca6ea1SDimitry Andric if (auto *Attr = CreateAMDGPUMaxNumWorkGroupsAttr(CI, XExpr, YExpr, ZExpr)) 301*0fca6ea1SDimitry Andric D->addAttr(Attr); 302*0fca6ea1SDimitry Andric } 303*0fca6ea1SDimitry Andric 304*0fca6ea1SDimitry Andric void SemaAMDGPU::handleAMDGPUMaxNumWorkGroupsAttr(Decl *D, 305*0fca6ea1SDimitry Andric const ParsedAttr &AL) { 306*0fca6ea1SDimitry Andric Expr *YExpr = (AL.getNumArgs() > 1) ? AL.getArgAsExpr(1) : nullptr; 307*0fca6ea1SDimitry Andric Expr *ZExpr = (AL.getNumArgs() > 2) ? AL.getArgAsExpr(2) : nullptr; 308*0fca6ea1SDimitry Andric addAMDGPUMaxNumWorkGroupsAttr(D, AL, AL.getArgAsExpr(0), YExpr, ZExpr); 309*0fca6ea1SDimitry Andric } 310*0fca6ea1SDimitry Andric 311*0fca6ea1SDimitry Andric } // namespace clang 312