forked from OSchip/llvm-project
[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:
parent
fa3410d5fb
commit
73329265ff
|
@ -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)};
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -41,6 +41,7 @@ struct Options {
|
|||
std::vector<Predefinition> predefinitions;
|
||||
bool instrumentedParse{false};
|
||||
bool isModuleFile{false};
|
||||
bool needProvenanceRangeToCharBlockMappings{false};
|
||||
};
|
||||
|
||||
class Parsing {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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_
|
||||
|
|
|
@ -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") {
|
||||
|
|
Loading…
Reference in New Issue