-
Notifications
You must be signed in to change notification settings - Fork 14.3k
[flang][Semantics][OpenMP] Check type of reduction variables #94596
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2378,6 +2378,87 @@ bool OmpStructureChecker::CheckIntrinsicOperator( | |
return false; | ||
} | ||
|
||
static bool IsReductionAllowedForType( | ||
const parser::OmpClause::Reduction &x, const DeclTypeSpec &type) { | ||
const auto &definedOp{std::get<parser::OmpReductionOperator>(x.v.t)}; | ||
// TODO: user defined reduction operators. Just allow everything for now. | ||
bool ok{true}; | ||
|
||
auto IsLogical{[](const DeclTypeSpec &type) -> bool { | ||
return type.category() == DeclTypeSpec::Logical; | ||
}}; | ||
auto IsCharacter{[](const DeclTypeSpec &type) -> bool { | ||
return type.category() == DeclTypeSpec::Character; | ||
}}; | ||
|
||
common::visit( | ||
common::visitors{ | ||
[&](const parser::DefinedOperator &dOpr) { | ||
if (const auto *intrinsicOp{ | ||
std::get_if<parser::DefinedOperator::IntrinsicOperator>( | ||
&dOpr.u)}) { | ||
// OMP5.2: The type [...] of a list item that appears in a | ||
// reduction clause must be valid for the combiner expression | ||
// See F2023: Table 10.2 | ||
// .LT., .LE., .GT., .GE. are handled as procedure designators | ||
// below. | ||
switch (*intrinsicOp) { | ||
case parser::DefinedOperator::IntrinsicOperator::Multiply: | ||
[[fallthrough]]; | ||
case parser::DefinedOperator::IntrinsicOperator::Add: | ||
[[fallthrough]]; | ||
case parser::DefinedOperator::IntrinsicOperator::Subtract: | ||
ok = type.IsNumeric(TypeCategory::Integer) || | ||
type.IsNumeric(TypeCategory::Real) || | ||
type.IsNumeric(TypeCategory::Complex); | ||
break; | ||
|
||
case parser::DefinedOperator::IntrinsicOperator::AND: | ||
[[fallthrough]]; | ||
case parser::DefinedOperator::IntrinsicOperator::OR: | ||
[[fallthrough]]; | ||
case parser::DefinedOperator::IntrinsicOperator::EQV: | ||
[[fallthrough]]; | ||
case parser::DefinedOperator::IntrinsicOperator::NEQV: | ||
ok = IsLogical(type); | ||
break; | ||
|
||
// Reduction identifier is not in OMP5.2 Table 5.2 | ||
default: | ||
DIE("This should have been caught in CheckIntrinsicOperator"); | ||
ok = false; | ||
break; | ||
} | ||
} | ||
}, | ||
[&](const parser::ProcedureDesignator &procD) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm surprised that the parse tree for OMP allows an arbitrary procedure designator here. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Besides the allowed intrinsic procedures (max, min, iand, ior, ieor), for OpenMP the reduction identifier can be a base language identifier, or a user-defined operator. The latter appears as a ProcedureDesignator. We also had instances of renaming of intrinsics (#68654). There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sure, but can it be a procedure component or a procedure binding? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think they cannot be a procedure component or binding. Are you suggesting changing the reduction-identifier from a ProcedureDesignator to a Name for procedure-name? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So long as a procedure component or procedure binding is caught as an error somewhere in semantics, it doesn't need to be a parsing issue. A |
||
const parser::Name *name{std::get_if<parser::Name>(&procD.u)}; | ||
if (name && name->symbol) { | ||
const SourceName &realName{name->symbol->GetUltimate().name()}; | ||
// OMP5.2: The type [...] of a list item that appears in a | ||
// reduction clause must be valid for the combiner expression | ||
if (realName == "iand" || realName == "ior" || | ||
realName == "ieor") { | ||
// IAND: arguments must be integers: F2023 16.9.100 | ||
// IEOR: arguments must be integers: F2023 16.9.106 | ||
// IOR: arguments must be integers: F2023 16.9.111 | ||
ok = type.IsNumeric(TypeCategory::Integer); | ||
} else if (realName == "max" || realName == "min") { | ||
// MAX: arguments must be integer, real, or character: | ||
// F2023 16.9.135 | ||
// MIN: arguments must be integer, real, or character: | ||
// F2023 16.9.141 | ||
ok = type.IsNumeric(TypeCategory::Integer) || | ||
type.IsNumeric(TypeCategory::Real) || IsCharacter(type); | ||
} | ||
} | ||
}, | ||
}, | ||
definedOp.u); | ||
|
||
return ok; | ||
} | ||
|
||
void OmpStructureChecker::CheckReductionTypeList( | ||
const parser::OmpClause::Reduction &x) { | ||
const auto &ompObjectList{std::get<parser::OmpObjectList>(x.v.t)}; | ||
|
@@ -2397,6 +2478,10 @@ void OmpStructureChecker::CheckReductionTypeList( | |
context_.Say(source, | ||
"A procedure pointer '%s' must not appear in a REDUCTION clause."_err_en_US, | ||
symbol->name()); | ||
} else if (!IsReductionAllowedForType(x, DEREF(symbol->GetType()))) { | ||
context_.Say(source, | ||
"The type of '%s' is incompatible with the reduction operator."_err_en_US, | ||
symbol->name()); | ||
} | ||
} | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
! RUN: %python %S/../test_errors.py %s %flang_fc1 -fopenmp | ||
! OpenMP Version 4.5 | ||
! 2.15.3.6 Reduction Clause | ||
program omp_reduction | ||
integer :: i | ||
real :: r | ||
character :: c | ||
complex :: z | ||
logical :: l | ||
|
||
! * is allowed for integer, real, and complex | ||
! but not for logical or character | ||
! ERROR: The type of 'c' is incompatible with the reduction operator. | ||
! ERROR: The type of 'l' is incompatible with the reduction operator. | ||
!$omp parallel reduction(*:i,r,c,z,l) | ||
!$omp end parallel | ||
|
||
! + is allowed for integer, real, and complex | ||
! but not for logical or character | ||
! ERROR: The type of 'c' is incompatible with the reduction operator. | ||
! ERROR: The type of 'l' is incompatible with the reduction operator. | ||
!$omp parallel reduction(+:i,r,c,z,l) | ||
!$omp end parallel | ||
|
||
! - is deprecated for all types | ||
! ERROR: The minus reduction operator is deprecated since OpenMP 5.2 and is not supported in the REDUCTION clause. | ||
!$omp parallel reduction(-:i,r,c,z,l) | ||
!$omp end parallel | ||
|
||
! .and. is only supported for logical operations | ||
! ERROR: The type of 'i' is incompatible with the reduction operator. | ||
! ERROR: The type of 'r' is incompatible with the reduction operator. | ||
! ERROR: The type of 'c' is incompatible with the reduction operator. | ||
! ERROR: The type of 'z' is incompatible with the reduction operator. | ||
!$omp parallel reduction(.and.:i,r,c,z,l) | ||
!$omp end parallel | ||
|
||
! .or. is only supported for logical operations | ||
! ERROR: The type of 'i' is incompatible with the reduction operator. | ||
! ERROR: The type of 'r' is incompatible with the reduction operator. | ||
! ERROR: The type of 'c' is incompatible with the reduction operator. | ||
! ERROR: The type of 'z' is incompatible with the reduction operator. | ||
!$omp parallel reduction(.or.:i,r,c,z,l) | ||
!$omp end parallel | ||
|
||
! .eqv. is only supported for logical operations | ||
! ERROR: The type of 'i' is incompatible with the reduction operator. | ||
! ERROR: The type of 'r' is incompatible with the reduction operator. | ||
! ERROR: The type of 'c' is incompatible with the reduction operator. | ||
! ERROR: The type of 'z' is incompatible with the reduction operator. | ||
!$omp parallel reduction(.eqv.:i,r,c,z,l) | ||
!$omp end parallel | ||
|
||
! .neqv. is only supported for logical operations | ||
! ERROR: The type of 'i' is incompatible with the reduction operator. | ||
! ERROR: The type of 'r' is incompatible with the reduction operator. | ||
! ERROR: The type of 'c' is incompatible with the reduction operator. | ||
! ERROR: The type of 'z' is incompatible with the reduction operator. | ||
!$omp parallel reduction(.neqv.:i,r,c,z,l) | ||
!$omp end parallel | ||
|
||
! iand only supports integers | ||
! ERROR: The type of 'r' is incompatible with the reduction operator. | ||
! ERROR: The type of 'c' is incompatible with the reduction operator. | ||
! ERROR: The type of 'z' is incompatible with the reduction operator. | ||
! ERROR: The type of 'l' is incompatible with the reduction operator. | ||
!$omp parallel reduction(iand:i,r,c,z,l) | ||
!$omp end parallel | ||
|
||
! ior only supports integers | ||
! ERROR: The type of 'r' is incompatible with the reduction operator. | ||
! ERROR: The type of 'c' is incompatible with the reduction operator. | ||
! ERROR: The type of 'z' is incompatible with the reduction operator. | ||
! ERROR: The type of 'l' is incompatible with the reduction operator. | ||
!$omp parallel reduction(ior:i,r,c,z,l) | ||
!$omp end parallel | ||
|
||
! ieor only supports integers | ||
! ERROR: The type of 'r' is incompatible with the reduction operator. | ||
! ERROR: The type of 'c' is incompatible with the reduction operator. | ||
! ERROR: The type of 'z' is incompatible with the reduction operator. | ||
! ERROR: The type of 'l' is incompatible with the reduction operator. | ||
!$omp parallel reduction(ieor:i,r,c,z,l) | ||
!$omp end parallel | ||
|
||
! max arguments may be integer, real, or character: | ||
! ERROR: The type of 'z' is incompatible with the reduction operator. | ||
! ERROR: The type of 'l' is incompatible with the reduction operator. | ||
!$omp parallel reduction(max:i,r,c,z,l) | ||
!$omp end parallel | ||
|
||
! min arguments may be integer, real, or character: | ||
! ERROR: The type of 'z' is incompatible with the reduction operator. | ||
! ERROR: The type of 'l' is incompatible with the reduction operator. | ||
!$omp parallel reduction(min:i,r,c,z,l) | ||
!$omp end parallel | ||
end program omp_reduction |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps you could abstract
CheckReduce()
in lib/Semantics/check-cuda.cpp and also use it here.Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the suggestion. The two functions are similar but not identical:
@kiranchandramohan what do you think? Would it be a good fit to change the representation to use
parser::ReductionOperator
instead ofparser::IntrinsicOperator
for these built in reduction operations, so that we can then refactor to share with the CUDA code? I think as a side effect this could let us use the existing array reduction machinery formin
andmax
without additional work (other than tests).Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For builtin reductions it might be OK. At the parser level, it might not work for renames (#68654). In OpenMP there is also user-defined declare reductions and reductions/user-defined operators.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah we would have to keep the
ProcedureDesignator
(or similar) around, I just mean not using it for the built in reduction intrinsic procedures.Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would that mean two different paths for a built-in reduction intrinsic and its renamed version?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess it would yes. Maybe that makes this a bad idea.