mirror of
https://github.com/revng/revng
synced 2026-06-21 14:07:57 +00:00
251 lines
9.3 KiB
C++
251 lines
9.3 KiB
C++
/// \file NodeSizeCalculation.cpp
|
|
|
|
//
|
|
// This file is distributed under the MIT License. See LICENSE.md for details.
|
|
//
|
|
|
|
#include "revng/EarlyFunctionAnalysis/CFGHelpers.h"
|
|
#include "revng/GraphLayout/Graphs.h"
|
|
#include "revng/Model/Binary.h"
|
|
#include "revng/Pipeline/Location.h"
|
|
#include "revng/Pipes/Ranks.h"
|
|
#include "revng/Yield/ControlFlow/Configuration.h"
|
|
#include "revng/Yield/ControlFlow/FallthroughDetection.h"
|
|
#include "revng/Yield/ControlFlow/NodeSizeCalculation.h"
|
|
#include "revng/Yield/Function.h"
|
|
|
|
static yield::layout::Size operator+(const yield::layout::Size &LHS,
|
|
const yield::layout::Size &RHS) {
|
|
return yield::layout::Size(LHS.W + RHS.W, LHS.H + RHS.H);
|
|
}
|
|
|
|
constexpr static yield::layout::Size textSize(std::string_view Text) {
|
|
size_t LineCount = 0;
|
|
size_t MaximumLineLength = 0;
|
|
|
|
size_t PreviousPosition = 0;
|
|
size_t CurrentPosition = Text.find('\n');
|
|
while (CurrentPosition != std::string_view::npos) {
|
|
size_t CurrentLineLength = CurrentPosition - PreviousPosition;
|
|
if (CurrentLineLength > MaximumLineLength)
|
|
MaximumLineLength = CurrentLineLength;
|
|
++LineCount;
|
|
|
|
PreviousPosition = CurrentPosition;
|
|
CurrentPosition = Text.find('\n', CurrentPosition + 1);
|
|
}
|
|
|
|
size_t LastLineLength = Text.size() - PreviousPosition;
|
|
if (LastLineLength > MaximumLineLength)
|
|
MaximumLineLength = LastLineLength;
|
|
if (LastLineLength != 0)
|
|
++LineCount;
|
|
|
|
return yield::layout::Size(MaximumLineLength, LineCount);
|
|
}
|
|
|
|
static yield::layout::Size textSize(const yield::TaggedString &Tagged) {
|
|
revng_assert(!Tagged.Content().empty());
|
|
revng_assert(Tagged.Content().find('\n') == std::string::npos);
|
|
|
|
return yield::layout::Size(Tagged.Content().size(), 1);
|
|
}
|
|
|
|
static yield::layout::Size
|
|
textSize(const SortedVector<yield::TaggedString> &Tagged) {
|
|
revng_assert(!Tagged.empty());
|
|
|
|
size_t LineLength = 0;
|
|
for (yield::TaggedString String : Tagged)
|
|
LineLength += textSize(String).W;
|
|
|
|
return yield::layout::Size(LineLength, 1);
|
|
}
|
|
|
|
constexpr static size_t firstLineSize(std::string_view Text) {
|
|
size_t FirstLineEnd = Text.find('\n');
|
|
if (FirstLineEnd == std::string_view::npos)
|
|
return Text.size();
|
|
else
|
|
return Text.size() - FirstLineEnd;
|
|
}
|
|
|
|
static yield::layout::Size
|
|
fontSize(yield::layout::Size &&Input,
|
|
yield::layout::Dimension FontSize,
|
|
const yield::cfg::Configuration &Configuration) {
|
|
Input.W *= FontSize * Configuration.HorizontalFontFactor;
|
|
Input.H *= FontSize * Configuration.VerticalFontFactor;
|
|
return std::move(Input);
|
|
}
|
|
|
|
static yield::layout::Size
|
|
fontSize(yield::layout::Size &&Input,
|
|
yield::layout::Dimension HorizontalFontSize,
|
|
yield::layout::Dimension VerticalFontSize,
|
|
const yield::cfg::Configuration &Configuration) {
|
|
Input.W *= HorizontalFontSize * Configuration.HorizontalFontFactor;
|
|
Input.H *= VerticalFontSize * Configuration.VerticalFontFactor;
|
|
return std::move(Input);
|
|
}
|
|
|
|
static yield::layout::Size
|
|
singleLineSize(llvm::StringRef Text,
|
|
float Font,
|
|
const yield::cfg::Configuration &Configuration) {
|
|
yield::layout::Size Result = fontSize(textSize(Text), Font, Configuration);
|
|
|
|
Result.W += Configuration.HorizontalInstructionMarginSize * 2;
|
|
Result.H += Configuration.VerticalInstructionMarginSize * 2;
|
|
|
|
return Result;
|
|
}
|
|
|
|
static yield::layout::Size &appendSize(yield::layout::Size &Original,
|
|
const yield::layout::Size &AddOn) {
|
|
if (AddOn.W > Original.W)
|
|
Original.W = AddOn.W;
|
|
Original.H += AddOn.H;
|
|
|
|
return Original;
|
|
}
|
|
|
|
static yield::layout::Size
|
|
instructionSize(const yield::Instruction &Instruction,
|
|
const yield::cfg::Configuration &Configuration,
|
|
size_t CommentIndicatorSize,
|
|
bool IsInDelayedSlot = false) {
|
|
// Instruction body.
|
|
yield::layout::Size DisassembledSize = textSize(Instruction.Disassembled());
|
|
for (const auto &Directive : Instruction.PrecedingDirectives())
|
|
appendSize(DisassembledSize, textSize(Directive.Tags()));
|
|
for (const auto &Directive : Instruction.FollowingDirectives())
|
|
appendSize(DisassembledSize, textSize(Directive.Tags()));
|
|
|
|
yield::layout::Size Result = fontSize(std::move(DisassembledSize),
|
|
Configuration.InstructionFontSize,
|
|
Configuration);
|
|
|
|
// Comment and delayed slot notice.
|
|
yield::layout::Size CommentSize;
|
|
if (!Instruction.Comment().empty()) {
|
|
CommentSize = textSize(Instruction.Comment());
|
|
revng_assert(CommentSize.H == 1, "Multi line comments are not supported.");
|
|
CommentSize.W += CommentIndicatorSize + 1;
|
|
}
|
|
if (IsInDelayedSlot) {
|
|
auto DelayedSlotNoticeSize = textSize("delayed");
|
|
DelayedSlotNoticeSize.W += CommentIndicatorSize + 1;
|
|
appendSize(CommentSize, DelayedSlotNoticeSize);
|
|
}
|
|
|
|
auto CommentBlockSize = fontSize(yield::layout::Size(CommentSize),
|
|
Configuration.CommentFontSize,
|
|
Configuration.InstructionFontSize,
|
|
Configuration);
|
|
if (CommentSize.H > 1) {
|
|
Result.W = std::max(firstLineSize(Instruction.Comment()) + Result.W
|
|
+ CommentIndicatorSize + 1,
|
|
CommentBlockSize.W);
|
|
auto OneLine = fontSize(yield::layout::Size(1, 1),
|
|
Configuration.CommentFontSize,
|
|
Configuration.InstructionFontSize,
|
|
Configuration);
|
|
Result.H += CommentBlockSize.H - OneLine.H;
|
|
} else {
|
|
Result.W += CommentBlockSize.W;
|
|
}
|
|
|
|
// Error.
|
|
if (!Instruction.Error().empty())
|
|
appendSize(Result,
|
|
fontSize(textSize(Instruction.Error())
|
|
+ yield::layout::Size(CommentIndicatorSize + 1, 0),
|
|
Configuration.CommentFontSize,
|
|
Configuration));
|
|
|
|
// Annotation.
|
|
yield::layout::Size RawBytesLengthWithOffsets{ 0, 0 };
|
|
RawBytesLengthWithOffsets.W += Instruction.RawBytes().size() * 3;
|
|
RawBytesLengthWithOffsets.W += CommentIndicatorSize + 5;
|
|
appendSize(Result,
|
|
fontSize(textSize(Instruction.Address().toString())
|
|
+ RawBytesLengthWithOffsets,
|
|
Configuration.AnnotationFontSize,
|
|
Configuration));
|
|
|
|
// Account for the padding.
|
|
Result.W += Configuration.HorizontalInstructionMarginSize * 2;
|
|
Result.H += Configuration.VerticalInstructionMarginSize * 2;
|
|
|
|
return Result;
|
|
}
|
|
|
|
static yield::layout::Size
|
|
basicBlockSize(const yield::BasicBlock &BasicBlock,
|
|
const yield::Function &Function,
|
|
const model::Binary &Binary,
|
|
const yield::cfg::Configuration &Configuration) {
|
|
// Account for the size of the label
|
|
namespace A = model::Architecture;
|
|
auto LabelIndicator = A::getAssemblyLabelIndicator(Binary.Architecture());
|
|
yield::layout::Size Indicator(LabelIndicator.size(), 0);
|
|
yield::layout::Size Result = fontSize(textSize(BasicBlock.Label())
|
|
+ Indicator,
|
|
Configuration.LabelFontSize,
|
|
Configuration);
|
|
Result.H += Configuration.VerticalInstructionMarginSize * 2;
|
|
|
|
namespace A = model::Architecture;
|
|
auto CommentIndicator = A::getAssemblyCommentIndicator(Binary.Architecture());
|
|
|
|
// Account for the sizes of each instruction.
|
|
auto FromIterator = BasicBlock.Instructions().begin();
|
|
auto ToIterator = std::prev(BasicBlock.Instructions().end());
|
|
for (auto Iterator = FromIterator; Iterator != ToIterator; ++Iterator) {
|
|
appendSize(Result,
|
|
instructionSize(*Iterator,
|
|
Configuration,
|
|
CommentIndicator.size()));
|
|
}
|
|
appendSize(Result,
|
|
instructionSize(*ToIterator++,
|
|
Configuration,
|
|
CommentIndicator.size(),
|
|
BasicBlock.HasDelaySlot()));
|
|
revng_assert(ToIterator == BasicBlock.Instructions().end());
|
|
|
|
return Result;
|
|
}
|
|
|
|
void yield::cfg::calculateNodeSizes(PreLayoutGraph &Graph,
|
|
const yield::Function &Function,
|
|
const model::Binary &Binary,
|
|
model::NameBuilder &NameBuilder,
|
|
const Configuration &Configuration) {
|
|
for (PreLayoutNode *Node : Graph.nodes()) {
|
|
revng_assert(Node != nullptr);
|
|
|
|
if (!Node->isEmpty()) {
|
|
// A normal node.
|
|
BasicBlockID BasicBlock = Node->getBasicBlock();
|
|
if (auto Iterator = Function.Blocks().find(BasicBlock);
|
|
Iterator != Function.Blocks().end()) {
|
|
Node->Size = basicBlockSize(*Iterator, Function, Binary, Configuration);
|
|
} else if (const auto *F = tryGetFunction(Binary, BasicBlock)) {
|
|
Node->Size = singleLineSize(NameBuilder.name(*F).str(),
|
|
Configuration.InstructionFontSize,
|
|
Configuration);
|
|
} else {
|
|
revng_abort("The value of this node is not a known address");
|
|
}
|
|
} else {
|
|
// A node without any content, most likely an entry/exit marker.
|
|
Node->Size = { 30, 30 };
|
|
}
|
|
|
|
Node->Size.W += Configuration.InternalNodeMarginSize * 2;
|
|
Node->Size.H += Configuration.InternalNodeMarginSize * 2;
|
|
}
|
|
}
|