1 | //===-- lib/runtime/pointer.cpp ---------------------------------*- C++ -*-===// |
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 | #include "flang/Runtime/pointer.h" |
10 | #include "flang-rt/runtime/allocator-registry.h" |
11 | #include "flang-rt/runtime/assign-impl.h" |
12 | #include "flang-rt/runtime/derived.h" |
13 | #include "flang-rt/runtime/environment.h" |
14 | #include "flang-rt/runtime/stat.h" |
15 | #include "flang-rt/runtime/terminator.h" |
16 | #include "flang-rt/runtime/tools.h" |
17 | #include "flang-rt/runtime/type-info.h" |
18 | |
19 | namespace Fortran::runtime { |
20 | extern "C" { |
21 | RT_EXT_API_GROUP_BEGIN |
22 | |
23 | void RTDEF(PointerNullifyIntrinsic)(Descriptor &pointer, TypeCategory category, |
24 | int kind, int rank, int corank) { |
25 | INTERNAL_CHECK(corank == 0); |
26 | pointer.Establish(TypeCode{category, kind}, |
27 | Descriptor::BytesFor(category, kind), nullptr, rank, nullptr, |
28 | CFI_attribute_pointer); |
29 | } |
30 | |
31 | void RTDEF(PointerNullifyCharacter)(Descriptor &pointer, SubscriptValue length, |
32 | int kind, int rank, int corank) { |
33 | INTERNAL_CHECK(corank == 0); |
34 | pointer.Establish( |
35 | kind, length, nullptr, rank, nullptr, CFI_attribute_pointer); |
36 | } |
37 | |
38 | void RTDEF(PointerNullifyDerived)(Descriptor &pointer, |
39 | const typeInfo::DerivedType &derivedType, int rank, int corank) { |
40 | INTERNAL_CHECK(corank == 0); |
41 | pointer.Establish(derivedType, nullptr, rank, nullptr, CFI_attribute_pointer); |
42 | } |
43 | |
44 | void RTDEF(PointerSetBounds)(Descriptor &pointer, int zeroBasedDim, |
45 | SubscriptValue lower, SubscriptValue upper) { |
46 | INTERNAL_CHECK(zeroBasedDim >= 0 && zeroBasedDim < pointer.rank()); |
47 | pointer.GetDimension(zeroBasedDim).SetBounds(lower, upper); |
48 | // The byte strides are computed when the pointer is allocated. |
49 | } |
50 | |
51 | // TODO: PointerSetCoBounds |
52 | |
53 | void RTDEF(PointerSetDerivedLength)( |
54 | Descriptor &pointer, int which, SubscriptValue x) { |
55 | DescriptorAddendum *addendum{pointer.Addendum()}; |
56 | INTERNAL_CHECK(addendum != nullptr); |
57 | addendum->SetLenParameterValue(which, x); |
58 | } |
59 | |
60 | void RTDEF(PointerApplyMold)( |
61 | Descriptor &pointer, const Descriptor &mold, int rank) { |
62 | pointer.ApplyMold(mold, rank); |
63 | } |
64 | |
65 | void RTDEF(PointerAssociateScalar)(Descriptor &pointer, void *target) { |
66 | pointer.set_base_addr(target); |
67 | } |
68 | |
69 | void RTDEF(PointerAssociate)(Descriptor &pointer, const Descriptor &target) { |
70 | pointer = target; |
71 | pointer.raw().attribute = CFI_attribute_pointer; |
72 | } |
73 | |
74 | void RTDEF(PointerAssociateLowerBounds)(Descriptor &pointer, |
75 | const Descriptor &target, const Descriptor &lowerBounds) { |
76 | pointer = target; |
77 | pointer.raw().attribute = CFI_attribute_pointer; |
78 | int rank{pointer.rank()}; |
79 | Terminator terminator{__FILE__, __LINE__}; |
80 | std::size_t boundElementBytes{lowerBounds.ElementBytes()}; |
81 | for (int j{0}; j < rank; ++j) { |
82 | Dimension &dim{pointer.GetDimension(j)}; |
83 | dim.SetLowerBound(dim.Extent() == 0 |
84 | ? 1 |
85 | : GetInt64(lowerBounds.ZeroBasedIndexedElement<const char>(j), |
86 | boundElementBytes, terminator)); |
87 | } |
88 | } |
89 | |
90 | void RTDEF(PointerAssociateRemapping)(Descriptor &pointer, |
91 | const Descriptor &target, const Descriptor &bounds, const char *sourceFile, |
92 | int sourceLine) { |
93 | Terminator terminator{sourceFile, sourceLine}; |
94 | SubscriptValue byteStride{/*captured from first dimension*/}; |
95 | std::size_t boundElementBytes{bounds.ElementBytes()}; |
96 | std::size_t boundsRank{ |
97 | static_cast<std::size_t>(bounds.GetDimension(1).Extent())}; |
98 | // We cannot just assign target into pointer descriptor, because |
99 | // the ranks may mismatch. Use target as a mold for initializing |
100 | // the pointer descriptor. |
101 | INTERNAL_CHECK(static_cast<std::size_t>(pointer.rank()) == boundsRank); |
102 | pointer.ApplyMold(target, boundsRank); |
103 | pointer.set_base_addr(target.raw().base_addr); |
104 | pointer.raw().attribute = CFI_attribute_pointer; |
105 | for (unsigned j{0}; j < boundsRank; ++j) { |
106 | auto &dim{pointer.GetDimension(j)}; |
107 | dim.SetBounds(GetInt64(bounds.ZeroBasedIndexedElement<const char>(2 * j), |
108 | boundElementBytes, terminator), |
109 | GetInt64(bounds.ZeroBasedIndexedElement<const char>(2 * j + 1), |
110 | boundElementBytes, terminator)); |
111 | if (j == 0) { |
112 | byteStride = dim.ByteStride() * dim.Extent(); |
113 | } else { |
114 | dim.SetByteStride(byteStride); |
115 | byteStride *= dim.Extent(); |
116 | } |
117 | } |
118 | if (pointer.Elements() > target.Elements()) { |
119 | terminator.Crash("PointerAssociateRemapping: too many elements in remapped " |
120 | "pointer (%zd > %zd)" , |
121 | pointer.Elements(), target.Elements()); |
122 | } |
123 | } |
124 | |
125 | RT_API_ATTRS void *AllocateValidatedPointerPayload( |
126 | std::size_t byteSize, int allocatorIdx) { |
127 | // Add space for a footer to validate during deallocation. |
128 | constexpr std::size_t align{sizeof(std::uintptr_t)}; |
129 | byteSize = ((byteSize + align - 1) / align) * align; |
130 | std::size_t total{byteSize + sizeof(std::uintptr_t)}; |
131 | AllocFct alloc{allocatorRegistry.GetAllocator(allocatorIdx)}; |
132 | void *p{alloc(total, /*asyncObject=*/nullptr)}; |
133 | if (p && allocatorIdx == 0) { |
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 *{ |
138 | reinterpret_cast<std::uintptr_t *>(static_cast<char *>(p) + byteSize)}; |
139 | *footer = ~reinterpret_cast<std::uintptr_t>(p); |
140 | } |
141 | return p; |
142 | } |
143 | |
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); |
149 | } |
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; |
155 | } |
156 | std::size_t byteSize{pointer.Elements() * elementBytes}; |
157 | void *p{AllocateValidatedPointerPayload(byteSize, pointer.GetAllocIdx())}; |
158 | if (!p) { |
159 | return ReturnError(terminator, CFI_ERROR_MEM_ALLOCATION, errMsg, hasStat); |
160 | } |
161 | pointer.set_base_addr(p); |
162 | pointer.SetByteStrides(); |
163 | int stat{StatOk}; |
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); |
168 | } |
169 | } |
170 | } |
171 | return ReturnError(terminator, stat, errMsg, hasStat); |
172 | } |
173 | |
174 | int RTDEF(PointerAllocateSource)(Descriptor &pointer, const Descriptor &source, |
175 | bool hasStat, const Descriptor *errMsg, const char *sourceFile, |
176 | int sourceLine) { |
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); |
182 | } |
183 | return stat; |
184 | } |
185 | |
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; |
193 | } |
194 | return byteSize; |
195 | } |
196 | |
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); |
205 | } |
206 | |
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); |
212 | } |
213 | if (!pointer.IsAllocated()) { |
214 | return ReturnError(terminator, StatBaseNull, errMsg, hasStat); |
215 | } |
216 | if (executionEnvironment.checkPointerDeallocation && |
217 | pointer.GetAllocIdx() == kDefaultAllocator && |
218 | !ValidatePointerPayload(pointer.raw())) { |
219 | return ReturnError(terminator, StatBadPointerDeallocation, errMsg, hasStat); |
220 | } |
221 | return ReturnError(terminator, |
222 | pointer.Destroy(/*finalize=*/true, /*destroyPointers=*/true, &terminator), |
223 | errMsg, hasStat); |
224 | } |
225 | |
226 | int RTDEF(PointerDeallocatePolymorphic)(Descriptor &pointer, |
227 | const typeInfo::DerivedType *derivedType, bool hasStat, |
228 | const Descriptor *errMsg, const char *sourceFile, int sourceLine) { |
229 | int stat{RTNAME(PointerDeallocate)( |
230 | pointer, hasStat, errMsg, sourceFile, sourceLine)}; |
231 | if (stat == StatOk) { |
232 | if (DescriptorAddendum * addendum{pointer.Addendum()}) { |
233 | addendum->set_derivedType(derivedType); |
234 | pointer.raw().type = derivedType ? CFI_type_struct : CFI_type_other; |
235 | } else { |
236 | // Unlimited polymorphic descriptors initialized with |
237 | // PointerNullifyIntrinsic do not have an addendum. Make sure the |
238 | // derivedType is null in that case. |
239 | INTERNAL_CHECK(!derivedType); |
240 | pointer.raw().type = CFI_type_other; |
241 | } |
242 | } |
243 | return stat; |
244 | } |
245 | |
246 | bool RTDEF(PointerIsAssociated)(const Descriptor &pointer) { |
247 | return pointer.raw().base_addr != nullptr; |
248 | } |
249 | |
250 | bool RTDEF(PointerIsAssociatedWith)( |
251 | const Descriptor &pointer, const Descriptor *target) { |
252 | if (!target) { |
253 | return pointer.raw().base_addr != nullptr; |
254 | } |
255 | if (!target->raw().base_addr || |
256 | (target->raw().type != CFI_type_struct && target->ElementBytes() == 0)) { |
257 | return false; |
258 | } |
259 | int rank{pointer.rank()}; |
260 | if (pointer.raw().base_addr != target->raw().base_addr || |
261 | pointer.ElementBytes() != target->ElementBytes() || |
262 | rank != target->rank()) { |
263 | return false; |
264 | } |
265 | for (int j{0}; j < rank; ++j) { |
266 | const Dimension &pDim{pointer.GetDimension(j)}; |
267 | const Dimension &tDim{target->GetDimension(j)}; |
268 | auto pExtent{pDim.Extent()}; |
269 | if (pExtent == 0 || pExtent != tDim.Extent() || |
270 | (pExtent != 1 && pDim.ByteStride() != tDim.ByteStride())) { |
271 | return false; |
272 | } |
273 | } |
274 | return true; |
275 | } |
276 | |
277 | // TODO: PointerCheckLengthParameter |
278 | |
279 | RT_EXT_API_GROUP_END |
280 | } // extern "C" |
281 | } // namespace Fortran::runtime |
282 | |