Skip to content

Commit 18364e9

Browse files
committed
Ensure temporaries are passed in calls for parenthesized arguments
foo(x) and foo((x)) are semantically different. In the former, the dummy argument is associated with x storage, while in the latter, the dummy argument is associated with a temp initialized with x value. This is an important distinction in calls like foo(x, (x)) where passing x storage twice would be wrong, also to not propagate back potential changes on the dummy. Parentheses are not lowered to temps in lowering because we do not want to create temp array sub-expression. Only a fir.no_reassoc is created for parentheses by expression lowering. This change keep this general behavior and add special handling in call lowering to ensure a temp is created for parenthesized arguments.
1 parent 0b3fa79 commit 18364e9

File tree

2 files changed

+292
-4
lines changed

2 files changed

+292
-4
lines changed

flang/lib/Lower/ConvertExpr.cpp

Lines changed: 79 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1977,6 +1977,73 @@ class ScalarExprLowering {
19771977
return call.getResult(0);
19781978
}
19791979

1980+
/// Is this a variable wrapped in parentheses ?
1981+
template <typename A>
1982+
bool isParenthesizedVariable(const A &) {
1983+
return false;
1984+
}
1985+
template <typename T>
1986+
bool isParenthesizedVariable(const Fortran::evaluate::Expr<T> &expr) {
1987+
using ExprVariant = decltype(Fortran::evaluate::Expr<T>::u);
1988+
using Parentheses = Fortran::evaluate::Parentheses<T>;
1989+
if constexpr (Fortran::common::HasMember<Parentheses, ExprVariant>) {
1990+
if (const auto *parentheses = std::get_if<Parentheses>(&expr.u))
1991+
return Fortran::evaluate::IsVariable(parentheses->left());
1992+
return false;
1993+
} else {
1994+
return std::visit(
1995+
[&](const auto &x) { return isParenthesizedVariable(x); }, expr.u);
1996+
}
1997+
}
1998+
1999+
/// Like genExtAddr, but ensure the address returned is a temporary even if \p
2000+
/// expr is variable inside parentheses.
2001+
ExtValue genTempExtAddr(const Fortran::lower::SomeExpr &expr) {
2002+
// In general, genExtAddr might nor create a temp for variable inside
2003+
// parentheses to avoid creating array temporary in sub-expressions. It only
2004+
// ensure the sub-expression is not re-associated with other part of the
2005+
// expression. In the call semantic, there is a difference between expr and
2006+
// variable (see R1524). For expressions, a variable storage must not be
2007+
// argument associated since it could be modified inside the call, or the
2008+
// variable could also be modified by other means during the call.
2009+
if (!isParenthesizedVariable(expr))
2010+
return genExtAddr(expr);
2011+
if (expr.Rank() > 0)
2012+
return asArray(expr);
2013+
auto loc = getLoc();
2014+
return genExtValue(expr).match(
2015+
[&](const fir::CharBoxValue &boxChar) -> ExtValue {
2016+
return fir::factory::CharacterExprHelper{builder, loc}.createTempFrom(
2017+
boxChar);
2018+
},
2019+
[&](const fir::UnboxedValue &v) -> ExtValue {
2020+
auto type = v.getType();
2021+
mlir::Value value = v;
2022+
if (fir::isa_ref_type(type))
2023+
value = builder.create<fir::LoadOp>(loc, value);
2024+
auto temp = builder.createTemporary(loc, value.getType());
2025+
builder.create<fir::StoreOp>(loc, value, temp);
2026+
return temp;
2027+
},
2028+
[&](const fir::BoxValue &x) -> ExtValue {
2029+
// Derived type scalar that may be polymorphic.
2030+
assert(!x.hasRank() && x.isDerived());
2031+
if (x.isDerivedWithLengthParameters())
2032+
fir::emitFatalError(
2033+
loc, "making temps for derived type with length parameters");
2034+
// TODO: polymorphic aspects should be kept but for now the temp
2035+
// created always has the declared type.
2036+
auto var = fir::getBase(fir::factory::readBoxValue(builder, loc, x));
2037+
auto value = builder.create<fir::LoadOp>(loc, var);
2038+
auto temp = builder.createTemporary(loc, value.getType());
2039+
builder.create<fir::StoreOp>(loc, value, temp);
2040+
return temp;
2041+
},
2042+
[&](const auto &) -> ExtValue {
2043+
fir::emitFatalError(loc, "expr is not a scalar value");
2044+
});
2045+
}
2046+
19802047
/// Lower a non-elemental procedure reference.
19812048
ExtValue genRawProcedureRef(const Fortran::evaluate::ProcedureRef &procRef,
19822049
llvm::Optional<mlir::Type> resultType) {
@@ -2049,11 +2116,11 @@ class ScalarExprLowering {
20492116
mutableModifiedByCall.emplace_back(std::move(mutableBox));
20502117
continue;
20512118
}
2052-
2119+
const bool actualArgIsVariable = Fortran::evaluate::IsVariable(*expr);
20532120
if (arg.passBy == PassBy::BaseAddress || arg.passBy == PassBy::BoxChar) {
20542121
auto argAddr = [&]() -> ExtValue {
20552122
ExtValue baseAddr;
2056-
if (Fortran::evaluate::IsVariable(*expr) && expr->Rank() > 0) {
2123+
if (actualArgIsVariable && expr->Rank() > 0) {
20572124
auto box = genBoxArg(*expr);
20582125
if (!Fortran::evaluate::IsSimplyContiguous(
20592126
*expr, converter.getFoldingContext())) {
@@ -2070,8 +2137,12 @@ class ScalarExprLowering {
20702137
// Contiguous: just use the box we created above!
20712138
// This gets "unboxed" below, if needed.
20722139
baseAddr = box;
2073-
} else
2140+
} else if (actualArgIsVariable) {
20742141
baseAddr = genExtAddr(*expr);
2142+
} else {
2143+
// Make sure a variable address is not passed.
2144+
baseAddr = genTempExtAddr(*expr);
2145+
}
20752146

20762147
// Scalar and contiguous expressions may be lowered to a fir.box,
20772148
// either to account for potential polymorphism, or because lowering
@@ -2126,7 +2197,11 @@ class ScalarExprLowering {
21262197
caller.placeInput(arg, builder.create<mlir::SelectOp>(
21272198
loc, isAllocated, convertedBox, absent));
21282199
} else {
2129-
auto box = builder.createBox(loc, genBoxArg(*expr));
2200+
// Make sure a variable address is only passed if the expression is
2201+
// actually a variable.
2202+
auto box = actualArgIsVariable
2203+
? builder.createBox(loc, genBoxArg(*expr))
2204+
: builder.createBox(getLoc(), genTempExtAddr(*expr));
21302205
caller.placeInput(arg, box);
21312206
}
21322207
} else if (arg.passBy == PassBy::AddressAndLength) {

0 commit comments

Comments
 (0)