Skip to content
Open
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion flang-rt/include/flang-rt/runtime/descriptor.h
Original file line number Diff line number Diff line change
Expand Up @@ -510,7 +510,9 @@ class Descriptor {

RT_API_ATTRS void Check() const;

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

RT_API_ATTRS inline bool HasAddendum() const {
return raw_.extra & _CFI_ADDENDUM_FLAG;
Expand Down
164 changes: 159 additions & 5 deletions flang-rt/lib/runtime/descriptor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -292,14 +292,168 @@ RT_API_ATTRS void Descriptor::Check() const {
// TODO
}

void Descriptor::Dump(FILE *f) const {
static const char *GetTypeStr(ISO::CFI_type_t type, bool dumpRawType) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

StringRef instead if const char*?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using llvm::StringRef would be a precedent for flang-rt.

if (dumpRawType) {
#define CASE(x) \
case (x): \
return #x;
switch (type) {
CASE(CFI_type_signed_char)
CASE(CFI_type_short)
CASE(CFI_type_int)
CASE(CFI_type_long)
CASE(CFI_type_long_long)
CASE(CFI_type_size_t)
CASE(CFI_type_int8_t)
CASE(CFI_type_int16_t)
CASE(CFI_type_int32_t)
CASE(CFI_type_int64_t)
CASE(CFI_type_int128_t)
CASE(CFI_type_int_least8_t)
CASE(CFI_type_int_least16_t)
CASE(CFI_type_int_least32_t)
CASE(CFI_type_int_least64_t)
CASE(CFI_type_int_least128_t)
CASE(CFI_type_int_fast8_t)
CASE(CFI_type_int_fast16_t)
CASE(CFI_type_int_fast32_t)
CASE(CFI_type_int_fast64_t)
CASE(CFI_type_int_fast128_t)
CASE(CFI_type_intmax_t)
CASE(CFI_type_intptr_t)
CASE(CFI_type_ptrdiff_t)
CASE(CFI_type_half_float)
CASE(CFI_type_bfloat)
CASE(CFI_type_float)
CASE(CFI_type_double)
CASE(CFI_type_extended_double)
CASE(CFI_type_long_double)
CASE(CFI_type_float128)
CASE(CFI_type_half_float_Complex)
CASE(CFI_type_bfloat_Complex)
CASE(CFI_type_float_Complex)
CASE(CFI_type_double_Complex)
CASE(CFI_type_extended_double_Complex)
CASE(CFI_type_long_double_Complex)
CASE(CFI_type_float128_Complex)
CASE(CFI_type_Bool)
CASE(CFI_type_char)
CASE(CFI_type_cptr)
CASE(CFI_type_struct)
CASE(CFI_type_char16_t)
CASE(CFI_type_char32_t)
CASE(CFI_type_uint8_t)
CASE(CFI_type_uint16_t)
CASE(CFI_type_uint32_t)
CASE(CFI_type_uint64_t)
CASE(CFI_type_uint128_t)
}
#undef CASE
return nullptr;
}
TypeCode code{type};

if (!code.IsValid())
return "invalid";

common::optional<std::pair<TypeCategory, int>> categoryAndKind =
code.GetCategoryAndKind();
if (!categoryAndKind)
return nullptr;

TypeCategory tcat;
int kind;
std::tie(tcat, kind) = *categoryAndKind;

#define CASE(cat, k) \
case (k): \
return #cat "(kind=" #k ")";
switch (tcat) {
case TypeCategory::Integer:
switch (kind) {
CASE(INTEGER, 1)
CASE(INTEGER, 2)
CASE(INTEGER, 4)
CASE(INTEGER, 8)
CASE(INTEGER, 16)
}
break;
case TypeCategory::Unsigned:
switch (kind) {
CASE(UNSIGNED, 1)
CASE(UNSIGNED, 2)
CASE(UNSIGNED, 4)
CASE(UNSIGNED, 8)
CASE(UNSIGNED, 16)
}
break;
case TypeCategory::Real:
switch (kind) {
CASE(REAL, 2)
CASE(REAL, 3)
CASE(REAL, 4)
CASE(REAL, 8)
CASE(REAL, 10)
CASE(REAL, 16)
}
break;
case TypeCategory::Complex:
switch (kind) {
CASE(COMPLEX, 2)
CASE(COMPLEX, 3)
CASE(COMPLEX, 4)
CASE(COMPLEX, 8)
CASE(COMPLEX, 10)
CASE(COMPLEX, 16)
}
break;
case TypeCategory::Character:
switch (kind) {
CASE(CHARACTER, 1)
CASE(CHARACTER, 2)
CASE(CHARACTER, 4)
}
break;
case TypeCategory::Logical:
switch (kind) {
CASE(LOGICAL, 1)
CASE(LOGICAL, 2)
CASE(LOGICAL, 4)
CASE(LOGICAL, 8)
}
break;
case TypeCategory::Derived:
return "DERIVED";
}
#undef CASE
return nullptr;
}

void Descriptor::Dump(FILE *f, bool dumpRawType) const {
std::fprintf(f, "Descriptor @ %p:\n", reinterpret_cast<const void *>(this));
std::fprintf(f, " base_addr %p\n", raw_.base_addr);
std::fprintf(f, " elem_len %zd\n", static_cast<std::size_t>(raw_.elem_len));
std::fprintf(f, " elem_len %zd\n", ElementBytes());
std::fprintf(f, " version %d\n", static_cast<int>(raw_.version));
std::fprintf(f, " rank %d\n", static_cast<int>(raw_.rank));
std::fprintf(f, " type %d\n", static_cast<int>(raw_.type));
std::fprintf(f, " attribute %d\n", static_cast<int>(raw_.attribute));
if (rank() > 0) {
std::fprintf(f, " rank %d\n", rank());
} else {
std::fprintf(f, " scalar\n");
}
int ty = static_cast<int>(raw_.type);
if (const char *tyStr = GetTypeStr(raw_.type, dumpRawType)) {
std::fprintf(f, " type %d \"%s\"\n", ty, tyStr);
} else {
std::fprintf(f, " type %d\n", ty);
}
int attr = static_cast<int>(raw_.attribute);
if (IsPointer()) {
std::fprintf(f, " attribute %d (pointer) \n", attr);
} else if (IsAllocatable()) {
std::fprintf(f, " attribute %d (allocatable)\n", attr);
} else {
std::fprintf(f, " attribute %d\n", attr);
}

std::fprintf(f, " extra %d\n", static_cast<int>(raw_.extra));
std::fprintf(f, " addendum %d\n", static_cast<int>(HasAddendum()));
std::fprintf(f, " alloc_idx %d\n", static_cast<int>(GetAllocIdx()));
Expand Down
8 changes: 8 additions & 0 deletions flang-rt/lib/runtime/extensions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -398,6 +398,14 @@ std::int64_t RTNAME(time)() { return time(nullptr); }
// MCLOCK: returns accumulated CPU time in ticks
std::int32_t FORTRAN_PROCEDURE_NAME(mclock)() { return std::clock(); }

void RTNAME(ShowDescriptor)(const Fortran::runtime::Descriptor *descr) {
if (descr) {
descr->Dump(stderr, /*dumpRawType=*/false);
} else {
std::fprintf(stderr, "NULL\n");
}
}

// Extension procedures related to I/O

namespace io {
Expand Down
113 changes: 113 additions & 0 deletions flang-rt/unittests/Runtime/Descriptor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include "flang-rt/runtime/descriptor.h"
#include "tools.h"
#include "gtest/gtest.h"
#include <regex>

using namespace Fortran::runtime;

Expand Down Expand Up @@ -158,3 +159,115 @@ TEST(Descriptor, FixedStride) {
EXPECT_TRUE(descriptor.IsContiguous());
EXPECT_EQ(descriptor.FixedStride().value_or(-666), 0);
}

static std::string getAddrFilteredContent(FILE *fin) {
rewind(fin);
std::ostringstream content;
char buffer[1024];
size_t bytes_read;
while ((bytes_read = fread(buffer, 1, sizeof(buffer), fin)) > 0) {
content.write(buffer, bytes_read);
}
return std::regex_replace(
content.str(), std::regex("(0x[0-9a-fA-F]*)"), "[address]");
}

TEST(Descriptor, Dump) {
StaticDescriptor<4> staticDesc[2];
Descriptor &descriptor{staticDesc[0].descriptor()};
using Type = std::int32_t;
Type data[8][8][8];
constexpr int four{static_cast<int>(sizeof data[0][0][0])};
TypeCode integer{TypeCategory::Integer, four};
// Scalar
descriptor.Establish(integer, four, data, 0);
FILE *tmpf = tmpfile();
ASSERT_TRUE(tmpf) << "tmpfile returned NULL";
auto resetTmpFile = [tmpf]() {
rewind(tmpf);
ftruncate(fileno(tmpf), 0);
};

descriptor.Dump(tmpf, /*dumpRawType=*/false);
// also dump as CFI type
descriptor.Dump(tmpf, /*dumpRawType=*/true);
std::string output = getAddrFilteredContent(tmpf);
ASSERT_STREQ(output.c_str(),
"Descriptor @ [address]:\n"
" base_addr [address]\n"
" elem_len 4\n"
" version 20240719\n"
" scalar\n"
" type 9 \"INTEGER(kind=4)\"\n"
" attribute 0\n"
" extra 0\n"
" addendum 0\n"
" alloc_idx 0\n"
"Descriptor @ [address]:\n"
" base_addr [address]\n"
" elem_len 4\n"
" version 20240719\n"
" scalar\n"
" type 9 \"CFI_type_int32_t\"\n"
" attribute 0\n"
" extra 0\n"
" addendum 0\n"
" alloc_idx 0\n");

// Contiguous matrix (0:7, 0:7)
SubscriptValue extent[3]{8, 8, 8};
descriptor.Establish(integer, four, data, 2, extent);
resetTmpFile();
descriptor.Dump(tmpf, /*dumpRawType=*/false);
output = getAddrFilteredContent(tmpf);
ASSERT_STREQ(output.c_str(),
"Descriptor @ [address]:\n"
" base_addr [address]\n"
" elem_len 4\n"
" version 20240719\n"
" rank 2\n"
" type 9 \"INTEGER(kind=4)\"\n"
" attribute 0\n"
" extra 0\n"
" addendum 0\n"
" alloc_idx 0\n"
" dim[0] lower_bound 0\n"
" extent 8\n"
" sm 4\n"
" dim[1] lower_bound 0\n"
" extent 8\n"
" sm 32\n");

TypeCode real{TypeCategory::Real, four};
// Discontiguous real 3-D array (0:7, 0:6:2, 0:6:2)
descriptor.Establish(real, four, data, 3, extent);
descriptor.GetDimension(1).SetExtent(4);
descriptor.GetDimension(1).SetByteStride(8 * 2 * four);
descriptor.GetDimension(2).SetExtent(4);
descriptor.GetDimension(2).SetByteStride(8 * 8 * 2 * four);

resetTmpFile();
descriptor.Dump(tmpf, /*dumpRawType=*/false);
output = getAddrFilteredContent(tmpf);
ASSERT_STREQ(output.c_str(),
"Descriptor @ [address]:\n"
" base_addr [address]\n"
" elem_len 4\n"
" version 20240719\n"
" rank 3\n"
" type 27 \"REAL(kind=4)\"\n"
" attribute 0\n"
" extra 0\n"
" addendum 0\n"
" alloc_idx 0\n"
" dim[0] lower_bound 0\n"
" extent 8\n"
" sm 4\n"
" dim[1] lower_bound 0\n"
" extent 4\n"
" sm 64\n"
" dim[2] lower_bound 0\n"
" extent 4\n"
" sm 512\n");
fclose(tmpf);
}
42 changes: 42 additions & 0 deletions flang/docs/Intrinsics.md
Original file line number Diff line number Diff line change
Expand Up @@ -1379,3 +1379,45 @@ This is prefixed by `STRING`, a colon and a space.
- **Standard:** GNU extension
- **Class:** subroutine
- **Syntax:** `CALL PERROR(STRING)`

### Non-Standard Intrinsics: SHOW_DESCRIPTOR

#### Description
`SHOW_DESCRIPTOR(VAR)` prints (on the C stderr stream) a contents of a descriptor for the variable VAR,
which can be of any type and rank, including scalars.
Requires use of flang_debug module.

Here is an example of its output:
```
Descriptor @ 0x7ffe506fc368:
base_addr 0x55944caef0f0
elem_len 4
version 20240719
rank 1
type 9 "INTEGER(kind=4)"
attribute 2 (allocatable)
extra 0
addendum 0
alloc_idx 0
dim[0] lower_bound 1
extent 5
sm 4
```

#### Usage and Info
- **Standard:** flang extension
- **Class:** subroutine
- **Syntax:** `CALL show_descriptor(VAR)`

#### Example
```Fortran
subroutine test
use flang_debug
implicit none
character(len=9) :: c = 'Hey buddy'
integer :: a(5)
call show_descriptor(c)
call show_descriptor(c(1:3))
call show_descriptor(a)
end subroutine test
```
1 change: 1 addition & 0 deletions flang/include/flang/Optimizer/Builder/IntrinsicCall.h
Original file line number Diff line number Diff line change
Expand Up @@ -408,6 +408,7 @@ struct IntrinsicLibrary {
template <typename Shift>
mlir::Value genShift(mlir::Type resultType, llvm::ArrayRef<mlir::Value>);
mlir::Value genShiftA(mlir::Type resultType, llvm::ArrayRef<mlir::Value>);
void genShowDescriptor(llvm::ArrayRef<fir::ExtendedValue>);
mlir::Value genSign(mlir::Type, llvm::ArrayRef<mlir::Value>);
mlir::Value genSind(mlir::Type, llvm::ArrayRef<mlir::Value>);
mlir::Value genSinpi(mlir::Type, llvm::ArrayRef<mlir::Value>);
Expand Down
3 changes: 3 additions & 0 deletions flang/include/flang/Optimizer/Builder/Runtime/Intrinsics.h
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,9 @@ void genSleep(fir::FirOpBuilder &builder, mlir::Location loc,
mlir::Value genChdir(fir::FirOpBuilder &builder, mlir::Location loc,
mlir::Value name);

/// generate dump of a descriptor
void genShowDescriptor(fir::FirOpBuilder &builder, mlir::Location loc,
mlir::Value descriptor);
} // namespace runtime
} // namespace fir

Expand Down
Loading