[flang] Implement provenance -> CharBlock map

Original-commit: flang-compiler/f18@f2e7b6cd72
Reviewed-on: https://github.com/flang-compiler/f18/pull/715
Tree-same-pre-rewrite: false
This commit is contained in:
peter klausler 2019-09-06 13:00:47 -07:00
parent fa3410d5fb
commit 73329265ff
7 changed files with 151 additions and 6 deletions

View File

@ -1,4 +1,4 @@
// Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved.
// Copyright (c) 2018-2019, 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.
@ -40,7 +40,7 @@ public:
return start_ == that.start_ && size_ == that.size_;
}
constexpr bool operator!=(const Interval &that) const {
return start_ != that.start_ || size_ != that.size_;
return !(*this == that);
}
constexpr const A &start() const { return start_; }
@ -99,16 +99,16 @@ public:
}
constexpr Interval Intersection(const Interval &that) const {
if (start_ >= that.NextAfter()) {
if (that.NextAfter() <= start_) {
return {};
} else if (start_ >= that.start_) {
} else if (that.start_ <= start_) {
auto skip{start_ - that.start_};
return {start_, std::min(size_, that.size_ - skip)};
} else if (NextAfter() <= that.start_) {
return {};
} else {
auto skip{that.start_ - start_};
return {that.start_, std::min(that.size_ - size_ - skip)};
return {that.start_, std::min(that.size_, size_ - skip)};
}
}

View File

@ -1,4 +1,4 @@
// Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved.
// Copyright (c) 2018-2019, 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.

View File

@ -78,6 +78,9 @@ void Parsing::Prescan(const std::string &path, Options options) {
*sourceFile, ProvenanceRange{}, options.isModuleFile)};
prescanner.Prescan(range);
cooked_.Marshal();
if (options.needProvenanceRangeToCharBlockMappings) {
cooked_.CompileProvenanceRangeToOffsetMappings();
}
}
void Parsing::DumpCookedChars(std::ostream &out) const {

View File

@ -41,6 +41,7 @@ struct Options {
std::vector<Predefinition> predefinitions;
bool instrumentedParse{false};
bool isModuleFile{false};
bool needProvenanceRangeToCharBlockMappings{false};
};
class Parsing {

View File

@ -14,10 +14,51 @@
#include "provenance.h"
#include "../common/idioms.h"
#include <algorithm>
#include <utility>
namespace Fortran::parser {
ProvenanceRangeToOffsetMappings::ProvenanceRangeToOffsetMappings() {}
ProvenanceRangeToOffsetMappings::~ProvenanceRangeToOffsetMappings() {}
void ProvenanceRangeToOffsetMappings::Put(
ProvenanceRange range, std::size_t offset) {
auto fromTo{map_.equal_range(range)};
for (auto iter{fromTo.first}; iter != fromTo.second; ++iter) {
if (range == iter->first) {
iter->second = std::min(offset, iter->second);
return;
}
}
if (fromTo.second != map_.end()) {
map_.emplace_hint(fromTo.second, range, offset);
} else {
map_.emplace(range, offset);
}
}
std::optional<std::size_t> ProvenanceRangeToOffsetMappings::Map(
ProvenanceRange range) const {
auto fromTo{map_.equal_range(range)};
std::optional<std::size_t> result;
for (auto iter{fromTo.first}; iter != fromTo.second; ++iter) {
ProvenanceRange that{iter->first};
if (that.Contains(range)) {
std::size_t offset{iter->second + that.MemberOffset(range.start())};
if (!result.has_value() || offset < *result) {
result = offset;
}
}
}
return result;
}
bool ProvenanceRangeToOffsetMappings::WhollyPrecedes::operator()(
ProvenanceRange before, ProvenanceRange after) const {
return before.start() + before.size() <= after.start();
}
void OffsetToProvenanceMappings::clear() { provenanceMap_.clear(); }
void OffsetToProvenanceMappings::swap(OffsetToProvenanceMappings &that) {
@ -83,6 +124,29 @@ void OffsetToProvenanceMappings::RemoveLastBytes(std::size_t bytes) {
}
}
ProvenanceRangeToOffsetMappings OffsetToProvenanceMappings::Invert(
const AllSources &allSources) const {
ProvenanceRangeToOffsetMappings result;
for (const auto &contig : provenanceMap_) {
ProvenanceRange range{contig.range};
while (!range.empty()) {
ProvenanceRange source{allSources.IntersectionWithSourceFiles(range)};
if (source.empty()) {
break;
}
result.Put(
source, contig.start + contig.range.MemberOffset(source.start()));
Provenance after{source.NextAfter()};
if (range.Contains(after)) {
range = range.Suffix(range.MemberOffset(after));
} else {
break;
}
}
}
return result;
}
AllSources::AllSources() : range_{1, 1} {
// Start the origin_ array with a dummy entry that has a forced provenance,
// so that provenance offset 0 remains reserved as an uninitialized
@ -264,6 +328,22 @@ Provenance AllSources::CompilerInsertionProvenance(char ch) {
return newCharProvenance;
}
ProvenanceRange AllSources::IntersectionWithSourceFiles(
ProvenanceRange range) const {
if (range.empty()) {
return {};
} else {
const Origin &origin{MapToOrigin(range.start())};
if (std::holds_alternative<Inclusion>(origin.u)) {
return range.Intersection(origin.covers);
} else {
auto skip{
origin.covers.size() - origin.covers.MemberOffset(range.start())};
return IntersectionWithSourceFiles(range.Suffix(skip));
}
}
}
AllSources::Origin::Origin(ProvenanceRange r, const SourceFile &source)
: u{Inclusion{source}}, covers{r} {}
AllSources::Origin::Origin(ProvenanceRange r, const SourceFile &included,
@ -321,6 +401,15 @@ std::optional<ProvenanceRange> CookedSource::GetProvenanceRange(
return {ProvenanceRange{first.start(), last.start() - first.start()}};
}
std::optional<CharBlock> CookedSource::GetCharBlock(
ProvenanceRange range) const {
if (auto to{invertedMap_.Map(range)}) {
return CharBlock{data_.c_str() + *to, range.size()};
} else {
return std::nullopt;
}
}
void CookedSource::Marshal() {
CHECK(provenanceMap_.SizeInBytes() == buffer_.bytes());
provenanceMap_.Put(allSources_.AddCompilerInsertion("(after end of source)"));
@ -328,11 +417,27 @@ void CookedSource::Marshal() {
buffer_.clear();
}
void CookedSource::CompileProvenanceRangeToOffsetMappings() {
if (invertedMap_.empty()) {
invertedMap_ = provenanceMap_.Invert(allSources_);
}
}
static void DumpRange(std::ostream &o, const ProvenanceRange &r) {
o << "[" << r.start().offset() << ".." << r.Last().offset() << "] ("
<< r.size() << " bytes)";
}
std::ostream &ProvenanceRangeToOffsetMappings::Dump(std::ostream &o) const {
for (const auto &m : map_) {
o << "provenances ";
DumpRange(o, m.first);
o << " -> offsets [" << m.second << ".." << (m.second + m.first.size() - 1)
<< "]\n";
}
return o;
}
std::ostream &OffsetToProvenanceMappings::Dump(std::ostream &o) const {
for (const ContiguousProvenanceMapping &m : provenanceMap_) {
std::size_t n{m.range.size()};
@ -384,6 +489,8 @@ std::ostream &CookedSource::Dump(std::ostream &o) const {
allSources_.Dump(o);
o << "CookedSource::provenanceMap_:\n";
provenanceMap_.Dump(o);
o << "CookedSource::invertedMap_:\n";
invertedMap_.Dump(o);
return o;
}
}

View File

@ -22,6 +22,7 @@
#include "../common/idioms.h"
#include "../common/interval.h"
#include <cstddef>
#include <functional>
#include <map>
#include <memory>
#include <optional>
@ -52,6 +53,8 @@ namespace Fortran::parser {
// by the upper bits of an offset, but that does not appear to be
// necessary.)
class AllSources;
class Provenance {
public:
Provenance() {}
@ -83,6 +86,31 @@ private:
using ProvenanceRange = common::Interval<Provenance>;
// Maps contiguous ranges of byte offsets in original source files to
// contiguous ranges in the cooked character stream; essentially a
// partial inversion of OffsetToProvenanceMappings (below).
// Used for implementing the first step of mapping an identifier
// selected in a code editor to one of its declarative statements.
class ProvenanceRangeToOffsetMappings {
public:
ProvenanceRangeToOffsetMappings();
~ProvenanceRangeToOffsetMappings();
bool empty() const { return map_.empty(); }
void Put(ProvenanceRange, std::size_t offset);
std::optional<std::size_t> Map(ProvenanceRange) const;
std::ostream &Dump(std::ostream &) const;
private:
// A comparison function object for use in std::multimap<Compare=>.
// Intersecting intervals will effectively compare equal, not being
// either < nor >= each other.
struct WhollyPrecedes {
bool operator()(ProvenanceRange, ProvenanceRange) const;
};
std::multimap<ProvenanceRange, std::size_t, WhollyPrecedes> map_;
};
// Maps 0-based local offsets in some contiguous range (e.g., a token
// sequence) to their provenances. Lookup time is on the order of
// O(log(#of intervals with contiguous provenances)). As mentioned
@ -98,6 +126,7 @@ public:
void Put(const OffsetToProvenanceMappings &);
ProvenanceRange Map(std::size_t at) const;
void RemoveLastBytes(std::size_t);
ProvenanceRangeToOffsetMappings Invert(const AllSources &) const;
std::ostream &Dump(std::ostream &) const;
private:
@ -149,6 +178,7 @@ public:
int GetLineNumber(Provenance) const; // __LINE__
Provenance CompilerInsertionProvenance(char ch);
Provenance CompilerInsertionProvenance(const char *, std::size_t);
ProvenanceRange IntersectionWithSourceFiles(ProvenanceRange) const;
std::ostream &Dump(std::ostream &) const;
private:
@ -207,6 +237,7 @@ public:
bool IsValid(ProvenanceRange r) const { return allSources_.IsValid(r); }
std::optional<ProvenanceRange> GetProvenanceRange(CharBlock) const;
std::optional<CharBlock> GetCharBlock(ProvenanceRange) const;
// The result of a Put() is the offset that the new data
// will have in the eventually marshaled contiguous buffer.
@ -227,6 +258,7 @@ public:
}
void Marshal(); // marshals text into one contiguous block
void CompileProvenanceRangeToOffsetMappings();
std::string AcquireData() { return std::move(data_); }
std::ostream &Dump(std::ostream &) const;
@ -235,6 +267,7 @@ private:
CharBuffer buffer_; // before Marshal()
std::string data_; // all of it, prescanned and preprocessed
OffsetToProvenanceMappings provenanceMap_;
ProvenanceRangeToOffsetMappings invertedMap_;
};
}
#endif // FORTRAN_PARSER_PROVENANCE_H_

View File

@ -424,6 +424,7 @@ int main(int argc, char *const argv[]) {
arg == "-fxor-operator");
} else if (arg == "-fdebug-dump-provenance") {
driver.dumpProvenance = true;
options.needProvenanceRangeToCharBlockMappings = true;
} else if (arg == "-fdebug-dump-parse-tree") {
driver.dumpParseTree = true;
} else if (arg == "-fdebug-dump-symbols") {