================
@@ -697,4 +707,299 @@ void SemaAMDGPU::handleAMDGPUMaxNumWorkGroupsAttr(Decl *D,
   addAMDGPUMaxNumWorkGroupsAttr(D, AL, AL.getArgAsExpr(0), YExpr, ZExpr);
 }
 
+Expr *SemaAMDGPU::ExpandAMDGPUPredicateBI(CallExpr *CE) {
+  ASTContext &Ctx = getASTContext();
+  QualType BoolTy = Ctx.getLogicalOperationType();
+  llvm::APInt False = llvm::APInt::getZero(Ctx.getIntWidth(BoolTy));
+  llvm::APInt True = llvm::APInt::getAllOnes(Ctx.getIntWidth(BoolTy));
+  SourceLocation Loc = CE->getExprLoc();
+
+  if (!CE->getBuiltinCallee())
+    return *ExpandedPredicates
+                .insert(IntegerLiteral::Create(Ctx, False, BoolTy, Loc))
+                .first;
+
+  bool P = false;
+  unsigned BI = CE->getBuiltinCallee();
+  if (Ctx.BuiltinInfo.isAuxBuiltinID(BI))
+    BI = Ctx.BuiltinInfo.getAuxBuiltinID(BI);
+
+  if (BI == AMDGPU::BI__builtin_amdgcn_processor_is) {
+    auto *GFX = dyn_cast<StringLiteral>(CE->getArg(0)->IgnoreParenCasts());
+    if (!GFX) {
+      Diag(Loc, diag::err_amdgcn_processor_is_arg_not_literal);
+      return nullptr;
+    }
+
+    StringRef N = GFX->getString();
+    const TargetInfo &TI = Ctx.getTargetInfo();
+    const TargetInfo *AuxTI = Ctx.getAuxTargetInfo();
+    if (!TI.isValidCPUName(N) && (!AuxTI || !AuxTI->isValidCPUName(N))) {
+      Diag(Loc, diag::err_amdgcn_processor_is_arg_invalid_value) << N;
+      SmallVector<StringRef, 32> ValidList;
+      if (TI.getTriple().getVendor() == llvm::Triple::VendorType::AMD)
+        TI.fillValidCPUList(ValidList);
+      else if (AuxTI) // Since the BI is present it must be an AMDGPU triple.
+        AuxTI->fillValidCPUList(ValidList);
+      if (!ValidList.empty())
+        Diag(Loc, diag::note_amdgcn_processor_is_valid_options)
+            << llvm::join(ValidList, ", ");
+      return nullptr;
+    }
+    if (Ctx.getTargetInfo().getTriple().isSPIRV()) {
+      CE->setType(BoolTy);
+      return *ExpandedPredicates.insert(CE).first;
+    }
+
+    if (auto TID = Ctx.getTargetInfo().getTargetID())
+      P = TID->find(N) == 0;
+  } else {
+    Expr *Arg = CE->getArg(0);
+    if (!Arg || Arg->getType() != Ctx.BuiltinFnTy) {
+      Diag(Loc, diag::err_amdgcn_is_invocable_arg_invalid_value) << Arg;
+      return nullptr;
+    }
+
+    if (Ctx.getTargetInfo().getTriple().isSPIRV()) {
+      CE->setType(BoolTy);
+      return *ExpandedPredicates.insert(CE).first;
+    }
+
+    auto *FD = cast<FunctionDecl>(Arg->getReferencedDeclOfCallee());
+
+    StringRef RF = Ctx.BuiltinInfo.getRequiredFeatures(FD->getBuiltinID());
+    llvm::StringMap<bool> CF;
+    Ctx.getFunctionFeatureMap(CF, FD);
+
+    P = Builtin::evaluateRequiredTargetFeatures(RF, CF);
+  }
+
+  return *ExpandedPredicates
+              .insert(
+                  IntegerLiteral::Create(Ctx, P ? True : False, BoolTy, Loc))
+              .first;
+}
+
+bool SemaAMDGPU::IsPredicate(Expr *E) const {
+  return ExpandedPredicates.contains(E);
+}
+
+void SemaAMDGPU::AddPotentiallyUnguardedBuiltinUser(FunctionDecl *FD) {
+  PotentiallyUnguardedBuiltinUsers.insert(FD);
+}
+
+bool SemaAMDGPU::HasPotentiallyUnguardedBuiltinUsage(FunctionDecl *FD) const {
+  return PotentiallyUnguardedBuiltinUsers.contains(FD);
+}
+
+namespace {
+/// This class implements -Wamdgpu-unguarded-builtin-usage.
+///
+/// This is done with a traversal of the AST of a function that includes a
+/// call to a target specific builtin. Whenever we encounter an \c if of the
+/// form: \c if(__builtin_amdgcn_is_invocable), we consider the then statement
+/// guarded.
+class DiagnoseUnguardedBuiltins : public DynamicRecursiveASTVisitor {
+  // TODO: this could eventually be extended to consider what happens when 
there
+  //       are multiple target architectures specified via 
target("arch=gfxXXX")
+  //       target("arch=gfxyyy") etc., as well as feature disabling via "-XXX".
+  Sema &SemaRef;
+
+  SmallVector<StringRef> TargetFeatures;
+  SmallVector<std::pair<SourceLocation, StringRef>> CurrentGFXIP;
+  SmallVector<unsigned> GuardedBuiltins;
+
+  static Expr *FindPredicate(Expr *Cond) {
+    if (auto *CE = dyn_cast<CallExpr>(Cond)) {
+      if (CE->getBuiltinCallee() == AMDGPU::BI__builtin_amdgcn_is_invocable ||
+          CE->getBuiltinCallee() == AMDGPU::BI__builtin_amdgcn_processor_is)
+        return Cond;
+    } else if (auto *UO = dyn_cast<UnaryOperator>(Cond)) {
+      return FindPredicate(UO->getSubExpr());
+    } else if (auto *BO = dyn_cast<BinaryOperator>(Cond)) {
+      if ((Cond = FindPredicate(BO->getLHS())))
+        return Cond;
+      return FindPredicate(BO->getRHS());
+    }
+    return nullptr;
+  }
+
+  bool EnterPredicateGuardedContext(CallExpr *P);
+  void ExitPredicateGuardedContext(bool WasProcessorCheck);
+  bool TraverseGuardedStmt(Stmt *S, CallExpr *P);
+
+public:
+  DiagnoseUnguardedBuiltins(Sema &SemaRef) : SemaRef(SemaRef) {
+    if (auto *TAT = SemaRef.getCurFunctionDecl(true)->getAttr<TargetAttr>()) {
+      // We use the somewhat misnamed x86 accessors because they provide 
exactly
+      // what we require.
+      TAT->getX86AddedFeatures(TargetFeatures);
+      if (auto GFXIP = TAT->getX86Architecture())
+        CurrentGFXIP.emplace_back(TAT->getLocation(), *GFXIP);
+    }
+  }
+
+  bool TraverseLambdaExpr(LambdaExpr *LE) override {
+    if (SemaRef.AMDGPU().HasPotentiallyUnguardedBuiltinUsage(
+            LE->getCallOperator()))
+      return true; // We have already handled this.
+    return DynamicRecursiveASTVisitor::TraverseLambdaExpr(LE);
+  }
+
+  bool TraverseStmt(Stmt *S) override {
+    if (!S)
+      return true;
+    return DynamicRecursiveASTVisitor::TraverseStmt(S);
+  }
+
+  void IssueDiagnostics(Stmt *S) { TraverseStmt(S); }
+
+  bool TraverseIfStmt(IfStmt *If) override {
+    if (auto *CE = dyn_cast_or_null<CallExpr>(FindPredicate(If->getCond())))
+      return TraverseGuardedStmt(If, CE);
+    return DynamicRecursiveASTVisitor::TraverseIfStmt(If);
+  }
+
+  bool TraverseCaseStmt(CaseStmt *CS) override {
+    return TraverseStmt(CS->getSubStmt());
+  }
+
+  bool TraverseConditionalOperator(ConditionalOperator *CO) override {
+    if (auto *CE = dyn_cast_or_null<CallExpr>(FindPredicate(CO->getCond())))
+      return TraverseGuardedStmt(CO, CE);
+    return DynamicRecursiveASTVisitor::TraverseConditionalOperator(CO);
+  }
+
+  bool VisitAsmStmt(AsmStmt *ASM) override;
+  bool VisitCallExpr(CallExpr *CE) override;
+};
+
+bool DiagnoseUnguardedBuiltins::EnterPredicateGuardedContext(CallExpr *P) {
+  bool IsProcessorCheck =
+      P->getBuiltinCallee() == AMDGPU::BI__builtin_amdgcn_processor_is;
+
+  if (IsProcessorCheck) {
+    StringRef G = cast<clang::StringLiteral>(P->getArg(0))->getString();
+    // TODO: handle generic ISAs.
+    if (!CurrentGFXIP.empty() && G != CurrentGFXIP.back().second) {
+      SemaRef.Diag(P->getExprLoc(),
+                   diag::err_amdgcn_conflicting_is_processor_options)
+          << P;
+      SemaRef.Diag(CurrentGFXIP.back().first,
+                   diag::note_amdgcn_previous_is_processor_guard);
+    }
+    CurrentGFXIP.emplace_back(P->getExprLoc(), G);
+  } else {
+    auto *FD = cast<FunctionDecl>(
+        cast<DeclRefExpr>(P->getArg(0))->getReferencedDeclOfCallee());
+    unsigned ID = FD->getBuiltinID();
+    StringRef F = SemaRef.getASTContext().BuiltinInfo.getRequiredFeatures(ID);
+    GuardedBuiltins.push_back(ID);
+  }
+
+  return IsProcessorCheck;
+}
+
+void DiagnoseUnguardedBuiltins::ExitPredicateGuardedContext(bool WasProcCheck) 
{
+  if (WasProcCheck)
+    CurrentGFXIP.pop_back();
+  else
+    GuardedBuiltins.pop_back();
+}
+
+inline std::pair<Stmt *, Stmt *> GetTraversalOrder(Stmt *S) {
+  std::pair<Stmt *, Stmt *> Ordered;
+  Expr *Condition = nullptr;
+
+  if (auto *CO = dyn_cast<ConditionalOperator>(S)) {
+    Condition = CO->getCond();
+    Ordered = {CO->getTrueExpr(), CO->getFalseExpr()};
+  } else if (auto *If = dyn_cast<IfStmt>(S)) {
+    Condition = If->getCond();
+    Ordered = {If->getThen(), If->getElse()};
+  }
+
+  if (auto *UO = dyn_cast<UnaryOperator>(Condition))
+    if (UO->getOpcode() == UnaryOperatorKind::UO_LNot)
+      std::swap(Ordered.first, Ordered.second);
+
+  return Ordered;
+}
+
+bool DiagnoseUnguardedBuiltins::TraverseGuardedStmt(Stmt *S, CallExpr *P) {
+  assert(S && "Unexpected missing Statement!");
+  assert(P && "Unexpected missing Predicate!");
+
+  auto [Guarded, Unguarded] = GetTraversalOrder(S);
+
+  bool WasProcessorCheck = EnterPredicateGuardedContext(P);
+
+  bool Continue = TraverseStmt(Guarded);
+
+  ExitPredicateGuardedContext(WasProcessorCheck);
+
+  return Continue && TraverseStmt(Unguarded);
+}
+
+bool DiagnoseUnguardedBuiltins::VisitAsmStmt(AsmStmt *ASM) {
+  // TODO: should we check if the ASM is valid for the target? Can we?
+  if (!CurrentGFXIP.empty())
+    return true;
+
+  std::string S = ASM->generateAsmString(SemaRef.getASTContext());
+  SemaRef.Diag(ASM->getAsmLoc(), diag::warn_amdgcn_unguarded_asm_stmt) << S;
+  SemaRef.Diag(ASM->getAsmLoc(), diag::note_amdgcn_unguarded_asm_silence) << S;
+
+  return true;
+}
+
+bool DiagnoseUnguardedBuiltins::VisitCallExpr(CallExpr *CE) {
+  unsigned ID = CE->getBuiltinCallee();
+  Builtin::Context &BInfo = SemaRef.getASTContext().BuiltinInfo;
+
+  if (!ID)
+    return true;
+  if (!BInfo.isTSBuiltin(ID))
+    return true;
+  if (ID == AMDGPU::BI__builtin_amdgcn_processor_is ||
+      ID == AMDGPU::BI__builtin_amdgcn_is_invocable)
+    return true;
+  if (llvm::find(GuardedBuiltins, ID) != GuardedBuiltins.end())
+    return true;
+
+  StringRef FL(BInfo.getRequiredFeatures(ID));
+  llvm::StringMap<bool> FeatureMap;
+  if (CurrentGFXIP.empty()) {
+    for (auto &&F : TargetFeatures)
+      FeatureMap[F] = true;
+    for (auto &&GID : GuardedBuiltins)
+      for (auto &&F : llvm::split(BInfo.getRequiredFeatures(GID), ','))
+        FeatureMap[F] = true;
+  } else {
+    static const llvm::Triple AMDGCN("amdgcn-amd-amdhsa");
+    llvm::AMDGPU::fillAMDGPUFeatureMap(CurrentGFXIP.back().second, AMDGCN,
+                                       FeatureMap);
+  }
+
+  FunctionDecl *BI = CE->getDirectCallee();
+  SourceLocation BICallLoc = CE->getExprLoc();
+  if (Builtin::evaluateRequiredTargetFeatures(FL, FeatureMap)) {
----------------
AlexVlx wrote:

> Thank you for circling back. In what regards the mechanics, that is correct: 
> the assumption is that if you used a predicate, or set the target attribute, 
> you have an expectation, so if those are wrong or yield contradictions, it 
> should be an error.
> 
> > There seems to be one part missing here, though. I think there should be 
> > some function attribute which marks a function as "guarded", i.e. the 
> > caller is responsible for providing an appropriate guard. On the callee 
> > side, it suppresses the warning. On the caller side, it propagates the 
> > error: it's treated like a "builtin function". Without this, you end up 
> > with weird situations where the user can't easily refactor their code: you 
> > can't move code from the body of an guard if statement into a separate 
> > function.
> 
> This is a good idea, thank you, will definitely add it. There will probably 
> be a few updates / refinements here over the coming months.



https://github.com/llvm/llvm-project/pull/134016
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to