diff --git a/flang/lib/semantics/CMakeLists.txt b/flang/lib/semantics/CMakeLists.txt index ad020e61b654..d797cfb2eb44 100644 --- a/flang/lib/semantics/CMakeLists.txt +++ b/flang/lib/semantics/CMakeLists.txt @@ -15,6 +15,7 @@ add_library(FortranSemantics attr.cc + canonicalize-do.cc expression.cc mod-file.cc resolve-labels.cc diff --git a/flang/lib/semantics/canonicalize-do.cc b/flang/lib/semantics/canonicalize-do.cc new file mode 100644 index 000000000000..c84b30b7780e --- /dev/null +++ b/flang/lib/semantics/canonicalize-do.cc @@ -0,0 +1,112 @@ +// Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "canonicalize-do.h" +#include "../parser/parse-tree-visitor.h" + +namespace Fortran::semantics { + +class CanonicalizationOfDoLoops { +public: + CanonicalizationOfDoLoops() = default; + + template<typename T> bool Pre(T &) { return true; } + template<typename T> void Post(T &) {} + bool Pre(parser::ExecutionPart &executionPart) { + const auto &endIter{executionPart.v.end()}; + currentList_ = &executionPart.v; + for (auto iter{executionPart.v.begin()}; iter != endIter; ++iter) { + iter = CheckStatement(iter); + } + return false; + } + template<typename T> bool Pre(parser::Statement<T> &statement) { + if (!labels_.empty() && statement.label.has_value() && + labels_.back() == *statement.label) { + auto currentLabel{labels_.back()}; + do { + currentIter_ = MakeCanonicalForm(labelDoIters_.back(), currentIter_); + labelDoIters_.pop_back(); + labels_.pop_back(); + } while (!labels_.empty() && labels_.back() == currentLabel); + } + return false; + } + +private: + parser::Block SpliceBlock( + parser::Block::iterator beginLoop, parser::Block::iterator endLoop) { + parser::Block block; + block.splice(block.begin(), *currentList_, ++beginLoop, ++endLoop); + return block; + } + std::optional<parser::LoopControl> CreateLoopControl( + std::optional<parser::LoopControl> &loopControlOpt) { + if (loopControlOpt.has_value()) { + return std::optional<parser::LoopControl>( + parser::LoopControl{loopControlOpt->u}); + } + return std::optional<parser::LoopControl>{}; + } + std::optional<parser::LoopControl> ExtractLoopControl( + const parser::Block::iterator &startLoop) { + return CreateLoopControl(std::get<std::optional<parser::LoopControl>>( + std::get<parser::Statement<common::Indirection<parser::LabelDoStmt>>>( + std::get<parser::ExecutableConstruct>(startLoop->u).u) + .statement->t)); + } + parser::Block::iterator MakeCanonicalForm( + const parser::Block::iterator &startLoop, + const parser::Block::iterator &endLoop) { + std::get<parser::ExecutableConstruct>(startLoop->u).u = + common::Indirection<parser::DoConstruct>{std::make_tuple( + parser::Statement<parser::NonLabelDoStmt>{ + std::optional<parser::Label>{}, + parser::NonLabelDoStmt{ + std::make_tuple(std::optional<parser::Name>{}, + ExtractLoopControl(startLoop))}}, + SpliceBlock(startLoop, endLoop), + parser::Statement<parser::EndDoStmt>{std::optional<parser::Label>{}, + parser::EndDoStmt{std::optional<parser::Name>{}}})}; + return startLoop; + } + parser::Block::iterator CheckStatement(const parser::Block::iterator &iter) { + currentIter_ = iter; + parser::ExecutionPartConstruct &executionPartConstruct{*iter}; + if (auto *executableConstruct = std::get_if<parser::ExecutableConstruct>( + &executionPartConstruct.u)) { + if (auto *labelDoLoop = std::get_if< + parser::Statement<common::Indirection<parser::LabelDoStmt>>>( + &executableConstruct->u)) { + labelDoIters_.push_back(iter); + labels_.push_back(std::get<parser::Label>(labelDoLoop->statement->t)); + } else if (!labels_.empty()) { + parser::Walk(executableConstruct->u, *this); + } + } + return currentIter_; + } + + std::vector<parser::Block::iterator> labelDoIters_; + std::vector<parser::Label> labels_; + parser::Block::iterator currentIter_; + parser::Block *currentList_; +}; + +void CanonicalizeDo(parser::Program &program) { + CanonicalizationOfDoLoops canonicalizationOfDoLoops; + parser::Walk(program, canonicalizationOfDoLoops); +} + +} // namespace Fortran::semantics diff --git a/flang/lib/semantics/canonicalize-do.h b/flang/lib/semantics/canonicalize-do.h new file mode 100644 index 000000000000..0f1ac987d016 --- /dev/null +++ b/flang/lib/semantics/canonicalize-do.h @@ -0,0 +1,25 @@ +// Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef FORTRAN_SEMANTICS_CANONICALIZE_DO_H_ +#define FORTRAN_SEMANTICS_CANONICALIZE_DO_H_ + +namespace Fortran::parser { +struct Program; +} // namespace Fortran::parser + +namespace Fortran::semantics { +void CanonicalizeDo(parser::Program &program); +} // namespace Fortran::semantics +#endif // FORTRAN_SEMANTICS_CANONICALIZE_DO_H_ diff --git a/flang/lib/semantics/semantics.cc b/flang/lib/semantics/semantics.cc index bd9f81e31c62..663071bcca09 100644 --- a/flang/lib/semantics/semantics.cc +++ b/flang/lib/semantics/semantics.cc @@ -13,6 +13,7 @@ // limitations under the License. #include "semantics.h" +#include "canonicalize-do.h" #include "mod-file.h" #include "resolve-labels.h" #include "resolve-names.h" @@ -52,6 +53,7 @@ bool Semantics::Perform(parser::Program &program) { if (AnyFatalError()) { return false; } + CanonicalizeDo(program); ModFileWriter writer; writer.set_directory(moduleDirectory_); if (!writer.WriteAll(globalScope_)) { diff --git a/flang/test/semantics/CMakeLists.txt b/flang/test/semantics/CMakeLists.txt index b132a463353c..f6425b1ed5dc 100644 --- a/flang/test/semantics/CMakeLists.txt +++ b/flang/test/semantics/CMakeLists.txt @@ -91,6 +91,10 @@ set(LABEL_TESTS label*.[Ff]90 ) +set(CANONDO_TESTS + canondo*.[Ff]90 +) + foreach(test ${ERROR_TESTS}) add_test(NAME ${test} COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/test_errors.sh ${test}) endforeach() @@ -106,3 +110,7 @@ endforeach() foreach(test ${LABEL_TESTS}) add_test(NAME ${test} COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/test_any.sh ${test}) endforeach() + +foreach(test ${CANONDO_TESTS}) + add_test(NAME ${test} COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/test_any.sh ${test}) +endforeach() diff --git a/flang/test/semantics/canondo01.f90 b/flang/test/semantics/canondo01.f90 new file mode 100644 index 000000000000..5cbb0ce29de6 --- /dev/null +++ b/flang/test/semantics/canondo01.f90 @@ -0,0 +1,28 @@ +! Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. +! +! Licensed under the Apache License, Version 2.0 (the "License"); +! you may not use this file except in compliance with the License. +! You may obtain a copy of the License at +! +! http://www.apache.org/licenses/LICENSE-2.0 +! +! Unless required by applicable law or agreed to in writing, software +! distributed under the License is distributed on an "AS IS" BASIS, +! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +! See the License for the specific language governing permissions and +! limitations under the License. + +! negative test -- invalid labels, out of range + +! RUN: ${F18} -funparse-with-symbols %s 2>&1 | ${FileCheck} %s +! CHECK: end do + +SUBROUTINE sub00(a,b,n,m) + INTEGER n,m + REAL a(n,m), b(n,m) + + DO 10 j = 1,m + DO 10 i = 1,n + g = a(i,j) - b(i,j) +10 PRINT *, g +END SUBROUTINE sub00 diff --git a/flang/test/semantics/canondo02.f90 b/flang/test/semantics/canondo02.f90 new file mode 100644 index 000000000000..a54e4512b0e0 --- /dev/null +++ b/flang/test/semantics/canondo02.f90 @@ -0,0 +1,28 @@ +! Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. +! +! Licensed under the Apache License, Version 2.0 (the "License"); +! you may not use this file except in compliance with the License. +! You may obtain a copy of the License at +! +! http://www.apache.org/licenses/LICENSE-2.0 +! +! Unless required by applicable law or agreed to in writing, software +! distributed under the License is distributed on an "AS IS" BASIS, +! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +! See the License for the specific language governing permissions and +! limitations under the License. + +! negative test -- invalid labels, out of range + +! RUN: ${F18} -funparse-with-symbols %s 2>&1 | ${FileCheck} %s +! CHECK: end do + +SUBROUTINE sub00(a,b,n,m) + INTEGER n,m + REAL a(n,m), b(n,m) + + i = n-1 + DO 10 j = 1,m + g = a(i,j) - b(i,j) +10 PRINT *, g +END SUBROUTINE sub00