diff --git a/lib/Decompiler/DLACreateIntraProceduralTypes.cpp b/lib/Decompiler/DLACreateIntraProceduralTypes.cpp index b377c6a74..60ee9ad8d 100644 --- a/lib/Decompiler/DLACreateIntraProceduralTypes.cpp +++ b/lib/Decompiler/DLACreateIntraProceduralTypes.cpp @@ -244,17 +244,20 @@ public: revng_assert(CTags.contains(FunctionTags::StructInitializer)); revng_assert(not Callee->isVarArg()); - auto *RetTy = cast(Callee->getReturnType()); - revng_assert(RetTy == F->getReturnType()); - revng_assert(RetTy->getNumElements() == Callee->arg_size()); - auto StructTypeNodes = TS.getLayoutTypes(*Call); + auto *RetTy = cast(Callee->getReturnType()); + revng_assert(RetTy->getNumElements() == Callee->arg_size()); + revng_assert(RetTy == F->getReturnType()); + + auto StructTypeNodes = TS.getOrCreateLayoutTypes(*Call); revng_assert(StructTypeNodes.size() == Callee->arg_size()); - for (const auto &[RetNode, Arg] : + for (const auto &[RetNodeNew, Arg] : llvm::zip_first(StructTypeNodes, Call->arg_operands())) { const auto &[ArgNode, New] = TS.getOrCreateLayoutType(Arg); Changed |= New; + const auto &[RetNode, NewNode] = RetNodeNew; + Changed |= NewNode; Changed |= TS.addEqualityLink(RetNode, ArgNode).second; } @@ -356,17 +359,19 @@ public: if (CTags.contains(FunctionTags::StructInitializer)) { revng_assert(not Callee->isVarArg()); + auto *RetTy = cast(Callee->getReturnType()); - revng_assert(RetTy == F->getReturnType()); revng_assert(RetTy->getNumElements() == Callee->arg_size()); - auto StructTypeNodes = TS.getLayoutTypes(*C); + auto StructTypeNodes = TS.getOrCreateLayoutTypes(*C); revng_assert(StructTypeNodes.size() == Callee->arg_size()); - for (const auto &[RetTypeNode, Arg] : + for (const auto &[RetTypeNodeNew, Arg] : llvm::zip_first(StructTypeNodes, C->arg_operands())) { const auto &[ArgTypeNode, New] = TS.getOrCreateLayoutType(Arg); Changed |= New; + const auto &[RetTypeNode, NewNode] = RetTypeNodeNew; + Changed |= NewNode; Changed |= TS.addEqualityLink(RetTypeNode, ArgTypeNode).second; } diff --git a/lib/Decompiler/DLAHelpers.cpp b/lib/Decompiler/DLAHelpers.cpp index de2963b71..091b0543b 100644 --- a/lib/Decompiler/DLAHelpers.cpp +++ b/lib/Decompiler/DLAHelpers.cpp @@ -95,16 +95,16 @@ template llvm::SmallVector, 2> getConstQualifiedExtractedValuesFromCall(T *Call) { llvm::SmallVector, 2> Results; - llvm::SmallSet FoundIds; auto *StructTy = llvm::cast(Call->getType()); unsigned NumFields = StructTy->getNumElements(); Results.resize(NumFields, {}); for (auto *Extract : Call->users()) { - auto *E = cast(Extract); + auto *E = dyn_cast(Extract); + if (not E) + continue; revng_assert(E->getNumIndices() == 1); unsigned FieldId = E->getIndices()[0]; revng_assert(FieldId < NumFields); - FoundIds.insert(FieldId); revng_assert(isa(E->getType()) or isa(E->getType())); Results[FieldId].insert(E); diff --git a/lib/Decompiler/DLATypeSystem.cpp b/lib/Decompiler/DLATypeSystem.cpp index 66aae8248..f871a903d 100644 --- a/lib/Decompiler/DLATypeSystem.cpp +++ b/lib/Decompiler/DLATypeSystem.cpp @@ -312,30 +312,59 @@ LayoutTypeSystem::getLayoutTypes(const Value &V) { auto *RetTy = cast(Callee->getReturnType()); revng_assert(RetTy->getNumElements() == Callee->arg_size()); - revng_assert(Call->getNumUses() == 1 - and isa(Call->uses().begin()->getUser())); - const Function *Caller = Call->getFunction(); - Results = getLayoutTypes(*Caller); - revng_assert(Results.size() == Callee->arg_size()); + bool OnlyReturnUses = true; + bool HasReturnUse = false; + for (const User *U : Call->users()) { + if (isa(U)) { + HasReturnUse = true; + + const Function *Caller = Call->getFunction(); + + if (Results.empty()) + Results = getLayoutTypes(*Caller); + else + revng_assert(Results == getLayoutTypes(*Caller)); + + revng_assert(Results.size() == Callee->arg_size()); + } else { + OnlyReturnUses = false; + } + } + revng_assert(not HasReturnUse or OnlyReturnUses); + } + + // If Results are full, we have detected a call to a struct_initializer + // that is returned, so we are done. Otherwise the have to look to for + // extractvalue instructions that are extracting values from the return + // value of the struct_initializer call. + if (Results.empty()) { - } else { const auto ExtractedValues = getExtractedValuesFromCall(Call); - for (const auto &ExtractedSet : ExtractedValues) { - // Inside here we're working on a single field of the struct. + Results.resize(ExtractedValues.size(), {}); + + for (auto &Group : llvm::enumerate(ExtractedValues)) { + const auto &ExtractedSet = Group.value(); + const auto FieldId = Group.index(); + // Inside here we're working on a signle field of the struct. // ExtractedSet contains all the ExtractValueInst that extract the // same field of the struct. - // We get or create a layout type for each of them, but they should // all be the same. - SmallVector FieldResults; - for (const llvm::ExtractValueInst *E : ExtractedSet) { - FieldResults.push_back(getLayoutType(E)); - revng_assert(FieldResults.front() == FieldResults.back()); + std::optional FieldNode; + for (const llvm::ExtractValueInst *Ext : ExtractedSet) { + LayoutTypeSystemNode *ExtNode = getLayoutType(Ext); + if (FieldNode.has_value()) { + LayoutTypeSystemNode *Node = FieldNode.value(); + revng_assert(not Node or not ExtNode or (Node == ExtNode)); + if (not Node) + Node = ExtNode; + } else { + FieldNode = ExtNode; + } } - - Results.push_back(std::move(FieldResults.front())); + Results[FieldId] = FieldNode.value_or(nullptr); } } @@ -391,30 +420,62 @@ LayoutTypeSystem::getOrCreateLayoutTypes(const Value &V) { auto *RetTy = cast(Callee->getReturnType()); revng_assert(RetTy->getNumElements() == Callee->arg_size()); - revng_assert(Call->getNumUses() == 1 - and isa(Call->uses().begin()->getUser())); - const Function *Caller = Call->getFunction(); - Results = getOrCreateLayoutTypes(*Caller); - revng_assert(Results.size() == Callee->arg_size()); + bool OnlyReturnUses = true; + bool HasReturnUse = false; + for (const User *U : Call->users()) { + if (isa(U)) { + HasReturnUse = true; + + const Function *Caller = Call->getFunction(); + + if (Results.empty()) + Results = getOrCreateLayoutTypes(*Caller); + else + revng_assert(Results == getOrCreateLayoutTypes(*Caller)); + + revng_assert(Results.size() == Callee->arg_size()); + } else { + OnlyReturnUses = false; + } + } + revng_assert(not HasReturnUse or OnlyReturnUses); + } + + // If Results are full, we have detected a call to a struct_initializer + // that is returned, so we are done. Otherwise the have to look to for + // extractvalue instructions that are extracting values from the return + // value of the struct_initializer call. + if (Results.empty()) { - } else { const auto ExtractedValues = getExtractedValuesFromCall(Call); - for (const auto &ExtractedSet : ExtractedValues) { - // Inside here we're working on a single field of the struct. + Results.resize(ExtractedValues.size(), {}); + + for (auto &Group : llvm::enumerate(ExtractedValues)) { + const auto &ExtractedSet = Group.value(); + const auto FieldId = Group.index(); + // Inside here we're working on a signle field of the struct. // ExtractedSet contains all the ExtractValueInst that extract the // same field of the struct. - // We get or create a layout type for each of them, but they should // all be the same. - SmallVector FldResults; - for (const llvm::ExtractValueInst *E : ExtractedSet) { - FldResults.push_back(getOrCreateLayoutType(E)); - revng_assert(FldResults.front().first == FldResults.back().first); + std::optional FieldResult; + for (const llvm::ExtractValueInst *Ext : ExtractedSet) { + GetOrCreateResult ExtResult = getOrCreateLayoutType(Ext); + if (FieldResult.has_value()) { + auto &[Node, New] = FieldResult.value(); + const auto &[ExtNode, ExtNew] = ExtResult; + revng_assert(not ExtNew or ExtNode); + revng_assert(not Node or not ExtNode or (Node == ExtNode)); + if (not Node) + Node = ExtNode; + New |= ExtNew; + } else { + FieldResult = ExtResult; + } } - - Results.push_back(std::move(FldResults.front())); + Results[FieldId] = FieldResult.value_or(GetOrCreateResult{}); } }