Skip to content

Commit e7748e9

Browse files
authored
[flang] implement show_descriptor intrinsic, a non-standard extension (#169137)
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 dim[0] lower_bound 1 extent 3 sm 4
1 parent aa727db commit e7748e9

File tree

15 files changed

+619
-6
lines changed

15 files changed

+619
-6
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: 159 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -292,14 +292,168 @@ 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+
}
351+
#undef CASE
352+
return nullptr;
353+
}
354+
TypeCode code{type};
355+
356+
if (!code.IsValid())
357+
return "invalid";
358+
359+
common::optional<std::pair<TypeCategory, int>> categoryAndKind =
360+
code.GetCategoryAndKind();
361+
if (!categoryAndKind)
362+
return nullptr;
363+
364+
TypeCategory tcat;
365+
int kind;
366+
std::tie(tcat, kind) = *categoryAndKind;
367+
368+
#define CASE(cat, k) \
369+
case (k): \
370+
return #cat "(kind=" #k ")";
371+
switch (tcat) {
372+
case TypeCategory::Integer:
373+
switch (kind) {
374+
CASE(INTEGER, 1)
375+
CASE(INTEGER, 2)
376+
CASE(INTEGER, 4)
377+
CASE(INTEGER, 8)
378+
CASE(INTEGER, 16)
379+
}
380+
break;
381+
case TypeCategory::Unsigned:
382+
switch (kind) {
383+
CASE(UNSIGNED, 1)
384+
CASE(UNSIGNED, 2)
385+
CASE(UNSIGNED, 4)
386+
CASE(UNSIGNED, 8)
387+
CASE(UNSIGNED, 16)
388+
}
389+
break;
390+
case TypeCategory::Real:
391+
switch (kind) {
392+
CASE(REAL, 2)
393+
CASE(REAL, 3)
394+
CASE(REAL, 4)
395+
CASE(REAL, 8)
396+
CASE(REAL, 10)
397+
CASE(REAL, 16)
398+
}
399+
break;
400+
case TypeCategory::Complex:
401+
switch (kind) {
402+
CASE(COMPLEX, 2)
403+
CASE(COMPLEX, 3)
404+
CASE(COMPLEX, 4)
405+
CASE(COMPLEX, 8)
406+
CASE(COMPLEX, 10)
407+
CASE(COMPLEX, 16)
408+
}
409+
break;
410+
case TypeCategory::Character:
411+
switch (kind) {
412+
CASE(CHARACTER, 1)
413+
CASE(CHARACTER, 2)
414+
CASE(CHARACTER, 4)
415+
}
416+
break;
417+
case TypeCategory::Logical:
418+
switch (kind) {
419+
CASE(LOGICAL, 1)
420+
CASE(LOGICAL, 2)
421+
CASE(LOGICAL, 4)
422+
CASE(LOGICAL, 8)
423+
}
424+
break;
425+
case TypeCategory::Derived:
426+
return "DERIVED";
427+
}
428+
#undef CASE
429+
return nullptr;
430+
}
431+
432+
void Descriptor::Dump(FILE *f, bool dumpRawType) const {
296433
std::fprintf(f, "Descriptor @ %p:\n", reinterpret_cast<const void *>(this));
297434
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));
435+
std::fprintf(f, " elem_len %zd\n", ElementBytes());
299436
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));
437+
if (rank() > 0) {
438+
std::fprintf(f, " rank %d\n", rank());
439+
} else {
440+
std::fprintf(f, " scalar\n");
441+
}
442+
int ty = static_cast<int>(raw_.type);
443+
if (const char *tyStr = GetTypeStr(raw_.type, dumpRawType)) {
444+
std::fprintf(f, " type %d \"%s\"\n", ty, tyStr);
445+
} else {
446+
std::fprintf(f, " type %d\n", ty);
447+
}
448+
int attr = static_cast<int>(raw_.attribute);
449+
if (IsPointer()) {
450+
std::fprintf(f, " attribute %d (pointer) \n", attr);
451+
} else if (IsAllocatable()) {
452+
std::fprintf(f, " attribute %d (allocatable)\n", attr);
453+
} else {
454+
std::fprintf(f, " attribute %d\n", attr);
455+
}
456+
303457
std::fprintf(f, " extra %d\n", static_cast<int>(raw_.extra));
304458
std::fprintf(f, " addendum %d\n", static_cast<int>(HasAddendum()));
305459
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
@@ -409,6 +409,14 @@ std::int64_t RTNAME(time)() { return time(nullptr); }
409409
// MCLOCK: returns accumulated CPU time in ticks
410410
std::int32_t FORTRAN_PROCEDURE_NAME(mclock)() { return std::clock(); }
411411

412+
void RTNAME(ShowDescriptor)(const Fortran::runtime::Descriptor *descr) {
413+
if (descr) {
414+
descr->Dump(stderr, /*dumpRawType=*/false);
415+
} else {
416+
std::fprintf(stderr, "NULL\n");
417+
}
418+
}
419+
412420
// Extension procedures related to I/O
413421

414422
namespace io {

flang-rt/unittests/Runtime/Descriptor.cpp

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

flang/docs/Intrinsics.md

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1413,3 +1413,45 @@ This is prefixed by `STRING`, a colon and a space.
14131413
- **Standard:** GNU extension
14141414
- **Class:** subroutine
14151415
- **Syntax:** `CALL PERROR(STRING)`
1416+
1417+
### Non-Standard Intrinsics: SHOW_DESCRIPTOR
1418+
1419+
#### Description
1420+
`SHOW_DESCRIPTOR(VAR)` prints (on the C stderr stream) a contents of a descriptor for the variable VAR,
1421+
which can be of any type and rank, including scalars.
1422+
Requires use of flang_debug module.
1423+
1424+
Here is an example of its output:
1425+
```
1426+
Descriptor @ 0x7ffe506fc368:
1427+
base_addr 0x55944caef0f0
1428+
elem_len 4
1429+
version 20240719
1430+
rank 1
1431+
type 9 "INTEGER(kind=4)"
1432+
attribute 2 (allocatable)
1433+
extra 0
1434+
addendum 0
1435+
alloc_idx 0
1436+
dim[0] lower_bound 1
1437+
extent 5
1438+
sm 4
1439+
```
1440+
1441+
#### Usage and Info
1442+
- **Standard:** flang extension
1443+
- **Class:** subroutine
1444+
- **Syntax:** `CALL show_descriptor(VAR)`
1445+
1446+
#### Example
1447+
```Fortran
1448+
subroutine test
1449+
use flang_debug
1450+
implicit none
1451+
character(len=9) :: c = 'Hey buddy'
1452+
integer :: a(5)
1453+
call show_descriptor(c)
1454+
call show_descriptor(c(1:3))
1455+
call show_descriptor(a)
1456+
end subroutine test
1457+
```

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -409,6 +409,7 @@ struct IntrinsicLibrary {
409409
template <typename Shift>
410410
mlir::Value genShift(mlir::Type resultType, llvm::ArrayRef<mlir::Value>);
411411
mlir::Value genShiftA(mlir::Type resultType, llvm::ArrayRef<mlir::Value>);
412+
void genShowDescriptor(llvm::ArrayRef<fir::ExtendedValue>);
412413
mlir::Value genSign(mlir::Type, llvm::ArrayRef<mlir::Value>);
413414
mlir::Value genSind(mlir::Type, llvm::ArrayRef<mlir::Value>);
414415
mlir::Value genSinpi(mlir::Type, llvm::ArrayRef<mlir::Value>);

flang/include/flang/Optimizer/Builder/Runtime/Intrinsics.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,9 @@ void genSleep(fir::FirOpBuilder &builder, mlir::Location loc,
111111
mlir::Value genChdir(fir::FirOpBuilder &builder, mlir::Location loc,
112112
mlir::Value name);
113113

114+
/// generate dump of a descriptor
115+
void genShowDescriptor(fir::FirOpBuilder &builder, mlir::Location loc,
116+
mlir::Value descriptor);
114117
} // namespace runtime
115118
} // namespace fir
116119

0 commit comments

Comments
 (0)