| //===-- lib/runtime/derived.cpp ---------------------------------*- C++ -*-===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "flang-rt/runtime/derived.h" |
| #include "flang-rt/runtime/descriptor.h" |
| #include "flang-rt/runtime/stat.h" |
| #include "flang-rt/runtime/terminator.h" |
| #include "flang-rt/runtime/tools.h" |
| #include "flang-rt/runtime/type-info.h" |
| #include "flang-rt/runtime/work-queue.h" |
| |
| namespace Fortran::runtime { |
| |
| RT_OFFLOAD_API_GROUP_BEGIN |
| |
| // Fill "extents" array with the extents of component "comp" from derived type |
| // instance "derivedInstance". |
| static RT_API_ATTRS void GetComponentExtents(SubscriptValue (&extents)[maxRank], |
| const typeInfo::Component &comp, const Descriptor &derivedInstance) { |
| const typeInfo::Value *bounds{comp.bounds()}; |
| for (int dim{0}; dim < comp.rank(); ++dim) { |
| auto lb{bounds[2 * dim].GetValue(&derivedInstance).value_or(0)}; |
| auto ub{bounds[2 * dim + 1].GetValue(&derivedInstance).value_or(0)}; |
| extents[dim] = ub >= lb ? static_cast<SubscriptValue>(ub - lb + 1) : 0; |
| } |
| } |
| |
| RT_API_ATTRS int Initialize(const Descriptor &instance, |
| const typeInfo::DerivedType &derived, Terminator &terminator, bool, |
| const Descriptor *) { |
| WorkQueue workQueue{terminator}; |
| int status{workQueue.BeginInitialize(instance, derived)}; |
| return status == StatContinue ? workQueue.Run() : status; |
| } |
| |
| RT_API_ATTRS int InitializeTicket::Begin(WorkQueue &) { |
| if (elements_ == 0) { |
| return StatOk; |
| } else { |
| // Initialize procedure pointer components in the first element, |
| // whence they will be copied later into all others. |
| const Descriptor &procPtrDesc{derived_.procPtr()}; |
| std::size_t numProcPtrs{procPtrDesc.InlineElements()}; |
| char *raw{instance_.OffsetElement<char>()}; |
| const auto *ppComponent{ |
| procPtrDesc.OffsetElement<typeInfo::ProcPtrComponent>()}; |
| for (std::size_t k{0}; k < numProcPtrs; ++k, ++ppComponent) { |
| auto &pptr{*reinterpret_cast<typeInfo::ProcedurePointer *>( |
| raw + ppComponent->offset)}; |
| pptr = ppComponent->procInitialization; |
| } |
| return StatContinue; |
| } |
| } |
| |
| RT_API_ATTRS int InitializeTicket::Continue(WorkQueue &workQueue) { |
| // Initialize the data components of the first element. |
| char *rawInstance{instance_.OffsetElement<char>()}; |
| for (; !Componentwise::IsComplete(); SkipToNextComponent()) { |
| char *rawComponent{rawInstance + component_->offset()}; |
| if (component_->genre() == typeInfo::Component::Genre::Allocatable) { |
| Descriptor &allocDesc{*reinterpret_cast<Descriptor *>(rawComponent)}; |
| component_->EstablishDescriptor( |
| allocDesc, instance_, workQueue.terminator()); |
| } else if (const void *init{component_->initialization()}) { |
| // Explicit initialization of data pointers and |
| // non-allocatable non-automatic components |
| std::size_t bytes{component_->SizeInBytes(instance_)}; |
| std::memcpy(rawComponent, init, bytes); |
| } else if (component_->genre() == typeInfo::Component::Genre::Pointer) { |
| // Data pointers without explicit initialization are established |
| // so that they are valid right-hand side targets of pointer |
| // assignment statements. |
| Descriptor &ptrDesc{*reinterpret_cast<Descriptor *>(rawComponent)}; |
| component_->EstablishDescriptor( |
| ptrDesc, instance_, workQueue.terminator()); |
| } else if (component_->genre() == typeInfo::Component::Genre::Data && |
| component_->derivedType() && |
| !component_->derivedType()->noInitializationNeeded()) { |
| // Default initialization of non-pointer non-allocatable/automatic |
| // data component. Handles parent component's elements. |
| SubscriptValue extents[maxRank]; |
| GetComponentExtents(extents, *component_, instance_); |
| Descriptor &compDesc{componentDescriptor_.descriptor()}; |
| const typeInfo::DerivedType &compType{*component_->derivedType()}; |
| compDesc.Establish(compType, rawComponent, component_->rank(), extents); |
| if (int status{workQueue.BeginInitialize(compDesc, compType)}; |
| status != StatOk) { |
| SkipToNextComponent(); |
| return status; |
| } |
| } |
| } |
| // The first element is now complete. Copy it into the others. |
| if (elements_ < 2) { |
| } else { |
| auto elementBytes{static_cast<SubscriptValue>(instance_.ElementBytes())}; |
| if (auto stride{instance_.FixedStride()}) { |
| if (*stride == elementBytes) { // contiguous |
| for (std::size_t done{1}; done < elements_;) { |
| std::size_t chunk{elements_ - done}; |
| if (chunk > done) { |
| chunk = done; |
| } |
| char *uninitialized{rawInstance + done * *stride}; |
| std::memcpy(uninitialized, rawInstance, chunk * *stride); |
| done += chunk; |
| } |
| } else { |
| for (std::size_t done{1}; done < elements_; ++done) { |
| char *uninitialized{rawInstance + done * *stride}; |
| std::memcpy(uninitialized, rawInstance, elementBytes); |
| } |
| } |
| } else { // one at a time with subscription |
| for (Elementwise::Advance(); !Elementwise::IsComplete(); |
| Elementwise::Advance()) { |
| char *element{instance_.Element<char>(subscripts_)}; |
| std::memcpy(element, rawInstance, elementBytes); |
| } |
| } |
| } |
| return StatOk; |
| } |
| |
| RT_API_ATTRS int InitializeClone(const Descriptor &clone, |
| const Descriptor &original, const typeInfo::DerivedType &derived, |
| Terminator &terminator, bool hasStat, const Descriptor *errMsg) { |
| if (original.IsPointer() || !original.IsAllocated()) { |
| return StatOk; // nothing to do |
| } else { |
| WorkQueue workQueue{terminator}; |
| int status{workQueue.BeginInitializeClone( |
| clone, original, derived, hasStat, errMsg)}; |
| return status == StatContinue ? workQueue.Run() : status; |
| } |
| } |
| |
| RT_API_ATTRS int InitializeCloneTicket::Continue(WorkQueue &workQueue) { |
| while (!IsComplete()) { |
| if (component_->genre() == typeInfo::Component::Genre::Allocatable) { |
| Descriptor &origDesc{*instance_.ElementComponent<Descriptor>( |
| subscripts_, component_->offset())}; |
| if (origDesc.IsAllocated()) { |
| Descriptor &cloneDesc{*clone_.ElementComponent<Descriptor>( |
| subscripts_, component_->offset())}; |
| if (phase_ == 0) { |
| ++phase_; |
| cloneDesc.ApplyMold(origDesc, origDesc.rank()); |
| if (int stat{ReturnError(workQueue.terminator(), |
| cloneDesc.Allocate(kNoAsyncObject), errMsg_, hasStat_)}; |
| stat != StatOk) { |
| return stat; |
| } |
| if (const DescriptorAddendum *addendum{cloneDesc.Addendum()}) { |
| if (const typeInfo::DerivedType *derived{addendum->derivedType()}) { |
| if (!derived->noInitializationNeeded()) { |
| // Perform default initialization for the allocated element. |
| if (int status{workQueue.BeginInitialize(cloneDesc, *derived)}; |
| status != StatOk) { |
| return status; |
| } |
| } |
| } |
| } |
| } |
| if (phase_ == 1) { |
| ++phase_; |
| if (const DescriptorAddendum *addendum{cloneDesc.Addendum()}) { |
| if (const typeInfo::DerivedType *derived{addendum->derivedType()}) { |
| // Initialize derived type's allocatables. |
| if (int status{workQueue.BeginInitializeClone( |
| cloneDesc, origDesc, *derived, hasStat_, errMsg_)}; |
| status != StatOk) { |
| return status; |
| } |
| } |
| } |
| } |
| } |
| Advance(); |
| } else if (component_->genre() == typeInfo::Component::Genre::Data) { |
| if (component_->derivedType()) { |
| // Handle nested derived types. |
| const typeInfo::DerivedType &compType{*component_->derivedType()}; |
| SubscriptValue extents[maxRank]; |
| GetComponentExtents(extents, *component_, instance_); |
| Descriptor &origDesc{componentDescriptor_.descriptor()}; |
| Descriptor &cloneDesc{cloneComponentDescriptor_.descriptor()}; |
| origDesc.Establish(compType, |
| instance_.ElementComponent<char>(subscripts_, component_->offset()), |
| component_->rank(), extents); |
| cloneDesc.Establish(compType, |
| clone_.ElementComponent<char>(subscripts_, component_->offset()), |
| component_->rank(), extents); |
| Advance(); |
| if (int status{workQueue.BeginInitializeClone( |
| cloneDesc, origDesc, compType, hasStat_, errMsg_)}; |
| status != StatOk) { |
| return status; |
| } |
| } else { |
| SkipToNextComponent(); |
| } |
| } else { |
| SkipToNextComponent(); |
| } |
| } |
| return StatOk; |
| } |
| |
| // Fortran 2018 subclause 7.5.6.2 |
| RT_API_ATTRS void Finalize(const Descriptor &descriptor, |
| const typeInfo::DerivedType &derived, Terminator *terminator) { |
| if (!derived.noFinalizationNeeded() && descriptor.IsAllocated()) { |
| Terminator stubTerminator{"Finalize() in Fortran runtime", 0}; |
| WorkQueue workQueue{terminator ? *terminator : stubTerminator}; |
| if (workQueue.BeginFinalize(descriptor, derived) == StatContinue) { |
| workQueue.Run(); |
| } |
| } |
| } |
| |
| static RT_API_ATTRS const typeInfo::SpecialBinding *FindFinal( |
| const typeInfo::DerivedType &derived, int rank) { |
| if (const auto *ranked{derived.FindSpecialBinding( |
| typeInfo::SpecialBinding::RankFinal(rank))}) { |
| return ranked; |
| } else if (const auto *assumed{derived.FindSpecialBinding( |
| typeInfo::SpecialBinding::Which::AssumedRankFinal)}) { |
| return assumed; |
| } else { |
| return derived.FindSpecialBinding( |
| typeInfo::SpecialBinding::Which::ElementalFinal); |
| } |
| } |
| |
| static RT_API_ATTRS void CallFinalSubroutine(const Descriptor &descriptor, |
| const typeInfo::DerivedType &derived, Terminator &terminator) { |
| if (const auto *special{FindFinal(derived, descriptor.rank())}) { |
| if (special->which() == typeInfo::SpecialBinding::Which::ElementalFinal) { |
| std::size_t elements{descriptor.InlineElements()}; |
| SubscriptValue at[maxRank]; |
| descriptor.GetLowerBounds(at); |
| if (special->IsArgDescriptor(0)) { |
| StaticDescriptor<maxRank, true, 8 /*?*/> statDesc; |
| Descriptor &elemDesc{statDesc.descriptor()}; |
| elemDesc = descriptor; |
| elemDesc.raw().attribute = CFI_attribute_pointer; |
| elemDesc.raw().rank = 0; |
| auto *p{special->GetProc<void (*)(const Descriptor &)>()}; |
| for (std::size_t j{0}; j++ < elements; |
| descriptor.IncrementSubscripts(at)) { |
| elemDesc.set_base_addr(descriptor.Element<char>(at)); |
| p(elemDesc); |
| } |
| } else { |
| auto *p{special->GetProc<void (*)(char *)>()}; |
| for (std::size_t j{0}; j++ < elements; |
| descriptor.IncrementSubscripts(at)) { |
| p(descriptor.Element<char>(at)); |
| } |
| } |
| } else { |
| StaticDescriptor<maxRank, true, 10> statDesc; |
| Descriptor ©{statDesc.descriptor()}; |
| const Descriptor *argDescriptor{&descriptor}; |
| if (descriptor.rank() > 0 && special->specialCaseFlag() && |
| !descriptor.IsContiguous()) { |
| // The FINAL subroutine demands a contiguous array argument, but |
| // this INTENT(OUT) or intrinsic assignment LHS isn't contiguous. |
| // Finalize a shallow copy of the data. |
| copy = descriptor; |
| copy.set_base_addr(nullptr); |
| copy.raw().attribute = CFI_attribute_allocatable; |
| RUNTIME_CHECK(terminator, copy.Allocate(kNoAsyncObject) == CFI_SUCCESS); |
| ShallowCopyDiscontiguousToContiguous(copy, descriptor); |
| argDescriptor = © |
| } |
| if (special->IsArgDescriptor(0)) { |
| StaticDescriptor<maxRank, true, 8 /*?*/> statDesc; |
| Descriptor &tmpDesc{statDesc.descriptor()}; |
| tmpDesc = *argDescriptor; |
| tmpDesc.raw().attribute = CFI_attribute_pointer; |
| tmpDesc.Addendum()->set_derivedType(&derived); |
| auto *p{special->GetProc<void (*)(const Descriptor &)>()}; |
| p(tmpDesc); |
| } else { |
| auto *p{special->GetProc<void (*)(char *)>()}; |
| p(argDescriptor->OffsetElement<char>()); |
| } |
| if (argDescriptor == ©) { |
| ShallowCopyContiguousToDiscontiguous(descriptor, copy); |
| copy.Deallocate(); |
| } |
| } |
| } |
| } |
| |
| RT_API_ATTRS int FinalizeTicket::Begin(WorkQueue &workQueue) { |
| CallFinalSubroutine(instance_, derived_, workQueue.terminator()); |
| // If there's a finalizable parent component, handle it last, as required |
| // by the Fortran standard (7.5.6.2), and do so recursively with the same |
| // descriptor so that the rank is preserved. |
| finalizableParentType_ = derived_.GetParentType(); |
| if (finalizableParentType_) { |
| if (finalizableParentType_->noFinalizationNeeded()) { |
| finalizableParentType_ = nullptr; |
| } else { |
| SkipToNextComponent(); |
| } |
| } |
| return StatContinue; |
| } |
| |
| RT_API_ATTRS int FinalizeTicket::Continue(WorkQueue &workQueue) { |
| while (!IsComplete()) { |
| if (component_->genre() == typeInfo::Component::Genre::Allocatable && |
| component_->category() == TypeCategory::Derived) { |
| // Component may be polymorphic or unlimited polymorphic. Need to use the |
| // dynamic type to check whether finalization is needed. |
| const Descriptor &compDesc{*instance_.ElementComponent<Descriptor>( |
| subscripts_, component_->offset())}; |
| Advance(); |
| if (compDesc.IsAllocated()) { |
| if (const DescriptorAddendum *addendum{compDesc.Addendum()}) { |
| if (const typeInfo::DerivedType *compDynamicType{ |
| addendum->derivedType()}) { |
| if (!compDynamicType->noFinalizationNeeded()) { |
| if (int status{ |
| workQueue.BeginFinalize(compDesc, *compDynamicType)}; |
| status != StatOk) { |
| return status; |
| } |
| } |
| } |
| } |
| } |
| } else if (component_->genre() == typeInfo::Component::Genre::Allocatable || |
| component_->genre() == typeInfo::Component::Genre::Automatic) { |
| if (const typeInfo::DerivedType *compType{component_->derivedType()}; |
| compType && !compType->noFinalizationNeeded()) { |
| const Descriptor &compDesc{*instance_.ElementComponent<Descriptor>( |
| subscripts_, component_->offset())}; |
| Advance(); |
| if (compDesc.IsAllocated()) { |
| if (int status{workQueue.BeginFinalize(compDesc, *compType)}; |
| status != StatOk) { |
| return status; |
| } |
| } |
| } else { |
| SkipToNextComponent(); |
| } |
| } else if (component_->genre() == typeInfo::Component::Genre::Data && |
| component_->derivedType() && |
| !component_->derivedType()->noFinalizationNeeded()) { |
| // todo: calculate and use fixedStride_ here as in DestroyTicket to |
| // avoid subscripts and repeated descriptor establishment. |
| SubscriptValue extents[maxRank]; |
| GetComponentExtents(extents, *component_, instance_); |
| Descriptor &compDesc{componentDescriptor_.descriptor()}; |
| const typeInfo::DerivedType &compType{*component_->derivedType()}; |
| compDesc.Establish(compType, |
| instance_.ElementComponent<char>(subscripts_, component_->offset()), |
| component_->rank(), extents); |
| Advance(); |
| if (int status{workQueue.BeginFinalize(compDesc, compType)}; |
| status != StatOk) { |
| return status; |
| } |
| } else { |
| SkipToNextComponent(); |
| } |
| } |
| // Last, do the parent component, if any and finalizable. |
| if (finalizableParentType_) { |
| Descriptor &tmpDesc{componentDescriptor_.descriptor()}; |
| tmpDesc = instance_; |
| tmpDesc.raw().attribute = CFI_attribute_pointer; |
| tmpDesc.Addendum()->set_derivedType(finalizableParentType_); |
| tmpDesc.raw().elem_len = finalizableParentType_->sizeInBytes(); |
| const auto &parentType{*finalizableParentType_}; |
| finalizableParentType_ = nullptr; |
| // Don't return StatOk here if the nested FInalize is still running; |
| // it needs this->componentDescriptor_. |
| return workQueue.BeginFinalize(tmpDesc, parentType); |
| } |
| return StatOk; |
| } |
| |
| // The order of finalization follows Fortran 2018 7.5.6.2, with |
| // elementwise finalization of non-parent components taking place |
| // before parent component finalization, and with all finalization |
| // preceding any deallocation. |
| RT_API_ATTRS void Destroy(const Descriptor &descriptor, bool finalize, |
| const typeInfo::DerivedType &derived, Terminator *terminator) { |
| if (descriptor.IsAllocated() && !derived.noDestructionNeeded()) { |
| Terminator stubTerminator{"Destroy() in Fortran runtime", 0}; |
| WorkQueue workQueue{terminator ? *terminator : stubTerminator}; |
| if (workQueue.BeginDestroy(descriptor, derived, finalize) == StatContinue) { |
| workQueue.Run(); |
| } |
| } |
| } |
| |
| RT_API_ATTRS int DestroyTicket::Begin(WorkQueue &workQueue) { |
| if (finalize_ && !derived_.noFinalizationNeeded()) { |
| if (int status{workQueue.BeginFinalize(instance_, derived_)}; |
| status != StatOk && status != StatContinue) { |
| return status; |
| } |
| } |
| return StatContinue; |
| } |
| |
| RT_API_ATTRS int DestroyTicket::Continue(WorkQueue &workQueue) { |
| // Deallocate all direct and indirect allocatable and automatic components. |
| // Contrary to finalization, the order of deallocation does not matter. |
| while (!IsComplete()) { |
| const auto *componentDerived{component_->derivedType()}; |
| if (component_->genre() == typeInfo::Component::Genre::Allocatable) { |
| if (fixedStride_ && |
| (!componentDerived || componentDerived->noDestructionNeeded())) { |
| // common fast path, just deallocate in every element |
| char *p{instance_.OffsetElement<char>(component_->offset())}; |
| for (std::size_t j{0}; j < elements_; ++j, p += *fixedStride_) { |
| Descriptor &d{*reinterpret_cast<Descriptor *>(p)}; |
| d.Deallocate(); |
| } |
| SkipToNextComponent(); |
| } else { |
| Descriptor &d{*instance_.ElementComponent<Descriptor>( |
| subscripts_, component_->offset())}; |
| if (d.IsAllocated()) { |
| if (componentDerived && !componentDerived->noDestructionNeeded() && |
| phase_ == 0) { |
| if (int status{workQueue.BeginDestroy( |
| d, *componentDerived, /*finalize=*/false)}; |
| status != StatOk) { |
| ++phase_; |
| return status; |
| } |
| } |
| d.Deallocate(); |
| } |
| Advance(); |
| } |
| } else if (component_->genre() == typeInfo::Component::Genre::Data) { |
| if (!componentDerived || componentDerived->noDestructionNeeded()) { |
| SkipToNextComponent(); |
| } else if (fixedStride_) { |
| // faster path, no need for subscripts, can reuse descriptor |
| char *p{instance_.OffsetElement<char>( |
| elementAt_ * *fixedStride_ + component_->offset())}; |
| Descriptor &compDesc{componentDescriptor_.descriptor()}; |
| const typeInfo::DerivedType &compType{*componentDerived}; |
| compDesc.UncheckedScalarEstablish(compType, p); |
| for (std::size_t j{elementAt_}; j < elements_; |
| ++j, p += *fixedStride_) { |
| compDesc.set_base_addr(p); |
| ++elementAt_; |
| if (int status{workQueue.BeginDestroy( |
| compDesc, compType, /*finalize=*/false)}; |
| status != StatOk) { |
| return status; |
| } |
| } |
| SkipToNextComponent(); |
| } else { |
| SubscriptValue extents[maxRank]; |
| GetComponentExtents(extents, *component_, instance_); |
| Descriptor &compDesc{componentDescriptor_.descriptor()}; |
| const typeInfo::DerivedType &compType{*componentDerived}; |
| compDesc.Establish(compType, |
| instance_.ElementComponent<char>(subscripts_, component_->offset()), |
| component_->rank(), extents); |
| Advance(); |
| if (int status{ |
| workQueue.BeginDestroy(compDesc, compType, /*finalize=*/false)}; |
| status != StatOk) { |
| return status; |
| } |
| } |
| } else { |
| SkipToNextComponent(); |
| } |
| } |
| return StatOk; |
| } |
| |
| RT_API_ATTRS bool HasDynamicComponent(const Descriptor &descriptor) { |
| if (const DescriptorAddendum * addendum{descriptor.Addendum()}) { |
| if (const auto *derived = addendum->derivedType()) { |
| // Destruction is needed if and only if there are direct or indirect |
| // allocatable or automatic components. |
| return !derived->noDestructionNeeded(); |
| } |
| } |
| return false; |
| } |
| |
| RT_OFFLOAD_API_GROUP_END |
| } // namespace Fortran::runtime |