@@ -2898,6 +2898,15 @@ OpenACCReductionRecipe SemaOpenACC::CreateReductionInitRecipe(
2898
2898
dyn_cast<ArraySectionExpr>(VarExpr->IgnoreParenImpCasts ()))
2899
2899
VarTy = ASE->getElementType ();
2900
2900
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
+
2901
2910
VarDecl *AllocaDecl = CreateAllocaDecl (
2902
2911
getASTContext (), SemaRef.getCurContext (), VarExpr->getBeginLoc (),
2903
2912
&getASTContext ().Idents .get (" openacc.reduction.init" ), VarTy);
@@ -2946,5 +2955,163 @@ OpenACCReductionRecipe SemaOpenACC::CreateReductionInitRecipe(
2946
2955
AllocaDecl->setInit (Init.get ());
2947
2956
AllocaDecl->setInitStyle (VarDecl::CallInit);
2948
2957
}
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 ;
2950
3117
}
0 commit comments