@@ -2898,6 +2898,15 @@ OpenACCReductionRecipe SemaOpenACC::CreateReductionInitRecipe(
28982898 dyn_cast<ArraySectionExpr>(VarExpr->IgnoreParenImpCasts ()))
28992899 VarTy = ASE->getElementType ();
29002900
2901+ llvm::SmallVector<OpenACCReductionRecipe::CombinerRecipe, 1 > CombinerRecipes;
2902+
2903+ // We use the 'set-ness' of the alloca-decl to determine whether the combiner
2904+ // is 'set' or not, so we can skip any attempts at it if we're going to fail
2905+ // at any of the combiners.
2906+ if (CreateReductionCombinerRecipe (VarExpr->getBeginLoc (), ReductionOperator,
2907+ VarTy, CombinerRecipes))
2908+ return OpenACCReductionRecipe::Empty ();
2909+
29012910 VarDecl *AllocaDecl = CreateAllocaDecl (
29022911 getASTContext (), SemaRef.getCurContext (), VarExpr->getBeginLoc (),
29032912 &getASTContext ().Idents .get (" openacc.reduction.init" ), VarTy);
@@ -2946,5 +2955,163 @@ OpenACCReductionRecipe SemaOpenACC::CreateReductionInitRecipe(
29462955 AllocaDecl->setInit (Init.get ());
29472956 AllocaDecl->setInitStyle (VarDecl::CallInit);
29482957 }
2949- return OpenACCReductionRecipe (AllocaDecl, {});
2958+
2959+ return OpenACCReductionRecipe (AllocaDecl, CombinerRecipes);
2960+ }
2961+
2962+ bool SemaOpenACC::CreateReductionCombinerRecipe (
2963+ SourceLocation Loc, OpenACCReductionOperator ReductionOperator,
2964+ QualType VarTy,
2965+ llvm::SmallVectorImpl<OpenACCReductionRecipe::CombinerRecipe>
2966+ &CombinerRecipes) {
2967+ // Now we can try to generate the 'combiner' recipe. This is a little
2968+ // complicated in that if the 'VarTy' is an array type, we want to take its
2969+ // element type so we can generate that. Additionally, if this is a struct,
2970+ // we have two options: If there is overloaded operators, we want to take
2971+ // THOSE, else we want to do the individual elements.
2972+
2973+ BinaryOperatorKind BinOp;
2974+ switch (ReductionOperator) {
2975+ case OpenACCReductionOperator::Invalid:
2976+ // This can only happen when there is an error, and since these inits
2977+ // are used for code generation, we can just ignore/not bother doing any
2978+ // initialization here.
2979+ CombinerRecipes.push_back ({nullptr , nullptr , nullptr });
2980+ return false ;
2981+ case OpenACCReductionOperator::Addition:
2982+ BinOp = BinaryOperatorKind::BO_AddAssign;
2983+ break ;
2984+ case OpenACCReductionOperator::Multiplication:
2985+ BinOp = BinaryOperatorKind::BO_MulAssign;
2986+ break ;
2987+ case OpenACCReductionOperator::BitwiseAnd:
2988+ BinOp = BinaryOperatorKind::BO_AndAssign;
2989+ break ;
2990+ case OpenACCReductionOperator::BitwiseOr:
2991+ BinOp = BinaryOperatorKind::BO_OrAssign;
2992+ break ;
2993+ case OpenACCReductionOperator::BitwiseXOr:
2994+ BinOp = BinaryOperatorKind::BO_XorAssign;
2995+ break ;
2996+
2997+ case OpenACCReductionOperator::Max:
2998+ case OpenACCReductionOperator::Min:
2999+ case OpenACCReductionOperator::And:
3000+ case OpenACCReductionOperator::Or:
3001+ // We just want a 'NYI' error in the backend, so leave an empty combiner
3002+ // recipe, and claim success.
3003+ CombinerRecipes.push_back ({nullptr , nullptr , nullptr });
3004+ return false ;
3005+ }
3006+
3007+ // If VarTy is an array type, at the top level only, we want to do our
3008+ // compares/decomp/etc at the element level.
3009+ if (auto *AT = getASTContext ().getAsArrayType (VarTy))
3010+ VarTy = AT->getElementType ();
3011+
3012+ assert (!VarTy->isArrayType () && " Only 1 level of array allowed" );
3013+
3014+ auto tryCombiner = [&, this ](DeclRefExpr *LHSDRE, DeclRefExpr *RHSDRE,
3015+ bool IncludeTrap) {
3016+ // TODO: OpenACC: we have to figure out based on the bin-op how to do the
3017+ // ones that we can't just use compound operators for. So &&, ||, max, and
3018+ // min aren't really clear what we could do here.
3019+ if (IncludeTrap) {
3020+ // Trap all of the errors here, we'll emit our own at the end.
3021+ Sema::TentativeAnalysisScope Trap{SemaRef};
3022+
3023+ return SemaRef.BuildBinOp (SemaRef.getCurScope (), Loc, BinOp, LHSDRE,
3024+ RHSDRE,
3025+ /* ForFoldExpr=*/ false );
3026+ } else {
3027+ return SemaRef.BuildBinOp (SemaRef.getCurScope (), Loc, BinOp, LHSDRE,
3028+ RHSDRE,
3029+ /* ForFoldExpr=*/ false );
3030+ }
3031+ };
3032+
3033+ struct CombinerAttemptTy {
3034+ VarDecl *LHS;
3035+ DeclRefExpr *LHSDRE;
3036+ VarDecl *RHS;
3037+ DeclRefExpr *RHSDRE;
3038+ Expr *Op;
3039+ };
3040+
3041+ auto formCombiner = [&, this ](QualType Ty) -> CombinerAttemptTy {
3042+ VarDecl *LHSDecl = CreateAllocaDecl (
3043+ getASTContext (), SemaRef.getCurContext (), Loc,
3044+ &getASTContext ().Idents .get (" openacc.reduction.combiner.lhs" ), Ty);
3045+ auto *LHSDRE = DeclRefExpr::Create (
3046+ getASTContext (), NestedNameSpecifierLoc{}, SourceLocation{}, LHSDecl,
3047+ /* ReferstoEnclosingVariableOrCapture=*/ false ,
3048+ DeclarationNameInfo{DeclarationName{LHSDecl->getDeclName ()},
3049+ LHSDecl->getBeginLoc ()},
3050+ Ty, clang::VK_LValue, LHSDecl, nullptr , NOUR_None);
3051+ VarDecl *RHSDecl = CreateAllocaDecl (
3052+ getASTContext (), SemaRef.getCurContext (), Loc,
3053+ &getASTContext ().Idents .get (" openacc.reduction.combiner.lhs" ), Ty);
3054+ auto *RHSDRE = DeclRefExpr::Create (
3055+ getASTContext (), NestedNameSpecifierLoc{}, SourceLocation{}, RHSDecl,
3056+ /* ReferstoEnclosingVariableOrCapture=*/ false ,
3057+ DeclarationNameInfo{DeclarationName{RHSDecl->getDeclName ()},
3058+ RHSDecl->getBeginLoc ()},
3059+ Ty, clang::VK_LValue, RHSDecl, nullptr , NOUR_None);
3060+
3061+ ExprResult BinOpResult = tryCombiner (LHSDRE, RHSDRE, /* IncludeTrap=*/ true );
3062+
3063+ return {LHSDecl, LHSDRE, RHSDecl, RHSDRE, BinOpResult.get ()};
3064+ };
3065+
3066+ CombinerAttemptTy TopLevelCombinerInfo = formCombiner (VarTy);
3067+
3068+ if (TopLevelCombinerInfo.Op ) {
3069+ if (!TopLevelCombinerInfo.Op ->containsErrors () &&
3070+ TopLevelCombinerInfo.Op ->isInstantiationDependent ()) {
3071+ // If this is instantiation dependent, we're just going to 'give up' here
3072+ // and count on us to get it right during instantaition.
3073+ CombinerRecipes.push_back ({nullptr , nullptr , nullptr });
3074+ return false ;
3075+ } else if (!TopLevelCombinerInfo.Op ->containsErrors ()) {
3076+ // Else, we succeeded, we can just return this combiner.
3077+ CombinerRecipes.push_back ({TopLevelCombinerInfo.LHS ,
3078+ TopLevelCombinerInfo.RHS ,
3079+ TopLevelCombinerInfo.Op });
3080+ return false ;
3081+ }
3082+ }
3083+
3084+ // Since the 'root' level didn't fail, the only thing that could be successful
3085+ // is a struct that we decompose on its individual fields.
3086+
3087+ RecordDecl *RD = VarTy->getAsRecordDecl ();
3088+ if (!RD) {
3089+ Diag (Loc, diag::err_acc_reduction_recipe_no_op) << VarTy;
3090+ tryCombiner (TopLevelCombinerInfo.LHSDRE , TopLevelCombinerInfo.RHSDRE ,
3091+ /* IncludeTrap=*/ false );
3092+ return true ;
3093+ }
3094+
3095+ for (const FieldDecl *FD : RD->fields ()) {
3096+ CombinerAttemptTy FieldCombinerInfo = formCombiner (FD->getType ());
3097+
3098+ if (!FieldCombinerInfo.Op || FieldCombinerInfo.Op ->containsErrors ()) {
3099+ Diag (Loc, diag::err_acc_reduction_recipe_no_op) << FD->getType ();
3100+ Diag (FD->getBeginLoc (), diag::note_acc_reduction_recipe_noop_field) << RD;
3101+ tryCombiner (FieldCombinerInfo.LHSDRE , FieldCombinerInfo.RHSDRE ,
3102+ /* IncludeTrap=*/ false );
3103+ return true ;
3104+ }
3105+
3106+ if (FieldCombinerInfo.Op ->isInstantiationDependent ()) {
3107+ // If this is instantiation dependent, we're just going to 'give up' here
3108+ // and count on us to get it right during instantaition.
3109+ CombinerRecipes.push_back ({nullptr , nullptr , nullptr });
3110+ } else {
3111+ CombinerRecipes.push_back (
3112+ {FieldCombinerInfo.LHS , FieldCombinerInfo.RHS , FieldCombinerInfo.Op });
3113+ }
3114+ }
3115+
3116+ return false ;
29503117}
0 commit comments