[Debug] Make paths in HGLDD files relative (#6451)

Make paths emitted into HGLDD companion files relative to the file
itself, or emit absolute paths. For example, the `foo/bar/hello.dd`
companion file to `foo/bar/hello.sv` will now refer to the SV file as
`hello.sv`, and to a `src/input.scala` file as `../../src/input.scala`.
If paths cannot be made relative, for example because the output
directory contains `../` components that cannot be handled, the paths
are made absolute.

This fixes an issue where beforehand a tool consuming the HGLDD file
would have to know the working directory in which `firtool` was run to
create the HGLDD file. Now the files are self-contained, and paths are
anchored at the HGLDD file itself.
This commit is contained in:
Fabian Schuiki 2023-12-08 09:37:38 -08:00 committed by GitHub
parent 37b25335b9
commit fd9392d31e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 126 additions and 29 deletions

View File

@ -115,6 +115,51 @@ struct DenseMapInfo<JValue> {
};
} // namespace llvm
/// Make the given `path` relative to the `relativeTo` path and store the result
/// in `relativePath`. Returns whether the conversion was successful. Fails if
/// the `relativeTo` path has a longer prefix of `../` than `path`, or if it
/// contains any non-prefix `../` components. Does not clear `relativePath`
/// before appending to it.
static bool makePathRelative(StringRef path, StringRef relativeTo,
SmallVectorImpl<char> &relativePath) {
using namespace llvm::sys;
auto sourceIt = path::begin(path);
auto outputIt = path::begin(relativeTo);
auto sourceEnd = path::end(path);
auto outputEnd = path::end(relativeTo);
// Strip common prefix:
// - (), () -> (), ()
// - (a/b/c/d), (a/b/e/f) -> (c/d), (e/f)
// - (a/b), (a/b/c/d) -> (), (c/d)
// - (../a/b), (../a/c) -> (b), (c)
while (outputIt != outputEnd && sourceIt != sourceEnd &&
*outputIt == *sourceIt) {
++outputIt;
++sourceIt;
}
// For every component in the output path insert a `../` into the source
// path. Abort if the output path contains a `../`, because we don't
// know where that climbs out to. Consider the changes to the following
// output-source pairs as an example:
//
// - (a/b), (c/d) -> (), (../../c/d)
// - (), (a/b) -> (), (a/b)
// - (../a), (c/d) -> (../a), (c/d)
// - (a/../b), (c/d) -> (../b), (../c/d)
for (; outputIt != outputEnd && *outputIt != ".."; ++outputIt)
path::append(relativePath, "..");
for (; sourceIt != sourceEnd; ++sourceIt)
path::append(relativePath, *sourceIt);
// If there are no more remaining components in the output path, we were
// successfully able to translate them into `..` in the source path.
// Otherwise the `relativeTo` path contained `../` components that we could
// not handle.
return outputIt == outputEnd;
}
//===----------------------------------------------------------------------===//
// HGLDD File Emission
//===----------------------------------------------------------------------===//
@ -192,7 +237,7 @@ struct FileEmitter {
SmallVector<DIModule *> modules;
SmallString<64> outputFileName;
StringAttr hdlFile;
SmallMapVector<StringAttr, unsigned, 8> sourceFiles;
SmallMapVector<StringAttr, std::pair<StringAttr, unsigned>, 8> sourceFiles;
Namespace objectNamespace;
SmallMapVector<JValue, StringRef, 8> structDefs;
SmallString<128> structNameHint;
@ -205,32 +250,7 @@ struct FileEmitter {
void emitVariable(JOStream &json, DIVariable *variable);
EmittedExpr emitExpression(Value value);
/// Get a numeric index for the given `sourceFile`. Populates `sourceFiles`
/// with a unique ID assignment for each source file.
unsigned getSourceFile(StringAttr sourceFile, bool emitted) {
// Apply the source file prefix if this is a source file (emitted = false).
if (!emitted && !options->sourceFilePrefix.empty() &&
!llvm::sys::path::is_absolute(sourceFile.getValue())) {
SmallString<64> buffer;
buffer = options->sourceFilePrefix;
llvm::sys::path::append(buffer, sourceFile.getValue());
sourceFile = StringAttr::get(sourceFile.getContext(), buffer);
}
// Apply the output file prefix if this is an outpu file (emitted = true).
if (emitted && !options->outputFilePrefix.empty() &&
!llvm::sys::path::is_absolute(sourceFile.getValue())) {
SmallString<64> buffer;
buffer = options->outputFilePrefix;
llvm::sys::path::append(buffer, sourceFile.getValue());
sourceFile = StringAttr::get(sourceFile.getContext(), buffer);
}
auto &slot = sourceFiles[sourceFile];
if (slot == 0)
slot = sourceFiles.size();
return slot;
}
unsigned getSourceFile(StringAttr sourceFile, bool emitted);
FileLineColLoc findBestLocation(Location loc, bool emitted) {
return ::findBestLocation(loc, emitted, options->onlyExistingFileLocs);
@ -314,6 +334,82 @@ struct FileEmitter {
} // namespace
/// Get a numeric index for the given `sourceFile`. Populates `sourceFiles`
/// with a unique ID assignment for each source file.
unsigned FileEmitter::getSourceFile(StringAttr sourceFile, bool emitted) {
using namespace llvm::sys;
// Check if we have already allocated an ID for this source file. If we
// have, return it. Otherwise, assign a new ID and normalize the path
// according to HGLDD requirements.
auto &slot = sourceFiles[sourceFile];
if (slot.first)
return slot.second;
slot.second = sourceFiles.size();
// If the source file is an absolute path, simply use that unchanged.
if (path::is_absolute(sourceFile.getValue())) {
slot.first = sourceFile;
return slot.second;
}
// If specified, apply the output file prefix if this is an output file
// (`emitted` is true), or the source file prefix if this is a source file
// (`emitted` is false).
StringRef filePrefix =
emitted ? options->outputFilePrefix : options->sourceFilePrefix;
if (!filePrefix.empty()) {
SmallString<64> buffer = filePrefix;
path::append(buffer, sourceFile.getValue());
slot.first = StringAttr::get(sourceFile.getContext(), buffer);
return slot.second;
}
// Otherwise make the path relative to the HGLDD output file.
// Remove any `./` and `../` inside the path. This has also been applied
// to the `outputFileName`. As a result, both paths start with zero or
// more `../`, followed by the rest of the path without any `./` or `../`.
SmallString<64> sourcePath = sourceFile.getValue();
path::remove_dots(sourcePath, true);
// If the output file is also relative, try to determine the relative path
// between them directly.
StringRef relativeToDir = path::parent_path(outputFileName);
if (!path::is_absolute(outputFileName)) {
SmallString<64> buffer;
if (makePathRelative(sourcePath, relativeToDir, buffer)) {
slot.first = StringAttr::get(sourceFile.getContext(), buffer);
return slot.second;
}
}
// If the above failed, try to make the output and source paths absolute and
// retry computing a relative path. Only do this if conversion to absolute
// paths is successful for both paths, and if the resulting paths have at
// least the first path component in common. This prevents computing a
// relative path between `/home/foo/bar` and `/tmp/baz/noob` as
// `../../../tmp/baz/noob`.
SmallString<64> outputPath = relativeToDir;
fs::make_absolute(sourcePath);
fs::make_absolute(outputPath);
if (path::is_absolute(sourcePath) && path::is_absolute(outputPath)) {
auto firstSourceComponent = *path::begin(path::relative_path(sourcePath));
auto firstOutputComponent = *path::begin(path::relative_path(outputPath));
if (firstSourceComponent == firstOutputComponent) {
SmallString<64> buffer;
if (makePathRelative(sourcePath, outputPath, buffer)) {
slot.first = StringAttr::get(sourceFile.getContext(), buffer);
return slot.second;
}
}
}
// Otherwise simply use the absolute source file path.
slot.first = StringAttr::get(sourceFile.getContext(), sourcePath);
return slot.second;
}
void FileEmitter::emit(llvm::raw_ostream &os) {
JOStream json(os, 2);
emit(json);
@ -346,8 +442,8 @@ void FileEmitter::emit(JOStream &json) {
json.attributeObject("HGLDD", [&] {
json.attribute("version", "1.0");
json.attributeArray("file_info", [&] {
for (auto [file, index] : sourceFiles)
json.value(file.getValue());
for (auto [key, fileAndId] : sourceFiles)
json.value(fileAndId.first.getValue());
});
if (hdlFileIndex)
json.attribute("hdl_file_index", *hdlFileIndex);
@ -832,6 +928,7 @@ Emitter::Emitter(Operation *module, const EmitHGLDDOptions &options)
else
llvm::sys::path::append(emitter.outputFileName, fileName);
llvm::sys::path::replace_extension(emitter.outputFileName, "dd");
llvm::sys::path::remove_dots(emitter.outputFileName, true);
files.push_back(std::move(emitter));
}