| File: | lib/OpenMP/Environment/Validation.pm |
| Coverage: | 99.8% |
| line | stmt | bran | cond | sub | pod | time | code |
|---|---|---|---|---|---|---|---|
| 1 | package OpenMP::Environment::Validation; | ||||||
| 2 | 8 8 8 | 40 11 213 | use strict; | ||||
| 3 | 8 8 8 | 29 10 325 | use warnings; | ||||
| 4 | |||||||
| 5 | 8 8 8 | 36 11 368 | use Carp qw/croak/; | ||||
| 6 | 8 8 8 | 34 9 229 | use Exporter qw/import/; | ||||
| 7 | 8 8 8 | 298 10 38498 | use OpenMP::Environment::Constants (); | ||||
| 8 | |||||||
| 9 | our $VERSION = q{1.5.0}; | ||||||
| 10 | our @EXPORT_OK = qw/validate_value validate_assignment assert_variable assert_environment analyze_environment validation_rules/; | ||||||
| 11 | |||||||
| 12 | my %UPPERCASE_FILTER = map { $_ => 1 } qw/ | ||||||
| 13 | OMP_CANCELLATION OMP_NESTED OMP_DISPLAY_AFFINITY OMP_DISPLAY_ENV | ||||||
| 14 | OMP_TARGET_OFFLOAD OMP_WAIT_POLICY | ||||||
| 15 | /; | ||||||
| 16 | |||||||
| 17 | |||||||
| 18 | my %LEGACY_VALIDATED = map { $_ => 1 } qw/ | ||||||
| 19 | OMP_CANCELLATION OMP_DISPLAY_AFFINITY OMP_DISPLAY_ENV OMP_DEFAULT_DEVICE | ||||||
| 20 | OMP_DYNAMIC OMP_MAX_ACTIVE_LEVELS OMP_MAX_TASK_PRIORITY OMP_NESTED | ||||||
| 21 | OMP_NUM_TEAMS OMP_NUM_THREADS OMP_TARGET_OFFLOAD OMP_TEAMS_THREAD_LIMIT | ||||||
| 22 | OMP_THREAD_LIMIT OMP_WAIT_POLICY GOMP_DEBUG | ||||||
| 23 | /; | ||||||
| 24 | my %VALIDATOR = ( | ||||||
| 25 | OMP_CANCELLATION => \&_validate_omp_cancellation, | ||||||
| 26 | OMP_DISPLAY_ENV => \&_validate_omp_display_env, | ||||||
| 27 | OMP_DEFAULT_DEVICE => \&_validate_omp_default_device, | ||||||
| 28 | OMP_NUM_TEAMS => \&_validate_omp_num_teams, | ||||||
| 29 | OMP_DYNAMIC => \&_validate_omp_dynamic, | ||||||
| 30 | OMP_MAX_ACTIVE_LEVELS => \&_validate_omp_max_active_levels, | ||||||
| 31 | OMP_MAX_TASK_PRIORITY => \&_validate_omp_max_task_priority, | ||||||
| 32 | OMP_NESTED => \&_validate_omp_nested, | ||||||
| 33 | OMP_NUM_THREADS => \&_validate_omp_num_threads, | ||||||
| 34 | OMP_PROC_BIND => \&_validate_omp_proc_bind, | ||||||
| 35 | OMP_PLACES => \&_validate_omp_places, | ||||||
| 36 | OMP_STACKSIZE => \&_validate_omp_stacksize, | ||||||
| 37 | OMP_SCHEDULE => \&_validate_omp_schedule, | ||||||
| 38 | OMP_TARGET_OFFLOAD => \&_validate_omp_target_offload, | ||||||
| 39 | OMP_THREAD_LIMIT => \&_validate_omp_thread_limit, | ||||||
| 40 | OMP_WAIT_POLICY => \&_validate_omp_wait_policy, | ||||||
| 41 | GOMP_CPU_AFFINITY => \&_validate_gomp_cpu_affinity, | ||||||
| 42 | GOMP_DEBUG => \&_validate_gomp_debug, | ||||||
| 43 | GOMP_STACKSIZE => \&_validate_gomp_stacksize, | ||||||
| 44 | GOMP_SPINCOUNT => \&_validate_gomp_spincount, | ||||||
| 45 | GOMP_RTEMS_THREAD_POOLS => \&_validate_gomp_rtems_thread_pools, | ||||||
| 46 | OMP_TEAMS_THREAD_LIMIT => \&_validate_omp_teams_thread_limit, | ||||||
| 47 | OMP_ALLOCATOR => \&_validate_omp_allocator, | ||||||
| 48 | OMP_AFFINITY_FORMAT => \&_validate_omp_affinity_format, | ||||||
| 49 | OMP_DISPLAY_AFFINITY => \&_validate_omp_display_affinity, | ||||||
| 50 | ); | ||||||
| 51 | |||||||
| 52 | sub validation_rules { | ||||||
| 53 | return { | ||||||
| 54 | 9 | 1 | 18 | fields => [ OpenMP::Environment::Constants::environment_names() ], | |||
| 55 | normalized => [ sort keys %UPPERCASE_FILTER ], | ||||||
| 56 | assignment_validated => [ sort keys %LEGACY_VALIDATED ], | ||||||
| 57 | whitespace_significant => [ q{OMP_AFFINITY_FORMAT} ], | ||||||
| 58 | validators => { %VALIDATOR }, | ||||||
| 59 | profile => q{OpenMP 5.2 + GCC 16.2/libgomp}, | ||||||
| 60 | }; | ||||||
| 61 | } | ||||||
| 62 | |||||||
| 63 | sub validate_value { | ||||||
| 64 | 787 | 1 | 1097 | my ( $name, $value ) = @_; | |||
| 65 | 787 | 1355 | croak q{Environment variable name is required} if not defined $name; | ||||
| 66 | 786 | 1091 | croak qq{Unsupported OpenMP/libgomp environment variable "$name"} | ||||
| 67 | if not OpenMP::Environment::Constants::is_environment_name($name); | ||||||
| 68 | 785 | 1140 | return undef if not defined $value; | ||||
| 69 | |||||||
| 70 | 784 | 800 | my $normalized = $value; | ||||
| 71 | |||||||
| 72 | # OpenMP 5.2 Chapter 21 states that OpenMP environment-variable values are | ||||||
| 73 | # generally case-insensitive and may contain leading/trailing whitespace. | ||||||
| 74 | # OMP_AFFINITY_FORMAT is the explicit exception: it is case-sensitive and | ||||||
| 75 | # leading/trailing whitespace is significant (Section 21.2.5). Preserve | ||||||
| 76 | # that format string byte-for-byte; trim the other OMP_* values before | ||||||
| 77 | # applying the module's established case normalization. | ||||||
| 78 | 784 | 3101 | if ( $name =~ m/\AOMP_/ and $name ne q{OMP_AFFINITY_FORMAT} ) { | ||||
| 79 | 681 | 1015 | $normalized = _trim($normalized); | ||||
| 80 | } | ||||||
| 81 | 784 | 1402 | $normalized = uc($normalized) if $UPPERCASE_FILTER{$name}; | ||||
| 82 | 784 | 925 | my $validator = $VALIDATOR{$name}; | ||||
| 83 | 784 | 985 | my $error = $validator->($normalized); | ||||
| 84 | 784 | 1935 | die qq{(fatal) $name="$value": $error\n\n} if defined $error; | ||||
| 85 | 784 | 1581 | return $normalized; | ||||
| 86 | } | ||||||
| 87 | |||||||
| 88 | sub validate_assignment { | ||||||
| 89 | 311 | 1 | 389 | my ( $name, $value ) = @_; | |||
| 90 | 311 | 621 | croak q{Environment variable name is required} if not defined $name; | ||||
| 91 | 310 | 444 | croak qq{Unsupported OpenMP/libgomp environment variable "$name"} | ||||
| 92 | if not OpenMP::Environment::Constants::is_environment_name($name); | ||||||
| 93 | 309 | 448 | return undef if not defined $value; | ||||
| 94 | |||||||
| 95 | # Assignment semantics are intentionally backward-compatible. Variables | ||||||
| 96 | # that were historically pass-through remain pass-through; the stricter | ||||||
| 97 | # grammar is available through assert()/assert_omp_environment and this | ||||||
| 98 | # module's validate_value() API. | ||||||
| 99 | 308 | 509 | return $value if not $LEGACY_VALIDATED{$name}; | ||||
| 100 | 266 | 373 | return validate_value( $name, $value ); | ||||
| 101 | } | ||||||
| 102 | |||||||
| 103 | sub assert_variable { | ||||||
| 104 | 36 | 1 | 45 | my ( $env, $name ) = @_; | |||
| 105 | 36 | 143 | croak q{Environment hash reference is required} if ref($env) ne q{HASH}; | ||||
| 106 | 35 | 49 | croak qq{Unsupported OpenMP/libgomp environment variable "$name"} | ||||
| 107 | if not OpenMP::Environment::Constants::is_environment_name($name); | ||||||
| 108 | |||||||
| 109 | 34 | 70 | if ( exists $env->{$name} ) { | ||||
| 110 | 32 | 45 | if ( defined $env->{$name} ) { | ||||
| 111 | 31 | 46 | $env->{$name} = validate_value( $name, $env->{$name} ); | ||||
| 112 | } | ||||||
| 113 | } | ||||||
| 114 | |||||||
| 115 | 33 | 45 | my $analysis = analyze_environment($env); | ||||
| 116 | 33 33 | 33 54 | foreach my $conflict ( @{ $analysis->{conflicts} } ) { | ||||
| 117 | 3 6 3 | 2 11 5 | next if not grep { $_ eq $name } @{ $conflict->{variables} }; | ||||
| 118 | 2 | 45 | die qq{(fatal) OpenMP environment conflict: $conflict->{message}\n}; | ||||
| 119 | } | ||||||
| 120 | 31 | 185 | return 1; | ||||
| 121 | } | ||||||
| 122 | |||||||
| 123 | sub assert_environment { | ||||||
| 124 | 26 | 1 | 35 | my ($env) = @_; | |||
| 125 | 26 | 191 | croak q{Environment hash reference is required} if ref($env) ne q{HASH}; | ||||
| 126 | |||||||
| 127 | 25 | 40 | foreach my $name ( OpenMP::Environment::Constants::environment_names() ) { | ||||
| 128 | 338 | 448 | next if not exists $env->{$name}; | ||||
| 129 | 188 | 237 | next if not defined $env->{$name}; | ||||
| 130 | 187 | 214 | $env->{$name} = validate_value( $name, $env->{$name} ); | ||||
| 131 | } | ||||||
| 132 | |||||||
| 133 | 9 | 25 | my $analysis = analyze_environment($env); | ||||
| 134 | 9 9 | 10 20 | if ( @{ $analysis->{conflicts} } ) { | ||||
| 135 | 1 1 1 | 2 3 2 | my $message = join q{; }, map { $_->{message} } @{ $analysis->{conflicts} }; | ||||
| 136 | 1 | 7 | die qq{(fatal) OpenMP environment conflict: $message\n}; | ||||
| 137 | } | ||||||
| 138 | 8 | 40 | return 1; | ||||
| 139 | } | ||||||
| 140 | |||||||
| 141 | sub analyze_environment { | ||||||
| 142 | 54 | 1 | 67 | my ($env) = @_; | |||
| 143 | 54 | 157 | croak q{Environment hash reference is required} if ref($env) ne q{HASH}; | ||||
| 144 | |||||||
| 145 | 53 | 47 | my @errors; | ||||
| 146 | 53 | 72 | foreach my $name ( OpenMP::Environment::Constants::environment_names() ) { | ||||
| 147 | 1325 | 1537 | next if not exists $env->{$name}; | ||||
| 148 | 156 | 200 | next if not defined $env->{$name}; | ||||
| 149 | 154 154 152 | 136 215 153 | my $ok = eval { validate_value( $name, $env->{$name} ); 1 }; | ||||
| 150 | 154 | 223 | push @errors, { variable => $name, message => $@ } if not $ok; | ||||
| 151 | } | ||||||
| 152 | |||||||
| 153 | 53 | 90 | my @conflicts; | ||||
| 154 | 53 | 95 | if ( _is_false( $env->{OMP_NESTED} ) ) { | ||||
| 155 | 9 | 15 | if ( defined $env->{OMP_MAX_ACTIVE_LEVELS} ) { | ||||
| 156 | 8 | 22 | if ( $env->{OMP_MAX_ACTIVE_LEVELS} =~ m/\A\d+\z/ ) { | ||||
| 157 | 7 | 12 | if ( $env->{OMP_MAX_ACTIVE_LEVELS} > 1 ) { | ||||
| 158 | 6 | 19 | push @conflicts, { | ||||
| 159 | variables => [qw/OMP_NESTED OMP_MAX_ACTIVE_LEVELS/], | ||||||
| 160 | class => q{implementation-defined}, | ||||||
| 161 | message => q{OMP_NESTED=FALSE with OMP_MAX_ACTIVE_LEVELS greater than 1 is implementation-defined by OpenMP 5.2}, | ||||||
| 162 | }; | ||||||
| 163 | } | ||||||
| 164 | } | ||||||
| 165 | } | ||||||
| 166 | } | ||||||
| 167 | |||||||
| 168 | 53 | 68 | my @runtime_dependent; | ||||
| 169 | 53 | 88 | _runtime_note( \@runtime_dependent, $env, q{OMP_PLACES}, q{processor numbering, abstract-place mapping, and resource availability are implementation/runtime dependent} ); | ||||
| 170 | 53 | 81 | _runtime_note( \@runtime_dependent, $env, q{OMP_DEFAULT_DEVICE}, q{the requested device number is not checked for existence} ); | ||||
| 171 | 53 | 75 | _runtime_note( \@runtime_dependent, $env, q{OMP_STACKSIZE}, q{the requested stack size is not checked against available runtime resources} ); | ||||
| 172 | 53 | 93 | _runtime_note( \@runtime_dependent, $env, q{OMP_ALLOCATOR}, q{memory-space and allocator availability are not probed} ); | ||||
| 173 | 53 | 77 | _runtime_note( \@runtime_dependent, $env, q{OMP_THREAD_LIMIT}, q{the implementation-supported thread limit is not probed} ); | ||||
| 174 | 53 | 84 | _runtime_note( \@runtime_dependent, $env, q{OMP_MAX_ACTIVE_LEVELS}, q{the implementation-supported active-level limit is not probed} ); | ||||
| 175 | 53 | 71 | _runtime_note( \@runtime_dependent, $env, q{OMP_NUM_THREADS}, q{the implementation-supported thread count is not probed} ); | ||||
| 176 | 53 | 72 | _runtime_note( \@runtime_dependent, $env, q{GOMP_CPU_AFFINITY}, q{CPU identifiers are syntax-checked but are not checked against the host} ); | ||||
| 177 | 53 | 78 | _runtime_note( \@runtime_dependent, $env, q{GOMP_RTEMS_THREAD_POOLS}, q{RTEMS scheduler names and priorities are syntax-checked but not queried from RTEMS} ); | ||||
| 178 | |||||||
| 179 | 53 | 45 | my @notes; | ||||
| 180 | 53 | 70 | if ( exists $env->{GOMP_CPU_AFFINITY} ) { | ||||
| 181 | 6 | 16 | if ( exists $env->{OMP_PROC_BIND} ) { | ||||
| 182 | 4 | 7 | push @notes, q{OMP_PROC_BIND takes precedence over GOMP_CPU_AFFINITY in GNU libgomp when both are set}; | ||||
| 183 | } | ||||||
| 184 | } | ||||||
| 185 | 53 | 70 | if ( exists $env->{OMP_NESTED} ) { | ||||
| 186 | 16 | 22 | if ( exists $env->{OMP_MAX_ACTIVE_LEVELS} ) { | ||||
| 187 | 13 | 14 | my $conflicting = 0; | ||||
| 188 | 13 | 17 | if ( _is_false( $env->{OMP_NESTED} ) ) { | ||||
| 189 | 8 | 21 | if ( $env->{OMP_MAX_ACTIVE_LEVELS} =~ m/\A\d+\z/ ) { | ||||
| 190 | 7 | 14 | $conflicting = 1 if $env->{OMP_MAX_ACTIVE_LEVELS} > 1; | ||||
| 191 | } | ||||||
| 192 | } | ||||||
| 193 | 13 | 27 | push @notes, q{when OMP_NESTED and OMP_MAX_ACTIVE_LEVELS are both set without the conflicting FALSE/>1 combination, OMP_NESTED has no effect} | ||||
| 194 | if not $conflicting; | ||||||
| 195 | } | ||||||
| 196 | } | ||||||
| 197 | 53 | 84 | if ( _has_multiple_items( $env->{OMP_NUM_THREADS} ) ) { | ||||
| 198 | 5 | 6 | push @notes, q{multi-item OMP_NUM_THREADS or OMP_PROC_BIND values participate in initialization of max-active-levels-var unless overridden by stronger nesting controls}; | ||||
| 199 | } | ||||||
| 200 | elsif ( _has_proc_bind_list( $env->{OMP_PROC_BIND} ) ) { | ||||||
| 201 | 2 | 3 | push @notes, q{multi-item OMP_NUM_THREADS or OMP_PROC_BIND values participate in initialization of max-active-levels-var unless overridden by stronger nesting controls}; | ||||
| 202 | } | ||||||
| 203 | |||||||
| 204 | 53 | 69 | my $valid = 1; | ||||
| 205 | 53 | 69 | $valid = 0 if @errors; | ||||
| 206 | 53 | 62 | $valid = 0 if @conflicts; | ||||
| 207 | return { | ||||||
| 208 | 53 | 159 | valid => $valid, | ||||
| 209 | errors => \@errors, | ||||||
| 210 | conflicts => \@conflicts, | ||||||
| 211 | runtime_dependent => \@runtime_dependent, | ||||||
| 212 | notes => \@notes, | ||||||
| 213 | }; | ||||||
| 214 | } | ||||||
| 215 | |||||||
| 216 | sub _runtime_note { | ||||||
| 217 | 477 | 479 | my ( $notes, $env, $name, $message ) = @_; | ||||
| 218 | 477 | 621 | return if not exists $env->{$name}; | ||||
| 219 | 63 | 111 | push @$notes, { variable => $name, message => $message }; | ||||
| 220 | 63 | 71 | return; | ||||
| 221 | } | ||||||
| 222 | |||||||
| 223 | sub _is_false { | ||||||
| 224 | 66 | 90 | my ($value) = @_; | ||||
| 225 | 66 | 107 | return 0 if not defined $value; | ||||
| 226 | 29 | 90 | return 1 if $value =~ m/\A(?:0|false)\z/i; | ||||
| 227 | 12 | 22 | return 0; | ||||
| 228 | } | ||||||
| 229 | |||||||
| 230 | sub _has_multiple_items { | ||||||
| 231 | 56 | 82 | my ($value) = @_; | ||||
| 232 | 56 | 132 | return 0 if not defined $value; | ||||
| 233 | 11 | 27 | return 1 if $value =~ /,/; | ||||
| 234 | 5 | 12 | return 0; | ||||
| 235 | } | ||||||
| 236 | |||||||
| 237 | sub _has_proc_bind_list { | ||||||
| 238 | 51 | 70 | my ($value) = @_; | ||||
| 239 | 51 | 90 | return 0 if not defined $value; | ||||
| 240 | 6 | 15 | return 1 if $value =~ /,/; | ||||
| 241 | 3 | 6 | return 0; | ||||
| 242 | } | ||||||
| 243 | |||||||
| 244 | sub _enum { | ||||||
| 245 | 252 | 449 | my ( $value, @allowed ) = @_; | ||||
| 246 | 252 730 | 323 1297 | my %allowed = map { uc($_) => 1 } @allowed; | ||||
| 247 | 252 | 789 | return undef if $allowed{ uc $value }; | ||||
| 248 | 29 | 121 | return q{Expected one of: } . join( q{, }, @allowed ); | ||||
| 249 | } | ||||||
| 250 | |||||||
| 251 | sub _integer_at_least { | ||||||
| 252 | 274 | 406 | my ( $value, $minimum ) = @_; | ||||
| 253 | 274 | 721 | return q{Value must be an integer} if $value !~ m/\A\d+\z/; | ||||
| 254 | 244 | 426 | return qq{Value must be an integer greater than or equal to $minimum} if $value < $minimum; | ||||
| 255 | 235 | 314 | return; | ||||
| 256 | } | ||||||
| 257 | |||||||
| 258 | sub _positive_integer_list { | ||||||
| 259 | 58 | 80 | my ($value) = @_; | ||||
| 260 | 58 | 258 | return if $value =~ m/\A\s*[1-9]\d*(?:\s*,\s*[1-9]\d*)*\s*\z/; | ||||
| 261 | 10 | 19 | return q{Value must be a comma-separated list of positive integers}; | ||||
| 262 | } | ||||||
| 263 | |||||||
| 264 | # GCC/libgomp: https://gcc.gnu.org/onlinedocs/gcc-16.2.0/libgomp/OMP_005fCANCELLATION.html | ||||||
| 265 | # OpenMP 5.2: Section 21.2.6, OMP_CANCELLATION. | ||||||
| 266 | # OpenMP requires TRUE/FALSE; other values make behavior implementation-defined. | ||||||
| 267 | # libgomp implements the standard boolean values. We validate syntax only and | ||||||
| 268 | # do not inspect whether cancellation points will actually be encountered. | ||||||
| 269 | 41 | 59 | sub _validate_omp_cancellation { return _enum( shift, qw/TRUE FALSE/ ) } | ||||
| 270 | |||||||
| 271 | # GCC/libgomp: https://gcc.gnu.org/onlinedocs/gcc-16.2.0/libgomp/OMP_005fDISPLAY_005fENV.html | ||||||
| 272 | # OpenMP 5.2: Section 21.7, OMP_DISPLAY_ENV. | ||||||
| 273 | # OpenMP defines TRUE, FALSE, and VERBOSE. libgomp uses VERBOSE to include GNU | ||||||
| 274 | # implementation-specific variables. We validate the portable values only. | ||||||
| 275 | 35 | 54 | sub _validate_omp_display_env { return _enum( shift, qw/TRUE FALSE VERBOSE/ ) } | ||||
| 276 | |||||||
| 277 | # GCC/libgomp: https://gcc.gnu.org/onlinedocs/gcc-16.2.0/libgomp/OMP_005fDEFAULT_005fDEVICE.html | ||||||
| 278 | # OpenMP 5.2: Section 21.2.7, OMP_DEFAULT_DEVICE. | ||||||
| 279 | # The grammar is a non-negative integer. Device existence/availability is a | ||||||
| 280 | # runtime property and is deliberately not checked by this portable module. | ||||||
| 281 | 55 | 95 | sub _validate_omp_default_device { return _integer_at_least( shift, 0 ) } | ||||
| 282 | |||||||
| 283 | # GCC/libgomp: https://gcc.gnu.org/onlinedocs/gcc-16.2.0/libgomp/OMP_005fNUM_005fTEAMS.html | ||||||
| 284 | # OpenMP 5.2: Section 21.6.1, OMP_NUM_TEAMS. | ||||||
| 285 | # The value is a positive integer. Whether that many teams can be created is a | ||||||
| 286 | # runtime/target matter and is deliberately not probed. | ||||||
| 287 | 39 | 58 | sub _validate_omp_num_teams { return _integer_at_least( shift, 1 ) } | ||||
| 288 | |||||||
| 289 | # GCC/libgomp: https://gcc.gnu.org/onlinedocs/gcc-16.2.0/libgomp/OMP_005fDYNAMIC.html | ||||||
| 290 | # OpenMP 5.2: Section 21.1.1, OMP_DYNAMIC. | ||||||
| 291 | # OpenMP specifies TRUE/FALSE and leaves other values implementation-defined. | ||||||
| 292 | # For compatibility with historical OpenMP::Environment behavior, 1/0 are also | ||||||
| 293 | # accepted; the public accessor retains its established false-value-unsets rule. | ||||||
| 294 | 31 | 56 | sub _validate_omp_dynamic { return _enum( shift, qw/TRUE FALSE 1 0/ ) } | ||||
| 295 | |||||||
| 296 | # GCC/libgomp: https://gcc.gnu.org/onlinedocs/gcc-16.2.0/libgomp/OMP_005fMAX_005fACTIVE_005fLEVELS.html | ||||||
| 297 | # OpenMP 5.2: Section 21.1.4, OMP_MAX_ACTIVE_LEVELS. | ||||||
| 298 | # OpenMP permits a non-negative integer, but GCC/libgomp documents a positive | ||||||
| 299 | # integer and the pre-1.5.0 module rejected zero. We follow libgomp and preserve | ||||||
| 300 | # that compatibility. The supported maximum is system/runtime dependent and is | ||||||
| 301 | # not queried here. | ||||||
| 302 | 55 | 76 | sub _validate_omp_max_active_levels { return _integer_at_least( shift, 1 ) } | ||||
| 303 | |||||||
| 304 | # GCC/libgomp: https://gcc.gnu.org/onlinedocs/gcc-16.2.0/libgomp/OMP_005fMAX_005fTASK_005fPRIORITY.html | ||||||
| 305 | # OpenMP 5.2: Section 21.2.9, OMP_MAX_TASK_PRIORITY. | ||||||
| 306 | # The portable grammar is a non-negative integer. Runtime priority support is | ||||||
| 307 | # not probed. | ||||||
| 308 | 44 | 62 | sub _validate_omp_max_task_priority { return _integer_at_least( shift, 0 ) } | ||||
| 309 | |||||||
| 310 | # GCC/libgomp: https://gcc.gnu.org/onlinedocs/gcc-16.2.0/libgomp/OMP_005fNESTED.html | ||||||
| 311 | # OpenMP 5.2: Section 21.1.5, OMP_NESTED (deprecated). | ||||||
| 312 | # TRUE/FALSE are defined; other values are implementation-defined. OpenMP also | ||||||
| 313 | # declares FALSE together with OMP_MAX_ACTIVE_LEVELS>1 implementation-defined; | ||||||
| 314 | # that relationship is checked by analyze_environment/assert_environment. | ||||||
| 315 | # 1/0 remain accepted for backward compatibility with this module. | ||||||
| 316 | 41 | 78 | sub _validate_omp_nested { return _enum( shift, qw/TRUE FALSE 1 0/ ) } | ||||
| 317 | |||||||
| 318 | # GCC/libgomp: https://gcc.gnu.org/onlinedocs/gcc-16.2.0/libgomp/OMP_005fNUM_005fTHREADS.html | ||||||
| 319 | # OpenMP 5.2: Section 21.1.2, OMP_NUM_THREADS. | ||||||
| 320 | # OpenMP permits a comma-separated list of positive integers for nested levels. | ||||||
| 321 | # Values above runtime capability are implementation-defined and are not | ||||||
| 322 | # system-checked. Multiple items also influence max-active-levels-var. | ||||||
| 323 | 58 | 85 | sub _validate_omp_num_threads { return _positive_integer_list(shift) } | ||||
| 324 | |||||||
| 325 | # GCC/libgomp: https://gcc.gnu.org/onlinedocs/gcc-16.2.0/libgomp/OMP_005fPROC_005fBIND.html | ||||||
| 326 | # OpenMP 5.2: Section 21.1.7, OMP_PROC_BIND. | ||||||
| 327 | # OpenMP accepts TRUE, FALSE, or a list of PRIMARY/CLOSE/SPREAD. MASTER is | ||||||
| 328 | # deprecated by OpenMP but libgomp explicitly continues to accept it, so this | ||||||
| 329 | # validator accepts MASTER as a GNU/backward-compatible spelling. Placement and | ||||||
| 330 | # the policy selected by TRUE are implementation/runtime dependent and not probed. | ||||||
| 331 | sub _validate_omp_proc_bind { | ||||||
| 332 | 22 | 39 | my ($value) = @_; | ||||
| 333 | 22 | 97 | return if $value =~ m/\A\s*(?:TRUE|FALSE)\s*\z/i; | ||||
| 334 | 20 | 110 | return if $value =~ m/\A\s*(?:PRIMARY|MASTER|CLOSE|SPREAD)(?:\s*,\s*(?:PRIMARY|MASTER|CLOSE|SPREAD))*\s*\z/i; | ||||
| 335 | 2 | 7 | return q{Expected TRUE, FALSE, or a comma-separated list of PRIMARY, MASTER, CLOSE, or SPREAD}; | ||||
| 336 | } | ||||||
| 337 | |||||||
| 338 | # GCC/libgomp: https://gcc.gnu.org/onlinedocs/gcc-16.2.0/libgomp/OMP_005fPLACES.html | ||||||
| 339 | # OpenMP 5.2: Section 21.1.6, OMP_PLACES. | ||||||
| 340 | # OpenMP defines abstract names, explicit resource lists, intervals, strides, | ||||||
| 341 | # and exclusions, while explicitly leaving processor numbering, abstract-name | ||||||
| 342 | # meaning, resource mapping, and additional abstract names implementation-defined. | ||||||
| 343 | # libgomp documents THREADS/CORES/SOCKETS/LL_CACHES/NUMA_DOMAINS and the standard | ||||||
| 344 | # explicit syntax. We validate grammar, including implementation-defined abstract | ||||||
| 345 | # identifiers, but never verify that a referenced processor/resource exists. | ||||||
| 346 | sub _validate_omp_places { | ||||||
| 347 | 34 | 61 | my ($value) = @_; | ||||
| 348 | 34 | 65 | my $text = _trim($value); | ||||
| 349 | 34 | 93 | return q{OMP_PLACES must not be empty} if $text eq q{}; | ||||
| 350 | 33 | 112 | return if $text =~ m/\A[A-Za-z_][A-Za-z0-9_]*(?:\(\s*[1-9]\d*\s*\))?\z/; | ||||
| 351 | |||||||
| 352 | 27 | 52 | my @items = _split_top_level( $text, q{,} ); | ||||
| 353 | 27 | 59 | return q{Malformed OMP_PLACES list} if not @items; | ||||
| 354 | 22 | 35 | foreach my $item (@items) { | ||||
| 355 | 41 | 57 | my $err = _validate_place_interval($item); | ||||
| 356 | 41 | 99 | return $err if defined $err; | ||||
| 357 | } | ||||||
| 358 | 16 | 29 | return; | ||||
| 359 | } | ||||||
| 360 | |||||||
| 361 | # GCC/libgomp: https://gcc.gnu.org/onlinedocs/gcc-16.2.0/libgomp/OMP_005fSTACKSIZE.html | ||||||
| 362 | # OpenMP 5.2: Section 21.2.2, OMP_STACKSIZE. | ||||||
| 363 | # OpenMP specifies a positive integer with optional B/K/M/G unit; an unsupported | ||||||
| 364 | # size is implementation-defined. libgomp uses kilobytes when no unit is given. | ||||||
| 365 | # We validate syntax but do not test whether the runtime can allocate the size. | ||||||
| 366 | sub _validate_omp_stacksize { | ||||||
| 367 | 18 | 25 | my ($value) = @_; | ||||
| 368 | 18 | 62 | return if $value =~ m/\A\s*[1-9]\d*\s*(?:[BKMG])?\s*\z/i; | ||||
| 369 | 2 | 3 | return q{Expected a positive integer optionally followed by B, K, M, or G}; | ||||
| 370 | } | ||||||
| 371 | |||||||
| 372 | # GCC/libgomp: https://gcc.gnu.org/onlinedocs/gcc-16.2.0/libgomp/OMP_005fSCHEDULE.html | ||||||
| 373 | # OpenMP 5.2: Section 21.2.1, OMP_SCHEDULE. | ||||||
| 374 | # OpenMP defines [MONOTONIC|NONMONOTONIC:]STATIC|DYNAMIC|GUIDED|AUTO[,chunk], | ||||||
| 375 | # with a positive chunk. The GCC/libgomp environment-variable page still | ||||||
| 376 | # documents the older type[,chunk] presentation and cites OpenMP 4.5. We accept | ||||||
| 377 | # the complete OpenMP 5.2 grammar; this is a deliberate standard-facing | ||||||
| 378 | # allowance, not a claim that libgomp's current prose explicitly documents the | ||||||
| 379 | # modifier syntax. | ||||||
| 380 | sub _validate_omp_schedule { | ||||||
| 381 | 28 | 33 | my ($value) = @_; | ||||
| 382 | 28 | 123 | return if $value =~ m/\A\s*(?:(?:MONOTONIC|NONMONOTONIC)\s*:\s*)?(?:STATIC|DYNAMIC|GUIDED|AUTO)(?:\s*,\s*[1-9]\d*)?\s*\z/i; | ||||
| 383 | 5 | 7 | return q{Expected [MONOTONIC|NONMONOTONIC:]STATIC|DYNAMIC|GUIDED|AUTO optionally followed by a positive chunk}; | ||||
| 384 | } | ||||||
| 385 | |||||||
| 386 | # GCC/libgomp: https://gcc.gnu.org/onlinedocs/gcc-16.2.0/libgomp/OMP_005fTARGET_005fOFFLOAD.html | ||||||
| 387 | # OpenMP 5.2: Section 21.2.8, OMP_TARGET_OFFLOAD. | ||||||
| 388 | # OpenMP defines MANDATORY, DISABLED, and DEFAULT; OpenMP 5.2 specifically | ||||||
| 389 | # leaves support of DISABLED implementation-defined. GCC/libgomp explicitly | ||||||
| 390 | # implements all three spellings and documents host execution for DISABLED. | ||||||
| 391 | # We validate the token but do not probe target devices or offload plugins. | ||||||
| 392 | 23 | 35 | sub _validate_omp_target_offload { return _enum( shift, qw/MANDATORY DISABLED DEFAULT/ ) } | ||||
| 393 | |||||||
| 394 | # GCC/libgomp: https://gcc.gnu.org/onlinedocs/gcc-16.2.0/libgomp/OMP_005fTHREAD_005fLIMIT.html | ||||||
| 395 | # OpenMP 5.2: Section 21.1.3, OMP_THREAD_LIMIT. | ||||||
| 396 | # A positive integer is required; exceeding implementation capability is | ||||||
| 397 | # implementation-defined and deliberately not system-checked. | ||||||
| 398 | 40 | 56 | sub _validate_omp_thread_limit { return _integer_at_least( shift, 1 ) } | ||||
| 399 | |||||||
| 400 | # GCC/libgomp: https://gcc.gnu.org/onlinedocs/gcc-16.2.0/libgomp/OMP_005fWAIT_005fPOLICY.html | ||||||
| 401 | # OpenMP 5.2: Section 21.2.3, OMP_WAIT_POLICY. | ||||||
| 402 | # ACTIVE/PASSIVE are portable values, but the detailed waiting behavior is | ||||||
| 403 | # explicitly implementation-defined. We validate the token, not timing behavior. | ||||||
| 404 | 20 | 28 | sub _validate_omp_wait_policy { return _enum( shift, qw/ACTIVE PASSIVE/ ) } | ||||
| 405 | |||||||
| 406 | # GCC/libgomp: https://gcc.gnu.org/onlinedocs/gcc-16.2.0/libgomp/GOMP_005fCPU_005fAFFINITY.html | ||||||
| 407 | # OpenMP 5.2: no GOMP_CPU_AFFINITY variable; this is a GNU extension related to | ||||||
| 408 | # OMP_PLACES/OMP_PROC_BIND. libgomp accepts CPU numbers, M-N ranges, and M-N:S | ||||||
| 409 | # strides separated by spaces or commas; OMP_PROC_BIND takes precedence. | ||||||
| 410 | # We validate this GNU grammar but do not verify CPU identifiers on the host. | ||||||
| 411 | sub _validate_gomp_cpu_affinity { | ||||||
| 412 | 16 | 23 | my ($value) = @_; | ||||
| 413 | 16 | 35 | my $entry = qr/\d+(?:-\d+(?::[1-9]\d*)?)?/; | ||||
| 414 | 16 | 674 | return if $value =~ m/\A\s*$entry(?:\s*(?:,|\s)\s*$entry)*\s*\z/; | ||||
| 415 | 2 | 6 | return q{Expected CPU numbers, M-N ranges, or M-N:S ranges separated by spaces or commas}; | ||||
| 416 | } | ||||||
| 417 | |||||||
| 418 | # GCC/libgomp: https://gcc.gnu.org/onlinedocs/gcc-16.2.0/libgomp/GOMP_005fDEBUG.html | ||||||
| 419 | # OpenMP 5.2: no GOMP_DEBUG variable; it is a GNU extension. | ||||||
| 420 | # libgomp documents 0/1. We validate only those GNU-defined values. | ||||||
| 421 | 19 | 29 | sub _validate_gomp_debug { return _enum( shift, qw/0 1/ ) } | ||||
| 422 | |||||||
| 423 | # GCC/libgomp: https://gcc.gnu.org/onlinedocs/gcc-16.2.0/libgomp/GOMP_005fSTACKSIZE.html | ||||||
| 424 | # OpenMP 5.2: no GOMP_STACKSIZE variable; OMP_STACKSIZE is the standard analogue. | ||||||
| 425 | # libgomp documents GOMP_STACKSIZE as a numeric kilobyte value with no unit | ||||||
| 426 | # suffix. Older OpenMP::Environment releases passed arbitrary values through, and | ||||||
| 427 | # the unchanged regression suite asserts an OMP_STACKSIZE-style suffixed value. | ||||||
| 428 | # To preserve that public behavior, both assignment and assertion accept B/K/M/G | ||||||
| 429 | # suffixes as an explicit OpenMP::Environment compatibility extension. This is | ||||||
| 430 | # NOT presented as documented libgomp syntax; callers wanting the GNU-native form | ||||||
| 431 | # should use an unsuffixed positive integer (kilobytes). | ||||||
| 432 | sub _validate_gomp_stacksize { | ||||||
| 433 | 12 | 17 | my ($value) = @_; | ||||
| 434 | 12 | 57 | return if $value =~ m/\A\s*[1-9]\d*\s*(?:[BKMG])?\s*\z/i; | ||||
| 435 | 1 | 2 | return q{Expected a positive integer, with an optional B, K, M, or G OpenMP::Environment compatibility suffix}; | ||||
| 436 | } | ||||||
| 437 | |||||||
| 438 | # GCC/libgomp: https://gcc.gnu.org/onlinedocs/gcc-16.2.0/libgomp/GOMP_005fSPINCOUNT.html | ||||||
| 439 | # OpenMP 5.2: no GOMP_SPINCOUNT variable; it is a GNU extension. | ||||||
| 440 | # libgomp accepts INFINITE/INFINITY or a non-negative integer optionally suffixed | ||||||
| 441 | # by k/M/G/T multipliers. The number of CPUs can alter libgomp's effective spin | ||||||
| 442 | # behavior, but this portable validator checks syntax only. | ||||||
| 443 | sub _validate_gomp_spincount { | ||||||
| 444 | 20 | 30 | my ($value) = @_; | ||||
| 445 | 20 | 51 | return if $value =~ m/\A\s*(?:INFINITE|INFINITY)\s*\z/i; | ||||
| 446 | 11 | 45 | return if $value =~ m/\A\s*\d+(?:k|M|G|T)?\s*\z/; | ||||
| 447 | 3 | 16 | return q{Expected INFINITE, INFINITY, or a non-negative integer optionally suffixed by k, M, G, or T}; | ||||
| 448 | } | ||||||
| 449 | |||||||
| 450 | # GCC/libgomp: https://gcc.gnu.org/onlinedocs/gcc-16.2.0/libgomp/GOMP_005fRTEMS_005fTHREAD_005fPOOLS.html | ||||||
| 451 | # OpenMP 5.2: no GOMP_RTEMS_THREAD_POOLS variable; it is a GNU/RTEMS extension. | ||||||
| 452 | # libgomp documents colon-separated count[$priority]@scheduler configurations. | ||||||
| 453 | # We validate that grammar only; scheduler names, RTEMS presence, and acceptable | ||||||
| 454 | # pthread priorities are intentionally not queried for portability. | ||||||
| 455 | sub _validate_gomp_rtems_thread_pools { | ||||||
| 456 | 13 | 17 | my ($value) = @_; | ||||
| 457 | 13 | 23 | my $config = qr/[1-9]\d*(?:\$\d+)?\@[A-Za-z_][A-Za-z0-9_.-]*/; | ||||
| 458 | 13 | 470 | return if $value =~ m/\A\s*$config(?:\s*:\s*$config)*\s*\z/; | ||||
| 459 | 2 | 5 | return q{Expected colon-separated count[$priority]@scheduler configurations}; | ||||
| 460 | } | ||||||
| 461 | |||||||
| 462 | # GCC/libgomp: https://gcc.gnu.org/onlinedocs/gcc-16.2.0/libgomp/OMP_005fTEAMS_005fTHREAD_005fLIMIT.html | ||||||
| 463 | # OpenMP 5.2: Section 21.6.2, OMP_TEAMS_THREAD_LIMIT. | ||||||
| 464 | # A positive integer is portable. Target/runtime capability is not probed. | ||||||
| 465 | 39 | 56 | sub _validate_omp_teams_thread_limit { return _integer_at_least( shift, 1 ) } | ||||
| 466 | |||||||
| 467 | # GCC/libgomp: https://gcc.gnu.org/onlinedocs/gcc-16.2.0/libgomp/OMP_005fALLOCATOR.html | ||||||
| 468 | # OpenMP 5.2: Section 21.5.1 plus Sections 6.1/6.2 (memory spaces/allocators). | ||||||
| 469 | # OpenMP permits a predefined allocator, a predefined memory space, or a memory | ||||||
| 470 | # space plus allocator traits. Several memory-space mappings and pool-size | ||||||
| 471 | # defaults are implementation-defined. libgomp additionally documents its GNU | ||||||
| 472 | # handling/mappings for implementation-defined allocators and GNU ompx_* names. | ||||||
| 473 | # OpenMP permits fb_data and allocator_fb. libgomp also lists allocator_fb as an | ||||||
| 474 | # allowed fallback token, but explicitly declares fb_data unsupported in the | ||||||
| 475 | # OMP_ALLOCATOR environment string because fb_data requires an allocator handle. | ||||||
| 476 | # We therefore accept fallback=allocator_fb as documented by libgomp, reject an | ||||||
| 477 | # explicit fb_data trait, and do not infer whether allocator_fb can provide a | ||||||
| 478 | # useful fallback without fb_data. We also do not test whether a memory space or | ||||||
| 479 | # allocator can actually be instantiated on the current runtime. | ||||||
| 480 | sub _validate_omp_allocator { | ||||||
| 481 | 36 | 41 | my ($value) = @_; | ||||
| 482 | 36 | 47 | my $text = lc _trim($value); | ||||
| 483 | 36 360 | 56 438 | my %allocator = map { $_ => 1 } qw/ | ||||
| 484 | omp_default_mem_alloc omp_large_cap_mem_alloc omp_const_mem_alloc | ||||||
| 485 | omp_high_bw_mem_alloc omp_low_lat_mem_alloc omp_cgroup_mem_alloc | ||||||
| 486 | omp_pteam_mem_alloc omp_thread_mem_alloc ompx_gnu_pinned_mem_alloc | ||||||
| 487 | ompx_gnu_managed_mem_alloc | ||||||
| 488 | /; | ||||||
| 489 | 36 216 | 59 254 | my %space = map { $_ => 1 } qw/ | ||||
| 490 | omp_default_mem_space omp_large_cap_mem_space omp_const_mem_space | ||||||
| 491 | omp_high_bw_mem_space omp_low_lat_mem_space ompx_gnu_managed_mem_space | ||||||
| 492 | /; | ||||||
| 493 | |||||||
| 494 | 36 | 91 | return if $allocator{$text}; | ||||
| 495 | 25 | 40 | return if $space{$text}; | ||||
| 496 | |||||||
| 497 | 23 | 85 | my ( $memspace, $traits ) = $text =~ m/\A([^:]+):(.*)\z/; | ||||
| 498 | 23 | 43 | return q{Expected a predefined OpenMP allocator or memory space, optionally followed by allocator traits} | ||||
| 499 | if not defined $memspace; | ||||||
| 500 | return q{Expected a predefined OpenMP allocator or memory space, optionally followed by allocator traits} | ||||||
| 501 | 22 | 37 | if not $space{$memspace}; | ||||
| 502 | 21 | 24 | return q{Allocator trait list must not be empty} if not length _trim($traits); | ||||
| 503 | |||||||
| 504 | 20 | 31 | my %seen; | ||||
| 505 | my %parsed; | ||||||
| 506 | 20 | 57 | foreach my $pair ( split /\s*,\s*/, $traits ) { | ||||
| 507 | 29 | 104 | my ( $name, $trait_value ) = $pair =~ m/\A\s*([A-Za-z_][A-Za-z0-9_]*)\s*=\s*([^\s,]+)\s*\z/; | ||||
| 508 | 29 | 47 | return q{Malformed allocator trait; expected name=value} if not defined $name; | ||||
| 509 | 28 | 32 | $name = lc $name; | ||||
| 510 | 28 | 54 | return qq{Duplicate allocator trait "$name"} if $seen{$name}++; | ||||
| 511 | 27 | 37 | my $err = _validate_allocator_trait( $name, $trait_value, \%allocator ); | ||||
| 512 | 27 | 67 | return $err if defined $err; | ||||
| 513 | 18 | 26 | $parsed{$name} = $trait_value; | ||||
| 514 | } | ||||||
| 515 | |||||||
| 516 | 9 | 16 | if ( defined $parsed{fb_data} ) { | ||||
| 517 | 1 | 4 | return q{fb_data is permitted by OpenMP but unsupported by GNU libgomp in OMP_ALLOCATOR}; | ||||
| 518 | } | ||||||
| 519 | 8 | 26 | return; | ||||
| 520 | } | ||||||
| 521 | |||||||
| 522 | # GCC/libgomp: https://gcc.gnu.org/onlinedocs/gcc-16.2.0/libgomp/OMP_005fAFFINITY_005fFORMAT.html | ||||||
| 523 | # OpenMP 5.2: Section 21.2.5, OMP_AFFINITY_FORMAT. | ||||||
| 524 | # OpenMP defines percent field syntax and explicitly permits additional | ||||||
| 525 | # implementation-defined field types. A width is a positive decimal integer and | ||||||
| 526 | # may appear bare (%4L), right-justified (%.4L), or zero-padded for numeric fields | ||||||
| 527 | # (%0.4L). libgomp documents the standard short and long field names and the same | ||||||
| 528 | # width forms. OMP_AFFINITY_FORMAT is case-sensitive and its leading/trailing | ||||||
| 529 | # whitespace is significant, so validate_value deliberately does not trim it. | ||||||
| 530 | # Unknown alphabetic short/long fields are accepted as possible implementation- | ||||||
| 531 | # defined extensions. We validate syntax only and do not reject the standard's | ||||||
| 532 | # explicitly unspecified combinations such as zero-padding a nonnumeric field. | ||||||
| 533 | sub _validate_omp_affinity_format { | ||||||
| 534 | 23 | 27 | my ($value) = @_; | ||||
| 535 | 23 | 26 | my $i = 0; | ||||
| 536 | 23 | 41 | while ( $i < length $value ) { | ||||
| 537 | 36 | 53 | my $pos = index( $value, q{%}, $i ); | ||||
| 538 | 36 | 49 | return if $pos < 0; | ||||
| 539 | 34 | 46 | return q{Trailing percent sign in affinity format} if $pos == length($value) - 1; | ||||
| 540 | 33 | 34 | $i = $pos + 1; | ||||
| 541 | 33 | 58 | if ( substr( $value, $i, 1 ) eq q{%} ) { | ||||
| 542 | 1 | 1 | $i++; | ||||
| 543 | 1 | 2 | next; | ||||
| 544 | } | ||||||
| 545 | 32 | 41 | my $tail = substr( $value, $i ); | ||||
| 546 | 32 | 59 | my $width = qr/(?:[1-9]\d*|(?:0\.|\.)[1-9]\d*)?/; | ||||
| 547 | 32 | 348 | if ( $tail =~ m/\A$width([A-Za-z])/ ) { | ||||
| 548 | 25 | 41 | $i += length $&; | ||||
| 549 | 25 | 62 | next; | ||||
| 550 | } | ||||||
| 551 | 7 | 117 | if ( $tail =~ m/\A$width\{[A-Za-z_][A-Za-z0-9_]*\}/ ) { | ||||
| 552 | 3 | 7 | $i += length $&; | ||||
| 553 | 3 | 10 | next; | ||||
| 554 | } | ||||||
| 555 | 4 | 11 | return q{Malformed affinity field; expected %% or %[width]field or %[width]{field_name}}; | ||||
| 556 | } | ||||||
| 557 | 16 | 23 | return; | ||||
| 558 | } | ||||||
| 559 | |||||||
| 560 | # GCC/libgomp: https://gcc.gnu.org/onlinedocs/gcc-16.2.0/libgomp/OMP_005fDISPLAY_005fAFFINITY.html | ||||||
| 561 | # OpenMP 5.2: Section 21.2.4, OMP_DISPLAY_AFFINITY. | ||||||
| 562 | # TRUE/FALSE are defined; all other values have implementation-defined display | ||||||
| 563 | # behavior. libgomp implements the standard boolean forms. No affinity topology | ||||||
| 564 | # is queried by this validator. | ||||||
| 565 | 22 | 44 | sub _validate_omp_display_affinity { return _enum( shift, qw/TRUE FALSE/ ) } | ||||
| 566 | |||||||
| 567 | sub _validate_allocator_trait { | ||||||
| 568 | 27 | 33 | my ( $name, $value, $allocators ) = @_; | ||||
| 569 | 27 | 38 | if ( $name eq q{sync_hint} ) { | ||||
| 570 | 2 | 3 | return _enum( $value, qw/contended uncontended serialized private/ ); | ||||
| 571 | } | ||||||
| 572 | 25 | 37 | if ( $name eq q{alignment} ) { | ||||
| 573 | 4 | 13 | return q{alignment must be a positive power of two} | ||||
| 574 | if $value !~ m/\A[1-9]\d*\z/; | ||||||
| 575 | 3 | 8 | return q{alignment must be a positive power of two} | ||||
| 576 | if ( $value & ( $value - 1 ) ) != 0; | ||||||
| 577 | 2 | 3 | return; | ||||
| 578 | } | ||||||
| 579 | 21 | 28 | if ( $name eq q{access} ) { | ||||
| 580 | 2 | 3 | return _enum( $value, qw/all cgroup pteam thread/ ); | ||||
| 581 | } | ||||||
| 582 | 19 | 22 | if ( $name eq q{pool_size} ) { | ||||
| 583 | 2 | 2 | return _integer_at_least( $value, 1 ); | ||||
| 584 | } | ||||||
| 585 | 17 | 17 | if ( $name eq q{fallback} ) { | ||||
| 586 | 2 | 4 | return _enum( $value, qw/default_mem_fb null_fb abort_fb allocator_fb/ ); | ||||
| 587 | } | ||||||
| 588 | 15 | 17 | if ( $name eq q{fb_data} ) { | ||||
| 589 | 2 | 6 | return undef if $allocators->{ lc $value }; | ||||
| 590 | 1 | 2 | return q{fb_data must name a predefined allocator}; | ||||
| 591 | } | ||||||
| 592 | 13 | 15 | if ( $name eq q{pinned} ) { | ||||
| 593 | 6 | 8 | return _enum( $value, qw/true false/ ); | ||||
| 594 | } | ||||||
| 595 | 7 | 26 | if ( $name eq q{partition} ) { | ||||
| 596 | 6 | 9 | return _enum( $value, qw/environment nearest blocked interleaved/ ); | ||||
| 597 | } | ||||||
| 598 | 1 | 2 | return qq{Unknown allocator trait "$name"}; | ||||
| 599 | } | ||||||
| 600 | |||||||
| 601 | sub _trim { | ||||||
| 602 | 1054 | 1324 | my ($value) = @_; | ||||
| 603 | 1054 | 1774 | $value =~ s/\A\s+//; | ||||
| 604 | 1054 | 1472 | $value =~ s/\s+\z//; | ||||
| 605 | 1054 | 1785 | return $value; | ||||
| 606 | } | ||||||
| 607 | |||||||
| 608 | sub _split_top_level { | ||||||
| 609 | 62 | 147 | my ( $text, $separator ) = @_; | ||||
| 610 | 62 | 75 | my @parts; | ||||
| 611 | 62 | 79 | my $part = q{}; | ||||
| 612 | 62 | 89 | my ( $brace, $paren ) = ( 0, 0 ); | ||||
| 613 | 62 | 181 | foreach my $char ( split //, $text ) { | ||||
| 614 | 472 | 658 | $brace++ if $char eq q[{]; | ||||
| 615 | 472 | 608 | $brace-- if $char eq q[}]; | ||||
| 616 | 472 | 597 | $paren++ if $char eq q{(}; | ||||
| 617 | 472 | 598 | $paren-- if $char eq q{)}; | ||||
| 618 | 472 | 592 | return () if $brace < 0; | ||||
| 619 | 471 | 599 | return () if $paren < 0; | ||||
| 620 | 470 | 615 | if ( $char eq $separator ) { | ||||
| 621 | 120 | 161 | if ( not $brace ) { | ||||
| 622 | 71 | 89 | if ( not $paren ) { | ||||
| 623 | 68 | 82 | push @parts, _trim($part); | ||||
| 624 | 68 | 172 | $part = q{}; | ||||
| 625 | 68 | 103 | next; | ||||
| 626 | } | ||||||
| 627 | } | ||||||
| 628 | } | ||||||
| 629 | 402 | 476 | $part .= $char; | ||||
| 630 | } | ||||||
| 631 | 60 | 131 | return () if $brace; | ||||
| 632 | 59 | 101 | return () if $paren; | ||||
| 633 | 58 | 84 | push @parts, _trim($part); | ||||
| 634 | 58 125 | 93 219 | return () if grep { $_ eq q{} } @parts; | ||||
| 635 | 55 | 146 | return @parts; | ||||
| 636 | } | ||||||
| 637 | |||||||
| 638 | sub _validate_place_interval { | ||||||
| 639 | 42 | 61 | my ($item) = @_; | ||||
| 640 | 42 | 55 | my $text = _trim($item); | ||||
| 641 | 42 | 60 | $text =~ s/\A!\s*//; | ||||
| 642 | |||||||
| 643 | 42 | 82 | my ( $base, $suffix ); | ||||
| 644 | 42 | 121 | if ( substr( $text, 0, 1 ) eq q[{] ) { | ||||
| 645 | 36 | 54 | my $close = _matching_brace($text); | ||||
| 646 | 36 | 60 | return q{Unbalanced braces in OMP_PLACES} if $close < 0; | ||||
| 647 | 35 | 55 | $base = substr( $text, 0, $close + 1 ); | ||||
| 648 | 35 | 54 | $suffix = substr( $text, $close + 1 ); | ||||
| 649 | 35 | 54 | my $inside = substr( $base, 1, length($base) - 2 ); | ||||
| 650 | 35 | 50 | my @resources = _split_top_level( $inside, q{,} ); | ||||
| 651 | 35 | 60 | return q{Empty resource list in OMP_PLACES} if not @resources; | ||||
| 652 | 33 | 72 | foreach my $resource (@resources) { | ||||
| 653 | 78 | 100 | my $err = _validate_resource_interval($resource); | ||||
| 654 | 78 | 176 | return $err if defined $err; | ||||
| 655 | } | ||||||
| 656 | } | ||||||
| 657 | elsif ( $text =~ m/\A(\d+)(.*)\z/s ) { | ||||||
| 658 | 4 | 14 | $base = $1; | ||||
| 659 | 4 | 8 | $suffix = $2; | ||||
| 660 | } | ||||||
| 661 | else { | ||||||
| 662 | 2 | 4 | return q{Expected an explicit place/resource or an abstract place name}; | ||||
| 663 | } | ||||||
| 664 | |||||||
| 665 | 36 | 51 | return if _trim($suffix) eq q{}; | ||||
| 666 | 5 | 35 | return q{Malformed place interval; expected :length or :length:stride} | ||||
| 667 | if $suffix !~ m/\A\s*:\s*[1-9]\d*(?:\s*:\s*[+-]?\d+)?\s*\z/; | ||||||
| 668 | 4 | 12 | return; | ||||
| 669 | } | ||||||
| 670 | |||||||
| 671 | sub _validate_resource_interval { | ||||||
| 672 | 78 | 102 | my ($resource) = @_; | ||||
| 673 | 78 | 87 | my $text = _trim($resource); | ||||
| 674 | 78 | 98 | $text =~ s/\A!\s*//; | ||||
| 675 | 78 | 258 | return if $text =~ m/\A\d+(?:\s*:\s*[1-9]\d*(?:\s*:\s*[+-]?\d+)?)?\z/; | ||||
| 676 | 1 | 3 | return q{Malformed OMP_PLACES resource; expected resource, resource:length, or resource:length:stride}; | ||||
| 677 | } | ||||||
| 678 | |||||||
| 679 | sub _matching_brace { | ||||||
| 680 | 37 | 54 | my ($text) = @_; | ||||
| 681 | 37 | 43 | my $depth = 0; | ||||
| 682 | 37 | 81 | for my $i ( 0 .. length($text) - 1 ) { | ||||
| 683 | 223 | 271 | my $char = substr( $text, $i, 1 ); | ||||
| 684 | 223 | 305 | $depth++ if $char eq q[{]; | ||||
| 685 | 223 | 317 | if ( $char eq q[}] ) { | ||||
| 686 | 35 | 39 | $depth--; | ||||
| 687 | 35 | 78 | return $i if $depth == 0; | ||||
| 688 | } | ||||||
| 689 | } | ||||||
| 690 | 2 | 3 | return -1; | ||||
| 691 | } | ||||||
| 692 | |||||||
| 693 | 1; | ||||||
| 694 | |||||||