Skip to content

Commit f232f55

Browse files
committed
[flang/flang-rt] Implement show_descriptor intrinsic, a non-standard extension
Reapply llvm#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 434127b commit f232f55

File tree

15 files changed

+622
-7
lines changed

15 files changed

+622
-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: 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: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -467,6 +467,15 @@ void FORTRAN_PROCEDURE_NAME(srand)(int *seed) {
467467
rand_seed_lock.Drop();
468468
}
469469

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

472481
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: 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)