1 //===-- runtime/transformational.cpp --------------------------------------===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 // Implements the transformational intrinsic functions of Fortran 2018 that 10 // rearrange or duplicate data without (much) regard to type. These are 11 // CSHIFT, EOSHIFT, PACK, RESHAPE, SPREAD, TRANSPOSE, and UNPACK. 12 // 13 // Many of these are defined in the 2018 standard with text that makes sense 14 // only if argument arrays have lower bounds of one. Rather than interpret 15 // these cases as implying a hidden constraint, these implementations 16 // work with arbitrary lower bounds. This may be technically an extension 17 // of the standard but it more likely to conform with its intent. 18 19 #include "flang/Runtime/transformational.h" 20 #include "copy.h" 21 #include "terminator.h" 22 #include "tools.h" 23 #include "flang/Runtime/descriptor.h" 24 #include <algorithm> 25 26 namespace Fortran::runtime { 27 28 // Utility for CSHIFT & EOSHIFT rank > 1 cases that determines the shift count 29 // for each of the vector sections of the result. 30 class ShiftControl { 31 public: 32 ShiftControl(const Descriptor &s, Terminator &t, int dim) 33 : shift_{s}, terminator_{t}, shiftRank_{s.rank()}, dim_{dim} {} 34 void Init(const Descriptor &source, const char *which) { 35 int rank{source.rank()}; 36 RUNTIME_CHECK(terminator_, shiftRank_ == 0 || shiftRank_ == rank - 1); 37 auto catAndKind{shift_.type().GetCategoryAndKind()}; 38 RUNTIME_CHECK( 39 terminator_, catAndKind && catAndKind->first == TypeCategory::Integer); 40 shiftElemLen_ = catAndKind->second; 41 if (shiftRank_ > 0) { 42 int k{0}; 43 for (int j{0}; j < rank; ++j) { 44 if (j + 1 != dim_) { 45 const Dimension &shiftDim{shift_.GetDimension(k)}; 46 lb_[k++] = shiftDim.LowerBound(); 47 if (shiftDim.Extent() != source.GetDimension(j).Extent()) { 48 terminator_.Crash("%s: on dimension %d, SHIFT= has extent %jd but " 49 "SOURCE= has extent %jd", 50 which, k, static_cast<std::intmax_t>(shiftDim.Extent()), 51 static_cast<std::intmax_t>(source.GetDimension(j).Extent())); 52 } 53 } 54 } 55 } else { 56 shiftCount_ = 57 GetInt64(shift_.OffsetElement<char>(), shiftElemLen_, terminator_); 58 } 59 } 60 SubscriptValue GetShift(const SubscriptValue resultAt[]) const { 61 if (shiftRank_ > 0) { 62 SubscriptValue shiftAt[maxRank]; 63 int k{0}; 64 for (int j{0}; j < shiftRank_ + 1; ++j) { 65 if (j + 1 != dim_) { 66 shiftAt[k] = lb_[k] + resultAt[j] - 1; 67 ++k; 68 } 69 } 70 return GetInt64( 71 shift_.Element<char>(shiftAt), shiftElemLen_, terminator_); 72 } else { 73 return shiftCount_; // invariant count extracted in Init() 74 } 75 } 76 77 private: 78 const Descriptor &shift_; 79 Terminator &terminator_; 80 int shiftRank_; 81 int dim_; 82 SubscriptValue lb_[maxRank]; 83 std::size_t shiftElemLen_; 84 SubscriptValue shiftCount_{}; 85 }; 86 87 // Fill an EOSHIFT result with default boundary values 88 static void DefaultInitialize( 89 const Descriptor &result, Terminator &terminator) { 90 auto catAndKind{result.type().GetCategoryAndKind()}; 91 RUNTIME_CHECK( 92 terminator, catAndKind && catAndKind->first != TypeCategory::Derived); 93 std::size_t elementLen{result.ElementBytes()}; 94 std::size_t bytes{result.Elements() * elementLen}; 95 if (catAndKind->first == TypeCategory::Character) { 96 switch (int kind{catAndKind->second}) { 97 case 1: 98 std::fill_n(result.OffsetElement<char>(), bytes, ' '); 99 break; 100 case 2: 101 std::fill_n(result.OffsetElement<char16_t>(), bytes / 2, 102 static_cast<char16_t>(' ')); 103 break; 104 case 4: 105 std::fill_n(result.OffsetElement<char32_t>(), bytes / 4, 106 static_cast<char32_t>(' ')); 107 break; 108 default: 109 terminator.Crash("not yet implemented: EOSHIFT: CHARACTER kind %d", kind); 110 } 111 } else { 112 std::memset(result.raw().base_addr, 0, bytes); 113 } 114 } 115 116 static inline std::size_t AllocateResult(Descriptor &result, 117 const Descriptor &source, int rank, const SubscriptValue extent[], 118 Terminator &terminator, const char *function) { 119 std::size_t elementLen{source.ElementBytes()}; 120 const DescriptorAddendum *sourceAddendum{source.Addendum()}; 121 result.Establish(source.type(), elementLen, nullptr, rank, extent, 122 CFI_attribute_allocatable, sourceAddendum != nullptr); 123 if (sourceAddendum) { 124 *result.Addendum() = *sourceAddendum; 125 } 126 for (int j{0}; j < rank; ++j) { 127 result.GetDimension(j).SetBounds(1, extent[j]); 128 } 129 if (int stat{result.Allocate()}) { 130 terminator.Crash( 131 "%s: Could not allocate memory for result (stat=%d)", function, stat); 132 } 133 return elementLen; 134 } 135 136 extern "C" { 137 138 // CSHIFT where rank of ARRAY argument > 1 139 void RTNAME(Cshift)(Descriptor &result, const Descriptor &source, 140 const Descriptor &shift, int dim, const char *sourceFile, int line) { 141 Terminator terminator{sourceFile, line}; 142 int rank{source.rank()}; 143 RUNTIME_CHECK(terminator, rank > 1); 144 if (dim < 1 || dim > rank) { 145 terminator.Crash( 146 "CSHIFT: DIM=%d must be >= 1 and <= SOURCE= rank %d", dim, rank); 147 } 148 ShiftControl shiftControl{shift, terminator, dim}; 149 shiftControl.Init(source, "CSHIFT"); 150 SubscriptValue extent[maxRank]; 151 source.GetShape(extent); 152 AllocateResult(result, source, rank, extent, terminator, "CSHIFT"); 153 SubscriptValue resultAt[maxRank]; 154 for (int j{0}; j < rank; ++j) { 155 resultAt[j] = 1; 156 } 157 SubscriptValue sourceLB[maxRank]; 158 source.GetLowerBounds(sourceLB); 159 SubscriptValue dimExtent{extent[dim - 1]}; 160 SubscriptValue dimLB{sourceLB[dim - 1]}; 161 SubscriptValue &resDim{resultAt[dim - 1]}; 162 for (std::size_t n{result.Elements()}; n > 0; n -= dimExtent) { 163 SubscriptValue shiftCount{shiftControl.GetShift(resultAt)}; 164 SubscriptValue sourceAt[maxRank]; 165 for (int j{0}; j < rank; ++j) { 166 sourceAt[j] = sourceLB[j] + resultAt[j] - 1; 167 } 168 SubscriptValue &sourceDim{sourceAt[dim - 1]}; 169 sourceDim = dimLB + shiftCount % dimExtent; 170 if (sourceDim < dimLB) { 171 sourceDim += dimExtent; 172 } 173 for (resDim = 1; resDim <= dimExtent; ++resDim) { 174 CopyElement(result, resultAt, source, sourceAt, terminator); 175 if (++sourceDim == dimLB + dimExtent) { 176 sourceDim = dimLB; 177 } 178 } 179 result.IncrementSubscripts(resultAt); 180 } 181 } 182 183 // CSHIFT where rank of ARRAY argument == 1 184 void RTNAME(CshiftVector)(Descriptor &result, const Descriptor &source, 185 std::int64_t shift, const char *sourceFile, int line) { 186 Terminator terminator{sourceFile, line}; 187 RUNTIME_CHECK(terminator, source.rank() == 1); 188 const Dimension &sourceDim{source.GetDimension(0)}; 189 SubscriptValue extent{sourceDim.Extent()}; 190 AllocateResult(result, source, 1, &extent, terminator, "CSHIFT"); 191 SubscriptValue lb{sourceDim.LowerBound()}; 192 for (SubscriptValue j{0}; j < extent; ++j) { 193 SubscriptValue resultAt{1 + j}; 194 SubscriptValue sourceAt{lb + (j + shift) % extent}; 195 if (sourceAt < lb) { 196 sourceAt += extent; 197 } 198 CopyElement(result, &resultAt, source, &sourceAt, terminator); 199 } 200 } 201 202 // EOSHIFT of rank > 1 203 void RTNAME(Eoshift)(Descriptor &result, const Descriptor &source, 204 const Descriptor &shift, const Descriptor *boundary, int dim, 205 const char *sourceFile, int line) { 206 Terminator terminator{sourceFile, line}; 207 SubscriptValue extent[maxRank]; 208 int rank{source.GetShape(extent)}; 209 RUNTIME_CHECK(terminator, rank > 1); 210 if (dim < 1 || dim > rank) { 211 terminator.Crash( 212 "EOSHIFT: DIM=%d must be >= 1 and <= SOURCE= rank %d", dim, rank); 213 } 214 std::size_t elementLen{ 215 AllocateResult(result, source, rank, extent, terminator, "EOSHIFT")}; 216 int boundaryRank{-1}; 217 if (boundary) { 218 boundaryRank = boundary->rank(); 219 RUNTIME_CHECK(terminator, boundaryRank == 0 || boundaryRank == rank - 1); 220 RUNTIME_CHECK(terminator, boundary->type() == source.type()); 221 if (boundary->ElementBytes() != elementLen) { 222 terminator.Crash("EOSHIFT: BOUNDARY= has element byte length %zd, but " 223 "SOURCE= has length %zd", 224 boundary->ElementBytes(), elementLen); 225 } 226 if (boundaryRank > 0) { 227 int k{0}; 228 for (int j{0}; j < rank; ++j) { 229 if (j != dim - 1) { 230 if (boundary->GetDimension(k).Extent() != extent[j]) { 231 terminator.Crash("EOSHIFT: BOUNDARY= has extent %jd on dimension " 232 "%d but must conform with extent %jd of SOURCE=", 233 static_cast<std::intmax_t>(boundary->GetDimension(k).Extent()), 234 k + 1, static_cast<std::intmax_t>(extent[j])); 235 } 236 ++k; 237 } 238 } 239 } 240 } 241 ShiftControl shiftControl{shift, terminator, dim}; 242 shiftControl.Init(source, "EOSHIFT"); 243 SubscriptValue resultAt[maxRank]; 244 for (int j{0}; j < rank; ++j) { 245 resultAt[j] = 1; 246 } 247 if (!boundary) { 248 DefaultInitialize(result, terminator); 249 } 250 SubscriptValue sourceLB[maxRank]; 251 source.GetLowerBounds(sourceLB); 252 SubscriptValue boundaryAt[maxRank]; 253 if (boundaryRank > 0) { 254 boundary->GetLowerBounds(boundaryAt); 255 } 256 SubscriptValue dimExtent{extent[dim - 1]}; 257 SubscriptValue dimLB{sourceLB[dim - 1]}; 258 SubscriptValue &resDim{resultAt[dim - 1]}; 259 for (std::size_t n{result.Elements()}; n > 0; n -= dimExtent) { 260 SubscriptValue shiftCount{shiftControl.GetShift(resultAt)}; 261 SubscriptValue sourceAt[maxRank]; 262 for (int j{0}; j < rank; ++j) { 263 sourceAt[j] = sourceLB[j] + resultAt[j] - 1; 264 } 265 SubscriptValue &sourceDim{sourceAt[dim - 1]}; 266 sourceDim = dimLB + shiftCount; 267 for (resDim = 1; resDim <= dimExtent; ++resDim) { 268 if (sourceDim >= dimLB && sourceDim < dimLB + dimExtent) { 269 CopyElement(result, resultAt, source, sourceAt, terminator); 270 } else if (boundary) { 271 CopyElement(result, resultAt, *boundary, boundaryAt, terminator); 272 } 273 ++sourceDim; 274 } 275 result.IncrementSubscripts(resultAt); 276 if (boundaryRank > 0) { 277 boundary->IncrementSubscripts(boundaryAt); 278 } 279 } 280 } 281 282 // EOSHIFT of vector 283 void RTNAME(EoshiftVector)(Descriptor &result, const Descriptor &source, 284 std::int64_t shift, const Descriptor *boundary, const char *sourceFile, 285 int line) { 286 Terminator terminator{sourceFile, line}; 287 RUNTIME_CHECK(terminator, source.rank() == 1); 288 SubscriptValue extent{source.GetDimension(0).Extent()}; 289 std::size_t elementLen{ 290 AllocateResult(result, source, 1, &extent, terminator, "EOSHIFT")}; 291 if (boundary) { 292 RUNTIME_CHECK(terminator, boundary->rank() == 0); 293 RUNTIME_CHECK(terminator, boundary->type() == source.type()); 294 if (boundary->ElementBytes() != elementLen) { 295 terminator.Crash("EOSHIFT: BOUNDARY= has element byte length %zd but " 296 "SOURCE= has length %zd", 297 boundary->ElementBytes(), elementLen); 298 } 299 } 300 if (!boundary) { 301 DefaultInitialize(result, terminator); 302 } 303 SubscriptValue lb{source.GetDimension(0).LowerBound()}; 304 for (SubscriptValue j{1}; j <= extent; ++j) { 305 SubscriptValue sourceAt{lb + j - 1 + shift}; 306 if (sourceAt >= lb && sourceAt < lb + extent) { 307 CopyElement(result, &j, source, &sourceAt, terminator); 308 } else if (boundary) { 309 CopyElement(result, &j, *boundary, 0, terminator); 310 } 311 } 312 } 313 314 // PACK 315 void RTNAME(Pack)(Descriptor &result, const Descriptor &source, 316 const Descriptor &mask, const Descriptor *vector, const char *sourceFile, 317 int line) { 318 Terminator terminator{sourceFile, line}; 319 CheckConformability(source, mask, terminator, "PACK", "ARRAY=", "MASK="); 320 auto maskType{mask.type().GetCategoryAndKind()}; 321 RUNTIME_CHECK( 322 terminator, maskType && maskType->first == TypeCategory::Logical); 323 SubscriptValue trues{0}; 324 if (mask.rank() == 0) { 325 if (IsLogicalElementTrue(mask, nullptr)) { 326 trues = source.Elements(); 327 } 328 } else { 329 SubscriptValue maskAt[maxRank]; 330 mask.GetLowerBounds(maskAt); 331 for (std::size_t n{mask.Elements()}; n > 0; --n) { 332 if (IsLogicalElementTrue(mask, maskAt)) { 333 ++trues; 334 } 335 mask.IncrementSubscripts(maskAt); 336 } 337 } 338 SubscriptValue extent{trues}; 339 if (vector) { 340 RUNTIME_CHECK(terminator, vector->rank() == 1); 341 RUNTIME_CHECK(terminator, source.type() == vector->type()); 342 if (source.ElementBytes() != vector->ElementBytes()) { 343 terminator.Crash("PACK: SOURCE= has element byte length %zd, but VECTOR= " 344 "has length %zd", 345 source.ElementBytes(), vector->ElementBytes()); 346 } 347 extent = vector->GetDimension(0).Extent(); 348 if (extent < trues) { 349 terminator.Crash("PACK: VECTOR= has extent %jd but there are %jd MASK= " 350 "elements that are .TRUE.", 351 static_cast<std::intmax_t>(extent), 352 static_cast<std::intmax_t>(trues)); 353 } 354 } 355 AllocateResult(result, source, 1, &extent, terminator, "PACK"); 356 SubscriptValue sourceAt[maxRank], resultAt{1}; 357 source.GetLowerBounds(sourceAt); 358 if (mask.rank() == 0) { 359 if (IsLogicalElementTrue(mask, nullptr)) { 360 for (SubscriptValue n{trues}; n > 0; --n) { 361 CopyElement(result, &resultAt, source, sourceAt, terminator); 362 ++resultAt; 363 source.IncrementSubscripts(sourceAt); 364 } 365 } 366 } else { 367 SubscriptValue maskAt[maxRank]; 368 mask.GetLowerBounds(maskAt); 369 for (std::size_t n{source.Elements()}; n > 0; --n) { 370 if (IsLogicalElementTrue(mask, maskAt)) { 371 CopyElement(result, &resultAt, source, sourceAt, terminator); 372 ++resultAt; 373 } 374 source.IncrementSubscripts(sourceAt); 375 mask.IncrementSubscripts(maskAt); 376 } 377 } 378 if (vector) { 379 SubscriptValue vectorAt{ 380 vector->GetDimension(0).LowerBound() + resultAt - 1}; 381 for (; resultAt <= extent; ++resultAt, ++vectorAt) { 382 CopyElement(result, &resultAt, *vector, &vectorAt, terminator); 383 } 384 } 385 } 386 387 // RESHAPE 388 // F2018 16.9.163 389 void RTNAME(Reshape)(Descriptor &result, const Descriptor &source, 390 const Descriptor &shape, const Descriptor *pad, const Descriptor *order, 391 const char *sourceFile, int line) { 392 // Compute and check the rank of the result. 393 Terminator terminator{sourceFile, line}; 394 RUNTIME_CHECK(terminator, shape.rank() == 1); 395 RUNTIME_CHECK(terminator, shape.type().IsInteger()); 396 SubscriptValue resultRank{shape.GetDimension(0).Extent()}; 397 if (resultRank < 0 || resultRank > static_cast<SubscriptValue>(maxRank)) { 398 terminator.Crash( 399 "RESHAPE: SHAPE= vector length %jd implies a bad result rank", 400 static_cast<std::intmax_t>(resultRank)); 401 } 402 403 // Extract and check the shape of the result; compute its element count. 404 SubscriptValue resultExtent[maxRank]; 405 std::size_t shapeElementBytes{shape.ElementBytes()}; 406 std::size_t resultElements{1}; 407 SubscriptValue shapeSubscript{shape.GetDimension(0).LowerBound()}; 408 for (int j{0}; j < resultRank; ++j, ++shapeSubscript) { 409 resultExtent[j] = GetInt64( 410 shape.Element<char>(&shapeSubscript), shapeElementBytes, terminator); 411 if (resultExtent[j] < 0) { 412 terminator.Crash("RESHAPE: bad value for SHAPE(%d)=%jd", j + 1, 413 static_cast<std::intmax_t>(resultExtent[j])); 414 } 415 resultElements *= resultExtent[j]; 416 } 417 418 // Check that there are sufficient elements in the SOURCE=, or that 419 // the optional PAD= argument is present and nonempty. 420 std::size_t elementBytes{source.ElementBytes()}; 421 std::size_t sourceElements{source.Elements()}; 422 std::size_t padElements{pad ? pad->Elements() : 0}; 423 if (resultElements > sourceElements) { 424 if (padElements <= 0) { 425 terminator.Crash( 426 "RESHAPE: not enough elements, need %zd but only have %zd", 427 resultElements, sourceElements); 428 } 429 if (pad->ElementBytes() != elementBytes) { 430 terminator.Crash("RESHAPE: PAD= has element byte length %zd but SOURCE= " 431 "has length %zd", 432 pad->ElementBytes(), elementBytes); 433 } 434 } 435 436 // Extract and check the optional ORDER= argument, which must be a 437 // permutation of [1..resultRank]. 438 int dimOrder[maxRank]; 439 if (order) { 440 RUNTIME_CHECK(terminator, order->rank() == 1); 441 RUNTIME_CHECK(terminator, order->type().IsInteger()); 442 if (order->GetDimension(0).Extent() != resultRank) { 443 terminator.Crash("RESHAPE: the extent of ORDER (%jd) must match the rank" 444 " of the SHAPE (%d)", 445 static_cast<std::intmax_t>(order->GetDimension(0).Extent()), 446 resultRank); 447 } 448 std::uint64_t values{0}; 449 SubscriptValue orderSubscript{order->GetDimension(0).LowerBound()}; 450 std::size_t orderElementBytes{order->ElementBytes()}; 451 for (SubscriptValue j{0}; j < resultRank; ++j, ++orderSubscript) { 452 auto k{GetInt64(order->Element<char>(&orderSubscript), orderElementBytes, 453 terminator)}; 454 if (k < 1 || k > resultRank || ((values >> k) & 1)) { 455 terminator.Crash("RESHAPE: bad value for ORDER element (%jd)", 456 static_cast<std::intmax_t>(k)); 457 } 458 values |= std::uint64_t{1} << k; 459 dimOrder[j] = k - 1; 460 } 461 } else { 462 for (int j{0}; j < resultRank; ++j) { 463 dimOrder[j] = j; 464 } 465 } 466 467 // Allocate result descriptor 468 AllocateResult( 469 result, source, resultRank, resultExtent, terminator, "RESHAPE"); 470 471 // Populate the result's elements. 472 SubscriptValue resultSubscript[maxRank]; 473 result.GetLowerBounds(resultSubscript); 474 SubscriptValue sourceSubscript[maxRank]; 475 source.GetLowerBounds(sourceSubscript); 476 std::size_t resultElement{0}; 477 std::size_t elementsFromSource{std::min(resultElements, sourceElements)}; 478 for (; resultElement < elementsFromSource; ++resultElement) { 479 CopyElement(result, resultSubscript, source, sourceSubscript, terminator); 480 source.IncrementSubscripts(sourceSubscript); 481 result.IncrementSubscripts(resultSubscript, dimOrder); 482 } 483 if (resultElement < resultElements) { 484 // Remaining elements come from the optional PAD= argument. 485 SubscriptValue padSubscript[maxRank]; 486 pad->GetLowerBounds(padSubscript); 487 for (; resultElement < resultElements; ++resultElement) { 488 CopyElement(result, resultSubscript, *pad, padSubscript, terminator); 489 pad->IncrementSubscripts(padSubscript); 490 result.IncrementSubscripts(resultSubscript, dimOrder); 491 } 492 } 493 } 494 495 // SPREAD 496 void RTNAME(Spread)(Descriptor &result, const Descriptor &source, int dim, 497 std::int64_t ncopies, const char *sourceFile, int line) { 498 Terminator terminator{sourceFile, line}; 499 int rank{source.rank() + 1}; 500 RUNTIME_CHECK(terminator, rank <= maxRank); 501 if (dim < 1 || dim > rank) { 502 terminator.Crash("SPREAD: DIM=%d argument for rank-%d source array " 503 "must be greater than 1 and less than or equal to %d", 504 dim, rank - 1, rank); 505 } 506 ncopies = std::max<std::int64_t>(ncopies, 0); 507 SubscriptValue extent[maxRank]; 508 int k{0}; 509 for (int j{0}; j < rank; ++j) { 510 extent[j] = j == dim - 1 ? ncopies : source.GetDimension(k++).Extent(); 511 } 512 AllocateResult(result, source, rank, extent, terminator, "SPREAD"); 513 SubscriptValue resultAt[maxRank]; 514 for (int j{0}; j < rank; ++j) { 515 resultAt[j] = 1; 516 } 517 SubscriptValue &resultDim{resultAt[dim - 1]}; 518 SubscriptValue sourceAt[maxRank]; 519 source.GetLowerBounds(sourceAt); 520 for (std::size_t n{result.Elements()}; n > 0; n -= ncopies) { 521 for (resultDim = 1; resultDim <= ncopies; ++resultDim) { 522 CopyElement(result, resultAt, source, sourceAt, terminator); 523 } 524 result.IncrementSubscripts(resultAt); 525 source.IncrementSubscripts(sourceAt); 526 } 527 } 528 529 // TRANSPOSE 530 void RTNAME(Transpose)(Descriptor &result, const Descriptor &matrix, 531 const char *sourceFile, int line) { 532 Terminator terminator{sourceFile, line}; 533 RUNTIME_CHECK(terminator, matrix.rank() == 2); 534 SubscriptValue extent[2]{ 535 matrix.GetDimension(1).Extent(), matrix.GetDimension(0).Extent()}; 536 AllocateResult(result, matrix, 2, extent, terminator, "TRANSPOSE"); 537 SubscriptValue resultAt[2]{1, 1}; 538 SubscriptValue matrixLB[2]; 539 matrix.GetLowerBounds(matrixLB); 540 for (std::size_t n{result.Elements()}; n-- > 0; 541 result.IncrementSubscripts(resultAt)) { 542 SubscriptValue matrixAt[2]{ 543 matrixLB[0] + resultAt[1] - 1, matrixLB[1] + resultAt[0] - 1}; 544 CopyElement(result, resultAt, matrix, matrixAt, terminator); 545 } 546 } 547 548 // UNPACK 549 void RTNAME(Unpack)(Descriptor &result, const Descriptor &vector, 550 const Descriptor &mask, const Descriptor &field, const char *sourceFile, 551 int line) { 552 Terminator terminator{sourceFile, line}; 553 RUNTIME_CHECK(terminator, vector.rank() == 1); 554 int rank{mask.rank()}; 555 RUNTIME_CHECK(terminator, rank > 0); 556 SubscriptValue extent[maxRank]; 557 mask.GetShape(extent); 558 CheckConformability(mask, field, terminator, "UNPACK", "MASK=", "FIELD="); 559 std::size_t elementLen{ 560 AllocateResult(result, field, rank, extent, terminator, "UNPACK")}; 561 RUNTIME_CHECK(terminator, vector.type() == field.type()); 562 if (vector.ElementBytes() != elementLen) { 563 terminator.Crash( 564 "UNPACK: VECTOR= has element byte length %zd but FIELD= has length %zd", 565 vector.ElementBytes(), elementLen); 566 } 567 SubscriptValue resultAt[maxRank], maskAt[maxRank], fieldAt[maxRank], 568 vectorAt{vector.GetDimension(0).LowerBound()}; 569 for (int j{0}; j < rank; ++j) { 570 resultAt[j] = 1; 571 } 572 mask.GetLowerBounds(maskAt); 573 field.GetLowerBounds(fieldAt); 574 SubscriptValue vectorElements{vector.GetDimension(0).Extent()}; 575 SubscriptValue vectorLeft{vectorElements}; 576 for (std::size_t n{result.Elements()}; n-- > 0;) { 577 if (IsLogicalElementTrue(mask, maskAt)) { 578 if (vectorLeft-- == 0) { 579 terminator.Crash( 580 "UNPACK: VECTOR= argument has fewer elements (%d) than " 581 "MASK= has .TRUE. entries", 582 vectorElements); 583 } 584 CopyElement(result, resultAt, vector, &vectorAt, terminator); 585 ++vectorAt; 586 } else { 587 CopyElement(result, resultAt, field, fieldAt, terminator); 588 } 589 result.IncrementSubscripts(resultAt); 590 mask.IncrementSubscripts(maskAt); 591 field.IncrementSubscripts(fieldAt); 592 } 593 } 594 595 } // extern "C" 596 } // namespace Fortran::runtime 597