diff --git a/flang/CMakeLists.txt b/flang/CMakeLists.txt --- a/flang/CMakeLists.txt +++ b/flang/CMakeLists.txt @@ -411,6 +411,9 @@ include(CMakeParseArguments) include(AddFlang) +if (FLANG_INCLUDE_TESTS) + add_compile_definitions(FLANG_INCLUDE_TESTS=1) +endif() add_subdirectory(include) add_subdirectory(lib) diff --git a/flang/include/flang/Optimizer/Analysis/AliasAnalysis.h b/flang/include/flang/Optimizer/Analysis/AliasAnalysis.h new file mode 100644 --- /dev/null +++ b/flang/include/flang/Optimizer/Analysis/AliasAnalysis.h @@ -0,0 +1,29 @@ +//===- AliasAnalysis.h - Alias Analysis in FIR -----------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef FIR_ANALYSIS_ALIASANALYSIS_H_ +#define FIR_ANALYSIS_ALIASANALYSIS_H_ + +#include "mlir/Analysis/AliasAnalysis.h" + +namespace fir { + +//===----------------------------------------------------------------------===// +// AliasAnalysis +//===----------------------------------------------------------------------===// +class AliasAnalysis { +public: + /// Given two values, return their aliasing behavior. + mlir::AliasResult alias(mlir::Value lhs, mlir::Value rhs); + + /// Return the modify-reference behavior of `op` on `location`. + mlir::ModRefResult getModRef(mlir::Operation *op, mlir::Value location); +}; +} // namespace fir + +#endif // FIR_ANALYSIS_ALIASANALYSIS_H_ diff --git a/flang/lib/Lower/IntrinsicCall.cpp b/flang/lib/Lower/IntrinsicCall.cpp --- a/flang/lib/Lower/IntrinsicCall.cpp +++ b/flang/lib/Lower/IntrinsicCall.cpp @@ -5004,7 +5004,7 @@ /// intrinsic function. Fortran::lower::ArgLoweringRule Fortran::lower::lowerIntrinsicArgumentAs( const IntrinsicArgumentLoweringRules &rules, unsigned position) { - assert(position < sizeof(rules.args) / sizeof(decltype(*rules.args)) && + assert(position < sizeof(rules.args) / (sizeof(decltype(*rules.args))) && "invalid argument"); return {rules.args[position].lowerAs, rules.args[position].handleDynamicOptional}; diff --git a/flang/lib/Optimizer/Analysis/AliasAnalysis.cpp b/flang/lib/Optimizer/Analysis/AliasAnalysis.cpp new file mode 100644 --- /dev/null +++ b/flang/lib/Optimizer/Analysis/AliasAnalysis.cpp @@ -0,0 +1,67 @@ +//===- AliasAnalysis.cpp - Alias Analysis for FIR ------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "flang/Optimizer/Analysis/AliasAnalysis.h" +#include "mlir/Interfaces/SideEffectInterfaces.h" + +using namespace mlir; + +//===----------------------------------------------------------------------===// +// AliasAnalysis: alias +//===----------------------------------------------------------------------===// + +namespace fir { +AliasResult AliasAnalysis::alias(Value lhs, Value rhs) { + // This is for now a mock analysis + if (lhs == rhs) { + return AliasResult::MustAlias; + } + return AliasResult::MayAlias; +} + +//===----------------------------------------------------------------------===// +// AliasAnalysis: getModRef +//===----------------------------------------------------------------------===// + +/// This is mostly inspired by MLIR::LocalAliasAnalysis with 2 notable +/// differences 1) Regions are not handled here but will be handled by a data +/// flow analysis to come 2) Allocate and Free effects are considered modifying +ModRefResult AliasAnalysis::getModRef(Operation *op, Value location) { + MemoryEffectOpInterface interface = dyn_cast(op); + if (!interface) + return ModRefResult::getModAndRef(); + + // Build a ModRefResult by merging the behavior of the effects of this + // operation. + SmallVector effects; + interface.getEffects(effects); + + ModRefResult result = ModRefResult::getNoModRef(); + for (const MemoryEffects::EffectInstance &effect : effects) { + + // Check for an alias between the effect and our memory location. + AliasResult aliasResult = AliasResult::MayAlias; + if (Value effectValue = effect.getValue()) + aliasResult = alias(effectValue, location); + + // If we don't alias, ignore this effect. + if (aliasResult.isNo()) + continue; + + // Merge in the corresponding mod or ref for this effect. + if (isa(effect.getEffect())) { + result = result.merge(ModRefResult::getRef()); + } else { + result = result.merge(ModRefResult::getMod()); + } + if (result.isModAndRef()) + break; + } + return result; +} +} // namespace fir diff --git a/flang/lib/Optimizer/Analysis/CMakeLists.txt b/flang/lib/Optimizer/Analysis/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/flang/lib/Optimizer/Analysis/CMakeLists.txt @@ -0,0 +1,16 @@ +add_flang_library(FIRAnalysis + AliasAnalysis.cpp + + DEPENDS + FIRBuilder + FIRDialect + FIRSupport + + LINK_LIBS + FIRBuilder + FIRDialect + MLIRFuncDialect + MLIRLLVMDialect + MLIRMathTransforms + FIRSupport +) diff --git a/flang/lib/Optimizer/CMakeLists.txt b/flang/lib/Optimizer/CMakeLists.txt --- a/flang/lib/Optimizer/CMakeLists.txt +++ b/flang/lib/Optimizer/CMakeLists.txt @@ -4,3 +4,4 @@ add_subdirectory(HLFIR) add_subdirectory(Support) add_subdirectory(Transforms) +add_subdirectory(Analysis) diff --git a/flang/test/CMakeLists.txt b/flang/test/CMakeLists.txt --- a/flang/test/CMakeLists.txt +++ b/flang/test/CMakeLists.txt @@ -1,5 +1,6 @@ # Test runner infrastructure for Flang. This configures the Flang test trees # for use by Lit, and delegates to LLVM's lit test handlers. +add_subdirectory(lib) llvm_canonicalize_cmake_booleans( FLANG_BUILD_EXAMPLES diff --git a/flang/test/lib/Analysis/AliasAnalysis/CMakeLists.txt b/flang/test/lib/Analysis/AliasAnalysis/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/flang/test/lib/Analysis/AliasAnalysis/CMakeLists.txt @@ -0,0 +1,29 @@ +# Exclude tests from libMLIR.so +add_flang_library(FIRTestAnalysis + TestAliasAnalysis.cpp + + DEPENDS + FIRDialect + FIRBuilder + FIRSupport + FIRTransforms + FIRAnalysis + ${dialect_libs} + + LINK_LIBS + FIRDialect + FIRBuilder + FIRSupport + FIRTransforms + FIRAnalysis + ${dialect_libs} + MLIRFuncDialect + MLIRLLVMDialect + MLIRAnalysis + MLIRTestAnalysis + ) + +target_include_directories(FIRTestAnalysis + PRIVATE + ${MLIR_MAIN_SRC_DIR}/.. + ) \ No newline at end of file diff --git a/flang/test/lib/Analysis/AliasAnalysis/TestAliasAnalysis.cpp b/flang/test/lib/Analysis/AliasAnalysis/TestAliasAnalysis.cpp new file mode 100644 --- /dev/null +++ b/flang/test/lib/Analysis/AliasAnalysis/TestAliasAnalysis.cpp @@ -0,0 +1,72 @@ +//===- TestAliasAnalysis.cpp - Test FIR lias analysis -----------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "mlir/test/lib/Analysis/TestAliasAnalysis.h" +#include "mlir/Analysis/AliasAnalysis.h" +#include "mlir/Pass/Pass.h" +#include "flang/Optimizer/Analysis/AliasAnalysis.h" + +using namespace mlir; + +namespace { + +//===----------------------------------------------------------------------===// +// Testing AliasResult +//===----------------------------------------------------------------------===// + +struct TestFIRAliasAnalysisPass + : public test::TestAliasAnalysisBase, + PassWrapper> { + MLIR_DEFINE_EXPLICIT_INTERNAL_INLINE_TYPE_ID(TestFIRAliasAnalysisPass) + + StringRef getArgument() const final { return "test-fir-alias-analysis"; } + StringRef getDescription() const final { + return "Test alias analysis results."; + } + void runOnOperation() override { + mlir::AliasAnalysis aliasAnalysis(getOperation()); + aliasAnalysis.addAnalysisImplementation(fir::AliasAnalysis()); + runAliasAnalysisOnOperation(getOperation(), aliasAnalysis); + } +}; + +//===----------------------------------------------------------------------===// +// Testing ModRefResult +//===----------------------------------------------------------------------===// + +struct TestFIRAliasAnalysisModRefPass + : public test::TestAliasAnalysisModRefBase, + PassWrapper> { + MLIR_DEFINE_EXPLICIT_INTERNAL_INLINE_TYPE_ID(TestFIRAliasAnalysisModRefPass) + + StringRef getArgument() const final { + return "test-fir-alias-analysis-modref"; + } + StringRef getDescription() const final { + return "Test alias analysis ModRef results."; + } + void runOnOperation() override { + mlir::AliasAnalysis aliasAnalysis(getOperation()); + aliasAnalysis.addAnalysisImplementation(fir::AliasAnalysis()); + runAliasAnalysisOnOperation(getOperation(), aliasAnalysis); + } +}; +} // namespace + +//===----------------------------------------------------------------------===// +// Pass Registration +//===----------------------------------------------------------------------===// + +namespace fir { +namespace test { +void registerTestFIRAliasAnalysisPass() { + PassRegistration(); + PassRegistration(); +} +} // namespace test +} // namespace fir \ No newline at end of file diff --git a/flang/test/lib/Analysis/AliasAnalysis/alias-analysis-1.fir b/flang/test/lib/Analysis/AliasAnalysis/alias-analysis-1.fir new file mode 100644 --- /dev/null +++ b/flang/test/lib/Analysis/AliasAnalysis/alias-analysis-1.fir @@ -0,0 +1,21 @@ +// RUN: fir-opt %s -pass-pipeline='func.func(test-fir-alias-analysis)' -split-input-file 2>&1 | FileCheck %s + +// CHECK-LABEL: Testing : "_QPtest" +// CHECK-DAG: alloca_1#0 <-> address_of#0: MayAlias +func.func @_QPtest(%arg1: !fir.ref) { + %c1_i32 = arith.constant 1 : i32 + %0 = fir.alloca () -> () {test.ptr = "alloca_1"} + %1 = fir.address_of(@_QPf) {test.ptr = "address_of"} : () -> i32 + %2 = fir.convert %1 : (() -> i32) -> (() -> ()) + %4 = fir.convert %0 : (!fir.ref<() -> ()>) -> !fir.llvm_ptr<() -> ()> + fir.store %2 to %4 : !fir.llvm_ptr<() -> ()> + %6 = fir.load %0 : !fir.ref<() -> ()> + fir.call @_QPs(%6) : (() -> ()) -> () + return +} + +// ----- +func.func private @_QPs(%arg0: () -> ()) + +// ----- +func.func private @_QPf() -> i32 diff --git a/flang/test/lib/Analysis/CMakeLists.txt b/flang/test/lib/Analysis/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/flang/test/lib/Analysis/CMakeLists.txt @@ -0,0 +1 @@ +add_subdirectory(AliasAnalysis) diff --git a/flang/test/lib/CMakeLists.txt b/flang/test/lib/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/flang/test/lib/CMakeLists.txt @@ -0,0 +1 @@ +add_subdirectory(Analysis) diff --git a/flang/test/lib/lit.local.cfg b/flang/test/lib/lit.local.cfg new file mode 100644 --- /dev/null +++ b/flang/test/lib/lit.local.cfg @@ -0,0 +1,7 @@ + +# Excluding .cpp file from the extensions since from this level down they are used for the development +config.suffixes = ['.c', '.f', '.F', '.ff', '.FOR', '.for', '.f77', '.f90', '.F90', + '.ff90', '.f95', '.F95', '.ff95', '.fpp', '.FPP', '.cuf' + '.CUF', '.f18', '.F18', '.f03', '.F03', '.f08', '.F08', + '.ll', '.fir', '.mlir'] + diff --git a/flang/tools/fir-opt/CMakeLists.txt b/flang/tools/fir-opt/CMakeLists.txt --- a/flang/tools/fir-opt/CMakeLists.txt +++ b/flang/tools/fir-opt/CMakeLists.txt @@ -2,12 +2,20 @@ llvm_update_compile_flags(fir-opt) get_property(dialect_libs GLOBAL PROPERTY MLIR_DIALECT_LIBS) +if(FLANG_INCLUDE_TESTS) + set(test_libs + FIRTestAnalysis + ) +endif() + target_link_libraries(fir-opt PRIVATE FIRDialect FIRSupport FIRTransforms FIRCodeGen HLFIRDialect + FIRAnalysis + ${test_libs} ${dialect_libs} # TODO: these should be transitive dependencies from a target providing diff --git a/flang/tools/fir-opt/fir-opt.cpp b/flang/tools/fir-opt/fir-opt.cpp --- a/flang/tools/fir-opt/fir-opt.cpp +++ b/flang/tools/fir-opt/fir-opt.cpp @@ -17,11 +17,19 @@ #include "flang/Optimizer/Transforms/Passes.h" using namespace mlir; +namespace fir { +namespace test { +void registerTestFIRAliasAnalysisPass(); +} // namespace test +} // namespace fir int main(int argc, char **argv) { fir::support::registerMLIRPassesForFortranTools(); fir::registerOptCodeGenPasses(); fir::registerOptTransformPasses(); +#ifdef FLANG_INCLUDE_TESTS + fir::test::registerTestFIRAliasAnalysisPass(); +#endif DialectRegistry registry; fir::support::registerDialects(registry); return failed(MlirOptMain(argc, argv, "FIR modular optimizer driver\n", diff --git a/mlir/test/lib/Analysis/TestAliasAnalysis.h b/mlir/test/lib/Analysis/TestAliasAnalysis.h new file mode 100644 --- /dev/null +++ b/mlir/test/lib/Analysis/TestAliasAnalysis.h @@ -0,0 +1,37 @@ +//===- TestAliasAnalysis.h - MLIR Test Utility ------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file provides a common facility that can be reused for the +// testing of various aliasing analyses +// +//===----------------------------------------------------------------------===// + +#ifndef MLIR_TEST_LIB_ANALYSIS_ALIASANALYSIS_H +#define MLIR_TEST_LIB_ANALYSIS_ALIASANALYSIS_H + +#include "mlir/Analysis/AliasAnalysis.h" + +namespace mlir { +namespace test { + +/// Print the result of an alias query. +void printAliasResult(AliasResult result, Value lhs, Value rhs); +void printModRefResult(ModRefResult result, Operation *op, Value location); + +struct TestAliasAnalysisBase { + void runAliasAnalysisOnOperation(Operation *op, AliasAnalysis &aliasAnalysis); +}; + +struct TestAliasAnalysisModRefBase { + void runAliasAnalysisOnOperation(Operation *op, AliasAnalysis &aliasAnalysis); +}; + +} // namespace test +} // namespace mlir + +#endif // MLIR_TEST_LIB_ANALYSIS_ALIASANALYSIS_H diff --git a/mlir/test/lib/Analysis/TestAliasAnalysis.cpp b/mlir/test/lib/Analysis/TestAliasAnalysis.cpp --- a/mlir/test/lib/Analysis/TestAliasAnalysis.cpp +++ b/mlir/test/lib/Analysis/TestAliasAnalysis.cpp @@ -11,6 +11,7 @@ // //===----------------------------------------------------------------------===// +#include "TestAliasAnalysis.h" #include "mlir/Analysis/AliasAnalysis.h" #include "mlir/Pass/Pass.h" @@ -39,13 +40,80 @@ llvm::errs() << "#" << result.getResultNumber(); } +namespace mlir { +namespace test { +void printAliasResult(AliasResult result, Value lhs, Value rhs) { + printAliasOperand(lhs); + llvm::errs() << " <-> "; + printAliasOperand(rhs); + llvm::errs() << ": " << result << "\n"; +} + +/// Print the result of an alias query. +void printModRefResult(ModRefResult result, Operation *op, Value location) { + printAliasOperand(op); + llvm::errs() << " -> "; + printAliasOperand(location); + llvm::errs() << ": " << result << "\n"; +} + +void TestAliasAnalysisBase::runAliasAnalysisOnOperation( + Operation *op, AliasAnalysis &aliasAnalysis) { + llvm::errs() << "Testing : " << op->getAttr("sym_name") << "\n"; + + // Collect all of the values to check for aliasing behavior. + SmallVector valsToCheck; + op->walk([&](Operation *op) { + if (!op->getAttr("test.ptr")) + return; + valsToCheck.append(op->result_begin(), op->result_end()); + for (Region ®ion : op->getRegions()) + for (Block &block : region) + valsToCheck.append(block.args_begin(), block.args_end()); + }); + + // Check for aliasing behavior between each of the values. + for (auto it = valsToCheck.begin(), e = valsToCheck.end(); it != e; ++it) + for (auto *innerIt = valsToCheck.begin(); innerIt != it; ++innerIt) + printAliasResult(aliasAnalysis.alias(*innerIt, *it), *innerIt, *it); +} + +void TestAliasAnalysisModRefBase::runAliasAnalysisOnOperation( + Operation *op, AliasAnalysis &aliasAnalysis) { + llvm::errs() << "Testing : " << op->getAttr("sym_name") << "\n"; + + // Collect all of the values to check for aliasing behavior. + SmallVector valsToCheck; + op->walk([&](Operation *op) { + if (!op->getAttr("test.ptr")) + return; + valsToCheck.append(op->result_begin(), op->result_end()); + for (Region ®ion : op->getRegions()) + for (Block &block : region) + valsToCheck.append(block.args_begin(), block.args_end()); + }); + + // Check for aliasing behavior between each of the values. + for (auto &it : valsToCheck) { + op->walk([&](Operation *op) { + if (!op->getAttr("test.ptr")) + return; + printModRefResult(aliasAnalysis.getModRef(op, it), op, it); + }); + } +} + +} // namespace test +} // namespace mlir + //===----------------------------------------------------------------------===// // Testing AliasResult //===----------------------------------------------------------------------===// namespace { struct TestAliasAnalysisPass - : public PassWrapper> { + : public test::TestAliasAnalysisBase, + PassWrapper> { MLIR_DEFINE_EXPLICIT_INTERNAL_INLINE_TYPE_ID(TestAliasAnalysisPass) StringRef getArgument() const final { return "test-alias-analysis"; } @@ -53,32 +121,8 @@ return "Test alias analysis results."; } void runOnOperation() override { - llvm::errs() << "Testing : " << getOperation()->getAttr("sym_name") << "\n"; - - // Collect all of the values to check for aliasing behavior. AliasAnalysis &aliasAnalysis = getAnalysis(); - SmallVector valsToCheck; - getOperation()->walk([&](Operation *op) { - if (!op->getAttr("test.ptr")) - return; - valsToCheck.append(op->result_begin(), op->result_end()); - for (Region ®ion : op->getRegions()) - for (Block &block : region) - valsToCheck.append(block.args_begin(), block.args_end()); - }); - - // Check for aliasing behavior between each of the values. - for (auto it = valsToCheck.begin(), e = valsToCheck.end(); it != e; ++it) - for (auto *innerIt = valsToCheck.begin(); innerIt != it; ++innerIt) - printAliasResult(aliasAnalysis.alias(*innerIt, *it), *innerIt, *it); - } - - /// Print the result of an alias query. - void printAliasResult(AliasResult result, Value lhs, Value rhs) { - printAliasOperand(lhs); - llvm::errs() << " <-> "; - printAliasOperand(rhs); - llvm::errs() << ": " << result << "\n"; + runAliasAnalysisOnOperation(getOperation(), aliasAnalysis); } }; } // namespace @@ -89,7 +133,8 @@ namespace { struct TestAliasAnalysisModRefPass - : public PassWrapper> { + : public test::TestAliasAnalysisModRefBase, + PassWrapper> { MLIR_DEFINE_EXPLICIT_INTERNAL_INLINE_TYPE_ID(TestAliasAnalysisModRefPass) StringRef getArgument() const final { return "test-alias-analysis-modref"; } @@ -97,36 +142,8 @@ return "Test alias analysis ModRef results."; } void runOnOperation() override { - llvm::errs() << "Testing : " << getOperation()->getAttr("sym_name") << "\n"; - - // Collect all of the values to check for aliasing behavior. AliasAnalysis &aliasAnalysis = getAnalysis(); - SmallVector valsToCheck; - getOperation()->walk([&](Operation *op) { - if (!op->getAttr("test.ptr")) - return; - valsToCheck.append(op->result_begin(), op->result_end()); - for (Region ®ion : op->getRegions()) - for (Block &block : region) - valsToCheck.append(block.args_begin(), block.args_end()); - }); - - // Check for aliasing behavior between each of the values. - for (auto &it : valsToCheck) { - getOperation()->walk([&](Operation *op) { - if (!op->getAttr("test.ptr")) - return; - printModRefResult(aliasAnalysis.getModRef(op, it), op, it); - }); - } - } - - /// Print the result of an alias query. - void printModRefResult(ModRefResult result, Operation *op, Value location) { - printAliasOperand(op); - llvm::errs() << " -> "; - printAliasOperand(location); - llvm::errs() << ": " << result << "\n"; + runAliasAnalysisOnOperation(getOperation(), aliasAnalysis); } }; } // namespace