Skip to content

Commit bef2fde

Browse files
SC llvm teamSC llvm team
SC llvm team
authored and
SC llvm team
committed
Merged main:b797a6aede3d into amd-gfx:a757a754c785
Local branch amd-gfx a757a75 Merged main:b0e28eb83271 into amd-gfx:41686a6f1391 Remote branch main b797a6a [flang] Lower special bind(c) cases without binding labels (llvm#65758)
2 parents a757a75 + b797a6a commit bef2fde

File tree

69 files changed

+533
-406
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

69 files changed

+533
-406
lines changed

clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h

+7
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,13 @@ class Environment {
260260
/// if `D` isn't assigned a storage location in the environment.
261261
StorageLocation *getStorageLocation(const ValueDecl &D) const;
262262

263+
/// Removes the location assigned to `D` in the environment.
264+
///
265+
/// Requirements:
266+
///
267+
/// `D` must have a storage location assigned in the environment.
268+
void removeDecl(const ValueDecl &D);
269+
263270
/// Assigns `Loc` as the storage location of the glvalue `E` in the
264271
/// environment.
265272
///

clang/lib/Analysis/FlowSensitive/ControlFlowContext.cpp

+1
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ ControlFlowContext::build(const Decl &D, Stmt &S, ASTContext &C) {
9797
Options.AddTemporaryDtors = true;
9898
Options.AddInitializers = true;
9999
Options.AddCXXDefaultInitExprInCtors = true;
100+
Options.AddLifetime = true;
100101

101102
// Ensure that all sub-expressions in basic blocks are evaluated.
102103
Options.setAllAlwaysAdd();

clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp

+20-4
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,20 @@ namespace dataflow {
3535
static constexpr int MaxCompositeValueDepth = 3;
3636
static constexpr int MaxCompositeValueSize = 1000;
3737

38+
/// Returns whether all declarations that `DeclToLoc1` and `DeclToLoc2` have in
39+
/// common map to the same storage location in both maps.
40+
bool declToLocConsistent(
41+
const llvm::DenseMap<const ValueDecl *, StorageLocation *> &DeclToLoc1,
42+
const llvm::DenseMap<const ValueDecl *, StorageLocation *> &DeclToLoc2) {
43+
for (auto &Entry : DeclToLoc1) {
44+
auto It = DeclToLoc2.find(Entry.first);
45+
if (It != DeclToLoc2.end() && Entry.second != It->second)
46+
return false;
47+
}
48+
49+
return true;
50+
}
51+
3852
/// Returns a map consisting of key-value entries that are present in both maps.
3953
template <typename K, typename V>
4054
llvm::DenseMap<K, V> intersectDenseMaps(const llvm::DenseMap<K, V> &Map1,
@@ -636,10 +650,7 @@ Environment Environment::join(const Environment &EnvA, const Environment &EnvB,
636650
else
637651
JoinedEnv.ReturnLoc = nullptr;
638652

639-
// FIXME: Once we're able to remove declarations from `DeclToLoc` when their
640-
// lifetime ends, add an assertion that there aren't any entries in
641-
// `DeclToLoc` and `Other.DeclToLoc` that map the same declaration to
642-
// different storage locations.
653+
assert(declToLocConsistent(EnvA.DeclToLoc, EnvB.DeclToLoc));
643654
JoinedEnv.DeclToLoc = intersectDenseMaps(EnvA.DeclToLoc, EnvB.DeclToLoc);
644655

645656
JoinedEnv.ExprToLoc = intersectDenseMaps(EnvA.ExprToLoc, EnvB.ExprToLoc);
@@ -691,6 +702,11 @@ StorageLocation *Environment::getStorageLocation(const ValueDecl &D) const {
691702
return Loc;
692703
}
693704

705+
void Environment::removeDecl(const ValueDecl &D) {
706+
assert(DeclToLoc.contains(&D));
707+
DeclToLoc.erase(&D);
708+
}
709+
694710
void Environment::setStorageLocation(const Expr &E, StorageLocation &Loc) {
695711
// `DeclRefExpr`s to builtin function types aren't glvalues, for some reason,
696712
// but we still want to be able to associate a `StorageLocation` with them,

clang/lib/Analysis/FlowSensitive/Transfer.cpp

+16-12
Original file line numberDiff line numberDiff line change
@@ -696,19 +696,23 @@ class TransferVisitor : public ConstStmtVisitor<TransferVisitor> {
696696
FieldLocs.insert({Field, &Loc});
697697
}
698698

699-
LLVM_DEBUG({
700-
// Check that we satisfy the invariant that a `RecordStorageLoation`
701-
// contains exactly the set of modeled fields for that type.
702-
// `ModeledFields` includes fields from all the bases, but only the
703-
// modeled ones. However, if a class type is initialized with an
704-
// `InitListExpr`, all fields in the class, including those from base
705-
// classes, are included in the set of modeled fields. The code above
706-
// should therefore populate exactly the modeled fields.
707-
auto ModeledFields = Env.getDataflowAnalysisContext().getModeledFields(Type);
708-
assert(ModeledFields.size() == FieldLocs.size());
699+
// Check that we satisfy the invariant that a `RecordStorageLoation`
700+
// contains exactly the set of modeled fields for that type.
701+
// `ModeledFields` includes fields from all the bases, but only the
702+
// modeled ones. However, if a class type is initialized with an
703+
// `InitListExpr`, all fields in the class, including those from base
704+
// classes, are included in the set of modeled fields. The code above
705+
// should therefore populate exactly the modeled fields.
706+
assert([&]() {
707+
auto ModeledFields =
708+
Env.getDataflowAnalysisContext().getModeledFields(Type);
709+
if (ModeledFields.size() != FieldLocs.size())
710+
return false;
709711
for ([[maybe_unused]] auto [Field, Loc] : FieldLocs)
710-
assert(ModeledFields.contains(cast_or_null<FieldDecl>(Field)));
711-
});
712+
if (!ModeledFields.contains(cast_or_null<FieldDecl>(Field)))
713+
return false;
714+
return true;
715+
}());
712716

713717
auto &Loc =
714718
Env.getDataflowAnalysisContext().arena().create<RecordStorageLocation>(

clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp

+10-12
Original file line numberDiff line numberDiff line change
@@ -439,19 +439,17 @@ static void builtinTransfer(const CFGElement &Elt,
439439
case CFGElement::Initializer:
440440
builtinTransferInitializer(Elt.castAs<CFGInitializer>(), State);
441441
break;
442+
case CFGElement::LifetimeEnds:
443+
// Removing declarations when their lifetime ends serves two purposes:
444+
// - Eliminate unnecessary clutter from `Environment::DeclToLoc`
445+
// - Allow us to assert that, when joining two `Environment`s, the two
446+
// `DeclToLoc` maps never contain entries that map the same declaration to
447+
// different storage locations.
448+
if (const ValueDecl *VD = Elt.castAs<CFGLifetimeEnds>().getVarDecl())
449+
State.Env.removeDecl(*VD);
450+
break;
442451
default:
443-
// FIXME: Evaluate other kinds of `CFGElement`, including:
444-
// - When encountering `CFGLifetimeEnds`, remove the declaration from
445-
// `Environment::DeclToLoc`. This would serve two purposes:
446-
// a) Eliminate unnecessary clutter from `Environment::DeclToLoc`
447-
// b) Allow us to implement an assertion that, when joining two
448-
// `Environments`, the two `DeclToLoc` maps never contain entries that
449-
// map the same declaration to different storage locations.
450-
// Unfortunately, however, we can't currently process `CFGLifetimeEnds`
451-
// because the corresponding CFG option `AddLifetime` is incompatible with
452-
// the option 'AddImplicitDtors`, which we already use. We will first
453-
// need to modify the CFG implementation to make these two options
454-
// compatible before we can process `CFGLifetimeEnds`.
452+
// FIXME: Evaluate other kinds of `CFGElement`
455453
break;
456454
}
457455
}

clang/unittests/Analysis/FlowSensitive/TransferTest.cpp

+1-4
Original file line numberDiff line numberDiff line change
@@ -2957,10 +2957,7 @@ TEST(TransferTest, VarDeclInDoWhile) {
29572957
const auto *BarVal = cast<IntegerValue>(EnvInLoop.getValue(*BarDecl));
29582958
EXPECT_EQ(BarVal, FooPointeeVal);
29592959

2960-
// FIXME: This assertion documents current behavior, but we would prefer
2961-
// declarations to be removed from the environment when their lifetime
2962-
// ends. Once this is the case, change this assertion accordingly.
2963-
ASSERT_THAT(EnvAfterLoop.getValue(*BarDecl), BarVal);
2960+
ASSERT_THAT(EnvAfterLoop.getValue(*BarDecl), IsNull());
29642961
});
29652962
}
29662963

clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp

+2-1
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,8 @@ TEST(DataflowAnalysisTest, DiagnoseFunctionDiagnoserCalledOnEachElement) {
108108
// `diagnoseFunction` provides no guarantees about the order in which elements
109109
// are visited, so we use `UnorderedElementsAre`.
110110
EXPECT_THAT_EXPECTED(Result, llvm::HasValue(UnorderedElementsAre(
111-
"0\n", "int x = 0;\n", "x\n", "++x\n")));
111+
"0\n", "int x = 0;\n", "x\n", "++x\n",
112+
" (Lifetime ends)\n")));
112113
}
113114

114115
struct NonConvergingLattice {

flang/lib/Lower/CallInterface.cpp

+4-18
Original file line numberDiff line numberDiff line change
@@ -23,22 +23,6 @@
2323
#include "flang/Semantics/tools.h"
2424
#include <optional>
2525

26-
//===----------------------------------------------------------------------===//
27-
// BIND(C) mangling helpers
28-
//===----------------------------------------------------------------------===//
29-
30-
// Return the binding label (from BIND(C...)) or the mangled name of a symbol.
31-
static std::string getMangledName(Fortran::lower::AbstractConverter &converter,
32-
const Fortran::semantics::Symbol &symbol) {
33-
const std::string *bindName = symbol.GetBindName();
34-
// TODO: update GetBindName so that it does not return a label for internal
35-
// procedures.
36-
if (bindName && Fortran::semantics::ClassifyProcedure(symbol) ==
37-
Fortran::semantics::ProcedureDefinitionClass::Internal)
38-
TODO(converter.getCurrentLocation(), "BIND(C) internal procedures");
39-
return bindName ? *bindName : converter.mangleName(symbol);
40-
}
41-
4226
mlir::Type Fortran::lower::getUntypedBoxProcType(mlir::MLIRContext *context) {
4327
llvm::SmallVector<mlir::Type> resultTys;
4428
llvm::SmallVector<mlir::Type> inputTys;
@@ -72,8 +56,10 @@ bool Fortran::lower::CallerInterface::hasAlternateReturns() const {
7256

7357
std::string Fortran::lower::CallerInterface::getMangledName() const {
7458
const Fortran::evaluate::ProcedureDesignator &proc = procRef.proc();
59+
// Return the binding label (from BIND(C...)) or the mangled name of the
60+
// symbol.
7561
if (const Fortran::semantics::Symbol *symbol = proc.GetSymbol())
76-
return ::getMangledName(converter, symbol->GetUltimate());
62+
return converter.mangleName(symbol->GetUltimate());
7763
assert(proc.GetSpecificIntrinsic() &&
7864
"expected intrinsic procedure in designator");
7965
return proc.GetName();
@@ -420,7 +406,7 @@ bool Fortran::lower::CalleeInterface::hasAlternateReturns() const {
420406
std::string Fortran::lower::CalleeInterface::getMangledName() const {
421407
if (funit.isMainProgram())
422408
return fir::NameUniquer::doProgramEntry().str();
423-
return ::getMangledName(converter, funit.getSubprogramSymbol());
409+
return converter.mangleName(funit.getSubprogramSymbol());
424410
}
425411

426412
const Fortran::semantics::Symbol *

flang/lib/Lower/Mangler.cpp

-8
Original file line numberDiff line numberDiff line change
@@ -96,14 +96,6 @@ std::string Fortran::lower::mangle::mangleName(
9696
if (auto *overrideName = ultimateSymbol.GetBindName())
9797
return *overrideName;
9898

99-
// TODO: A procedure that inherits BIND(C) through another interface
100-
// (procedure(iface)) should be dealt with in GetBindName() or some wrapper.
101-
if (!Fortran::semantics::IsPointer(ultimateSymbol) &&
102-
Fortran::semantics::IsBindCProcedure(ultimateSymbol) &&
103-
Fortran::semantics::ClassifyProcedure(symbol) !=
104-
Fortran::semantics::ProcedureDefinitionClass::Internal)
105-
return ultimateSymbol.name().ToString();
106-
10799
llvm::StringRef symbolName = toStringRef(ultimateSymbol.name());
108100
llvm::SmallVector<llvm::StringRef> modules;
109101
llvm::SmallVector<llvm::StringRef> procs;

flang/lib/Semantics/resolve-names.cpp

+13-1
Original file line numberDiff line numberDiff line change
@@ -1739,9 +1739,11 @@ bool AttrsVisitor::SetPassNameOn(Symbol &symbol) {
17391739
}
17401740

17411741
void AttrsVisitor::SetBindNameOn(Symbol &symbol) {
1742-
if (!attrs_ || !attrs_->test(Attr::BIND_C)) {
1742+
if ((!attrs_ || !attrs_->test(Attr::BIND_C)) &&
1743+
!symbol.attrs().test(Attr::BIND_C)) {
17431744
return;
17441745
}
1746+
17451747
std::optional<std::string> label{
17461748
evaluate::GetScalarConstantValue<evaluate::Ascii>(bindName_)};
17471749
// 18.9.2(2): discard leading and trailing blanks
@@ -1754,6 +1756,9 @@ void AttrsVisitor::SetBindNameOn(Symbol &symbol) {
17541756
}
17551757
auto last{label->find_last_not_of(" ")};
17561758
label = label->substr(first, last - first + 1);
1759+
} else if (ClassifyProcedure(symbol) == ProcedureDefinitionClass::Internal) {
1760+
// BIND(C) does not give an implicit binding label to internal procedures.
1761+
return;
17571762
} else {
17581763
label = symbol.name().ToString();
17591764
}
@@ -4834,6 +4839,13 @@ Symbol &DeclarationVisitor::DeclareProcEntity(
48344839
} else if (interface->test(Symbol::Flag::Subroutine)) {
48354840
symbol.set(Symbol::Flag::Subroutine);
48364841
}
4842+
if (IsBindCProcedure(*interface) && !IsPointer(symbol) &&
4843+
!IsDummy(symbol)) {
4844+
// Inherit BIND_C attribute from the interface, but not the NAME="..."
4845+
// if any. This is not clearly described in the standard, but matches
4846+
// the behavior of other compilers.
4847+
SetImplicitAttr(symbol, Attr::BIND_C);
4848+
}
48374849
} else if (auto *type{GetDeclTypeSpec()}) {
48384850
SetType(name, *type);
48394851
symbol.set(Symbol::Flag::Function);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
! Test mangling with BIND(C) inherited from procedure interface.
2+
! RUN: bbc -emit-hlfir -o - %s | FileCheck %s
3+
4+
subroutine test()
5+
interface
6+
subroutine iface_notbindc()
7+
end subroutine
8+
subroutine iface_bindc() bind(c)
9+
end subroutine
10+
subroutine iface_explicit_name() bind(c, name="explicit_name")
11+
end subroutine
12+
subroutine iface_nobinding() bind(c, name="")
13+
end subroutine
14+
end interface
15+
16+
procedure(iface_bindc) :: foo_iface_bindc
17+
procedure(iface_explicit_name) :: foo_iface_explicit_name
18+
procedure(iface_nobinding) :: foo_iface_nobinding
19+
20+
procedure(iface_bindc), bind(c) :: extra_bindc_iface_bindc
21+
procedure(iface_explicit_name), bind(c) :: extra_bindc_iface_explicit_name
22+
procedure(iface_nobinding), bind(c) :: extra_bindc_iface_nobinding
23+
24+
procedure(iface_bindc), bind(c, name="bar_iface_bindc_2") :: bar_iface_bindc
25+
procedure(iface_explicit_name), bind(c,name="bar_iface_explicit_name_2") :: bar_iface_explicit_name
26+
procedure(iface_nobinding), bind(c, name="bar_iface_nobinding_2") :: bar_iface_nobinding
27+
28+
procedure(iface_bindc), bind(c, name="") :: nobinding_iface_bindc
29+
procedure(iface_explicit_name), bind(c, name="") :: nobinding_iface_explicit_name
30+
procedure(iface_nobinding), bind(c, name="") :: nobinding_iface_nobinding
31+
32+
call iface_notbindc()
33+
call iface_bindc()
34+
call iface_explicit_name()
35+
call iface_nobinding()
36+
37+
call foo_iface_bindc()
38+
call foo_iface_explicit_name()
39+
call foo_iface_nobinding()
40+
41+
call extra_bindc_iface_bindc()
42+
call extra_bindc_iface_explicit_name()
43+
call extra_bindc_iface_nobinding()
44+
45+
call bar_iface_bindc()
46+
call bar_iface_explicit_name()
47+
call bar_iface_nobinding()
48+
49+
call nobinding_iface_bindc()
50+
call nobinding_iface_explicit_name()
51+
call nobinding_iface_nobinding()
52+
53+
! CHECK: fir.call @_QPiface_notbindc()
54+
! CHECK: fir.call @iface_bindc()
55+
! CHECK: fir.call @explicit_name()
56+
! CHECK: fir.call @_QPiface_nobinding()
57+
! CHECK: fir.call @foo_iface_bindc()
58+
! CHECK: fir.call @foo_iface_explicit_name()
59+
! CHECK: fir.call @foo_iface_nobinding()
60+
! CHECK: fir.call @extra_bindc_iface_bindc()
61+
! CHECK: fir.call @extra_bindc_iface_explicit_name()
62+
! CHECK: fir.call @extra_bindc_iface_nobinding()
63+
! CHECK: fir.call @bar_iface_bindc_2()
64+
! CHECK: fir.call @bar_iface_explicit_name_2()
65+
! CHECK: fir.call @bar_iface_nobinding_2()
66+
! CHECK: fir.call @_QPnobinding_iface_bindc()
67+
! CHECK: fir.call @_QPnobinding_iface_explicit_name()
68+
! CHECK: fir.call @_QPnobinding_iface_nobinding()
69+
end subroutine
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
! Test that lowering makes a difference between NAME="" and no NAME
2+
! in BIND(C). See Fortran 2018 standard 18.10.2 point 2.
3+
! BIND(C, NAME="") implies there is no binding label, meaning that
4+
! the Fortran mangled name has to be used.
5+
! RUN: bbc -emit-hlfir %s -o - | FileCheck %s
6+
7+
!CHECK: func.func @_QPfoo(%{{.*}}: !fir.ref<i16>
8+
subroutine foo(x) bind(c, name="")
9+
integer(2) :: x
10+
end subroutine
11+
12+
!CHECK: func.func @bar(%{{.*}}: !fir.ref<i32>
13+
subroutine foo(x) bind(c, name="bar")
14+
integer(4) :: x
15+
end subroutine
16+
17+
!CHECK: func.func @_QMinamodule1Pfoo(%{{.*}}: !fir.ref<i64>
18+
module inamodule1
19+
contains
20+
subroutine foo(x) bind(c, name="")
21+
integer(8) :: x
22+
end subroutine
23+
end module
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
! Test that internal procedure with BIND(C) do not have binding labels,
2+
! that is, that they are generated using usual flang mangling for non BIND(C)
3+
! internal procedures.
4+
! RUN: bbc -emit-hlfir %s -o - | FileCheck %s
5+
6+
!CHECK: func.func @_QFsub1Pfoo(%{{.*}}: i32
7+
subroutine sub1()
8+
call foo(42)
9+
contains
10+
subroutine foo(i) bind(c)
11+
integer, value :: i
12+
print *, i
13+
end subroutine
14+
end subroutine
15+
16+
!CHECK: func.func @_QFsub2Pfoo(%{{.*}}: i64
17+
subroutine sub2()
18+
call foo(42_8)
19+
contains
20+
subroutine foo(i) bind(c)
21+
integer(8), value :: i
22+
print *, i
23+
end subroutine
24+
end subroutine

llvm/include/llvm/Config/llvm-config.h.cmake

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616

1717
/* Indicate that this is LLVM compiled from the amd-gfx branch. */
1818
#define LLVM_HAVE_BRANCH_AMD_GFX
19-
#define LLVM_MAIN_REVISION 475914
19+
#define LLVM_MAIN_REVISION 475920
2020

2121
/* Define if LLVM_ENABLE_DUMP is enabled */
2222
#cmakedefine LLVM_ENABLE_DUMP

0 commit comments

Comments
 (0)