Skip to content

Commit 4b2714f

Browse files
authored
[flang/flang-rt] Implement show_descriptor intrinsic, a non-standard extension. (#170389)
This is a reapply the original patch (#169137) with the flang-rt unit test changes limiting it to linux platform only. Additionally accommodated style changes from Peter Klausler (#170227) show_descriptor intrinsic prints details of a descriptor (extended Fortran pointer). It accepts a descriptor for any type and rank, including scalars. Requires use of flang_debug module. Example: ``` program test use flang_debug implicit none integer :: a(4) = (/ 1,3,5,7 /) call show_descriptor(a(1:3)) end program test ``` and its output: ``` Descriptor @ 0x7ffe01ec6a98: base_addr 0x563b7035103c elem_len 4 version 20240719 rank 1 type 9 "INTEGER(kind=4)" attribute 0 extra 0 addendum 0 alloc_idx 0 ```
1 parent 87ccc55 commit 4b2714f

File tree

15 files changed

+623
-7
lines changed

15 files changed

+623
-7
lines changed

flang-rt/include/flang-rt/runtime/descriptor.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -510,7 +510,9 @@ class Descriptor {
510510

511511
RT_API_ATTRS void Check() const;
512512

513-
void Dump(FILE * = stdout) const;
513+
// When dumpRawType, dumps stringified CFI_type_*, otherwise
514+
// try to canonicalize and print as a Fortran type.
515+
void Dump(FILE * = stdout, bool dumpRawType = true) const;
514516

515517
RT_API_ATTRS inline bool HasAddendum() const {
516518
return raw_.extra & _CFI_ADDENDUM_FLAG;

flang-rt/lib/runtime/descriptor.cpp

Lines changed: 152 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -292,14 +292,161 @@ RT_API_ATTRS void Descriptor::Check() const {
292292
// TODO
293293
}
294294

295-
void Descriptor::Dump(FILE *f) const {
295+
static const char *GetTypeStr(ISO::CFI_type_t type, bool dumpRawType) {
296+
if (dumpRawType) {
297+
#define CASE(x) \
298+
case (x): \
299+
return #x;
300+
switch (type) {
301+
CASE(CFI_type_signed_char)
302+
CASE(CFI_type_short)
303+
CASE(CFI_type_int)
304+
CASE(CFI_type_long)
305+
CASE(CFI_type_long_long)
306+
CASE(CFI_type_size_t)
307+
CASE(CFI_type_int8_t)
308+
CASE(CFI_type_int16_t)
309+
CASE(CFI_type_int32_t)
310+
CASE(CFI_type_int64_t)
311+
CASE(CFI_type_int128_t)
312+
CASE(CFI_type_int_least8_t)
313+
CASE(CFI_type_int_least16_t)
314+
CASE(CFI_type_int_least32_t)
315+
CASE(CFI_type_int_least64_t)
316+
CASE(CFI_type_int_least128_t)
317+
CASE(CFI_type_int_fast8_t)
318+
CASE(CFI_type_int_fast16_t)
319+
CASE(CFI_type_int_fast32_t)
320+
CASE(CFI_type_int_fast64_t)
321+
CASE(CFI_type_int_fast128_t)
322+
CASE(CFI_type_intmax_t)
323+
CASE(CFI_type_intptr_t)
324+
CASE(CFI_type_ptrdiff_t)
325+
CASE(CFI_type_half_float)
326+
CASE(CFI_type_bfloat)
327+
CASE(CFI_type_float)
328+
CASE(CFI_type_double)
329+
CASE(CFI_type_extended_double)
330+
CASE(CFI_type_long_double)
331+
CASE(CFI_type_float128)
332+
CASE(CFI_type_half_float_Complex)
333+
CASE(CFI_type_bfloat_Complex)
334+
CASE(CFI_type_float_Complex)
335+
CASE(CFI_type_double_Complex)
336+
CASE(CFI_type_extended_double_Complex)
337+
CASE(CFI_type_long_double_Complex)
338+
CASE(CFI_type_float128_Complex)
339+
CASE(CFI_type_Bool)
340+
CASE(CFI_type_char)
341+
CASE(CFI_type_cptr)
342+
CASE(CFI_type_struct)
343+
CASE(CFI_type_char16_t)
344+
CASE(CFI_type_char32_t)
345+
CASE(CFI_type_uint8_t)
346+
CASE(CFI_type_uint16_t)
347+
CASE(CFI_type_uint32_t)
348+
CASE(CFI_type_uint64_t)
349+
CASE(CFI_type_uint128_t)
350+
default:
351+
return nullptr;
352+
}
353+
#undef CASE
354+
}
355+
TypeCode code{type};
356+
if (!code.IsValid()) {
357+
return "invalid";
358+
}
359+
auto categoryAndKind{code.GetCategoryAndKind()};
360+
if (!categoryAndKind) {
361+
return nullptr;
362+
}
363+
TypeCategory tcat{categoryAndKind->first};
364+
int kind{categoryAndKind->second};
365+
366+
#define CASE(cat, k) \
367+
case (k): \
368+
return #cat "(kind=" #k ")";
369+
switch (tcat) {
370+
case TypeCategory::Integer:
371+
switch (kind) {
372+
CASE(INTEGER, 1)
373+
CASE(INTEGER, 2)
374+
CASE(INTEGER, 4)
375+
CASE(INTEGER, 8)
376+
CASE(INTEGER, 16)
377+
}
378+
break;
379+
case TypeCategory::Unsigned:
380+
switch (kind) {
381+
CASE(UNSIGNED, 1)
382+
CASE(UNSIGNED, 2)
383+
CASE(UNSIGNED, 4)
384+
CASE(UNSIGNED, 8)
385+
CASE(UNSIGNED, 16)
386+
}
387+
break;
388+
case TypeCategory::Real:
389+
switch (kind) {
390+
CASE(REAL, 2)
391+
CASE(REAL, 3)
392+
CASE(REAL, 4)
393+
CASE(REAL, 8)
394+
CASE(REAL, 10)
395+
CASE(REAL, 16)
396+
}
397+
break;
398+
case TypeCategory::Complex:
399+
switch (kind) {
400+
CASE(COMPLEX, 2)
401+
CASE(COMPLEX, 3)
402+
CASE(COMPLEX, 4)
403+
CASE(COMPLEX, 8)
404+
CASE(COMPLEX, 10)
405+
CASE(COMPLEX, 16)
406+
}
407+
break;
408+
case TypeCategory::Character:
409+
switch (kind) {
410+
CASE(CHARACTER, 1)
411+
CASE(CHARACTER, 2)
412+
CASE(CHARACTER, 4)
413+
}
414+
break;
415+
case TypeCategory::Logical:
416+
switch (kind) {
417+
CASE(LOGICAL, 1)
418+
CASE(LOGICAL, 2)
419+
CASE(LOGICAL, 4)
420+
CASE(LOGICAL, 8)
421+
}
422+
break;
423+
case TypeCategory::Derived:
424+
return "DERIVED";
425+
}
426+
#undef CASE
427+
return nullptr;
428+
}
429+
430+
void Descriptor::Dump(FILE *f, bool dumpRawType) const {
296431
std::fprintf(f, "Descriptor @ %p:\n", reinterpret_cast<const void *>(this));
297432
std::fprintf(f, " base_addr %p\n", raw_.base_addr);
298-
std::fprintf(f, " elem_len %zd\n", static_cast<std::size_t>(raw_.elem_len));
433+
std::fprintf(f, " elem_len %zd\n", ElementBytes());
299434
std::fprintf(f, " version %d\n", static_cast<int>(raw_.version));
300-
std::fprintf(f, " rank %d\n", static_cast<int>(raw_.rank));
301-
std::fprintf(f, " type %d\n", static_cast<int>(raw_.type));
302-
std::fprintf(f, " attribute %d\n", static_cast<int>(raw_.attribute));
435+
std::fprintf(f, " rank %d%s\n", rank(), rank() ? "" : " (scalar)");
436+
int ty{static_cast<int>(raw_.type)};
437+
if (const char *tyStr{GetTypeStr(raw_.type, dumpRawType)}) {
438+
std::fprintf(f, " type %d \"%s\"\n", ty, tyStr);
439+
} else {
440+
std::fprintf(f, " type %d\n", ty);
441+
}
442+
int attr{static_cast<int>(raw_.attribute)};
443+
if (IsPointer()) {
444+
std::fprintf(f, " attribute %d (pointer) \n", attr);
445+
} else if (IsAllocatable()) {
446+
std::fprintf(f, " attribute %d (allocatable)\n", attr);
447+
} else {
448+
std::fprintf(f, " attribute %d\n", attr);
449+
}
303450
std::fprintf(f, " extra %d\n", static_cast<int>(raw_.extra));
304451
std::fprintf(f, " addendum %d\n", static_cast<int>(HasAddendum()));
305452
std::fprintf(f, " alloc_idx %d\n", static_cast<int>(GetAllocIdx()));

flang-rt/lib/runtime/extensions.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -467,6 +467,14 @@ void FORTRAN_PROCEDURE_NAME(srand)(int *seed) {
467467
rand_seed_lock.Drop();
468468
}
469469

470+
void RTNAME(ShowDescriptor)(const Fortran::runtime::Descriptor *descr) {
471+
if (descr) {
472+
descr->Dump(stderr, /*dumpRawType=*/false);
473+
} else {
474+
std::fprintf(stderr, "NULL\n");
475+
}
476+
}
477+
470478
// Extension procedures related to I/O
471479

472480
namespace io {

flang-rt/unittests/Runtime/Descriptor.cpp

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include "flang-rt/runtime/descriptor.h"
1010
#include "tools.h"
1111
#include "gtest/gtest.h"
12+
#include <regex>
1213

1314
using namespace Fortran::runtime;
1415

@@ -158,3 +159,124 @@ TEST(Descriptor, FixedStride) {
158159
EXPECT_TRUE(descriptor.IsContiguous());
159160
EXPECT_EQ(descriptor.FixedStride().value_or(-666), 0);
160161
}
162+
163+
// The test below uses file operations that have nuances across multiple
164+
// platforms. Hence limit coverage by linux only unless wider coverage
165+
// should be required.
166+
#if defined(__linux__) && !defined(__ANDROID__)
167+
TEST(Descriptor, Dump) {
168+
StaticDescriptor<4> staticDesc[2];
169+
Descriptor &descriptor{staticDesc[0].descriptor()};
170+
using Type = std::int32_t;
171+
Type data[8][8][8];
172+
constexpr int four{static_cast<int>(sizeof data[0][0][0])};
173+
TypeCode integer{TypeCategory::Integer, four};
174+
// Scalar
175+
descriptor.Establish(integer, four, data, 0);
176+
FILE *tmpf{tmpfile()};
177+
ASSERT_TRUE(tmpf) << "tmpfile returned NULL";
178+
auto resetTmpFile = [tmpf]() {
179+
fflush(tmpf);
180+
rewind(tmpf);
181+
ftruncate(fileno(tmpf), 0);
182+
};
183+
184+
auto getAddrFilteredContent = [tmpf]() -> std::string {
185+
rewind(tmpf);
186+
std::ostringstream content;
187+
char buffer[1024];
188+
size_t bytes_read;
189+
while ((bytes_read = fread(buffer, 1, sizeof(buffer), tmpf)) > 0) {
190+
content.write(buffer, bytes_read);
191+
}
192+
193+
return std::regex_replace(
194+
std::regex_replace(content.str(), std::regex("Descriptor @.*:"),
195+
"Descriptor @ [addr]:"),
196+
std::regex("base_addr .*"), "base_addr [addr]");
197+
};
198+
199+
descriptor.Dump(tmpf, /*dumpRawType=*/false);
200+
// also dump as CFI type
201+
descriptor.Dump(tmpf, /*dumpRawType=*/true);
202+
std::string output{getAddrFilteredContent()};
203+
ASSERT_STREQ(output.c_str(),
204+
"Descriptor @ [addr]:\n"
205+
" base_addr [addr]\n"
206+
" elem_len 4\n"
207+
" version 20240719\n"
208+
" rank 0 (scalar)\n"
209+
" type 9 \"INTEGER(kind=4)\"\n"
210+
" attribute 0\n"
211+
" extra 0\n"
212+
" addendum 0\n"
213+
" alloc_idx 0\n"
214+
"Descriptor @ [addr]:\n"
215+
" base_addr [addr]\n"
216+
" elem_len 4\n"
217+
" version 20240719\n"
218+
" rank 0 (scalar)\n"
219+
" type 9 \"CFI_type_int32_t\"\n"
220+
" attribute 0\n"
221+
" extra 0\n"
222+
" addendum 0\n"
223+
" alloc_idx 0\n");
224+
225+
// Contiguous matrix (0:7, 0:7)
226+
SubscriptValue extent[3]{8, 8, 8};
227+
descriptor.Establish(integer, four, data, 2, extent);
228+
resetTmpFile();
229+
descriptor.Dump(tmpf, /*dumpRawType=*/false);
230+
output = getAddrFilteredContent();
231+
ASSERT_STREQ(output.c_str(),
232+
"Descriptor @ [addr]:\n"
233+
" base_addr [addr]\n"
234+
" elem_len 4\n"
235+
" version 20240719\n"
236+
" rank 2\n"
237+
" type 9 \"INTEGER(kind=4)\"\n"
238+
" attribute 0\n"
239+
" extra 0\n"
240+
" addendum 0\n"
241+
" alloc_idx 0\n"
242+
" dim[0] lower_bound 0\n"
243+
" extent 8\n"
244+
" sm 4\n"
245+
" dim[1] lower_bound 0\n"
246+
" extent 8\n"
247+
" sm 32\n");
248+
249+
TypeCode real{TypeCategory::Real, four};
250+
// Discontiguous real 3-D array (0:7, 0:6:2, 0:6:2)
251+
descriptor.Establish(real, four, data, 3, extent);
252+
descriptor.GetDimension(1).SetExtent(4);
253+
descriptor.GetDimension(1).SetByteStride(8 * 2 * four);
254+
descriptor.GetDimension(2).SetExtent(4);
255+
descriptor.GetDimension(2).SetByteStride(8 * 8 * 2 * four);
256+
257+
resetTmpFile();
258+
descriptor.Dump(tmpf, /*dumpRawType=*/false);
259+
output = getAddrFilteredContent();
260+
ASSERT_STREQ(output.c_str(),
261+
"Descriptor @ [addr]:\n"
262+
" base_addr [addr]\n"
263+
" elem_len 4\n"
264+
" version 20240719\n"
265+
" rank 3\n"
266+
" type 27 \"REAL(kind=4)\"\n"
267+
" attribute 0\n"
268+
" extra 0\n"
269+
" addendum 0\n"
270+
" alloc_idx 0\n"
271+
" dim[0] lower_bound 0\n"
272+
" extent 8\n"
273+
" sm 4\n"
274+
" dim[1] lower_bound 0\n"
275+
" extent 4\n"
276+
" sm 64\n"
277+
" dim[2] lower_bound 0\n"
278+
" extent 4\n"
279+
" sm 512\n");
280+
fclose(tmpf);
281+
}
282+
#endif // defined(__linux__) && !defined(__ANDROID__)

flang/docs/Intrinsics.md

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1414,6 +1414,7 @@ This is prefixed by `STRING`, a colon and a space.
14141414
- **Class:** subroutine
14151415
- **Syntax:** `CALL PERROR(STRING)`
14161416

1417+
<<<<<<< HEAD
14171418
### Non-Standard Intrinsics: SRAND
14181419

14191420
#### Description
@@ -1455,3 +1456,45 @@ The return value is of `REAL` type with the default kind.
14551456
- **Standard:** GNU extension
14561457
- **Class:** function
14571458
- **Syntax:** `RESULT = RAND(I)`
1459+
1460+
### Non-Standard Intrinsics: SHOW_DESCRIPTOR
1461+
1462+
#### Description
1463+
`SHOW_DESCRIPTOR(VAR)` prints (on the C stderr stream) a contents of a descriptor for the variable VAR,
1464+
which can be of any type and rank, including scalars.
1465+
Requires use of flang_debug module.
1466+
1467+
Here is an example of its output:
1468+
```
1469+
Descriptor @ 0x7ffe506fc368:
1470+
base_addr 0x55944caef0f0
1471+
elem_len 4
1472+
version 20240719
1473+
rank 1
1474+
type 9 "INTEGER(kind=4)"
1475+
attribute 2 (allocatable)
1476+
extra 0
1477+
addendum 0
1478+
alloc_idx 0
1479+
dim[0] lower_bound 1
1480+
extent 5
1481+
sm 4
1482+
```
1483+
1484+
#### Usage and Info
1485+
- **Standard:** flang extension
1486+
- **Class:** subroutine
1487+
- **Syntax:** `CALL show_descriptor(VAR)`
1488+
1489+
#### Example
1490+
```Fortran
1491+
subroutine test
1492+
use flang_debug
1493+
implicit none
1494+
character(len=9) :: c = 'Hey buddy'
1495+
integer :: a(5)
1496+
call show_descriptor(c)
1497+
call show_descriptor(c(1:3))
1498+
call show_descriptor(a)
1499+
end subroutine test
1500+
```

flang/include/flang/Optimizer/Builder/IntrinsicCall.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -413,6 +413,7 @@ struct IntrinsicLibrary {
413413
template <typename Shift>
414414
mlir::Value genShift(mlir::Type resultType, llvm::ArrayRef<mlir::Value>);
415415
mlir::Value genShiftA(mlir::Type resultType, llvm::ArrayRef<mlir::Value>);
416+
void genShowDescriptor(llvm::ArrayRef<fir::ExtendedValue>);
416417
mlir::Value genSign(mlir::Type, llvm::ArrayRef<mlir::Value>);
417418
mlir::Value genSind(mlir::Type, llvm::ArrayRef<mlir::Value>);
418419
mlir::Value genSinpi(mlir::Type, llvm::ArrayRef<mlir::Value>);

0 commit comments

Comments
 (0)