#include "rewrite-parse-tree.h"
#include "flang/Common/indirection.h"
#include "flang/Parser/openmp-utils.h"
#include "flang/Parser/parse-tree-visitor.h"
#include "flang/Parser/parse-tree.h"
#include "flang/Parser/tools.h"
#include "flang/Semantics/openmp-directive-sets.h"
#include "flang/Semantics/scope.h"
#include "flang/Semantics/semantics.h"
#include "flang/Semantics/symbol.h"
#include "flang/Semantics/tools.h"
#include <list>
namespace Fortran::semantics {
using namespace parser::literals;
class RewriteMutator {
public:
RewriteMutator(SemanticsContext &context)
: context_{context}, errorOnUnresolvedName_{!context.AnyFatalError()},
messages_{context.messages()} {}
template <typename T> bool Pre(T &) { return true; }
template <typename T> void Post(T &) {}
void Post(parser::Name &);
bool Pre(parser::MainProgram &);
bool Pre(parser::Module &);
bool Pre(parser::FunctionSubprogram &);
bool Pre(parser::SubroutineSubprogram &);
bool Pre(parser::SeparateModuleSubprogram &);
bool Pre(parser::BlockConstruct &);
bool Pre(parser::Block &);
bool Pre(parser::DoConstruct &);
bool Pre(parser::IfConstruct &);
bool Pre(parser::ActionStmt &);
void Post(parser::MainProgram &);
void Post(parser::FunctionSubprogram &);
void Post(parser::SubroutineSubprogram &);
void Post(parser::SeparateModuleSubprogram &);
void Post(parser::BlockConstruct &);
void Post(parser::Block &);
void Post(parser::DoConstruct &);
void Post(parser::IfConstruct &);
void Post(parser::ReadStmt &);
void Post(parser::WriteStmt &);
bool Pre(parser::EquivalenceStmt &) { return false; }
bool Pre(parser::Keyword &) { return false; }
bool Pre(parser::EntryStmt &) { return false; }
bool Pre(parser::CompilerDirective &) { return false; }
bool Pre(parser::EndBlockDataStmt &) { return false; }
bool Pre(parser::EndFunctionStmt &) { return false; }
bool Pre(parser::EndInterfaceStmt &) { return false; }
bool Pre(parser::EndModuleStmt &) { return false; }
bool Pre(parser::EndMpSubprogramStmt &) { return false; }
bool Pre(parser::EndProgramStmt &) { return false; }
bool Pre(parser::EndSubmoduleStmt &) { return false; }
bool Pre(parser::EndSubroutineStmt &) { return false; }
bool Pre(parser::EndTypeStmt &) { return false; }
bool Pre(parser::OmpBlockConstruct &);
bool Pre(parser::OpenMPLoopConstruct &);
void Post(parser::OmpBlockConstruct &);
void Post(parser::OpenMPLoopConstruct &);
private:
void FixMisparsedStmtFuncs(parser::SpecificationPart &, parser::Block &);
void OpenMPSimdOnly(parser::Block &, bool);
void OpenMPSimdOnly(parser::SpecificationPart &);
SemanticsContext &context_;
bool errorOnUnresolvedName_{true};
parser::Messages &messages_;
};
void RewriteMutator::Post(parser::Name &name) {
if (!name.symbol && errorOnUnresolvedName_) {
messages_.Say(name.source, "Internal: no symbol found for '%s'"_err_en_US,
name.source);
}
}
static bool ReturnsDataPointer(const Symbol &symbol) {
if (const Symbol * funcRes{FindFunctionResult(symbol)}) {
return IsPointer(*funcRes) && !IsProcedure(*funcRes);
} else if (const auto *generic{symbol.detailsIf<GenericDetails>()}) {
for (auto ref : generic->specificProcs()) {
if (ReturnsDataPointer(*ref)) {
return true;
}
}
}
return false;
}
static bool LoopConstructIsSIMD(parser::OpenMPLoopConstruct *ompLoop) {
return llvm::omp::allSimdSet.test(ompLoop->BeginDir().DirName().v);
}
void RewriteMutator::OpenMPSimdOnly(parser::SpecificationPart &specPart) {
auto &list{std::get<std::list<parser::DeclarationConstruct>>(specPart.t)};
for (auto it{list.begin()}; it != list.end();) {
if (auto *specConstr{std::get_if<parser::SpecificationConstruct>(&it->u)}) {
if (auto *ompDecl{std::get_if<
common::Indirection<parser::OpenMPDeclarativeConstruct>>(
&specConstr->u)}) {
if (std::holds_alternative<parser::OpenMPThreadprivate>(
ompDecl->value().u) ||
std::holds_alternative<parser::OpenMPDeclareMapperConstruct>(
ompDecl->value().u)) {
it = list.erase(it);
continue;
}
}
}
++it;
}
}
void RewriteMutator::OpenMPSimdOnly(
parser::Block &block, bool isNonSimdLoopBody = false) {
auto replaceInlineBlock =
[&](std::list<parser::ExecutionPartConstruct> &innerBlock,
auto it) -> auto {
auto insertPos = std::next(it);
block.splice(insertPos, innerBlock);
block.erase(it);
return insertPos;
};
for (auto it{block.begin()}; it != block.end();) {
if (auto *stmt{std::get_if<parser::ExecutableConstruct>(&it->u)}) {
if (auto *omp{std::get_if<common::Indirection<parser::OpenMPConstruct>>(
&stmt->u)}) {
if (auto *ompStandalone{std::get_if<parser::OpenMPStandaloneConstruct>(
&omp->value().u)}) {
if (std::holds_alternative<parser::OpenMPCancelConstruct>(
ompStandalone->u) ||
std::holds_alternative<parser::OpenMPFlushConstruct>(
ompStandalone->u) ||
std::holds_alternative<parser::OpenMPCancellationPointConstruct>(
ompStandalone->u)) {
it = block.erase(it);
continue;
}
if (auto *constr{std::get_if<parser::OpenMPSimpleStandaloneConstruct>(
&ompStandalone->u)}) {
auto directive = constr->v.DirId();
if (llvm::omp::simpleStandaloneNonSimdOnlySet.test(directive) ||
(isNonSimdLoopBody && directive == llvm::omp::OMPD_scan)) {
it = block.erase(it);
continue;
}
}
} else if (auto *ompBlock{std::get_if<parser::OmpBlockConstruct>(
&omp->value().u)}) {
it = replaceInlineBlock(std::get<parser::Block>(ompBlock->t), it);
continue;
} else if (auto *ompLoop{std::get_if<parser::OpenMPLoopConstruct>(
&omp->value().u)}) {
if (LoopConstructIsSIMD(ompLoop)) {
++it;
continue;
}
std::list<parser::ExecutionPartConstruct> doList;
for (auto &construct : std::get<parser::Block>(ompLoop->t)) {
if (auto *doConstruct = const_cast<parser::DoConstruct *>(
parser::omp::GetDoConstruct(construct))) {
auto &loopBody = std::get<parser::Block>(doConstruct->t);
OpenMPSimdOnly(const_cast<parser::Block &>(loopBody),
true);
auto newLoop = parser::ExecutionPartConstruct{
parser::ExecutableConstruct{std::move(*doConstruct)}};
doList.insert(doList.end(), std::move(newLoop));
}
}
if (!doList.empty()) {
it = block.erase(it);
for (auto &newLoop : doList)
block.insert(it, std::move(newLoop));
continue;
}
} else if (auto *ompCon{std::get_if<parser::OpenMPSectionsConstruct>(
&omp->value().u)}) {
auto §ions =
std::get<std::list<parser::OpenMPConstruct>>(ompCon->t);
auto insertPos = std::next(it);
for (auto §ionCon : sections) {
auto §ion =
std::get<parser::OpenMPSectionConstruct>(sectionCon.u);
auto &innerBlock = std::get<parser::Block>(section.t);
block.splice(insertPos, innerBlock);
}
block.erase(it);
it = insertPos;
continue;
} else if (auto *atomic{std::get_if<parser::OpenMPAtomicConstruct>(
&omp->value().u)}) {
it = replaceInlineBlock(std::get<parser::Block>(atomic->t), it);
continue;
} else if (auto *critical{std::get_if<parser::OpenMPCriticalConstruct>(
&omp->value().u)}) {
it = replaceInlineBlock(std::get<parser::Block>(critical->t), it);
continue;
}
}
}
++it;
}
}
void RewriteMutator::FixMisparsedStmtFuncs(
parser::SpecificationPart &specPart, parser::Block &block) {
auto &list{std::get<std::list<parser::DeclarationConstruct>>(specPart.t)};
auto origFirst{block.begin()};
for (auto it{list.begin()}; it != list.end();) {
bool convert{false};
if (auto *stmt{std::get_if<
parser::Statement<common::Indirection<parser::StmtFunctionStmt>>>(
&it->u)}) {
if (const Symbol *
symbol{std::get<parser::Name>(stmt->statement.value().t).symbol}) {
const Symbol &ultimate{symbol->GetUltimate()};
convert =
ultimate.has<ObjectEntityDetails>() || ReturnsDataPointer(ultimate);
if (convert) {
auto newStmt{stmt->statement.value().ConvertToAssignment()};
newStmt.source = stmt->source;
block.insert(origFirst,
parser::ExecutionPartConstruct{
parser::ExecutableConstruct{std::move(newStmt)}});
}
}
}
if (convert) {
it = list.erase(it);
} else {
++it;
}
}
}
bool RewriteMutator::Pre(parser::MainProgram &program) {
FixMisparsedStmtFuncs(std::get<parser::SpecificationPart>(program.t),
std::get<parser::ExecutionPart>(program.t).v);
if (context_.langOptions().OpenMPSimd) {
OpenMPSimdOnly(std::get<parser::ExecutionPart>(program.t).v);
OpenMPSimdOnly(std::get<parser::SpecificationPart>(program.t));
}
return true;
}
void RewriteMutator::Post(parser::MainProgram &program) {
if (context_.langOptions().OpenMPSimd) {
OpenMPSimdOnly(std::get<parser::ExecutionPart>(program.t).v);
}
}
bool RewriteMutator::Pre(parser::Module &module) {
if (context_.langOptions().OpenMPSimd) {
OpenMPSimdOnly(std::get<parser::SpecificationPart>(module.t));
}
return true;
}
bool RewriteMutator::Pre(parser::FunctionSubprogram &func) {
FixMisparsedStmtFuncs(std::get<parser::SpecificationPart>(func.t),
std::get<parser::ExecutionPart>(func.t).v);
if (context_.langOptions().OpenMPSimd) {
OpenMPSimdOnly(std::get<parser::ExecutionPart>(func.t).v);
}
return true;
}
void RewriteMutator::Post(parser::FunctionSubprogram &func) {
if (context_.langOptions().OpenMPSimd) {
OpenMPSimdOnly(std::get<parser::ExecutionPart>(func.t).v);
}
}
bool RewriteMutator::Pre(parser::SubroutineSubprogram &subr) {
FixMisparsedStmtFuncs(std::get<parser::SpecificationPart>(subr.t),
std::get<parser::ExecutionPart>(subr.t).v);
if (context_.langOptions().OpenMPSimd) {
OpenMPSimdOnly(std::get<parser::ExecutionPart>(subr.t).v);
}
return true;
}
void RewriteMutator::Post(parser::SubroutineSubprogram &subr) {
if (context_.langOptions().OpenMPSimd) {
OpenMPSimdOnly(std::get<parser::ExecutionPart>(subr.t).v);
}
}
bool RewriteMutator::Pre(parser::SeparateModuleSubprogram &subp) {
FixMisparsedStmtFuncs(std::get<parser::SpecificationPart>(subp.t),
std::get<parser::ExecutionPart>(subp.t).v);
if (context_.langOptions().OpenMPSimd) {
OpenMPSimdOnly(std::get<parser::ExecutionPart>(subp.t).v);
}
return true;
}
void RewriteMutator::Post(parser::SeparateModuleSubprogram &subp) {
if (context_.langOptions().OpenMPSimd) {
OpenMPSimdOnly(std::get<parser::ExecutionPart>(subp.t).v);
}
}
bool RewriteMutator::Pre(parser::BlockConstruct &block) {
FixMisparsedStmtFuncs(std::get<parser::BlockSpecificationPart>(block.t).v,
std::get<parser::Block>(block.t));
if (context_.langOptions().OpenMPSimd) {
OpenMPSimdOnly(std::get<parser::Block>(block.t));
}
return true;
}
void RewriteMutator::Post(parser::BlockConstruct &block) {
if (context_.langOptions().OpenMPSimd) {
OpenMPSimdOnly(std::get<parser::Block>(block.t));
}
}
bool RewriteMutator::Pre(parser::Block &block) {
if (context_.langOptions().OpenMPSimd) {
OpenMPSimdOnly(block);
}
return true;
}
void RewriteMutator::Post(parser::Block &block) { this->Pre(block); }
bool RewriteMutator::Pre(parser::OmpBlockConstruct &block) {
if (context_.langOptions().OpenMPSimd) {
auto &innerBlock = std::get<parser::Block>(block.t);
OpenMPSimdOnly(innerBlock);
}
return true;
}
void RewriteMutator::Post(parser::OmpBlockConstruct &block) {
this->Pre(block);
}
bool RewriteMutator::Pre(parser::OpenMPLoopConstruct &ompLoop) {
if (context_.langOptions().OpenMPSimd) {
if (LoopConstructIsSIMD(&ompLoop)) {
return true;
}
for (auto &construct : std::get<parser::Block>(ompLoop.t)) {
if (auto *doConstruct = parser::omp::GetDoConstruct(construct)) {
auto &innerBlock = std::get<parser::Block>(doConstruct->t);
OpenMPSimdOnly(const_cast<parser::Block &>(innerBlock),
true);
}
}
}
return true;
}
void RewriteMutator::Post(parser::OpenMPLoopConstruct &ompLoop) {
this->Pre(ompLoop);
}
bool RewriteMutator::Pre(parser::DoConstruct &doConstruct) {
if (context_.langOptions().OpenMPSimd) {
auto &innerBlock = std::get<parser::Block>(doConstruct.t);
OpenMPSimdOnly(innerBlock);
}
return true;
}
void RewriteMutator::Post(parser::DoConstruct &doConstruct) {
this->Pre(doConstruct);
}
bool RewriteMutator::Pre(parser::IfConstruct &ifConstruct) {
if (context_.langOptions().OpenMPSimd) {
auto &innerBlock = std::get<parser::Block>(ifConstruct.t);
OpenMPSimdOnly(innerBlock);
}
return true;
}
void RewriteMutator::Post(parser::IfConstruct &ifConstruct) {
this->Pre(ifConstruct);
}
bool RewriteMutator::Pre(parser::ActionStmt &x) {
if (auto *print{std::get_if<common::Indirection<parser::PrintStmt>>(&x.u)};
print &&
std::get<std::list<parser::OutputItem>>(print->value().t).empty()) {
auto &format{std::get<parser::Format>(print->value().t)};
if (std::holds_alternative<parser::Expr>(format.u)) {
if (auto *name{parser::Unwrap<parser::Name>(format)}; name &&
name->symbol && name->symbol->GetUltimate().has<NamelistDetails>() &&
context_.IsEnabled(common::LanguageFeature::PrintNamelist)) {
context_.Warn(common::LanguageFeature::PrintNamelist, name->source,
"nonstandard: namelist in PRINT statement"_port_en_US);
std::list<parser::IoControlSpec> controls;
controls.emplace_back(std::move(*name));
x.u = common::Indirection<parser::WriteStmt>::Make(
parser::IoUnit{parser::Star{}}, std::optional<parser::Format>{},
std::move(controls), std::list<parser::OutputItem>{});
}
}
}
return true;
}
template <typename READ_OR_WRITE>
void FixMisparsedUntaggedNamelistName(READ_OR_WRITE &x) {
if (x.iounit && x.format &&
std::holds_alternative<parser::Expr>(x.format->u)) {
if (const parser::Name * name{parser::Unwrap<parser::Name>(x.format)}) {
if (name->symbol && name->symbol->GetUltimate().has<NamelistDetails>()) {
x.controls.emplace_front(parser::IoControlSpec{std::move(*name)});
x.format.reset();
}
}
}
}
void RewriteMutator::Post(parser::ReadStmt &x) {
if (x.iounit && !x.format && x.controls.empty()) {
if (auto *var{std::get_if<parser::Variable>(&x.iounit->u)}) {
const parser::Name &last{parser::GetLastName(*var)};
DeclTypeSpec *type{last.symbol ? last.symbol->GetType() : nullptr};
if (type && type->category() == DeclTypeSpec::Character) {
x.format = common::visit(
[](auto &&indirection) {
return parser::Expr{std::move(indirection)};
},
std::move(var->u));
x.iounit.reset();
}
}
}
FixMisparsedUntaggedNamelistName(x);
}
void RewriteMutator::Post(parser::WriteStmt &x) {
FixMisparsedUntaggedNamelistName(x);
}
bool RewriteParseTree(SemanticsContext &context, parser::Program &program) {
RewriteMutator mutator{context};
parser::Walk(program, mutator);
return !context.AnyFatalError();
}
}