mirror of
https://github.com/intel/llvm.git
synced 2026-02-04 03:26:06 +08:00
[flang] Compute sizes and offsets for symbols
Summary: Add size and offset properties to symbols, representing their byte size and offset within their enclosing scope. Add size and align properties to scopes so that they are available for scopes representing derived types. Add ComputeOffsets pass over the symbol table to fill in those fields. Compute descriptor size based on rank and length parameters. Extract DerivedTypeSpec::NumLengthParameters from DynamicType::RequiresDescriptor to share the code. Add Scope::GetSymbols to get symbols in canonical order. compute-offsets.cpp and mod-file.cpp both need to process symbols in the order in which they are declared. Move the collecting of those symbols into Scope so that it can be shared. Add symbol size and offset to output of `-fdebug-dump-symbols` and use that in some tests. Still to do: - make size and alignment rules configurable based on target - use offsets to check EQUIVALENCE statements Differential Revision: https://reviews.llvm.org/D78680
This commit is contained in:
@@ -108,12 +108,15 @@ public:
|
||||
const_iterator cbegin() const { return symbols_.cbegin(); }
|
||||
const_iterator cend() const { return symbols_.cend(); }
|
||||
|
||||
// Return symbols in declaration order (the iterators above are in name order)
|
||||
SymbolVector GetSymbols() const;
|
||||
std::vector<common::Reference<Symbol>> GetSymbols();
|
||||
|
||||
iterator find(const SourceName &name);
|
||||
const_iterator find(const SourceName &name) const {
|
||||
return symbols_.find(name);
|
||||
}
|
||||
size_type erase(const SourceName &);
|
||||
size_type size() const { return symbols_.size(); }
|
||||
bool empty() const { return symbols_.empty(); }
|
||||
|
||||
// Look for symbol by name in this scope and host (depending on imports).
|
||||
@@ -182,6 +185,11 @@ public:
|
||||
// that are referenced by SourceName objects.
|
||||
void set_chars(parser::CookedSource &);
|
||||
|
||||
std::size_t size() const { return size_; }
|
||||
void set_size(std::size_t size) { size_ = size; }
|
||||
std::size_t align() const { return align_; }
|
||||
void set_align(std::size_t align) { align_ = align; }
|
||||
|
||||
ImportKind GetImportKind() const;
|
||||
// Names appearing in IMPORT statements in this scope
|
||||
std::set<SourceName> importNames() const { return importNames_; }
|
||||
@@ -217,6 +225,8 @@ public:
|
||||
private:
|
||||
Scope &parent_; // this is enclosing scope, not extended derived type base
|
||||
const Kind kind_;
|
||||
std::size_t size_{0}; // size in bytes
|
||||
std::size_t align_{0}; // required alignment in bytes
|
||||
parser::CharBlock sourceRange_;
|
||||
Symbol *const symbol_; // if not null, symbol_->scope() == this
|
||||
std::list<Scope> children_;
|
||||
|
||||
@@ -499,6 +499,10 @@ public:
|
||||
Scope *scope() { return scope_; }
|
||||
const Scope *scope() const { return scope_; }
|
||||
void set_scope(Scope *scope) { scope_ = scope; }
|
||||
std::size_t size() const { return size_; }
|
||||
void set_size(std::size_t size) { size_ = size; }
|
||||
std::size_t offset() const { return offset_; }
|
||||
void set_offset(std::size_t offset) { offset_ = offset; }
|
||||
// Give the symbol a name with a different source location but same chars.
|
||||
void ReplaceName(const SourceName &);
|
||||
|
||||
@@ -665,6 +669,8 @@ private:
|
||||
Attrs attrs_;
|
||||
Flags flags_;
|
||||
Scope *scope_{nullptr};
|
||||
std::size_t size_{0}; // size in bytes
|
||||
std::size_t offset_{0}; // byte offset in enclosing scope
|
||||
Details details_;
|
||||
|
||||
Symbol() {} // only created in class Symbols
|
||||
@@ -730,6 +736,10 @@ inline bool ProcEntityDetails::HasExplicitInterface() const {
|
||||
}
|
||||
|
||||
inline bool operator<(SymbolRef x, SymbolRef y) { return *x < *y; }
|
||||
inline bool operator<(
|
||||
common::Reference<Symbol> x, common::Reference<Symbol> y) {
|
||||
return *x < *y;
|
||||
}
|
||||
using SymbolSet = std::set<SymbolRef>;
|
||||
|
||||
} // namespace Fortran::semantics
|
||||
|
||||
@@ -250,6 +250,7 @@ public:
|
||||
void ReplaceScope(const Scope &);
|
||||
RawParameters &rawParameters() { return rawParameters_; }
|
||||
const ParameterMapType ¶meters() const { return parameters_; }
|
||||
int NumLengthParameters() const;
|
||||
|
||||
bool MightBeParameterized() const;
|
||||
bool IsForwardReferenced() const;
|
||||
|
||||
@@ -431,28 +431,8 @@ DynamicType DynamicType::ResultTypeForMultiply(const DynamicType &that) const {
|
||||
}
|
||||
|
||||
bool DynamicType::RequiresDescriptor() const {
|
||||
if (IsPolymorphic() || IsUnknownLengthCharacter()) {
|
||||
return true;
|
||||
}
|
||||
if (derived_) {
|
||||
// Any length type parameter?
|
||||
if (const auto *scope{derived_->scope()}) {
|
||||
if (const auto *symbol{scope->symbol()}) {
|
||||
if (const auto *details{
|
||||
symbol->detailsIf<semantics::DerivedTypeDetails>()}) {
|
||||
for (const Symbol ¶m : details->paramDecls()) {
|
||||
if (const auto *details{
|
||||
param.detailsIf<semantics::TypeParamDetails>()}) {
|
||||
if (details->attr() == common::TypeParamAttr::Len) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return IsPolymorphic() || IsUnknownLengthCharacter() ||
|
||||
(derived_ && derived_->NumLengthParameters() > 0);
|
||||
}
|
||||
|
||||
bool DynamicType::HasDeferredTypeParameter() const {
|
||||
|
||||
@@ -21,6 +21,7 @@ add_flang_library(FortranSemantics
|
||||
check-purity.cpp
|
||||
check-return.cpp
|
||||
check-stop.cpp
|
||||
compute-offsets.cpp
|
||||
expression.cpp
|
||||
mod-file.cpp
|
||||
pointer-assignment.cpp
|
||||
|
||||
172
flang/lib/Semantics/compute-offsets.cpp
Normal file
172
flang/lib/Semantics/compute-offsets.cpp
Normal file
@@ -0,0 +1,172 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "compute-offsets.h"
|
||||
#include "../../runtime/descriptor.h"
|
||||
#include "flang/Evaluate/fold.h"
|
||||
#include "flang/Evaluate/shape.h"
|
||||
#include "flang/Evaluate/type.h"
|
||||
#include "flang/Semantics/scope.h"
|
||||
#include "flang/Semantics/semantics.h"
|
||||
#include "flang/Semantics/symbol.h"
|
||||
#include "flang/Semantics/tools.h"
|
||||
#include "flang/Semantics/type.h"
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
|
||||
namespace Fortran::semantics {
|
||||
|
||||
class ComputeOffsetsHelper {
|
||||
public:
|
||||
// TODO: configure based on target
|
||||
static constexpr int descriptorSize{3 * 8};
|
||||
static constexpr int maxAlignment{8};
|
||||
|
||||
ComputeOffsetsHelper(SemanticsContext &context) : context_{context} {}
|
||||
void Compute() { Compute(context_.globalScope()); }
|
||||
|
||||
private:
|
||||
struct SizeAndAlign {
|
||||
SizeAndAlign() {}
|
||||
SizeAndAlign(std::size_t size) : size{size}, align{size} {}
|
||||
SizeAndAlign(std::size_t size, std::size_t align)
|
||||
: size{size}, align{align} {}
|
||||
std::size_t size{0};
|
||||
std::size_t align{0};
|
||||
};
|
||||
|
||||
void Compute(Scope &);
|
||||
void DoScope(Scope &);
|
||||
void DoSymbol(Symbol &);
|
||||
SizeAndAlign GetSizeAndAlign(const Symbol &);
|
||||
std::size_t CountElements(const Symbol &);
|
||||
static std::size_t Align(std::size_t, std::size_t);
|
||||
static SizeAndAlign GetIntrinsicSizeAndAlign(TypeCategory, int);
|
||||
|
||||
SemanticsContext &context_;
|
||||
evaluate::FoldingContext &foldingContext_{context_.foldingContext()};
|
||||
std::size_t offset_{0};
|
||||
std::size_t align_{0};
|
||||
};
|
||||
|
||||
void ComputeOffsetsHelper::Compute(Scope &scope) {
|
||||
for (Scope &child : scope.children()) {
|
||||
Compute(child);
|
||||
}
|
||||
DoScope(scope);
|
||||
}
|
||||
|
||||
void ComputeOffsetsHelper::DoScope(Scope &scope) {
|
||||
if (scope.symbol() && scope.IsParameterizedDerivedType()) {
|
||||
return; // only process instantiations of parameterized derived types
|
||||
}
|
||||
offset_ = 0;
|
||||
align_ = 0;
|
||||
for (auto symbol : scope.GetSymbols()) {
|
||||
if (!symbol->has<TypeParamDetails>() && !symbol->has<SubprogramDetails>()) {
|
||||
DoSymbol(*symbol);
|
||||
}
|
||||
}
|
||||
scope.set_size(offset_);
|
||||
scope.set_align(align_);
|
||||
}
|
||||
|
||||
void ComputeOffsetsHelper::DoSymbol(Symbol &symbol) {
|
||||
SizeAndAlign s{GetSizeAndAlign(symbol)};
|
||||
if (s.size == 0) {
|
||||
return;
|
||||
}
|
||||
offset_ = Align(offset_, s.align);
|
||||
symbol.set_size(s.size);
|
||||
symbol.set_offset(offset_);
|
||||
offset_ += s.size;
|
||||
if (s.align > align_) {
|
||||
align_ = s.align;
|
||||
}
|
||||
}
|
||||
|
||||
auto ComputeOffsetsHelper::GetSizeAndAlign(const Symbol &symbol)
|
||||
-> SizeAndAlign {
|
||||
const DeclTypeSpec *type{symbol.GetType()};
|
||||
if (!type) {
|
||||
return {};
|
||||
}
|
||||
if (IsDescriptor(symbol) || IsProcedure(symbol)) {
|
||||
int lenParams{0};
|
||||
if (const DerivedTypeSpec * derived{type->AsDerived()}) {
|
||||
lenParams = derived->NumLengthParameters();
|
||||
}
|
||||
std::size_t size{
|
||||
runtime::Descriptor::SizeInBytes(symbol.Rank(), false, lenParams)};
|
||||
return {size, maxAlignment};
|
||||
}
|
||||
SizeAndAlign result;
|
||||
if (const IntrinsicTypeSpec * intrinsic{type->AsIntrinsic()}) {
|
||||
if (auto kind{ToInt64(intrinsic->kind())}) {
|
||||
result = GetIntrinsicSizeAndAlign(intrinsic->category(), *kind);
|
||||
}
|
||||
if (type->category() == DeclTypeSpec::Character) {
|
||||
ParamValue length{type->characterTypeSpec().length()};
|
||||
CHECK(length.isExplicit()); // else should be descriptor
|
||||
if (MaybeIntExpr lengthExpr{length.GetExplicit()}) {
|
||||
if (auto lengthInt{ToInt64(*lengthExpr)}) {
|
||||
result.size *= *lengthInt;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (const DerivedTypeSpec * derived{type->AsDerived()}) {
|
||||
if (derived->scope()) {
|
||||
result.size = derived->scope()->size();
|
||||
result.align = derived->scope()->align();
|
||||
}
|
||||
} else {
|
||||
DIE("not intrinsic or derived");
|
||||
}
|
||||
std::size_t elements{CountElements(symbol)};
|
||||
if (elements > 1) {
|
||||
result.size = Align(result.size, result.align);
|
||||
}
|
||||
result.size *= elements;
|
||||
return result;
|
||||
}
|
||||
|
||||
std::size_t ComputeOffsetsHelper::CountElements(const Symbol &symbol) {
|
||||
if (auto shape{GetShape(foldingContext_, symbol)}) {
|
||||
if (auto sizeExpr{evaluate::GetSize(std::move(*shape))}) {
|
||||
if (auto size{ToInt64(Fold(foldingContext_, std::move(*sizeExpr)))}) {
|
||||
return *size;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Align a size to its natural alignment, up to maxAlignment.
|
||||
std::size_t ComputeOffsetsHelper::Align(std::size_t x, std::size_t alignment) {
|
||||
if (alignment > maxAlignment) {
|
||||
alignment = maxAlignment;
|
||||
}
|
||||
return (x + alignment - 1) & -alignment;
|
||||
}
|
||||
|
||||
auto ComputeOffsetsHelper::GetIntrinsicSizeAndAlign(
|
||||
TypeCategory category, int kind) -> SizeAndAlign {
|
||||
// TODO: does kind==10 need special handling?
|
||||
std::size_t size{kind == 3 ? 2 : static_cast<std::size_t>(kind)};
|
||||
if (category == TypeCategory::Complex) {
|
||||
return {2 * size, size};
|
||||
} else {
|
||||
return {size};
|
||||
}
|
||||
}
|
||||
|
||||
void ComputeOffsets(SemanticsContext &context) {
|
||||
ComputeOffsetsHelper{context}.Compute();
|
||||
}
|
||||
|
||||
} // namespace Fortran::semantics
|
||||
17
flang/lib/Semantics/compute-offsets.h
Normal file
17
flang/lib/Semantics/compute-offsets.h
Normal file
@@ -0,0 +1,17 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef FORTRAN_SEMANTICS_COMPUTE_OFFSETS_H_
|
||||
#define FORTRAN_SEMANTICS_COMPUTE_OFFSETS_H_
|
||||
namespace Fortran::semantics {
|
||||
|
||||
class SemanticsContext;
|
||||
void ComputeOffsets(SemanticsContext &);
|
||||
|
||||
} // namespace Fortran::semantics
|
||||
#endif
|
||||
@@ -414,36 +414,25 @@ void ModFileWriter::PutUseExtraAttr(
|
||||
// Collect the symbols of this scope sorted by their original order, not name.
|
||||
// Namelists are an exception: they are sorted after other symbols.
|
||||
SymbolVector CollectSymbols(const Scope &scope) {
|
||||
SymbolSet symbols; // to prevent duplicates
|
||||
SymbolVector sorted;
|
||||
SymbolVector namelist;
|
||||
SymbolVector common;
|
||||
sorted.reserve(scope.size() + scope.commonBlocks().size());
|
||||
for (const auto &pair : scope) {
|
||||
const Symbol &symbol{*pair.second};
|
||||
if (!symbol.test(Symbol::Flag::ParentComp)) {
|
||||
if (symbols.insert(symbol).second) {
|
||||
if (symbol.has<NamelistDetails>()) {
|
||||
namelist.push_back(symbol);
|
||||
} else {
|
||||
sorted.push_back(symbol);
|
||||
}
|
||||
std::size_t commonSize{scope.commonBlocks().size()};
|
||||
auto symbols{scope.GetSymbols()};
|
||||
sorted.reserve(symbols.size() + commonSize);
|
||||
for (SymbolRef symbol : symbols) {
|
||||
if (!symbol->test(Symbol::Flag::ParentComp)) {
|
||||
if (symbol->has<NamelistDetails>()) {
|
||||
namelist.push_back(symbol);
|
||||
} else {
|
||||
sorted.push_back(symbol);
|
||||
}
|
||||
}
|
||||
}
|
||||
sorted.insert(sorted.end(), namelist.begin(), namelist.end());
|
||||
for (const auto &pair : scope.commonBlocks()) {
|
||||
const Symbol &symbol{*pair.second};
|
||||
if (symbols.insert(symbol).second) {
|
||||
common.push_back(symbol);
|
||||
}
|
||||
sorted.push_back(*pair.second);
|
||||
}
|
||||
// sort normal symbols, then namelists, then common blocks:
|
||||
auto cursor{sorted.begin()};
|
||||
std::sort(cursor, sorted.end());
|
||||
cursor = sorted.insert(sorted.end(), namelist.begin(), namelist.end());
|
||||
std::sort(cursor, sorted.end());
|
||||
cursor = sorted.insert(sorted.end(), common.begin(), common.end());
|
||||
std::sort(cursor, sorted.end());
|
||||
std::sort(sorted.end() - commonSize, sorted.end());
|
||||
return sorted;
|
||||
}
|
||||
|
||||
|
||||
@@ -60,6 +60,25 @@ Scope &Scope::MakeScope(Kind kind, Symbol *symbol) {
|
||||
return children_.emplace_back(*this, kind, symbol);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static std::vector<common::Reference<T>> GetSortedSymbols(
|
||||
std::map<SourceName, common::Reference<Symbol>> symbols) {
|
||||
std::vector<common::Reference<T>> result;
|
||||
result.reserve(symbols.size());
|
||||
for (auto &pair : symbols) {
|
||||
result.push_back(*pair.second);
|
||||
}
|
||||
std::sort(result.begin(), result.end());
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<common::Reference<Symbol>> Scope::GetSymbols() {
|
||||
return GetSortedSymbols<Symbol>(symbols_);
|
||||
}
|
||||
SymbolVector Scope::GetSymbols() const {
|
||||
return GetSortedSymbols<const Symbol>(symbols_);
|
||||
}
|
||||
|
||||
Scope::iterator Scope::find(const SourceName &name) {
|
||||
return symbols_.find(name);
|
||||
}
|
||||
@@ -292,6 +311,9 @@ llvm::raw_ostream &operator<<(llvm::raw_ostream &os, const Scope &scope) {
|
||||
if (auto *symbol{scope.symbol()}) {
|
||||
os << *symbol << ' ';
|
||||
}
|
||||
if (scope.derivedTypeSpec_) {
|
||||
os << "instantiation of " << *scope.derivedTypeSpec_ << ' ';
|
||||
}
|
||||
os << scope.children_.size() << " children\n";
|
||||
for (const auto &pair : scope.symbols_) {
|
||||
const Symbol &symbol{*pair.second};
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#include "check-purity.h"
|
||||
#include "check-return.h"
|
||||
#include "check-stop.h"
|
||||
#include "compute-offsets.h"
|
||||
#include "mod-file.h"
|
||||
#include "resolve-labels.h"
|
||||
#include "resolve-names.h"
|
||||
@@ -161,6 +162,7 @@ static bool PerformStatementSemantics(
|
||||
SemanticsContext &context, parser::Program &program) {
|
||||
ResolveNames(context, program);
|
||||
RewriteParseTree(context, program);
|
||||
ComputeOffsets(context);
|
||||
CheckDeclarations(context);
|
||||
StatementSemanticsPass1{context}.Walk(program);
|
||||
StatementSemanticsPass2{context}.Walk(program);
|
||||
@@ -351,6 +353,12 @@ void DoDumpSymbols(llvm::raw_ostream &os, const Scope &scope, int indent) {
|
||||
if (const auto *symbol{scope.symbol()}) {
|
||||
os << ' ' << symbol->name();
|
||||
}
|
||||
if (scope.size()) {
|
||||
os << " size=" << scope.size() << " align=" << scope.align();
|
||||
}
|
||||
if (scope.derivedTypeSpec()) {
|
||||
os << " instantiation of " << *scope.derivedTypeSpec();
|
||||
}
|
||||
os << '\n';
|
||||
++indent;
|
||||
for (const auto &pair : scope) {
|
||||
|
||||
@@ -482,6 +482,9 @@ llvm::raw_ostream &operator<<(llvm::raw_ostream &os, const Symbol &symbol) {
|
||||
if (!symbol.flags().empty()) {
|
||||
os << " (" << symbol.flags() << ')';
|
||||
}
|
||||
if (symbol.size_) {
|
||||
os << " size=" << symbol.size_ << " offset=" << symbol.offset_;
|
||||
}
|
||||
os << ": " << symbol.details_;
|
||||
return os;
|
||||
}
|
||||
|
||||
@@ -160,6 +160,16 @@ void DerivedTypeSpec::AddParamValue(SourceName name, ParamValue &&value) {
|
||||
CHECK(pair.second); // name was not already present
|
||||
}
|
||||
|
||||
int DerivedTypeSpec::NumLengthParameters() const {
|
||||
int result{0};
|
||||
for (const auto &pair : parameters_) {
|
||||
if (pair.second.isLen()) {
|
||||
++result;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool DerivedTypeSpec::MightBeParameterized() const {
|
||||
return !cooked_ || !parameters_.empty();
|
||||
}
|
||||
|
||||
52
flang/test/Semantics/offsets01.f90
Normal file
52
flang/test/Semantics/offsets01.f90
Normal file
@@ -0,0 +1,52 @@
|
||||
!RUN: %f18 -fdebug-dump-symbols -fparse-only %s | FileCheck %s
|
||||
|
||||
! Size and alignment of intrinsic types
|
||||
subroutine s1
|
||||
integer(1) :: a_i1 !CHECK: a_i1 size=1 offset=0:
|
||||
integer(8) :: b_i8 !CHECK: b_i8 size=8 offset=8:
|
||||
real(2) :: c_r2 !CHECK: c_r2 size=2 offset=16:
|
||||
real(2) :: d_r2 !CHECK: d_r2 size=2 offset=18:
|
||||
real(8) :: e_r8 !CHECK: e_r8 size=8 offset=24:
|
||||
real(4) :: f_r4 !CHECK: f_r4 size=4 offset=32:
|
||||
complex(8) :: g_c8 !CHECK: g_c8 size=16 offset=40:
|
||||
complex(4) :: h_c4 !CHECK: h_c4 size=8 offset=56:
|
||||
logical :: i_l4 !CHECK: i_l4 size=4 offset=64:
|
||||
end
|
||||
|
||||
! Character
|
||||
subroutine s2
|
||||
character(10) :: c1 !CHECK: c1 size=10 offset=0:
|
||||
character(1) :: c2 !CHECK: c2 size=1 offset=10:
|
||||
character(10,kind=2) :: c3 !CHECK: c3 size=20 offset=12:
|
||||
end
|
||||
|
||||
! Descriptors
|
||||
subroutine s3(n)
|
||||
integer :: n
|
||||
real, pointer :: x !CHECK: x, POINTER size=24 offset=8:
|
||||
character(n) :: y !CHECK: y size=24 offset=32:
|
||||
end
|
||||
|
||||
! Descriptors for arrays
|
||||
subroutine s4
|
||||
integer, allocatable :: z0 !CHECK: z0, ALLOCATABLE size=24 offset=
|
||||
integer, allocatable :: z1(:) !CHECK: z1, ALLOCATABLE size=48 offset=
|
||||
integer, allocatable :: z2(:,:) !CHECK: z2, ALLOCATABLE size=72 offset=
|
||||
integer, allocatable :: z3(:,:,:) !CHECK: z3, ALLOCATABLE size=96 offset=
|
||||
end
|
||||
|
||||
! Descriptors with length parameters
|
||||
subroutine s5(n)
|
||||
integer :: n
|
||||
type :: t1(l)
|
||||
integer, len :: l
|
||||
real :: a(l)
|
||||
end type
|
||||
type :: t2(l1, l2)
|
||||
integer, len :: l1
|
||||
integer, len :: l2
|
||||
real :: b(l1, l2)
|
||||
end type
|
||||
type(t1(n)) :: x1 !CHECK: x1 size=48 offset=
|
||||
type(t2(n,n)) :: x2 !CHECK: x2 size=56 offset=
|
||||
end
|
||||
54
flang/test/Semantics/offsets02.f90
Normal file
54
flang/test/Semantics/offsets02.f90
Normal file
@@ -0,0 +1,54 @@
|
||||
!RUN: %f18 -fdebug-dump-symbols -fparse-only %s | FileCheck %s
|
||||
|
||||
! Size and alignment of derived types
|
||||
|
||||
! Array of derived type with 64-bit alignment
|
||||
subroutine s1
|
||||
type t1
|
||||
real(8) :: a
|
||||
real(4) :: b
|
||||
end type
|
||||
!CHECK: x1 size=12 offset=0:
|
||||
!CHECK: y1 size=12 offset=16:
|
||||
type(t1) :: x1, y1
|
||||
!CHECK: z1 size=160 offset=32:
|
||||
type(t1) :: z1(10)
|
||||
end
|
||||
|
||||
! Like t1 but t2 does not need to be aligned on 64-bit boundary
|
||||
subroutine s2
|
||||
type t2
|
||||
real(4) :: a
|
||||
real(4) :: b
|
||||
real(4) :: c
|
||||
end type
|
||||
!CHECK: x2 size=12 offset=0:
|
||||
!CHECK: y2 size=12 offset=12:
|
||||
type(t2) :: x2, y2
|
||||
!CHECK: z2 size=120 offset=24:
|
||||
type(t2) :: z2(10)
|
||||
end
|
||||
|
||||
! Parameterized derived types
|
||||
subroutine s3
|
||||
type :: t(k, l)
|
||||
integer, kind :: k
|
||||
integer, len :: l
|
||||
real(k) :: a3
|
||||
integer(kind=k) :: b3
|
||||
character(kind=k, len=8) :: c3
|
||||
character(kind=k, len=l) :: d3
|
||||
end type
|
||||
!CHECK: DerivedType scope: size=48 align=8 instantiation of t(k=2_4,l=10_4)
|
||||
!CHECK: a3 size=2 offset=0:
|
||||
!CHECK: b3 size=2 offset=2:
|
||||
!CHECK: c3 size=16 offset=4:
|
||||
!CHECK: d3 size=24 offset=24:
|
||||
type(t(2, 10)) :: x3
|
||||
!CHECK: DerivedType scope: size=64 align=8 instantiation of t(k=4_4,l=20_4)
|
||||
!CHECK: a3 size=4 offset=0:
|
||||
!CHECK: b3 size=4 offset=4:
|
||||
!CHECK: c3 size=32 offset=8:
|
||||
!CHECK: d3 size=24 offset=40:
|
||||
type(t(4, 20)) :: x4
|
||||
end
|
||||
Reference in New Issue
Block a user