Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
157 changes: 152 additions & 5 deletions flang-rt/lib/runtime/descriptor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -292,14 +292,161 @@ 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) {
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)
default:
return nullptr;
}
#undef CASE
}
TypeCode code{type};
if (!code.IsValid()) {
return "invalid";
}
auto categoryAndKind{code.GetCategoryAndKind()};
if (!categoryAndKind) {
return nullptr;
}
TypeCategory tcat{categoryAndKind->first};
int kind{categoryAndKind->second};

#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));
std::fprintf(f, " rank %d%s\n", rank(), rank() ? "" : " (scalar)");
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 @@ -467,6 +467,14 @@ void FORTRAN_PROCEDURE_NAME(srand)(int *seed) {
rand_seed_lock.Drop();
}

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
122 changes: 122 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,124 @@ TEST(Descriptor, FixedStride) {
EXPECT_TRUE(descriptor.IsContiguous());
EXPECT_EQ(descriptor.FixedStride().value_or(-666), 0);
}

// The test below uses file operations that have nuances across multiple
// platforms. Hence limit coverage by linux only unless wider coverage
// should be required.
#if defined(__linux__) && !defined(__ANDROID__)
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]() {
fflush(tmpf);
rewind(tmpf);
ftruncate(fileno(tmpf), 0);
};

auto getAddrFilteredContent = [tmpf]() -> std::string {
rewind(tmpf);
std::ostringstream content;
char buffer[1024];
size_t bytes_read;
while ((bytes_read = fread(buffer, 1, sizeof(buffer), tmpf)) > 0) {
content.write(buffer, bytes_read);
}

return std::regex_replace(
std::regex_replace(content.str(), std::regex("Descriptor @.*:"),
"Descriptor @ [addr]:"),
std::regex("base_addr .*"), "base_addr [addr]");
};

descriptor.Dump(tmpf, /*dumpRawType=*/false);
// also dump as CFI type
descriptor.Dump(tmpf, /*dumpRawType=*/true);
std::string output{getAddrFilteredContent()};
ASSERT_STREQ(output.c_str(),
"Descriptor @ [addr]:\n"
" base_addr [addr]\n"
" elem_len 4\n"
" version 20240719\n"
" rank 0 (scalar)\n"
" type 9 \"INTEGER(kind=4)\"\n"
" attribute 0\n"
" extra 0\n"
" addendum 0\n"
" alloc_idx 0\n"
"Descriptor @ [addr]:\n"
" base_addr [addr]\n"
" elem_len 4\n"
" version 20240719\n"
" rank 0 (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();
ASSERT_STREQ(output.c_str(),
"Descriptor @ [addr]:\n"
" base_addr [addr]\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();
ASSERT_STREQ(output.c_str(),
"Descriptor @ [addr]:\n"
" base_addr [addr]\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);
}
#endif // defined(__linux__) && !defined(__ANDROID__)
43 changes: 43 additions & 0 deletions flang/docs/Intrinsics.md
Original file line number Diff line number Diff line change
Expand Up @@ -1414,6 +1414,7 @@ This is prefixed by `STRING`, a colon and a space.
- **Class:** subroutine
- **Syntax:** `CALL PERROR(STRING)`

<<<<<<< HEAD
### Non-Standard Intrinsics: SRAND

#### Description
Expand Down Expand Up @@ -1455,3 +1456,45 @@ The return value is of `REAL` type with the default kind.
- **Standard:** GNU extension
- **Class:** function
- **Syntax:** `RESULT = RAND(I)`

### 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 @@ -413,6 +413,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
Loading