mirror of
https://github.com/revng/revng
synced 2026-06-21 14:07:57 +00:00
1429b526ab
This commit drops libptc in favor of its new form libtcg. It brings several improvements, among which: * The QEMU version we work on has been upgraded. * CPUStateAccessAnalysis has been reimplemented in a way that makes it easier to debug and solves some limitations (e.g., tracking leaking pointers). * Identification of pieces of the CPU state that are read by each helper and fixing access to the CPU state is now performed at build-time. * We no longer mmap the code we need to translate, dropping all the issues related to code that needed to be mapped where something is already present. * We now have two distinct flavors of helper modules: the full one and the "slim" one. The latter contains the definition only of functions we intend to inline. It is used in most of the pipeline, a good thing since we spend less time optimizing code we don't really care about. The full module is only used on the re-compilation branch of the pipeline. * We no longer split the `cpu_loop` function. * We change MetaAddress to rely on architectures from `model::` as opposed to the LLVM ones. * We no longer attach debug info to LLVM IR containing the original assembly. * We now verify that the lifted code only contains code we expect.
597 lines
20 KiB
C++
597 lines
20 KiB
C++
/// \file CPUStateUsage.cpp
|
|
|
|
//
|
|
// This file is distributed under the MIT License. See LICENSE.md for details.
|
|
//
|
|
|
|
#include <cstdint>
|
|
#include <iterator>
|
|
#include <optional>
|
|
|
|
#include "llvm/ADT/STLExtras.h"
|
|
#include "llvm/ADT/SmallVector.h"
|
|
#include "llvm/IR/LLVMContext.h"
|
|
#include "llvm/IR/Metadata.h"
|
|
#include "llvm/IR/ModuleSlotTracker.h"
|
|
|
|
#include "revng/ADT/RecursiveCoroutine.h"
|
|
#include "revng/HelperArgumentsAnalysis/Annotation.h"
|
|
#include "revng/Support/IRHelpers.h"
|
|
|
|
#include "CPUStateUsage.h"
|
|
#include "Function.h"
|
|
|
|
namespace aua {
|
|
|
|
static RecursiveCoroutine<PointerSet> fromValueImpl(const Value &V) {
|
|
switch (V.kind()) {
|
|
case Value::Kind::Invalid:
|
|
revng_abort();
|
|
case Value::Kind::Constant:
|
|
rc_return PointerSet::fromConstant(llvm::cast<ConstantValue>(V).value());
|
|
|
|
case Value::Kind::AnyOf: {
|
|
PointerSet Result;
|
|
for (const Value *Operand : llvm::cast<AnyOfValue>(V).operands())
|
|
Result.merge(rc_recur fromValueImpl(*Operand));
|
|
rc_return Result;
|
|
}
|
|
|
|
case Value::Kind::BinaryOperator: {
|
|
auto &Operator = llvm::cast<BinaryOperatorValue>(V);
|
|
PointerSet LHS = rc_recur fromValueImpl(Operator.firstOperand());
|
|
PointerSet RHS = rc_recur fromValueImpl(Operator.secondOperand());
|
|
|
|
switch (Operator.type()) {
|
|
case BinaryOperatorValue::Invalid:
|
|
revng_abort();
|
|
case BinaryOperatorValue::Add:
|
|
rc_return LHS.add(RHS);
|
|
case BinaryOperatorValue::Subtract:
|
|
rc_return LHS.add(RHS.negate());
|
|
case BinaryOperatorValue::Multiply:
|
|
// TODO: we could restrict the possible values using SCEV/LVI
|
|
if (const int64_t *Value = LHS.getConstant())
|
|
rc_return PointerSet::fromStrided(*Value);
|
|
else if (const int64_t *Value = RHS.getConstant())
|
|
rc_return PointerSet::fromStrided(*Value);
|
|
else
|
|
rc_return PointerSet::unknown();
|
|
}
|
|
}
|
|
|
|
case Value::Kind::Argument:
|
|
rc_return PointerSet::unknown();
|
|
case Value::Kind::FunctionOf:
|
|
rc_return PointerSet::fromStrided(1);
|
|
}
|
|
}
|
|
|
|
PointerSet PointerSet::fromValue(const Value &V) {
|
|
return fromValueImpl(V);
|
|
}
|
|
|
|
[[nodiscard]] PointerSet PointerSet::add(const PointerSet &Other) const {
|
|
PointerSet Result;
|
|
|
|
for (int64_t LHS : Offsets)
|
|
for (int64_t RHS : Other.Offsets)
|
|
Result.Offsets.insert(LHS + RHS);
|
|
|
|
std::set_union(Strides.begin(),
|
|
Strides.end(),
|
|
Other.Strides.begin(),
|
|
Other.Strides.end(),
|
|
std::inserter(Result.Strides, Result.Strides.end()));
|
|
|
|
return Result;
|
|
}
|
|
|
|
[[nodiscard]] std::string PointerSet::toString() const {
|
|
std::string Result = "{";
|
|
for (int64_t Offset : Offsets)
|
|
Result += " " + std::to_string(Offset);
|
|
Result += " }";
|
|
|
|
for (auto &&[Index, Stride] : llvm::enumerate(Strides))
|
|
Result += " + i" + std::to_string(Index) + " * " + std::to_string(Stride);
|
|
|
|
return Result;
|
|
}
|
|
|
|
std::optional<llvm::DenseSet<uint64_t>>
|
|
CPUStateUsageAnalysis::computeAccessesInRoot(const Value &Offset) const {
|
|
llvm::DenseSet<uint64_t> Result;
|
|
|
|
revng_log(Log, "PointerSet: " << PointerSet::fromValue(Offset).toString());
|
|
|
|
auto Pointers = PointerSet::fromValue(Offset).enumerate();
|
|
|
|
revng_assert(Pointers.size() > 0);
|
|
|
|
for (const PointerSet &Pointer : Pointers) {
|
|
revng_log(Log, "Considering " << Pointer.toString());
|
|
LoggerIndent<> Indent(Log);
|
|
|
|
// Collect the number of elements in arrays whose elements match the
|
|
// strides in Pointer
|
|
using namespace llvm;
|
|
APInt Offset(64, Pointer.offset());
|
|
llvm::DenseMap<int64_t, uint64_t> StrideToArraySize;
|
|
|
|
Type *CurrentType = &RootType;
|
|
std::optional<APInt> MaybeIndex = APInt(32, 0);
|
|
while (MaybeIndex.has_value()) {
|
|
if (Log.isEnabled()) {
|
|
Log << "Considering offset " << Offset.getLimitedValue() << " in type ";
|
|
CurrentType->print(*Log.getAsLLVMStream(), true);
|
|
Log << DoLog;
|
|
}
|
|
|
|
if (auto *Array = dyn_cast<ArrayType>(CurrentType)) {
|
|
auto ElementSize = DL.getTypeAllocSize(Array->getElementType());
|
|
revng_assert(StrideToArraySize.count(ElementSize) == 0);
|
|
auto Elements = Array->getArrayNumElements();
|
|
revng_log(Log,
|
|
"Registering array of " << Elements << " elements of size "
|
|
<< ElementSize);
|
|
StrideToArraySize[ElementSize] = Elements;
|
|
} else if (auto *Struct = dyn_cast<StructType>(CurrentType)) {
|
|
auto Elements = Struct->getNumElements();
|
|
if (Elements > 1) {
|
|
llvm::Type *FirstType = Struct->getElementType(0);
|
|
auto IsAsFirst = [FirstType](llvm::Type *ElementType) {
|
|
return ElementType == FirstType;
|
|
};
|
|
if (llvm::all_of(Struct->elements(), IsAsFirst)) {
|
|
auto ElementSize = DL.getTypeAllocSize(FirstType);
|
|
revng_assert(StrideToArraySize.count(ElementSize) == 0);
|
|
revng_log(Log,
|
|
"Registering struct of "
|
|
<< Elements << " elements of size " << ElementSize);
|
|
StrideToArraySize[ElementSize] = Elements;
|
|
}
|
|
}
|
|
}
|
|
|
|
MaybeIndex = DL.getGEPIndexForOffset(CurrentType, Offset);
|
|
if (MaybeIndex) {
|
|
revng_log(Log, "Found at index " << MaybeIndex->getLimitedValue());
|
|
} else {
|
|
revng_log(Log, "Not found");
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Enumerate all the possible offsets considering all the arrays
|
|
struct ArrayEntry {
|
|
uint64_t Index = 0;
|
|
const uint64_t ArraySize = 0;
|
|
const int64_t Stride = 0;
|
|
};
|
|
SmallVector<ArrayEntry, 2> WorkList;
|
|
|
|
// Check if we found all the strides
|
|
if (Pointer.strides().size() > 0) {
|
|
|
|
// Identify the strides we couldn't assign
|
|
SmallVector<int64_t, 2> MissingStrides;
|
|
for (int64_t Stride : Pointer.strides()) {
|
|
auto It = StrideToArraySize.find(Stride);
|
|
if (It == StrideToArraySize.end()) {
|
|
revng_log(Log,
|
|
"Couldn't find an array with elements of size "
|
|
<< Stride << ". Will try with a compatible array later.");
|
|
MissingStrides.push_back(Stride);
|
|
continue;
|
|
}
|
|
StrideToArraySize.erase(It);
|
|
WorkList.push_back({ 0, It->second, Stride });
|
|
}
|
|
|
|
// Try to assign to a compatible array, if it had a different array
|
|
// element size
|
|
for (int64_t Stride : MissingStrides) {
|
|
bool Found = false;
|
|
for (auto &&[ElementSize, Elements] : StrideToArraySize) {
|
|
auto ArraySize = Elements * ElementSize;
|
|
if (ArraySize % Stride == 0) {
|
|
WorkList.push_back({ 0, ArraySize / Stride, Stride });
|
|
StrideToArraySize.erase(ElementSize);
|
|
revng_log(Log,
|
|
"Stride " << Stride << " assigned to an array of "
|
|
<< Elements << " of size " << ElementSize);
|
|
Found = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (not Found) {
|
|
revng_log(Log,
|
|
"Couldn't find an array with elements compatible with "
|
|
"stride "
|
|
<< Stride);
|
|
return std::nullopt;
|
|
}
|
|
}
|
|
|
|
} else {
|
|
WorkList.push_back({ 0, 1, 0 });
|
|
}
|
|
|
|
bool Done = WorkList.empty();
|
|
while (not Done) {
|
|
// Compute new entry
|
|
int64_t NewOffset = Pointer.offset();
|
|
for (ArrayEntry &Entry : WorkList)
|
|
NewOffset += Entry.Index * Entry.Stride;
|
|
Result.insert(NewOffset);
|
|
|
|
// Move forward
|
|
Done = true;
|
|
for (ArrayEntry &Entry : WorkList) {
|
|
++Entry.Index;
|
|
if (Entry.Index == Entry.ArraySize) {
|
|
Entry.Index = 0;
|
|
} else {
|
|
Done = false;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return Result;
|
|
}
|
|
|
|
void CPUStateUsageAnalysis::analyze(llvm::Function &Function) {
|
|
llvm::Task T(2, "Analyze CPU state usage of " + Function.getName());
|
|
FastValuePrinter Printer(*Function.getParent());
|
|
revng_log(Log, "Collecting interprocedural data in " << Function.getName());
|
|
LoggerIndent<> Indent(Log);
|
|
|
|
revng_log(Log, "Collecting global results");
|
|
T.advance("Collecting global results");
|
|
CPUStateUsage HelperResult;
|
|
HelperResult.RawAUAResults = collectGlobalAUAResults(Function);
|
|
|
|
T.advance("Processing arguments");
|
|
revng_log(Log, "Processing arguments of " << Function.getName());
|
|
LoggerIndent<> Indent2(Log);
|
|
|
|
for (auto &&[ArgumentIndex, Argument] : llvm::enumerate(Function.args())) {
|
|
auto &Offsets = Initializer.getOffsetsFor(Argument);
|
|
SmallVector<const aua::Value *, 2> OffsetsValues;
|
|
for (uint64_t Offset : Offsets)
|
|
OffsetsValues.push_back(&TheContext.getConstant(Offset));
|
|
|
|
if (Offsets.size() == 0)
|
|
continue;
|
|
|
|
if (Log.isEnabled()) {
|
|
Log << "Processing argument " << ArgumentIndex << " of "
|
|
<< Function.getName() << " which can be at the following offsets:";
|
|
for (uint64_t Offset : Offsets) {
|
|
Log << " " << Offset;
|
|
}
|
|
Log << DoLog;
|
|
}
|
|
|
|
LoggerIndent<> Indent(Log);
|
|
|
|
// If the argument is escaping, bail out
|
|
bool Escapes = false;
|
|
for (const EscapedArgument &Escaper :
|
|
HelperResult.RawAUAResults.EscapedArguments) {
|
|
// TODO: we could use lower_bound with
|
|
// EscapedArgument(nullptr, Escaper.index());
|
|
if (Escaper.index() == ArgumentIndex) {
|
|
HelperCPUStateUsage[&Function] = CPUStateUsage::escapes();
|
|
llvm::Instruction *I = &Escaper.location();
|
|
Escaping.insert(I);
|
|
Escapes = true;
|
|
revng_log(Log,
|
|
"Argument escapes at " << Printer.toString(*I) << " in "
|
|
<< Function.getName());
|
|
}
|
|
}
|
|
|
|
if (Escapes)
|
|
return;
|
|
|
|
revng_log(Log,
|
|
"Consindering " << HelperResult.RawAUAResults.Accesses.size()
|
|
<< " memory accesses");
|
|
for (auto &[Access, Count] : HelperResult.RawAUAResults.Accesses) {
|
|
|
|
if (not Access.start().collectArguments().contains(ArgumentIndex)) {
|
|
revng_log(Log,
|
|
"Ignoring memory access since it's not based on argument "
|
|
<< ArgumentIndex);
|
|
continue;
|
|
}
|
|
|
|
revng_log(Log,
|
|
"Considering access starting at "
|
|
<< Access.start().toString() << " in "
|
|
<< getName(Access.location()));
|
|
|
|
llvm::DenseMap<uint64_t, const aua::Value *> Replacements;
|
|
|
|
auto &C = TheContext;
|
|
SmallVector<const aua::Value *, 2> Copy = OffsetsValues;
|
|
Replacements[ArgumentIndex] = &C.getAnyOf(std::move(Copy));
|
|
const Value &Pointer = *C.replaceArguments(Access.start(), Replacements);
|
|
revng_log(Log, "Folding it to " << Pointer.toString());
|
|
auto MaybeOffsets = computeAccessesInRoot(Pointer);
|
|
|
|
if (not MaybeOffsets) {
|
|
auto *I = cast<llvm::Instruction>(Access.location().getUser());
|
|
revng_log(Log,
|
|
"Marking argument as escaped: we couldn't compute the set "
|
|
"of offsets for "
|
|
<< getName(I) << " in "
|
|
<< I->getParent()->getParent()->getName());
|
|
HelperCPUStateUsage[&Function] = CPUStateUsage::escapes();
|
|
Escaping.insert(I);
|
|
return;
|
|
}
|
|
|
|
revng_assert(MaybeOffsets->size() > 0);
|
|
|
|
// Update the number of accesses this expands to
|
|
revng_assert(Count == 0);
|
|
Count = MaybeOffsets->size();
|
|
|
|
if (Log.isEnabled()) {
|
|
Log << "Offsets: {";
|
|
for (uint64_t Offset : *MaybeOffsets) {
|
|
Log << " " << Offset;
|
|
}
|
|
Log << " }" << DoLog;
|
|
}
|
|
|
|
auto Size = size(Access);
|
|
bool IsWrite = Access.isWrite();
|
|
|
|
for (uint64_t Offset : *MaybeOffsets)
|
|
MemoryAccessOffsets[&Access.location()].insert({ Offset, Size });
|
|
|
|
if (IsWrite) {
|
|
for (uint64_t Offset : *MaybeOffsets) {
|
|
HelperResult.Writes.insert({ Offset, Size });
|
|
}
|
|
} else {
|
|
for (uint64_t Offset : *MaybeOffsets) {
|
|
HelperResult.Reads.insert({ Offset, Size });
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Commit results
|
|
HelperCPUStateUsage[&Function] = std::move(HelperResult);
|
|
}
|
|
|
|
GlobalAUAResults
|
|
CPUStateUsageAnalysis::collectGlobalAUAResults(const llvm::Function &Function) {
|
|
llvm::Task T({}, "Collecting global Argument Usage Analysis results");
|
|
FastValuePrinter Printer(*Function.getParent());
|
|
|
|
GlobalAUAResults Result;
|
|
const aua::Function &FunctionResults = AUA.at(&Function);
|
|
auto &C = TheContext;
|
|
|
|
if (Log.isEnabled()) {
|
|
FunctionResults.dump(Log, "");
|
|
Log << DoLog;
|
|
}
|
|
|
|
// We are now going to collect interprocedural data for the requested
|
|
// function. This means that we'll integrate the information of the current
|
|
// function "inlining" all of the functions it calls directly or indirectly.
|
|
|
|
// If we are going to visit a call with the same context as a previous
|
|
// visit, we'll stop, since it wouldn't provide any additional information.
|
|
// Moreover, in case of recursion, we'll turn the context into FunctionOf
|
|
// the used arguments so that the previous condition will be triggered for
|
|
// sure at the next iteration.
|
|
|
|
for (auto &Entry : FunctionResults.localAccesses())
|
|
Result.registerAccess(Entry);
|
|
|
|
Result.EscapedArguments = FunctionResults.localEscapedArguments();
|
|
|
|
unsigned Iterations = 0;
|
|
std::unordered_set<aua::Call> VisitedCalls;
|
|
struct QueueEntry {
|
|
aua::Call Call;
|
|
llvm::DenseSet<const aua::Function *> FunctionsInStack;
|
|
};
|
|
SmallVector<QueueEntry, 2> Queue;
|
|
|
|
// Initialize queue
|
|
for (const aua::Call &Call : FunctionResults.calls())
|
|
Queue.push_back({ Call, {} });
|
|
|
|
// TODO: In this loop, we analyze many many times the same function with the
|
|
// same context.
|
|
// Some data suggests that we could go from considering 2496029 calls to
|
|
// just 18014 distinct calls. We'd benefit from having a cache of
|
|
// analysis results.
|
|
while (not Queue.empty()) {
|
|
++Iterations;
|
|
const auto &[Current, FunctionsInStack] = Queue.pop_back_val();
|
|
auto &Callee = Current.callee();
|
|
auto &ActualArguments = Current.actualArguments();
|
|
auto SimplifiedActualArguments = ActualArguments;
|
|
auto CalleeName = getCalledFunction(&Current.callInstruction())->getName();
|
|
T.advance(CalleeName);
|
|
|
|
for (auto &&[_, SimplifiedActualArgument] : SimplifiedActualArguments) {
|
|
SmallVector<const Value *, 2> Arguments;
|
|
llvm::copy(SimplifiedActualArgument->collect<ArgumentValue>(),
|
|
std::back_inserter(Arguments));
|
|
SimplifiedActualArgument = &C.getFunctionOf(std::move(Arguments));
|
|
}
|
|
|
|
VisitedCalls.insert(Current);
|
|
|
|
auto NewFunctionsInStack = FunctionsInStack;
|
|
NewFunctionsInStack.insert(&Callee);
|
|
|
|
bool IsRecursiveCall = FunctionsInStack.contains(&Callee);
|
|
|
|
if (Log.isEnabled()) {
|
|
Log << "Processing";
|
|
if (IsRecursiveCall)
|
|
Log << " recursive";
|
|
Log << " call:\n";
|
|
Current.dump(Log, " ", false);
|
|
Log << " Callee:\n";
|
|
Callee.dump(Log, " ");
|
|
Log << DoLog;
|
|
}
|
|
LoggerIndent<> Indent(Log);
|
|
|
|
// Register escaped arguments replacing arguments
|
|
for (const EscapedArgument &EscapedArgument :
|
|
Callee.localEscapedArguments()) {
|
|
const auto &Adjusted = *C.replaceArguments(C.getArgument(EscapedArgument
|
|
.index()),
|
|
ActualArguments);
|
|
for (unsigned ArgumentIndex : Adjusted.collectArguments()) {
|
|
// If an argument is not pointing into the tracked data structure,
|
|
// ignore the fact that's escaping
|
|
if (not Initializer.pointsIntoStruct(*Function.getArg(ArgumentIndex)))
|
|
continue;
|
|
|
|
bool New = Result.EscapedArguments
|
|
.insert({ EscapedArgument.location(), ArgumentIndex })
|
|
.second;
|
|
if (New) {
|
|
revng_log(Log,
|
|
"Argument "
|
|
<< ArgumentIndex << " of " << Function.getName().str()
|
|
<< " escapes from "
|
|
<< Printer.toString(EscapedArgument.location()) << " in "
|
|
<< EscapedArgument.location().getFunction()->getName());
|
|
}
|
|
}
|
|
}
|
|
|
|
// Register memory accesses replacing arguments
|
|
for (const MemoryAccess &Access : Callee.localAccesses()) {
|
|
const auto &Adjusted = *C.replaceArguments(Access.start(),
|
|
ActualArguments);
|
|
MemoryAccess AdjustedAccess = Access.replaceStart(Adjusted);
|
|
revng_log(Log,
|
|
"Registering access " << Access.toString() << " as "
|
|
<< AdjustedAccess.toString() << " at "
|
|
<< getName(Access.location()));
|
|
Result.registerAccess(AdjustedAccess);
|
|
}
|
|
|
|
// Enqueue calls replacing arguments
|
|
for (const aua::Call &InnerCall : Callee.calls()) {
|
|
auto AdjustedCall = InnerCall;
|
|
|
|
const llvm::DenseMap<uint64_t, const Value *>
|
|
*ArgumentsMap = IsRecursiveCall ? &SimplifiedActualArguments :
|
|
&ActualArguments;
|
|
|
|
for (auto &&[_, ActualArgument] : AdjustedCall.actualArguments()) {
|
|
ActualArgument = C.replaceArguments(*ActualArgument, *ArgumentsMap);
|
|
}
|
|
|
|
if (not VisitedCalls.contains(AdjustedCall)) {
|
|
revng_log(Log,
|
|
"Adding call " << getName(&AdjustedCall.callInstruction()));
|
|
Queue.push_back({ std::move(AdjustedCall), NewFunctionsInStack });
|
|
}
|
|
}
|
|
}
|
|
|
|
revng_log(Log, "Collect performed " << Iterations << " iterations");
|
|
|
|
return Result;
|
|
}
|
|
|
|
void CPUStateUsageAnalysis::annotate(llvm::Module &M) const {
|
|
// Annotate functions
|
|
for (auto &&[Function, Usage] : HelperCPUStateUsage)
|
|
Annotation(Usage.Escapes, Usage.Reads, Usage.Writes).serialize(*Function);
|
|
|
|
// Collect instruction annotations
|
|
std::map<llvm::Instruction *, Annotation> InstructionAnnotations;
|
|
|
|
for (auto &&[Use, Access] : MemoryAccessOffsets) {
|
|
auto *I = cast<llvm::Instruction>(Use->getUser());
|
|
if (MemoryAccess::isWrite(Use))
|
|
InstructionAnnotations[I].Writes = Access;
|
|
else
|
|
InstructionAnnotations[I].Reads = Access;
|
|
}
|
|
|
|
for (llvm::Instruction *I : Escaping)
|
|
InstructionAnnotations[I].Escapes = true;
|
|
|
|
// Annotate instructions
|
|
for (auto &&[ToAnnotate, Annotation] : InstructionAnnotations)
|
|
Annotation.serialize(*ToAnnotate);
|
|
}
|
|
|
|
void StructPointers::visitType(llvm::Type &Type, uint64_t StartingOffset) {
|
|
// Note: this function is recursive but its depth is limited by build time
|
|
// features, i.e., the depth of the CPU state.
|
|
if (auto *Struct = dyn_cast<llvm::StructType>(&Type)) {
|
|
revng_log(Log,
|
|
"Registering an instance of " << Struct->getName()
|
|
<< " at offset " << StartingOffset);
|
|
LoggerIndent<> Indent(Log);
|
|
if (Struct->getName().size() != 0)
|
|
OffsetsOfStructs[Struct].push_back(StartingOffset);
|
|
|
|
const llvm::StructLayout *Layout = DL.getStructLayout(Struct);
|
|
for (unsigned Index = 0; Index < Struct->getNumElements(); ++Index) {
|
|
visitType(*Struct->getTypeAtIndex(Index),
|
|
StartingOffset + Layout->getElementOffset(Index));
|
|
}
|
|
} else if (auto *Array = dyn_cast<llvm::ArrayType>(&Type)) {
|
|
auto ElementsCount = Array->getNumElements();
|
|
revng_log(Log, "Handling an array of " << ElementsCount << " elements");
|
|
LoggerIndent<> Indent(Log);
|
|
auto &ElementType = *Array->getElementType();
|
|
auto ElementSize = DL.getTypeAllocSize(&ElementType);
|
|
for (unsigned I = 0; I < ElementsCount; ++I)
|
|
visitType(ElementType, StartingOffset + I * ElementSize);
|
|
}
|
|
}
|
|
|
|
void StructPointers::propagateFromActualArguments() {
|
|
bool Again = true;
|
|
|
|
while (Again) {
|
|
Again = false;
|
|
SmallVector<std::pair<llvm::Value *, llvm::StructType *>> ToAdd;
|
|
for (auto &&[Value, Struct] : Pointers) {
|
|
auto *Argument = dyn_cast<llvm::Argument>(Value);
|
|
if (Argument == nullptr)
|
|
continue;
|
|
|
|
for (llvm::CallBase *Call : callers(Argument->getParent())) {
|
|
auto *V = Call->getArgOperand(Argument->getArgNo());
|
|
if (Pointers.count(V) != 0)
|
|
continue;
|
|
|
|
ToAdd.emplace_back(V, Struct);
|
|
Again = true;
|
|
}
|
|
}
|
|
for (auto &&[Value, Struct] : ToAdd)
|
|
Pointers[Value] = Struct;
|
|
}
|
|
}
|
|
|
|
} // namespace aua
|