[flang] new refactored Fold

Original-commit: flang-compiler/f18@061868fd85
Reviewed-on: https://github.com/flang-compiler/f18/pull/219
Tree-same-pre-rewrite: false
This commit is contained in:
peter klausler 2018-10-23 16:48:06 -07:00
parent 43a0a63441
commit d6ea6af7c4
6 changed files with 384 additions and 49 deletions

View file

@ -144,7 +144,8 @@ template<typename A> class Expr;
// Classes that support a Fold(FoldingContext &) member function have the
// IsFoldableTrait.
CLASS_TRAIT(IsFoldableTrait)
CLASS_TRAIT(IsFoldableTrait) // TODO pmk rm
struct FoldingContext {
explicit FoldingContext(const parser::ContextualMessages &m,
Rounding round = defaultRounding, bool flush = false)

View file

@ -14,6 +14,7 @@
#include "expression.h"
#include "common.h"
#include "fold.h"
#include "int-power.h"
#include "tools.h"
#include "variable.h"

View file

@ -29,6 +29,7 @@
#include "../lib/common/template.h"
#include "../lib/parser/char-block.h"
#include "../lib/parser/message.h"
#include <algorithm>
#include <ostream>
#include <tuple>
#include <type_traits>
@ -158,12 +159,10 @@ public:
int Rank() const {
int rank{left().Rank()};
if constexpr (operands > 1) {
int rightRank{right().Rank()};
if (rightRank > rank) {
rank = rightRank;
}
return std::max(rank, right().Rank());
} else {
return rank;
}
return rank;
}
std::ostream &Dump(std::ostream &) const;
@ -570,6 +569,9 @@ FOR_EACH_REAL_KIND(extern template struct Relational)
FOR_EACH_CHARACTER_KIND(extern template struct Relational)
extern template struct Relational<SomeType>;
// Logical expressions of a kind bigger than LogicalResult
// do not include Relational<> operations as possibilities
// since their results are always LogicalResult (kind=1).
template<int KIND>
class Expr<Type<TypeCategory::Logical, KIND>>
: public ExpressionBase<Type<TypeCategory::Logical, KIND>> {
@ -581,14 +583,15 @@ public:
explicit Expr(bool x) : u{Constant<Result>{x}} {}
private:
using Operations =
std::variant<Convert<Result, TypeCategory::Logical>, Parentheses<Result>,
Not<KIND>, LogicalOperation<KIND>, Relational<SomeType>>;
using Operations = std::variant<Convert<Result, TypeCategory::Logical>,
Parentheses<Result>, Not<KIND>, LogicalOperation<KIND>>;
using Relations = std::conditional_t<KIND == LogicalResult::kind,
std::variant<Relational<SomeType>>, std::variant<>>;
using Others =
std::variant<Constant<Result>, Designator<Result>, FunctionRef<Result>>;
public:
common::CombineVariants<Operations, Others> u;
common::CombineVariants<Operations, Relations, Others> u;
};
FOR_EACH_LOGICAL_KIND(extern template class Expr)
@ -607,7 +610,7 @@ public:
// Note that Expr<SomeDerived> does not inherit from ExpressionBase
// since Constant<SomeDerived> and Scalar<SomeDerived> are not defined
// for derived types..
// for derived types.
template<> class Expr<SomeDerived> {
public:
using Result = SomeDerived;

302
flang/lib/evaluate/fold.h Normal file
View file

@ -0,0 +1,302 @@
// 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_EVALUATE_FOLD_H_
#define FORTRAN_EVALUATE_FOLD_H_
#include "common.h"
#include "expression.h"
#include "int-power.h"
#include "tools.h"
#include "type.h"
#include "../common/indirection.h"
#include "../parser/message.h"
#include <cstdio>
#include <optional>
#include <type_traits>
#include <variant>
namespace Fortran::evaluate {
using namespace Fortran::parser::literals;
// The result of Fold() is always packaged as an Expr<>.
// This allows Fold() to replace an operation with a constant or
// a canonicalized expression.
// When the operand is an Expr<A>, the result has the same type.
// Base cases
template<typename A> Expr<ResultType<A>> Fold(FoldingContext &, A &&x) {
return Expr<ResultType<A>>{std::move(x)};
}
template<typename A> Expr<A> Fold(FoldingContext &context, Expr<A> &&expr) {
static_assert(A::isSpecificIntrinsicType);
return std::visit([&](auto &&x) -> Expr<A> { return Fold(context, std::move(x)); }, std::move(expr.u));
}
template<TypeCategory CAT>
Expr<SomeKind<CAT>>
Fold(FoldingContext &context, Expr<SomeKind<CAT>> &&expr) {
return std::visit([&](auto &&x) -> Expr<SomeKind<CAT>> {
if constexpr (CAT == TypeCategory::Derived) {
return Fold(context, std::move(x));
} else {
return Expr<SomeKind<CAT>>{Fold(context, std::move(x))};
}
}, std::move(expr.u));
}
template<> inline Expr<SomeType> Fold(FoldingContext &context, Expr<SomeType> &&expr) {
return std::visit([&](auto &&x) -> Expr<SomeType> {
if constexpr (std::is_same_v<std::decay_t<decltype(x)>, BOZLiteralConstant>) {
return std::move(expr);
} else {
return Expr<SomeType>{Fold(context, std::move(x))};
}
}, std::move(expr.u));
}
// Unary operations
template<typename TO, TypeCategory FROMCAT> Expr<TO> Fold(FoldingContext &context, Convert<TO, FROMCAT> &&convert) {
return std::visit([&](auto &kindExpr) -> Expr<TO> {
kindExpr = Fold(context, std::move(kindExpr));
using Operand = ResultType<decltype(kindExpr)>;
if (const auto *c{std::get_if<Constant<Operand>>(&kindExpr.u)}) {
if constexpr (TO::category == TypeCategory::Integer) {
if constexpr (Operand::category == TypeCategory::Integer) {
auto converted{Scalar<TO>::ConvertSigned(c->value)};
if (converted.overflow) {
context.messages.Say("INTEGER(%d) to INTEGER(%d) conversion overflowed"_en_US, Operand::kind, TO::kind);
}
return Expr<TO>{Constant<TO>{std::move(converted.value)}};
} else if constexpr (Operand::category == TypeCategory::Real) {
auto converted{c->value.template ToInteger<Scalar<TO>>()};
if (converted.flags.test(RealFlag::InvalidArgument)) {
context.messages.Say(
"REAL(%d) to INTEGER(%d) conversion: invalid argument"_en_US, Operand::kind, TO::kind);
} else if (converted.flags.test(RealFlag::Overflow)) {
context.messages.Say(
"REAL(%d) to INTEGER(%d) conversion overflowed"_en_US, Operand::kind, TO::kind);
}
return Expr<TO>{Constant<TO>{std::move(converted.value)}};
}
} else if constexpr (TO::category == TypeCategory::Real) {
if constexpr (Operand::category == TypeCategory::Integer) {
auto converted{Scalar<TO>::FromInteger(c->value)};
if (!converted.flags.empty()) {
char buffer[64];
std::snprintf(buffer, sizeof buffer, "INTEGER(%d) to REAL(%d) conversion", Operand::kind, TO::kind);
RealFlagWarnings(context, converted.flags, buffer);
}
return Expr<TO>{Constant<TO>{std::move(converted.value)}};
} else if constexpr (Operand::category == TypeCategory::Real) {
auto converted{Scalar<TO>::Convert(c->value)};
if (!converted.flags.empty()) {
char buffer[64];
std::snprintf(buffer, sizeof buffer, "REAL(%d) to REAL(%d) conversion", Operand::kind, TO::kind);
RealFlagWarnings(context, converted.flags, buffer);
}
return Expr<TO>{Constant<TO>{std::move(converted.value)}};
}
} else if constexpr (TO::category == TypeCategory::Logical &&
Operand::category == TypeCategory::Logical) {
return Expr<TO>{Constant<TO>{c->value.IsTrue()}};
}
}
return Expr<TO>{std::move(convert)};
}, convert.left().u);
}
template<typename T> Expr<T> Fold(FoldingContext &context, Negate<T> &&x) {
auto &operand{x.left()};
operand = Fold(context, std::move(operand));
if (const auto *c{std::get_if<Constant<T>>(&operand.u)}) {
if constexpr (T::category == TypeCategory::Integer) {
auto negated{c->value.Negate()};
if (negated.overflow) {
context.messages.Say("INTEGER(%d) negation overflowed"_en_US, T::kind);
}
return Expr<T>{Constant<T>{std::move(negated.value)}};
} else {
return Expr<T>{Constant<T>{c->value.Negate()}}; // REAL & COMPLEX negation: no exceptions possible
}
}
return Expr<T>{std::move(x)};
}
template<int KIND> Expr<Type<TypeCategory::Real, KIND>> Fold(FoldingContext &context, ComplexComponent<KIND> &&x) {
using Operand = Type<TypeCategory::Complex, KIND>;
using Part = Type<TypeCategory::Real, KIND>;
auto &operand{x.left()};
operand = Fold(context, std::move(operand));
if (const auto *z{std::get_if<Constant<Operand>>(&operand.u)}) {
if (x.isImaginaryPart) {
return Expr<Part>{Constant<Part>{z->value.AIMAG()}};
} else {
return Expr<Part>{Constant<Part>{z->value.REAL()}};
}
}
return Expr<Part>{std::move(x)};
}
template<int KIND> Expr<Type<TypeCategory::Logical, KIND>> Fold(FoldingContext &context, Not<KIND> &&x) {
using Ty = Type<TypeCategory::Logical, KIND>;
auto &operand{x.left()};
operand = Fold(context, std::move(operand));
if (const auto *c{std::get_if<Constant<Ty>>(&operand.u)}) {
return Expr<Ty>{Constant<Ty>{c->value.IsTrue()}};
}
return Expr<Ty>{x};
}
// Binary (dyadic) operations
template<typename T1, typename T2> std::optional<std::pair<Scalar<T1>, Scalar<T2>>>
FoldOperands(FoldingContext &context, Expr<T1> &x, Expr<T2> &y) {
x = Fold(context, std::move(x));
y = Fold(context, std::move(y));
if (const auto *xc{std::get_if<Constant<T1>>(&x.u)}) {
if (const auto *yc{std::get_if<Constant<T2>>(&y.u)}) {
return {std::make_pair(xc->value, yc->value)};
}
}
return std::nullopt;
}
template<typename T> Expr<T> Fold(FoldingContext &context, Add<T> &&x) {
if (auto folded{FoldOperands(context, x.left(), x.right())}) {
if constexpr (T::category == TypeCategory::Integer) {
auto sum{folded->first.AddSigned(folded->second)};
if (sum.overflow) {
context.messages.Say("INTEGER(%d) addition overflowed"_en_US, T::kind);
}
return Expr<T>{Constant<T>{sum.value}};
} else {
auto sum{folded->first.Add(folded->second, context.rounding)};
RealFlagWarnings(context, sum.flags, "addition");
return Expr<T>{Constant<T>{sum.value}};
}
}
return Expr<T>{std::move(x)};
}
template<typename T> Expr<T> Fold(FoldingContext &context, Subtract<T> &&x) {
if (auto folded{FoldOperands(context, x.left(), x.right())}) {
if constexpr (T::category == TypeCategory::Integer) {
auto difference{folded->first.SubtractSigned(folded->second)};
if (difference.overflow) {
context.messages.Say("INTEGER(%d) subtraction overflowed"_en_US, T::kind);
}
return Expr<T>{Constant<T>{difference.value}};
} else {
auto difference{folded->first.Subtract(folded->second, context.rounding)};
RealFlagWarnings(context, difference.flags, "subtraction");
return Expr<T>{Constant<T>{difference.value}};
}
}
return Expr<T>{std::move(x)};
}
template<typename T> Expr<T> Fold(FoldingContext &context, Multiply<T> &&x) {
if (auto folded{FoldOperands(context, x.left(), x.right())}) {
if constexpr (T::category == TypeCategory::Integer) {
auto product{folded->first.MultiplySigned(folded->second)};
if (product.SignedMultiplicationOverflowed()) {
context.messages.Say("INTEGER(%d) multiplication overflowed"_en_US, T::kind);
}
return Expr<T>{Constant<T>{product.lower}};
} else {
auto product{folded->first.Multiply(folded->second, context.rounding)};
RealFlagWarnings(context, product.flags, "multiplication");
return Expr<T>{Constant<T>{product.value}};
}
}
return Expr<T>{std::move(x)};
}
template<typename T> Expr<T> Fold(FoldingContext &context, Divide<T> &&x) {
if (auto folded{FoldOperands(context, x.left(), x.right())}) {
if constexpr (T::category == TypeCategory::Integer) {
auto quotAndRem{folded->first.DivideSigned(folded->second)};
if (quotAndRem.divisionByZero) {
context.messages.Say("INTEGER(%d) division by zero"_en_US, T::kind);
}
if (quotAndRem.overflow) {
context.messages.Say("INTEGER(%d) division overflowed"_en_US, T::kind);
}
return Expr<T>{Constant<T>{quotAndRem.quotient}};
} else {
auto quotient{folded->first.Divide(folded->second, context.rounding)};
RealFlagWarnings(context, quotient.flags, "division");
return Expr<T>{Constant<T>{quotient.value}};
}
}
return Expr<T>{std::move(x)};
}
template<typename T> Expr<T> Fold(FoldingContext &context, Power<T> &&x) {
if (auto folded{FoldOperands(context, x.left(), x.right())}) {
if constexpr (T::category == TypeCategory::Integer) {
auto power{folded->first.Power(folded->second)};
if (power.divisionByZero) {
context.messages.Say("INTEGER(%d) zero to negative power"_en_US, T::kind);
} else if (power.overflow) {
context.messages.Say("INTEGER(%d) power overflowed"_en_US, T::kind);
} else if (power.zeroToZero) {
context.messages.Say("INTEGER(%d) 0**0 is not defined"_en_US, T::kind);
}
return Expr<T>{Constant<T>{power.power}};
} else {
// TODO: real & complex power with non-integral exponent
}
}
return Expr<T>{std::move(x)};
}
template<typename T> Expr<T> Fold(FoldingContext &context, RealToIntPower<T> &&x) {
return std::visit([&](auto &y) -> Expr<T> {
if (auto folded{FoldOperands(context, x.left(), y)}) {
auto power{evaluate::IntPower(folded->first, folded->second)};
RealFlagWarnings(context, power.flags, "power with INTEGER exponent");
return Expr<T>{Constant<T>{power.value}};
} else {
return Expr<T>{std::move(x)};
}
}, x.right().u);
}
template<typename T> Expr<T> Fold(FoldingContext &context, Extremum<T> &&x) {
if (auto folded{FoldOperands(context, x.left(), x.right())}) {
if constexpr (T::category == TypeCategory::Integer) {
if (folded->first.CompareSigned(folded->second) == x.ordering) {
return Expr<T>{Constant<T>{folded->first}};
}
} else if constexpr (T::category == TypeCategory::Real) {
if (folded->first.IsNotANumber() || (folded->first.Compare(folded->second) == Relation::Less) == (x.ordering == Ordering::Less)) {
return Expr<T>{Constant<T>{folded->first}};
}
} else {
if (x.ordering == Compare(folded->first, folded->second)) {
return Expr<T>{Constant<T>{folded->first}};
}
}
return Expr<T>{Constant<T>{folded->second}};
}
return Expr<T>{std::move(x)};
}
}
#endif // FORTRAN_EVALUATE_FOLD_H_

View file

@ -40,23 +40,41 @@ SourceFile::~SourceFile() { Close(); }
static std::vector<std::size_t> FindLineStarts(
const char *source, std::size_t bytes) {
if (bytes == 0) {
return {};
}
CHECK(source[bytes - 1] == '\n' && "missing ultimate newline");
std::vector<std::size_t> result;
std::size_t at{0};
do {
result.push_back(at);
const void *vp{static_cast<const void *>(&source[at])};
const void *vnl{std::memchr(vp, '\n', bytes - at)};
const char *nl{static_cast<const char *>(vnl)};
at = nl + 1 - source;
} while (at < bytes);
result.shrink_to_fit();
if (bytes > 0) {
CHECK(source[bytes - 1] == '\n' && "missing ultimate newline");
std::size_t at{0};
do {
result.push_back(at);
const void *vp{static_cast<const void *>(&source[at])};
const void *vnl{std::memchr(vp, '\n', bytes - at)};
const char *nl{static_cast<const char *>(vnl)};
at = nl + 1 - source;
} while (at < bytes);
result.shrink_to_fit();
}
return result;
}
void SourceFile::RecordLineStarts() {
lineStart_ = FindLineStarts(content_, bytes_);
}
// Cut down the contiguous content of a source file to skip
// things like byte order marks.
void SourceFile::IdentifyPayload() {
content_ = address_;
bytes_ = size_;
if (content_ != nullptr) {
if (bytes_ >= 3 && content_[0] == 0xef && content_[1] == 0xbb &&
content_[2] == 0xbf) {
// UTF-8 encoding of Unicode byte order mark (BOM)
content_ += 3;
bytes_ -= 3;
}
}
}
std::string DirectoryName(std::string path) {
auto lastSlash{path.rfind("/")};
return lastSlash == std::string::npos ? path : path.substr(0, lastSlash);
@ -136,24 +154,28 @@ bool SourceFile::ReadFile(std::string errorPath, std::stringstream *error) {
// Don't bother with small ones. This also helps keep the number
// of open file descriptors from getting out of hand.
if (useMMap && S_ISREG(statbuf.st_mode)) {
bytes_ = static_cast<std::size_t>(statbuf.st_size);
if (bytes_ >= minMapFileBytes &&
size_ = static_cast<std::size_t>(statbuf.st_size);
if (size_ >= minMapFileBytes &&
openFileDescriptors <= maxMapOpenFileDescriptors) {
void *vp = mmap(0, bytes_, PROT_READ, MAP_SHARED, fileDescriptor_, 0);
void *vp = mmap(0, size_, PROT_READ, MAP_SHARED, fileDescriptor_, 0);
if (vp != MAP_FAILED) {
content_ = static_cast<const char *>(const_cast<const void *>(vp));
if (content_[bytes_ - 1] == '\n' &&
std::memchr(vp, '\r', bytes_) == nullptr) {
address_ = static_cast<const char *>(const_cast<const void *>(vp));
IdentifyPayload();
if (bytes_ > 0 && content_[bytes_ - 1] == '\n' &&
std::memchr(static_cast<const void *>(content_), '\r', bytes_) ==
nullptr) {
isMemoryMapped_ = true;
lineStart_ = FindLineStarts(content_, bytes_);
RecordLineStarts();
return true;
}
// The file needs to have its line endings normalized to simple
// newlines. Remap it for a private rewrite in place.
vp = mmap(vp, bytes_, PROT_READ | PROT_WRITE, MAP_PRIVATE,
fileDescriptor_, 0);
vp = mmap(
vp, size_, PROT_READ | PROT_WRITE, MAP_PRIVATE, fileDescriptor_, 0);
if (vp != MAP_FAILED) {
auto mutableContent{static_cast<char *>(vp)};
address_ = static_cast<const char *>(const_cast<const void *>(vp));
IdentifyPayload();
auto mutableContent{const_cast<char *>(content_)};
bytes_ = RemoveCarriageReturns(mutableContent, bytes_);
if (bytes_ > 0) {
if (mutableContent[bytes_ - 1] == '\n' ||
@ -166,13 +188,14 @@ bool SourceFile::ReadFile(std::string errorPath, std::stringstream *error) {
CHECK(isNowReadOnly);
content_ = mutableContent;
isMemoryMapped_ = true;
lineStart_ = FindLineStarts(content_, bytes_);
RecordLineStarts();
return true;
}
}
}
munmap(vp, bytes_);
content_ = nullptr;
munmap(vp, size_);
address_ = content_ = nullptr;
size_ = bytes_ = 0;
}
}
}
@ -199,30 +222,31 @@ bool SourceFile::ReadFile(std::string errorPath, std::stringstream *error) {
--openFileDescriptors;
}
fileDescriptor_ = -1;
bytes_ = buffer.size();
if (bytes_ == 0) {
if (buffer.size() == 0) {
// empty file
content_ = nullptr;
address_ = content_ = nullptr;
size_ = bytes_ = 0;
} else {
normalized_ = buffer.MarshalNormalized();
content_ = normalized_.data();
bytes_ = normalized_.size();
lineStart_ = FindLineStarts(content_, bytes_);
address_ = normalized_.data();
size_ = normalized_.size();
IdentifyPayload();
RecordLineStarts();
}
return true;
}
void SourceFile::Close() {
if (useMMap && isMemoryMapped_) {
munmap(reinterpret_cast<void *>(const_cast<char *>(content_)), bytes_);
munmap(reinterpret_cast<void *>(const_cast<char *>(address_)), size_);
isMemoryMapped_ = false;
} else if (!normalized_.empty()) {
normalized_.clear();
} else if (content_ != nullptr) {
delete[] content_;
} else if (address_ != nullptr) {
delete[] address_;
}
content_ = nullptr;
bytes_ = 0;
address_ = content_ = nullptr;
size_ = bytes_ = 0;
if (fileDescriptor_ > 0) {
close(fileDescriptor_);
--openFileDescriptors;

View file

@ -50,11 +50,15 @@ public:
private:
bool ReadFile(std::string errorPath, std::stringstream *error);
void IdentifyPayload();
void RecordLineStarts();
std::string path_;
int fileDescriptor_{-1};
bool isMemoryMapped_{false};
const char *content_{nullptr};
const char *address_{nullptr}; // raw content
std::size_t size_{0};
const char *content_{nullptr}; // usable content
std::size_t bytes_{0};
std::vector<std::size_t> lineStart_;
std::string normalized_;