[flang] canonicalize DO constructs

Fix a bug with BLOCK DATA name matching
Add tests and finish up DO loop canonicalization

Original-commit: flang-compiler/f18@2d93b2819c
Reviewed-on: https://github.com/flang-compiler/f18/pull/193
Tree-same-pre-rewrite: false
This commit is contained in:
Eric Schweitz 2018-09-19 14:26:02 -07:00
parent 507ba5c3a1
commit 4972485598
7 changed files with 204 additions and 0 deletions

View File

@ -15,6 +15,7 @@
add_library(FortranSemantics
attr.cc
canonicalize-do.cc
expression.cc
mod-file.cc
resolve-labels.cc

View File

@ -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

View File

@ -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_

View File

@ -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_)) {

View File

@ -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()

View File

@ -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

View File

@ -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