1 //===-- runtime/pointer.cpp -----------------------------------------------===//
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
7 //===----------------------------------------------------------------------===//
9 #include "flang/Runtime/pointer.h"
10 #include "assign-impl.h"
12 #include "environment.h"
14 #include "terminator.h"
16 #include "type-info.h"
18 namespace Fortran::runtime
{
20 RT_EXT_API_GROUP_BEGIN
22 void RTDEF(PointerNullifyIntrinsic
)(Descriptor
&pointer
, TypeCategory category
,
23 int kind
, int rank
, int corank
) {
24 INTERNAL_CHECK(corank
== 0);
25 pointer
.Establish(TypeCode
{category
, kind
},
26 Descriptor::BytesFor(category
, kind
), nullptr, rank
, nullptr,
27 CFI_attribute_pointer
);
30 void RTDEF(PointerNullifyCharacter
)(Descriptor
&pointer
, SubscriptValue length
,
31 int kind
, int rank
, int corank
) {
32 INTERNAL_CHECK(corank
== 0);
34 kind
, length
, nullptr, rank
, nullptr, CFI_attribute_pointer
);
37 void RTDEF(PointerNullifyDerived
)(Descriptor
&pointer
,
38 const typeInfo::DerivedType
&derivedType
, int rank
, int corank
) {
39 INTERNAL_CHECK(corank
== 0);
40 pointer
.Establish(derivedType
, nullptr, rank
, nullptr, CFI_attribute_pointer
);
43 void RTDEF(PointerSetBounds
)(Descriptor
&pointer
, int zeroBasedDim
,
44 SubscriptValue lower
, SubscriptValue upper
) {
45 INTERNAL_CHECK(zeroBasedDim
>= 0 && zeroBasedDim
< pointer
.rank());
46 pointer
.GetDimension(zeroBasedDim
).SetBounds(lower
, upper
);
47 // The byte strides are computed when the pointer is allocated.
50 // TODO: PointerSetCoBounds
52 void RTDEF(PointerSetDerivedLength
)(
53 Descriptor
&pointer
, int which
, SubscriptValue x
) {
54 DescriptorAddendum
*addendum
{pointer
.Addendum()};
55 INTERNAL_CHECK(addendum
!= nullptr);
56 addendum
->SetLenParameterValue(which
, x
);
59 void RTDEF(PointerApplyMold
)(
60 Descriptor
&pointer
, const Descriptor
&mold
, int rank
) {
61 pointer
.ApplyMold(mold
, rank
);
64 void RTDEF(PointerAssociateScalar
)(Descriptor
&pointer
, void *target
) {
65 pointer
.set_base_addr(target
);
68 void RTDEF(PointerAssociate
)(Descriptor
&pointer
, const Descriptor
&target
) {
70 pointer
.raw().attribute
= CFI_attribute_pointer
;
73 void RTDEF(PointerAssociateLowerBounds
)(Descriptor
&pointer
,
74 const Descriptor
&target
, const Descriptor
&lowerBounds
) {
76 pointer
.raw().attribute
= CFI_attribute_pointer
;
77 int rank
{pointer
.rank()};
78 Terminator terminator
{__FILE__
, __LINE__
};
79 std::size_t boundElementBytes
{lowerBounds
.ElementBytes()};
80 for (int j
{0}; j
< rank
; ++j
) {
81 Dimension
&dim
{pointer
.GetDimension(j
)};
82 dim
.SetLowerBound(dim
.Extent() == 0
84 : GetInt64(lowerBounds
.ZeroBasedIndexedElement
<const char>(j
),
85 boundElementBytes
, terminator
));
89 void RTDEF(PointerAssociateRemapping
)(Descriptor
&pointer
,
90 const Descriptor
&target
, const Descriptor
&bounds
, const char *sourceFile
,
93 pointer
.raw().attribute
= CFI_attribute_pointer
;
94 Terminator terminator
{sourceFile
, sourceLine
};
95 SubscriptValue byteStride
{/*captured from first dimension*/};
96 std::size_t boundElementBytes
{bounds
.ElementBytes()};
97 std::size_t boundsRank
{
98 static_cast<std::size_t>(bounds
.GetDimension(1).Extent())};
99 pointer
.raw().rank
= boundsRank
;
100 for (unsigned j
{0}; j
< boundsRank
; ++j
) {
101 auto &dim
{pointer
.GetDimension(j
)};
102 dim
.SetBounds(GetInt64(bounds
.ZeroBasedIndexedElement
<const char>(2 * j
),
103 boundElementBytes
, terminator
),
104 GetInt64(bounds
.ZeroBasedIndexedElement
<const char>(2 * j
+ 1),
105 boundElementBytes
, terminator
));
107 byteStride
= dim
.ByteStride() * dim
.Extent();
109 dim
.SetByteStride(byteStride
);
110 byteStride
*= dim
.Extent();
113 if (pointer
.Elements() > target
.Elements()) {
114 terminator
.Crash("PointerAssociateRemapping: too many elements in remapped "
115 "pointer (%zd > %zd)",
116 pointer
.Elements(), target
.Elements());
118 if (auto *pointerAddendum
{pointer
.Addendum()}) {
119 if (const auto *targetAddendum
{target
.Addendum()}) {
120 if (const auto *derived
{targetAddendum
->derivedType()}) {
121 pointerAddendum
->set_derivedType(derived
);
127 RT_API_ATTRS
void *AllocateValidatedPointerPayload(std::size_t byteSize
) {
128 // Add space for a footer to validate during deallocation.
129 constexpr std::size_t align
{sizeof(std::uintptr_t)};
130 byteSize
= ((byteSize
+ align
- 1) / align
) * align
;
131 std::size_t total
{byteSize
+ sizeof(std::uintptr_t)};
132 void *p
{std::malloc(total
)};
134 // Fill the footer word with the XOR of the ones' complement of
135 // the base address, which is a value that would be highly unlikely
136 // to appear accidentally at the right spot.
137 std::uintptr_t *footer
{
138 reinterpret_cast<std::uintptr_t *>(static_cast<char *>(p
) + byteSize
)};
139 *footer
= ~reinterpret_cast<std::uintptr_t>(p
);
144 int RTDEF(PointerAllocate
)(Descriptor
&pointer
, bool hasStat
,
145 const Descriptor
*errMsg
, const char *sourceFile
, int sourceLine
) {
146 Terminator terminator
{sourceFile
, sourceLine
};
147 if (!pointer
.IsPointer()) {
148 return ReturnError(terminator
, StatInvalidDescriptor
, errMsg
, hasStat
);
150 std::size_t elementBytes
{pointer
.ElementBytes()};
151 if (static_cast<std::int64_t>(elementBytes
) < 0) {
152 // F'2023 7.4.4.2 p5: "If the character length parameter value evaluates
153 // to a negative value, the length of character entities declared is zero."
154 elementBytes
= pointer
.raw().elem_len
= 0;
156 std::size_t byteSize
{pointer
.Elements() * elementBytes
};
157 void *p
{AllocateValidatedPointerPayload(byteSize
)};
159 return ReturnError(terminator
, CFI_ERROR_MEM_ALLOCATION
, errMsg
, hasStat
);
161 pointer
.set_base_addr(p
);
162 pointer
.SetByteStrides();
164 if (const DescriptorAddendum
* addendum
{pointer
.Addendum()}) {
165 if (const auto *derived
{addendum
->derivedType()}) {
166 if (!derived
->noInitializationNeeded()) {
167 stat
= Initialize(pointer
, *derived
, terminator
, hasStat
, errMsg
);
171 return ReturnError(terminator
, stat
, errMsg
, hasStat
);
174 int RTDEF(PointerAllocateSource
)(Descriptor
&pointer
, const Descriptor
&source
,
175 bool hasStat
, const Descriptor
*errMsg
, const char *sourceFile
,
177 int stat
{RTNAME(PointerAllocate
)(
178 pointer
, hasStat
, errMsg
, sourceFile
, sourceLine
)};
179 if (stat
== StatOk
) {
180 Terminator terminator
{sourceFile
, sourceLine
};
181 DoFromSourceAssign(pointer
, source
, terminator
);
186 static RT_API_ATTRS
std::size_t GetByteSize(
187 const ISO::CFI_cdesc_t
&descriptor
) {
188 std::size_t rank
{descriptor
.rank
};
189 const ISO::CFI_dim_t
*dim
{descriptor
.dim
};
190 std::size_t byteSize
{descriptor
.elem_len
};
191 for (std::size_t j
{0}; j
< rank
; ++j
) {
192 byteSize
*= dim
[j
].extent
;
197 bool RT_API_ATTRS
ValidatePointerPayload(const ISO::CFI_cdesc_t
&desc
) {
198 std::size_t byteSize
{GetByteSize(desc
)};
199 constexpr std::size_t align
{sizeof(std::uintptr_t)};
200 byteSize
= ((byteSize
+ align
- 1) / align
) * align
;
201 const void *p
{desc
.base_addr
};
202 const std::uintptr_t *footer
{reinterpret_cast<const std::uintptr_t *>(
203 static_cast<const char *>(p
) + byteSize
)};
204 return *footer
== ~reinterpret_cast<std::uintptr_t>(p
);
207 int RTDEF(PointerDeallocate
)(Descriptor
&pointer
, bool hasStat
,
208 const Descriptor
*errMsg
, const char *sourceFile
, int sourceLine
) {
209 Terminator terminator
{sourceFile
, sourceLine
};
210 if (!pointer
.IsPointer()) {
211 return ReturnError(terminator
, StatInvalidDescriptor
, errMsg
, hasStat
);
213 if (!pointer
.IsAllocated()) {
214 return ReturnError(terminator
, StatBaseNull
, errMsg
, hasStat
);
216 if (executionEnvironment
.checkPointerDeallocation
&&
217 !ValidatePointerPayload(pointer
.raw())) {
218 return ReturnError(terminator
, StatBadPointerDeallocation
, errMsg
, hasStat
);
220 return ReturnError(terminator
,
221 pointer
.Destroy(/*finalize=*/true, /*destroyPointers=*/true, &terminator
),
225 int RTDEF(PointerDeallocatePolymorphic
)(Descriptor
&pointer
,
226 const typeInfo::DerivedType
*derivedType
, bool hasStat
,
227 const Descriptor
*errMsg
, const char *sourceFile
, int sourceLine
) {
228 int stat
{RTNAME(PointerDeallocate
)(
229 pointer
, hasStat
, errMsg
, sourceFile
, sourceLine
)};
230 if (stat
== StatOk
) {
231 if (DescriptorAddendum
* addendum
{pointer
.Addendum()}) {
232 addendum
->set_derivedType(derivedType
);
233 pointer
.raw().type
= derivedType
? CFI_type_struct
: CFI_type_other
;
235 // Unlimited polymorphic descriptors initialized with
236 // PointerNullifyIntrinsic do not have an addendum. Make sure the
237 // derivedType is null in that case.
238 INTERNAL_CHECK(!derivedType
);
239 pointer
.raw().type
= CFI_type_other
;
245 bool RTDEF(PointerIsAssociated
)(const Descriptor
&pointer
) {
246 return pointer
.raw().base_addr
!= nullptr;
249 bool RTDEF(PointerIsAssociatedWith
)(
250 const Descriptor
&pointer
, const Descriptor
*target
) {
252 return pointer
.raw().base_addr
!= nullptr;
254 if (!target
->raw().base_addr
||
255 (target
->raw().type
!= CFI_type_struct
&& target
->ElementBytes() == 0)) {
258 int rank
{pointer
.rank()};
259 if (pointer
.raw().base_addr
!= target
->raw().base_addr
||
260 pointer
.ElementBytes() != target
->ElementBytes() ||
261 rank
!= target
->rank()) {
264 for (int j
{0}; j
< rank
; ++j
) {
265 const Dimension
&pDim
{pointer
.GetDimension(j
)};
266 const Dimension
&tDim
{target
->GetDimension(j
)};
267 auto pExtent
{pDim
.Extent()};
268 if (pExtent
== 0 || pExtent
!= tDim
.Extent() ||
269 (pExtent
!= 1 && pDim
.ByteStride() != tDim
.ByteStride())) {
276 // TODO: PointerCheckLengthParameter
280 } // namespace Fortran::runtime