diff --git a/.github/workflows/builds.yml b/.github/workflows/builds.yml index 17f3011d8..df1456117 100644 --- a/.github/workflows/builds.yml +++ b/.github/workflows/builds.yml @@ -2,7 +2,7 @@ name: Builds on: [push, pull_request] env: - llvm: 17 + llvm: 19 jobs: nix-build: @@ -13,7 +13,7 @@ jobs: - name: Build using nix run: nix build - name: Run built Diffkemp - run: result/bin/diffkemp --help + run: nix develop --command result/bin/diffkemp --help local-build: runs-on: ubuntu-22.04 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 871d5cc43..78ee6aedb 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -20,7 +20,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - llvm: [12, 13, 14, 15, 16, 17, 18] + llvm: [12, 13, 14, 15, 16, 17, 18, 19] env: - CC: gcc CXX: g++ @@ -28,14 +28,14 @@ jobs: regression-tests: [true] include: - - llvm: 18 + - llvm: 19 env: CC: clang CXX: clang++ asan: OFF regression-tests: true - - llvm: 18 + - llvm: 19 env: CC: gcc CXX: g++ diff --git a/diffkemp/cli.py b/diffkemp/cli.py index 65a7f5385..588f8cea8 100644 --- a/diffkemp/cli.py +++ b/diffkemp/cli.py @@ -2,6 +2,8 @@ from argparse import ArgumentParser, ArgumentTypeError, SUPPRESS from diffkemp.building.build_kernel import build_kernel from diffkemp.compare import compare +from diffkemp.utils import get_llvm_version +from diffkemp.simpll.library import get_llvm_build_version import diffkemp.diffkemp import diffkemp.viewer import os @@ -219,6 +221,18 @@ def make_argument_parser(): def run_from_cli(): """Main method to run the tool.""" + + llvm_version = get_llvm_version() + build_llvm_version = get_llvm_build_version().major + if llvm_version != build_llvm_version: + raise RuntimeError( + "Incompatible LLVM versions: DiffKemp was built with" + "{} and the installed version is {}".format( + build_llvm_version, + llvm_version + ) + ) + ap = make_argument_parser() args = ap.parse_args() if args.verbose or args.debug: diff --git a/diffkemp/llvm_ir/optimiser.py b/diffkemp/llvm_ir/optimiser.py index 09576c549..b591833dd 100644 --- a/diffkemp/llvm_ir/optimiser.py +++ b/diffkemp/llvm_ir/optimiser.py @@ -1,5 +1,6 @@ """Functions for optimizations of LLVM IR.""" from diffkemp.utils import get_opt_command +from diffkemp.utils import get_llvm_version from subprocess import check_call, CalledProcessError import os @@ -14,7 +15,12 @@ def opt_llvm(llvm_file): Run basic simplification passes and -constmerge to remove duplicate constants that might have come from linked files. """ - passes = [("lowerswitch", "function"), + if get_llvm_version() < 19: + lower_switch_pass_name = "lowerswitch" + else: + lower_switch_pass_name = "lower-switch" + + passes = [(lower_switch_pass_name, "function"), ("mem2reg", "function"), ("loop-simplify", "function"), ("simplifycfg", "function"), diff --git a/diffkemp/simpll/Config.h b/diffkemp/simpll/Config.h index 8dc3e0a2e..11a4fd844 100644 --- a/diffkemp/simpll/Config.h +++ b/diffkemp/simpll/Config.h @@ -15,9 +15,9 @@ #ifndef DIFFKEMP_SIMPLL_CONFIG_H #define DIFFKEMP_SIMPLL_CONFIG_H -#include "llvm/Support/CommandLine.h" #include #include +#include #include #define DEBUG_SIMPLL "debug-simpll" diff --git a/diffkemp/simpll/DifferentialFunctionComparator.cpp b/diffkemp/simpll/DifferentialFunctionComparator.cpp index 230f42e04..2460f0f73 100644 --- a/diffkemp/simpll/DifferentialFunctionComparator.cpp +++ b/diffkemp/simpll/DifferentialFunctionComparator.cpp @@ -206,7 +206,7 @@ int DifferentialFunctionComparator::cmpGEPs(const GEPOperator *GEPL, if (MemberNameL == DI->StructFieldNames.end() || MemberNameR == DI->StructFieldNames.end() - || !MemberNameL->second.equals(MemberNameR->second)) + || !(MemberNameL->second == MemberNameR->second)) if (int Res = cmpValues(idxL->get(), idxR->get())) RETURN_WITH_LOG(Res); @@ -516,11 +516,12 @@ int DifferentialFunctionComparator::cmpAllocs(const CallInst *CL, RETURN_WITH_LOG(1); // If the next instruction is a bitcast, compare its type instead - const Value *ValL = - isa(CL->getNextNode()) ? CL->getNextNode() : CL; - const Value *ValR = - isa(CR->getNextNode()) ? CR->getNextNode() : CR; - + const auto *nextInstL = CL->getNextNode(); + const auto *nextInstR = CR->getNextNode(); + const bool isNextInstBitcastL = nextInstL && isa(nextInstL); + const bool isNextInstBitcastR = nextInstR && isa(nextInstR); + const Value *ValL = isNextInstBitcastL ? nextInstL : CL; + const Value *ValR = isNextInstBitcastR ? nextInstR : CR; // Retrieve type names and sizes TypeInfo TypeInfoL = getPointeeStructTypeInfo(ValL, &LayoutL, FnL->getName()); diff --git a/diffkemp/simpll/Logger.h b/diffkemp/simpll/Logger.h index c6b3fad98..d11ff2703 100644 --- a/diffkemp/simpll/Logger.h +++ b/diffkemp/simpll/Logger.h @@ -96,7 +96,7 @@ #ifndef DIFFKEMP_SIMPLL_LOGGER_H #define DIFFKEMP_SIMPLL_LOGGER_H -#include +#include "Config.h" #include #include #include diff --git a/diffkemp/simpll/SimpLL.cpp b/diffkemp/simpll/SimpLL.cpp index af58476b1..3c4823cbc 100644 --- a/diffkemp/simpll/SimpLL.cpp +++ b/diffkemp/simpll/SimpLL.cpp @@ -17,6 +17,8 @@ #include "Output.h" #include "Utils.h" +#include + using namespace llvm; // Command line options diff --git a/diffkemp/simpll/Utils.cpp b/diffkemp/simpll/Utils.cpp index 8c0007fe5..930ddb3b6 100644 --- a/diffkemp/simpll/Utils.cpp +++ b/diffkemp/simpll/Utils.cpp @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -404,6 +405,60 @@ void findAndReplace(std::string &input, std::string find, std::string replace) { } } +#if LLVM_VERSION_MAJOR >= 19 + +const Instruction *getConstExprAsInstruction(const ConstantExpr *CEx) { + SmallVector ValueOperands(CEx->op_begin(), CEx->op_end()); + ArrayRef Ops(ValueOperands); + + switch (CEx->getOpcode()) { + case Instruction::Trunc: +#if LLVM_VERSION_MAJOR >= 21 + case Instruction::PtrToAddr: +#endif + case Instruction::PtrToInt: + case Instruction::IntToPtr: + case Instruction::BitCast: + case Instruction::AddrSpaceCast: + return CastInst::Create((Instruction::CastOps)CEx->getOpcode(), + Ops[0], + CEx->getType(), + ""); + case Instruction::InsertElement: + return InsertElementInst::Create(Ops[0], Ops[1], Ops[2], ""); + case Instruction::ExtractElement: + return ExtractElementInst::Create(Ops[0], Ops[1], ""); + case Instruction::ShuffleVector: + return new ShuffleVectorInst(Ops[0], Ops[1], CEx->getShuffleMask(), ""); + + case Instruction::GetElementPtr: { + const auto *GO = cast(CEx); + return GetElementPtrInst::Create(GO->getSourceElementType(), + Ops[0], + Ops.slice(1), + GO->getNoWrapFlags(), + ""); + } + default: + assert(CEx->getNumOperands() == 2 && "Must be binary operator?"); + BinaryOperator *BO = BinaryOperator::Create( + (Instruction::BinaryOps)CEx->getOpcode(), Ops[0], Ops[1], ""); + if (isa(BO)) { + BO->setHasNoUnsignedWrap( + CEx->getRawSubclassOptionalData() + & OverflowingBinaryOperator::NoUnsignedWrap); + BO->setHasNoSignedWrap(CEx->getRawSubclassOptionalData() + & OverflowingBinaryOperator::NoSignedWrap); + } + if (isa(BO)) + BO->setIsExact(CEx->getRawSubclassOptionalData() + & PossiblyExactOperator::IsExact); + return BO; + } +} + +#else + /// Convert constant expression to instruction. (Copied from LLVM and modified /// to work outside the ConstantExpr class; otherwise the function is the same, /// the only purpose of copying the function is making it work on constant @@ -478,6 +533,8 @@ const Instruction *getConstExprAsInstruction(const ConstantExpr *CEx) { } } +#endif + /// Generates human-readable C-like identifier for type. std::string getIdentifierForType(Type *Ty) { if (auto STy = dyn_cast(Ty)) { @@ -635,38 +692,73 @@ std::string getIdentifierForValue( return ""; } -/// Retrieve information about a structured type being pointed to by a value. -/// Note: There are two completely different approaches used. Up to LLVM 14, -/// type information can be obtained directly from the value. Since LLVM 15, -/// type information is obtained from calls to debug intrinsics. It is necessary -/// to provide current function name to use the correct debug intrinsic call -/// (there can be multiple different ones). -TypeInfo getPointeeStructTypeInfo(const Value *Val, - const DataLayout *Layout, - [[maybe_unused]] const StringRef &FunName) { -#if LLVM_VERSION_MAJOR >= 15 - (void)Layout; - // Look for the type of the value in debug intrinsics - SmallVector DbgValues; - findDbgValues(DbgValues, const_cast(Val)); +#if LLVM_VERSION_MAJOR >= 19 + +/// Returns the debug type of the given Value based on the provided scope +/// (function name). Returns nullptr if the type cannot be determined. +/// In LLVM 19, debug records were introduced. Both debug intrinsics and +/// records are searched to find the type. +DIType *getDbgTypeForValue(const Value *Val, const StringRef &FunName) { + SmallVector DbgIntrinsics; + SmallVector DbgRecords; + findDbgValues(DbgIntrinsics, const_cast(Val), &DbgRecords); + for (auto &Dbg : DbgRecords) { + auto scopeName = + Dbg->getVariable()->getScope()->getSubprogram()->getName(); + if (scopeName == FunName) { + return Dbg->getVariable()->getType(); + } + } + // If type was not found in debug records, search in debug intrinsics. + for (auto &Dbg : DbgIntrinsics) { + auto scopeName = + Dbg->getVariable()->getScope()->getSubprogram()->getName(); + if (scopeName == FunName) { + return Dbg->getVariable()->getType(); + } + } + return nullptr; +} +#elif LLVM_VERSION_MAJOR >= 15 + +/// Returns the debug type of the given Value based on the provided scope +/// (function name). Returns nullptr if the type cannot be determined. +/// The type of the value is determined using debug intrinsics. +DIType *getDbgTypeForValue(const Value *Val, const StringRef &FunName) { + SmallVector DbgIntrinsics; + findDbgValues(DbgIntrinsics, const_cast(Val)); // There can be potentially multiple different dbg info for the same // value. It is necessary to find the one belonging to the current function. // The other dbg info can belong to called functions where the value could // be provided as (void *) and therefore does not have to contain necessary // information about the pointee type. - DbgValueInst *DbgValue = nullptr; - for (auto &Dbg : DbgValues) { + for (auto &Dbg : DbgIntrinsics) { auto scopeName = Dbg->getVariable()->getScope()->getSubprogram()->getName(); if (scopeName == FunName) { - DbgValue = Dbg; - break; + return Dbg->getVariable()->getType(); } } - if (!DbgValue) + return nullptr; +} + +#endif + +#if LLVM_VERSION_MAJOR >= 15 + +/// Retrieve information about a structured type being pointed to by a value. +/// Type information is obtained from calls to debug intrinsics/records. It is +/// necessary to provide current function name to use the correct debug +/// intrinsic call (there can be multiple different ones). The data layout +/// parameter is unused. +TypeInfo getPointeeStructTypeInfo(const Value *Val, + [[maybe_unused]] const DataLayout *Layout, + const StringRef &FunName) { + auto *Ty = getDbgTypeForValue(Val, FunName); + if (!Ty) { return {"", 0}; - const DIType *Ty = DbgValue->getVariable()->getType(); + } // Check if it is a pointer type (derived type) const DIDerivedType *PtrTy = dyn_cast(Ty); @@ -705,7 +797,16 @@ TypeInfo getPointeeStructTypeInfo(const Value *Val, } return {typeName, StrTy->getSizeInBits() / 8}; -#else +} + +#else /* LLVM_VERSION_MAJOR < 15 */ + +/// Retrieve information about a structured type being pointed to by a value. +/// Type information can be obtained directly from the value and data layout, +/// the function name is unused. +TypeInfo getPointeeStructTypeInfo(const Value *Val, + const DataLayout *Layout, + [[maybe_unused]] const StringRef &FunName) { // Get the type of the value Type *Ty = Val->getType(); @@ -719,9 +820,10 @@ TypeInfo getPointeeStructTypeInfo(const Value *Val, return {"", 0}; return {StrTy->getName(), Layout->getTypeStoreSize(StrTy)}; -#endif } +#endif /* LLVM_VERSION_MAJOR < 15 */ + /// Retrieves the type of the value based on its C source code expression. const DIType *getCSourceIdentifierType(std::string expr, const Function *Parent, diff --git a/diffkemp/simpll/library.py b/diffkemp/simpll/library.py index f4fd121cd..4c3a83d10 100644 --- a/diffkemp/simpll/library.py +++ b/diffkemp/simpll/library.py @@ -2,6 +2,7 @@ Python interface for the SimpLL library. """ from diffkemp.simpll.simpll_lib import ffi, lib +import collections def _ptrarray_to_list(ptrarray): @@ -156,3 +157,11 @@ def get_child(self, sysctl_name): def get_data(self, sysctl_name): return self._get_global_variable(sysctl_name, lib.getData) + + +def get_llvm_build_version(): + Version = collections.namedtuple("Version", ["major", "minor", "patch"]) + version = ffi.new("int[3]") + + lib.getLlvmBuildVersion(version) + return Version(version[0], version[1], version[2]) diff --git a/diffkemp/simpll/library/FFI.cpp b/diffkemp/simpll/library/FFI.cpp index c2dfd5070..61000a599 100644 --- a/diffkemp/simpll/library/FFI.cpp +++ b/diffkemp/simpll/library/FFI.cpp @@ -19,8 +19,11 @@ #include "library/DiffKempUtils.h" #include "library/SysctlTable.h" #include "passes/CalledFunctionsAnalysis.h" -#include + +#include #include + +#include #include /// Map to store LLVMContext objects for modules. @@ -323,5 +326,11 @@ void preprocessModuleC(void *Mod, struct builtin_patterns PatternsC) { preprocessModule(*LLVMMod, nullptr, nullptr, Patterns); } +void getLlvmBuildVersion(int *out) { + out[0] = LLVM_VERSION_MAJOR; + out[1] = LLVM_VERSION_MINOR; + out[2] = LLVM_VERSION_PATCH; +} + void shutdownSimpLL() { llvm_shutdown(); } } // extern "C" diff --git a/diffkemp/simpll/library/FFI.h b/diffkemp/simpll/library/FFI.h index 1177222e1..2378b647c 100644 --- a/diffkemp/simpll/library/FFI.h +++ b/diffkemp/simpll/library/FFI.h @@ -149,6 +149,9 @@ void parseAndRunSimpLL(const char *ModL, /// won't be run again when the module is compared. void preprocessModuleC(void *Mod, struct builtin_patterns PatternsC); +/// Returns the version of LLVM with which was SimpLL built. +void getLlvmBuildVersion(int *out); + void shutdownSimpLL(); // CFFI_DECLARATIONS_END diff --git a/diffkemp/simpll/llvm-lib/19/FunctionComparator.cpp b/diffkemp/simpll/llvm-lib/19/FunctionComparator.cpp new file mode 100644 index 000000000..760200764 --- /dev/null +++ b/diffkemp/simpll/llvm-lib/19/FunctionComparator.cpp @@ -0,0 +1,1033 @@ +//===- FunctionComparator.h - Function Comparator -------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file implements the FunctionComparator and GlobalNumberState classes +// which are used by the MergeFunctions pass for comparing functions. +// +//===----------------------------------------------------------------------===// + +#include "FunctionComparator.h" +#include "llvm/ADT/APFloat.h" +#include "llvm/ADT/APInt.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/Hashing.h" +#include "llvm/ADT/SmallPtrSet.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/IR/Attributes.h" +#include "llvm/IR/BasicBlock.h" +#include "llvm/IR/Constant.h" +#include "llvm/IR/Constants.h" +#include "llvm/IR/DataLayout.h" +#include "llvm/IR/DerivedTypes.h" +#include "llvm/IR/Function.h" +#include "llvm/IR/GlobalValue.h" +#include "llvm/IR/InlineAsm.h" +#include "llvm/IR/InstrTypes.h" +#include "llvm/IR/Instruction.h" +#include "llvm/IR/Instructions.h" +#include "llvm/IR/LLVMContext.h" +#include "llvm/IR/Metadata.h" +#include "llvm/IR/Module.h" +#include "llvm/IR/Operator.h" +#include "llvm/IR/Type.h" +#include "llvm/IR/Value.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/Compiler.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/raw_ostream.h" +#include +#include +#include +#include + +using namespace llvm; + +#define DEBUG_TYPE "functioncomparator" + +int FunctionComparator::cmpNumbers(uint64_t L, uint64_t R) const { + if (L < R) + return -1; + if (L > R) + return 1; + return 0; +} + +int FunctionComparator::cmpAligns(Align L, Align R) const { + if (L.value() < R.value()) + return -1; + if (L.value() > R.value()) + return 1; + return 0; +} + +int FunctionComparator::cmpOrderings(AtomicOrdering L, AtomicOrdering R) const { + if ((int)L < (int)R) + return -1; + if ((int)L > (int)R) + return 1; + return 0; +} + +int FunctionComparator::cmpAPInts(const APInt &L, const APInt &R) const { + if (int Res = cmpNumbers(L.getBitWidth(), R.getBitWidth())) + return Res; + if (L.ugt(R)) + return 1; + if (R.ugt(L)) + return -1; + return 0; +} + +int FunctionComparator::cmpAPFloats(const APFloat &L, const APFloat &R) const { + // Floats are ordered first by semantics (i.e. float, double, half, etc.), + // then by value interpreted as a bitstring (aka APInt). + const fltSemantics &SL = L.getSemantics(), &SR = R.getSemantics(); + if (int Res = cmpNumbers(APFloat::semanticsPrecision(SL), + APFloat::semanticsPrecision(SR))) + return Res; + if (int Res = cmpNumbers(APFloat::semanticsMaxExponent(SL), + APFloat::semanticsMaxExponent(SR))) + return Res; + if (int Res = cmpNumbers(APFloat::semanticsMinExponent(SL), + APFloat::semanticsMinExponent(SR))) + return Res; + if (int Res = cmpNumbers(APFloat::semanticsSizeInBits(SL), + APFloat::semanticsSizeInBits(SR))) + return Res; + return cmpAPInts(L.bitcastToAPInt(), R.bitcastToAPInt()); +} + +int FunctionComparator::cmpMem(StringRef L, StringRef R) const { + // Prevent heavy comparison, compare sizes first. + if (int Res = cmpNumbers(L.size(), R.size())) + return Res; + + // Compare strings lexicographically only when it is necessary: only when + // strings are equal in size. + return std::clamp(L.compare(R), -1, 1); +} + +int FunctionComparator::cmpAttrs(const AttributeList L, + const AttributeList R) const { + if (int Res = cmpNumbers(L.getNumAttrSets(), R.getNumAttrSets())) + return Res; + + for (unsigned i : L.indexes()) { + AttributeSet LAS = L.getAttributes(i); + AttributeSet RAS = R.getAttributes(i); + AttributeSet::iterator LI = LAS.begin(), LE = LAS.end(); + AttributeSet::iterator RI = RAS.begin(), RE = RAS.end(); + for (; LI != LE && RI != RE; ++LI, ++RI) { + Attribute LA = *LI; + Attribute RA = *RI; + if (LA.isTypeAttribute() && RA.isTypeAttribute()) { + if (LA.getKindAsEnum() != RA.getKindAsEnum()) + return cmpNumbers(LA.getKindAsEnum(), RA.getKindAsEnum()); + + Type *TyL = LA.getValueAsType(); + Type *TyR = RA.getValueAsType(); + if (TyL && TyR) { + if (int Res = cmpTypes(TyL, TyR)) + return Res; + continue; + } + + // Two pointers, at least one null, so the comparison result is + // independent of the value of a real pointer. + if (int Res = cmpNumbers((uint64_t)TyL, (uint64_t)TyR)) + return Res; + continue; + } else if (LA.isConstantRangeAttribute() && + RA.isConstantRangeAttribute()) { + if (LA.getKindAsEnum() != RA.getKindAsEnum()) + return cmpNumbers(LA.getKindAsEnum(), RA.getKindAsEnum()); + + const ConstantRange &LCR = LA.getRange(); + const ConstantRange &RCR = RA.getRange(); + if (int Res = cmpAPInts(LCR.getLower(), RCR.getLower())) + return Res; + if (int Res = cmpAPInts(LCR.getUpper(), RCR.getUpper())) + return Res; + continue; + } + if (LA < RA) + return -1; + if (RA < LA) + return 1; + } + if (LI != LE) + return 1; + if (RI != RE) + return -1; + } + return 0; +} + +int FunctionComparator::cmpMetadata(const Metadata *L, + const Metadata *R) const { + // TODO: the following routine coerce the metadata contents into constants + // or MDStrings before comparison. + // It ignores any other cases, so that the metadata nodes are considered + // equal even though this is not correct. + // We should structurally compare the metadata nodes to be perfect here. + + auto *MDStringL = dyn_cast(L); + auto *MDStringR = dyn_cast(R); + if (MDStringL && MDStringR) { + if (MDStringL == MDStringR) + return 0; + return MDStringL->getString().compare(MDStringR->getString()); + } + if (MDStringR) + return -1; + if (MDStringL) + return 1; + + auto *CL = dyn_cast(L); + auto *CR = dyn_cast(R); + if (CL == CR) + return 0; + if (!CL) + return -1; + if (!CR) + return 1; + return cmpConstants(CL->getValue(), CR->getValue()); +} + +int FunctionComparator::cmpMDNode(const MDNode *L, const MDNode *R) const { + if (L == R) + return 0; + if (!L) + return -1; + if (!R) + return 1; + // TODO: Note that as this is metadata, it is possible to drop and/or merge + // this data when considering functions to merge. Thus this comparison would + // return 0 (i.e. equivalent), but merging would become more complicated + // because the ranges would need to be unioned. It is not likely that + // functions differ ONLY in this metadata if they are actually the same + // function semantically. + if (int Res = cmpNumbers(L->getNumOperands(), R->getNumOperands())) + return Res; + for (size_t I = 0; I < L->getNumOperands(); ++I) + if (int Res = cmpMetadata(L->getOperand(I), R->getOperand(I))) + return Res; + return 0; +} + +int FunctionComparator::cmpInstMetadata(Instruction const *L, + Instruction const *R) const { + /// These metadata affects the other optimization passes by making assertions + /// or constraints. + /// Values that carry different expectations should be considered different. + SmallVector> MDL, MDR; + L->getAllMetadataOtherThanDebugLoc(MDL); + R->getAllMetadataOtherThanDebugLoc(MDR); + if (MDL.size() > MDR.size()) + return 1; + else if (MDL.size() < MDR.size()) + return -1; + for (size_t I = 0, N = MDL.size(); I < N; ++I) { + auto const [KeyL, ML] = MDL[I]; + auto const [KeyR, MR] = MDR[I]; + if (int Res = cmpNumbers(KeyL, KeyR)) + return Res; + if (int Res = cmpMDNode(ML, MR)) + return Res; + } + return 0; +} + +int FunctionComparator::cmpOperandBundlesSchema(const CallBase &LCS, + const CallBase &RCS) const { + assert(LCS.getOpcode() == RCS.getOpcode() && "Can't compare otherwise!"); + + if (int Res = + cmpNumbers(LCS.getNumOperandBundles(), RCS.getNumOperandBundles())) + return Res; + + for (unsigned I = 0, E = LCS.getNumOperandBundles(); I != E; ++I) { + auto OBL = LCS.getOperandBundleAt(I); + auto OBR = RCS.getOperandBundleAt(I); + + if (int Res = OBL.getTagName().compare(OBR.getTagName())) + return Res; + + if (int Res = cmpNumbers(OBL.Inputs.size(), OBR.Inputs.size())) + return Res; + } + + return 0; +} + +/// Constants comparison: +/// 1. Check whether type of L constant could be losslessly bitcasted to R +/// type. +/// 2. Compare constant contents. +/// For more details see declaration comments. +int FunctionComparator::cmpConstants(const Constant *L, + const Constant *R) const { + Type *TyL = L->getType(); + Type *TyR = R->getType(); + + // Check whether types are bitcastable. This part is just re-factored + // Type::canLosslesslyBitCastTo method, but instead of returning true/false, + // we also pack into result which type is "less" for us. + int TypesRes = cmpTypes(TyL, TyR); + if (TypesRes != 0) { + // Types are different, but check whether we can bitcast them. + if (!TyL->isFirstClassType()) { + if (TyR->isFirstClassType()) + return -1; + // Neither TyL nor TyR are values of first class type. Return the result + // of comparing the types + return TypesRes; + } + if (!TyR->isFirstClassType()) { + if (TyL->isFirstClassType()) + return 1; + return TypesRes; + } + + // Vector -> Vector conversions are always lossless if the two vector types + // have the same size, otherwise not. + unsigned TyLWidth = 0; + unsigned TyRWidth = 0; + + if (auto *VecTyL = dyn_cast(TyL)) + TyLWidth = VecTyL->getPrimitiveSizeInBits().getFixedValue(); + if (auto *VecTyR = dyn_cast(TyR)) + TyRWidth = VecTyR->getPrimitiveSizeInBits().getFixedValue(); + + if (TyLWidth != TyRWidth) + return cmpNumbers(TyLWidth, TyRWidth); + + // Zero bit-width means neither TyL nor TyR are vectors. + if (!TyLWidth) { + PointerType *PTyL = dyn_cast(TyL); + PointerType *PTyR = dyn_cast(TyR); + if (PTyL && PTyR) { + unsigned AddrSpaceL = PTyL->getAddressSpace(); + unsigned AddrSpaceR = PTyR->getAddressSpace(); + if (int Res = cmpNumbers(AddrSpaceL, AddrSpaceR)) + return Res; + } + if (PTyL) + return 1; + if (PTyR) + return -1; + + // TyL and TyR aren't vectors, nor pointers. We don't know how to + // bitcast them. + return TypesRes; + } + } + + // OK, types are bitcastable, now check constant contents. + + if (L->isNullValue() && R->isNullValue()) + return TypesRes; + if (L->isNullValue() && !R->isNullValue()) + return 1; + if (!L->isNullValue() && R->isNullValue()) + return -1; + + auto GlobalValueL = const_cast(dyn_cast(L)); + auto GlobalValueR = const_cast(dyn_cast(R)); + if (GlobalValueL && GlobalValueR) { + return cmpGlobalValues(GlobalValueL, GlobalValueR); + } + + if (int Res = cmpNumbers(L->getValueID(), R->getValueID())) + return Res; + + if (const auto *SeqL = dyn_cast(L)) { + const auto *SeqR = cast(R); + // This handles ConstantDataArray and ConstantDataVector. Note that we + // compare the two raw data arrays, which might differ depending on the host + // endianness. This isn't a problem though, because the endiness of a module + // will affect the order of the constants, but this order is the same + // for a given input module and host platform. + return cmpMem(SeqL->getRawDataValues(), SeqR->getRawDataValues()); + } + + switch (L->getValueID()) { + case Value::UndefValueVal: + case Value::PoisonValueVal: + case Value::ConstantTokenNoneVal: + return TypesRes; + case Value::ConstantIntVal: { + const APInt &LInt = cast(L)->getValue(); + const APInt &RInt = cast(R)->getValue(); + return cmpAPInts(LInt, RInt); + } + case Value::ConstantFPVal: { + const APFloat &LAPF = cast(L)->getValueAPF(); + const APFloat &RAPF = cast(R)->getValueAPF(); + return cmpAPFloats(LAPF, RAPF); + } + case Value::ConstantArrayVal: { + const ConstantArray *LA = cast(L); + const ConstantArray *RA = cast(R); + uint64_t NumElementsL = cast(TyL)->getNumElements(); + uint64_t NumElementsR = cast(TyR)->getNumElements(); + if (int Res = cmpNumbers(NumElementsL, NumElementsR)) + return Res; + for (uint64_t i = 0; i < NumElementsL; ++i) { + if (int Res = cmpConstants(cast(LA->getOperand(i)), + cast(RA->getOperand(i)))) + return Res; + } + return 0; + } + case Value::ConstantStructVal: { + const ConstantStruct *LS = cast(L); + const ConstantStruct *RS = cast(R); + unsigned NumElementsL = cast(TyL)->getNumElements(); + unsigned NumElementsR = cast(TyR)->getNumElements(); + if (int Res = cmpNumbers(NumElementsL, NumElementsR)) + return Res; + for (unsigned i = 0; i != NumElementsL; ++i) { + if (int Res = cmpConstants(cast(LS->getOperand(i)), + cast(RS->getOperand(i)))) + return Res; + } + return 0; + } + case Value::ConstantVectorVal: { + const ConstantVector *LV = cast(L); + const ConstantVector *RV = cast(R); + unsigned NumElementsL = cast(TyL)->getNumElements(); + unsigned NumElementsR = cast(TyR)->getNumElements(); + if (int Res = cmpNumbers(NumElementsL, NumElementsR)) + return Res; + for (uint64_t i = 0; i < NumElementsL; ++i) { + if (int Res = cmpConstants(cast(LV->getOperand(i)), + cast(RV->getOperand(i)))) + return Res; + } + return 0; + } + case Value::ConstantExprVal: { + const ConstantExpr *LE = cast(L); + const ConstantExpr *RE = cast(R); + if (int Res = cmpNumbers(LE->getOpcode(), RE->getOpcode())) + return Res; + unsigned NumOperandsL = LE->getNumOperands(); + unsigned NumOperandsR = RE->getNumOperands(); + if (int Res = cmpNumbers(NumOperandsL, NumOperandsR)) + return Res; + for (unsigned i = 0; i < NumOperandsL; ++i) { + if (int Res = cmpConstants(cast(LE->getOperand(i)), + cast(RE->getOperand(i)))) + return Res; + } + if (auto *GEPL = dyn_cast(LE)) { + auto *GEPR = cast(RE); + if (int Res = cmpTypes(GEPL->getSourceElementType(), + GEPR->getSourceElementType())) + return Res; + if (int Res = cmpNumbers(GEPL->getNoWrapFlags().getRaw(), + GEPR->getNoWrapFlags().getRaw())) + return Res; + + std::optional InRangeL = GEPL->getInRange(); + std::optional InRangeR = GEPR->getInRange(); + if (InRangeL) { + if (!InRangeR) + return 1; + if (int Res = cmpAPInts(InRangeL->getLower(), InRangeR->getLower())) + return Res; + if (int Res = cmpAPInts(InRangeL->getUpper(), InRangeR->getUpper())) + return Res; + } else if (InRangeR) { + return -1; + } + } + if (auto *OBOL = dyn_cast(LE)) { + auto *OBOR = cast(RE); + if (int Res = + cmpNumbers(OBOL->hasNoUnsignedWrap(), OBOR->hasNoUnsignedWrap())) + return Res; + if (int Res = + cmpNumbers(OBOL->hasNoSignedWrap(), OBOR->hasNoSignedWrap())) + return Res; + } + return 0; + } + case Value::BlockAddressVal: { + const BlockAddress *LBA = cast(L); + const BlockAddress *RBA = cast(R); + if (int Res = cmpValues(LBA->getFunction(), RBA->getFunction())) + return Res; + if (LBA->getFunction() == RBA->getFunction()) { + // They are BBs in the same function. Order by which comes first in the + // BB order of the function. This order is deterministic. + Function *F = LBA->getFunction(); + BasicBlock *LBB = LBA->getBasicBlock(); + BasicBlock *RBB = RBA->getBasicBlock(); + if (LBB == RBB) + return 0; + for (BasicBlock &BB : *F) { + if (&BB == LBB) { + assert(&BB != RBB); + return -1; + } + if (&BB == RBB) + return 1; + } + llvm_unreachable("Basic Block Address does not point to a basic block in " + "its function."); + return -1; + } else { + // cmpValues said the functions are the same. So because they aren't + // literally the same pointer, they must respectively be the left and + // right functions. + assert(LBA->getFunction() == FnL && RBA->getFunction() == FnR); + // cmpValues will tell us if these are equivalent BasicBlocks, in the + // context of their respective functions. + return cmpValues(LBA->getBasicBlock(), RBA->getBasicBlock()); + } + } + case Value::DSOLocalEquivalentVal: { + // dso_local_equivalent is functionally equivalent to whatever it points to. + // This means the behavior of the IR should be the exact same as if the + // function was referenced directly rather than through a + // dso_local_equivalent. + const auto *LEquiv = cast(L); + const auto *REquiv = cast(R); + return cmpGlobalValues(LEquiv->getGlobalValue(), REquiv->getGlobalValue()); + } + default: // Unknown constant, abort. + LLVM_DEBUG(dbgs() << "Looking at valueID " << L->getValueID() << "\n"); + llvm_unreachable("Constant ValueID not recognized."); + return -1; + } +} + +int FunctionComparator::cmpGlobalValues(GlobalValue *L, GlobalValue *R) const { + uint64_t LNumber = GlobalNumbers->getNumber(L); + uint64_t RNumber = GlobalNumbers->getNumber(R); + return cmpNumbers(LNumber, RNumber); +} + +/// cmpType - compares two types, +/// defines total ordering among the types set. +/// See method declaration comments for more details. +int FunctionComparator::cmpTypes(Type *TyL, Type *TyR) const { + PointerType *PTyL = dyn_cast(TyL); + PointerType *PTyR = dyn_cast(TyR); + + const DataLayout &DL = FnL->getDataLayout(); + if (PTyL && PTyL->getAddressSpace() == 0) + TyL = DL.getIntPtrType(TyL); + if (PTyR && PTyR->getAddressSpace() == 0) + TyR = DL.getIntPtrType(TyR); + + if (TyL == TyR) + return 0; + + if (int Res = cmpNumbers(TyL->getTypeID(), TyR->getTypeID())) + return Res; + + switch (TyL->getTypeID()) { + default: + llvm_unreachable("Unknown type!"); + case Type::IntegerTyID: + return cmpNumbers(cast(TyL)->getBitWidth(), + cast(TyR)->getBitWidth()); + // TyL == TyR would have returned true earlier, because types are uniqued. + case Type::VoidTyID: + case Type::FloatTyID: + case Type::DoubleTyID: + case Type::X86_FP80TyID: + case Type::FP128TyID: + case Type::PPC_FP128TyID: + case Type::LabelTyID: + case Type::MetadataTyID: + case Type::TokenTyID: + return 0; + + case Type::PointerTyID: + assert(PTyL && PTyR && "Both types must be pointers here."); + return cmpNumbers(PTyL->getAddressSpace(), PTyR->getAddressSpace()); + + case Type::StructTyID: { + StructType *STyL = cast(TyL); + StructType *STyR = cast(TyR); + if (STyL->getNumElements() != STyR->getNumElements()) + return cmpNumbers(STyL->getNumElements(), STyR->getNumElements()); + + if (STyL->isPacked() != STyR->isPacked()) + return cmpNumbers(STyL->isPacked(), STyR->isPacked()); + + for (unsigned i = 0, e = STyL->getNumElements(); i != e; ++i) { + if (int Res = cmpTypes(STyL->getElementType(i), STyR->getElementType(i))) + return Res; + } + return 0; + } + + case Type::FunctionTyID: { + FunctionType *FTyL = cast(TyL); + FunctionType *FTyR = cast(TyR); + if (FTyL->getNumParams() != FTyR->getNumParams()) + return cmpNumbers(FTyL->getNumParams(), FTyR->getNumParams()); + + if (FTyL->isVarArg() != FTyR->isVarArg()) + return cmpNumbers(FTyL->isVarArg(), FTyR->isVarArg()); + + if (int Res = cmpTypes(FTyL->getReturnType(), FTyR->getReturnType())) + return Res; + + for (unsigned i = 0, e = FTyL->getNumParams(); i != e; ++i) { + if (int Res = cmpTypes(FTyL->getParamType(i), FTyR->getParamType(i))) + return Res; + } + return 0; + } + + case Type::ArrayTyID: { + auto *STyL = cast(TyL); + auto *STyR = cast(TyR); + if (STyL->getNumElements() != STyR->getNumElements()) + return cmpNumbers(STyL->getNumElements(), STyR->getNumElements()); + return cmpTypes(STyL->getElementType(), STyR->getElementType()); + } + case Type::FixedVectorTyID: + case Type::ScalableVectorTyID: { + auto *STyL = cast(TyL); + auto *STyR = cast(TyR); + if (STyL->getElementCount().isScalable() != + STyR->getElementCount().isScalable()) + return cmpNumbers(STyL->getElementCount().isScalable(), + STyR->getElementCount().isScalable()); + if (STyL->getElementCount() != STyR->getElementCount()) + return cmpNumbers(STyL->getElementCount().getKnownMinValue(), + STyR->getElementCount().getKnownMinValue()); + return cmpTypes(STyL->getElementType(), STyR->getElementType()); + } + } +} + +// Determine whether the two operations are the same except that pointer-to-A +// and pointer-to-B are equivalent. This should be kept in sync with +// Instruction::isSameOperationAs. +// Read method declaration comments for more details. +int FunctionComparator::cmpOperations(const Instruction *L, + const Instruction *R, + bool &needToCmpOperands) const { + needToCmpOperands = true; + if (int Res = cmpValues(L, R)) + return Res; + + // Differences from Instruction::isSameOperationAs: + // * replace type comparison with calls to cmpTypes. + // * we test for I->getRawSubclassOptionalData (nuw/nsw/tail) at the top. + // * because of the above, we don't test for the tail bit on calls later on. + if (int Res = cmpNumbers(L->getOpcode(), R->getOpcode())) + return Res; + + if (const GetElementPtrInst *GEPL = dyn_cast(L)) { + needToCmpOperands = false; + const GetElementPtrInst *GEPR = cast(R); + if (int Res = + cmpValues(GEPL->getPointerOperand(), GEPR->getPointerOperand())) + return Res; + return cmpGEPs(GEPL, GEPR); + } + + if (int Res = cmpNumbers(L->getNumOperands(), R->getNumOperands())) + return Res; + + if (int Res = cmpTypes(L->getType(), R->getType())) + return Res; + + if (int Res = cmpNumbers(L->getRawSubclassOptionalData(), + R->getRawSubclassOptionalData())) + return Res; + + // We have two instructions of identical opcode and #operands. Check to see + // if all operands are the same type + for (unsigned i = 0, e = L->getNumOperands(); i != e; ++i) { + if (int Res = + cmpTypes(L->getOperand(i)->getType(), R->getOperand(i)->getType())) + return Res; + } + + // Check special state that is a part of some instructions. + if (const AllocaInst *AI = dyn_cast(L)) { + if (int Res = cmpTypes(AI->getAllocatedType(), + cast(R)->getAllocatedType())) + return Res; + return cmpAligns(AI->getAlign(), cast(R)->getAlign()); + } + if (const LoadInst *LI = dyn_cast(L)) { + if (int Res = cmpNumbers(LI->isVolatile(), cast(R)->isVolatile())) + return Res; + if (int Res = cmpAligns(LI->getAlign(), cast(R)->getAlign())) + return Res; + if (int Res = + cmpOrderings(LI->getOrdering(), cast(R)->getOrdering())) + return Res; + if (int Res = cmpNumbers(LI->getSyncScopeID(), + cast(R)->getSyncScopeID())) + return Res; + return cmpInstMetadata(L, R); + } + if (const StoreInst *SI = dyn_cast(L)) { + if (int Res = + cmpNumbers(SI->isVolatile(), cast(R)->isVolatile())) + return Res; + if (int Res = cmpAligns(SI->getAlign(), cast(R)->getAlign())) + return Res; + if (int Res = + cmpOrderings(SI->getOrdering(), cast(R)->getOrdering())) + return Res; + return cmpNumbers(SI->getSyncScopeID(), + cast(R)->getSyncScopeID()); + } + if (const CmpInst *CI = dyn_cast(L)) + return cmpNumbers(CI->getPredicate(), cast(R)->getPredicate()); + if (auto *CBL = dyn_cast(L)) { + auto *CBR = cast(R); + if (int Res = cmpNumbers(CBL->getCallingConv(), CBR->getCallingConv())) + return Res; + if (int Res = cmpAttrs(CBL->getAttributes(), CBR->getAttributes())) + return Res; + if (int Res = cmpOperandBundlesSchema(*CBL, *CBR)) + return Res; + if (const CallInst *CI = dyn_cast(L)) + if (int Res = cmpNumbers(CI->getTailCallKind(), + cast(R)->getTailCallKind())) + return Res; + return cmpMDNode(L->getMetadata(LLVMContext::MD_range), + R->getMetadata(LLVMContext::MD_range)); + } + if (const InsertValueInst *IVI = dyn_cast(L)) { + ArrayRef LIndices = IVI->getIndices(); + ArrayRef RIndices = cast(R)->getIndices(); + if (int Res = cmpNumbers(LIndices.size(), RIndices.size())) + return Res; + for (size_t i = 0, e = LIndices.size(); i != e; ++i) { + if (int Res = cmpNumbers(LIndices[i], RIndices[i])) + return Res; + } + return 0; + } + if (const ExtractValueInst *EVI = dyn_cast(L)) { + ArrayRef LIndices = EVI->getIndices(); + ArrayRef RIndices = cast(R)->getIndices(); + if (int Res = cmpNumbers(LIndices.size(), RIndices.size())) + return Res; + for (size_t i = 0, e = LIndices.size(); i != e; ++i) { + if (int Res = cmpNumbers(LIndices[i], RIndices[i])) + return Res; + } + } + if (const FenceInst *FI = dyn_cast(L)) { + if (int Res = + cmpOrderings(FI->getOrdering(), cast(R)->getOrdering())) + return Res; + return cmpNumbers(FI->getSyncScopeID(), + cast(R)->getSyncScopeID()); + } + if (const AtomicCmpXchgInst *CXI = dyn_cast(L)) { + if (int Res = cmpNumbers(CXI->isVolatile(), + cast(R)->isVolatile())) + return Res; + if (int Res = + cmpNumbers(CXI->isWeak(), cast(R)->isWeak())) + return Res; + if (int Res = + cmpOrderings(CXI->getSuccessOrdering(), + cast(R)->getSuccessOrdering())) + return Res; + if (int Res = + cmpOrderings(CXI->getFailureOrdering(), + cast(R)->getFailureOrdering())) + return Res; + return cmpNumbers(CXI->getSyncScopeID(), + cast(R)->getSyncScopeID()); + } + if (const AtomicRMWInst *RMWI = dyn_cast(L)) { + if (int Res = cmpNumbers(RMWI->getOperation(), + cast(R)->getOperation())) + return Res; + if (int Res = cmpNumbers(RMWI->isVolatile(), + cast(R)->isVolatile())) + return Res; + if (int Res = cmpOrderings(RMWI->getOrdering(), + cast(R)->getOrdering())) + return Res; + return cmpNumbers(RMWI->getSyncScopeID(), + cast(R)->getSyncScopeID()); + } + if (const ShuffleVectorInst *SVI = dyn_cast(L)) { + ArrayRef LMask = SVI->getShuffleMask(); + ArrayRef RMask = cast(R)->getShuffleMask(); + if (int Res = cmpNumbers(LMask.size(), RMask.size())) + return Res; + for (size_t i = 0, e = LMask.size(); i != e; ++i) { + if (int Res = cmpNumbers(LMask[i], RMask[i])) + return Res; + } + } + if (const PHINode *PNL = dyn_cast(L)) { + const PHINode *PNR = cast(R); + // Ensure that in addition to the incoming values being identical + // (checked by the caller of this function), the incoming blocks + // are also identical. + for (unsigned i = 0, e = PNL->getNumIncomingValues(); i != e; ++i) { + if (int Res = + cmpValues(PNL->getIncomingBlock(i), PNR->getIncomingBlock(i))) + return Res; + } + } + return 0; +} + +// Determine whether two GEP operations perform the same underlying arithmetic. +// Read method declaration comments for more details. +int FunctionComparator::cmpGEPs(const GEPOperator *GEPL, + const GEPOperator *GEPR) const { + unsigned int ASL = GEPL->getPointerAddressSpace(); + unsigned int ASR = GEPR->getPointerAddressSpace(); + + if (int Res = cmpNumbers(ASL, ASR)) + return Res; + + // When we have target data, we can reduce the GEP down to the value in bytes + // added to the address. + const DataLayout &DL = FnL->getDataLayout(); + unsigned OffsetBitWidth = DL.getIndexSizeInBits(ASL); + APInt OffsetL(OffsetBitWidth, 0), OffsetR(OffsetBitWidth, 0); + if (GEPL->accumulateConstantOffset(DL, OffsetL) && + GEPR->accumulateConstantOffset(DL, OffsetR)) + return cmpAPInts(OffsetL, OffsetR); + if (int Res = + cmpTypes(GEPL->getSourceElementType(), GEPR->getSourceElementType())) + return Res; + + if (int Res = cmpNumbers(GEPL->getNumOperands(), GEPR->getNumOperands())) + return Res; + + for (unsigned i = 0, e = GEPL->getNumOperands(); i != e; ++i) { + if (int Res = cmpValues(GEPL->getOperand(i), GEPR->getOperand(i))) + return Res; + } + + return 0; +} + +int FunctionComparator::cmpInlineAsm(const InlineAsm *L, + const InlineAsm *R) const { + // InlineAsm's are uniqued. If they are the same pointer, obviously they are + // the same, otherwise compare the fields. + if (L == R) + return 0; + if (int Res = cmpTypes(L->getFunctionType(), R->getFunctionType())) + return Res; + if (int Res = cmpMem(L->getAsmString(), R->getAsmString())) + return Res; + if (int Res = cmpMem(L->getConstraintString(), R->getConstraintString())) + return Res; + if (int Res = cmpNumbers(L->hasSideEffects(), R->hasSideEffects())) + return Res; + if (int Res = cmpNumbers(L->isAlignStack(), R->isAlignStack())) + return Res; + if (int Res = cmpNumbers(L->getDialect(), R->getDialect())) + return Res; + assert(L->getFunctionType() != R->getFunctionType()); + return 0; +} + +/// Compare two values used by the two functions under pair-wise comparison. If +/// this is the first time the values are seen, they're added to the mapping so +/// that we will detect mismatches on next use. +/// See comments in declaration for more details. +int FunctionComparator::cmpValues(const Value *L, const Value *R) const { + // Catch self-reference case. + if (L == FnL) { + if (R == FnR) + return 0; + return -1; + } + if (R == FnR) { + if (L == FnL) + return 0; + return 1; + } + + const Constant *ConstL = dyn_cast(L); + const Constant *ConstR = dyn_cast(R); + if (ConstL && ConstR) { + if (L == R) + return 0; + return cmpConstants(ConstL, ConstR); + } + + if (ConstL) + return 1; + if (ConstR) + return -1; + + const MetadataAsValue *MetadataValueL = dyn_cast(L); + const MetadataAsValue *MetadataValueR = dyn_cast(R); + if (MetadataValueL && MetadataValueR) { + if (MetadataValueL == MetadataValueR) + return 0; + + return cmpMetadata(MetadataValueL->getMetadata(), + MetadataValueR->getMetadata()); + } + + if (MetadataValueL) + return 1; + if (MetadataValueR) + return -1; + + const InlineAsm *InlineAsmL = dyn_cast(L); + const InlineAsm *InlineAsmR = dyn_cast(R); + + if (InlineAsmL && InlineAsmR) + return cmpInlineAsm(InlineAsmL, InlineAsmR); + if (InlineAsmL) + return 1; + if (InlineAsmR) + return -1; + + auto LeftSN = sn_mapL.insert(std::make_pair(L, sn_mapL.size())), + RightSN = sn_mapR.insert(std::make_pair(R, sn_mapR.size())); + + return cmpNumbers(LeftSN.first->second, RightSN.first->second); +} + +// Test whether two basic blocks have equivalent behaviour. +int FunctionComparator::cmpBasicBlocks(const BasicBlock *BBL, + const BasicBlock *BBR) const { + BasicBlock::const_iterator InstL = BBL->begin(), InstLE = BBL->end(); + BasicBlock::const_iterator InstR = BBR->begin(), InstRE = BBR->end(); + + do { + bool needToCmpOperands = true; + if (int Res = cmpOperations(&*InstL, &*InstR, needToCmpOperands)) + return Res; + if (needToCmpOperands) { + assert(InstL->getNumOperands() == InstR->getNumOperands()); + + for (unsigned i = 0, e = InstL->getNumOperands(); i != e; ++i) { + Value *OpL = InstL->getOperand(i); + Value *OpR = InstR->getOperand(i); + if (int Res = cmpValues(OpL, OpR)) + return Res; + // cmpValues should ensure this is true. + assert(cmpTypes(OpL->getType(), OpR->getType()) == 0); + } + } + + ++InstL; + ++InstR; + } while (InstL != InstLE && InstR != InstRE); + + if (InstL != InstLE && InstR == InstRE) + return 1; + if (InstL == InstLE && InstR != InstRE) + return -1; + return 0; +} + +int FunctionComparator::compareSignature() const { + if (int Res = cmpAttrs(FnL->getAttributes(), FnR->getAttributes())) + return Res; + + if (int Res = cmpNumbers(FnL->hasGC(), FnR->hasGC())) + return Res; + + if (FnL->hasGC()) { + if (int Res = cmpMem(FnL->getGC(), FnR->getGC())) + return Res; + } + + if (int Res = cmpNumbers(FnL->hasSection(), FnR->hasSection())) + return Res; + + if (FnL->hasSection()) { + if (int Res = cmpMem(FnL->getSection(), FnR->getSection())) + return Res; + } + + if (int Res = cmpNumbers(FnL->isVarArg(), FnR->isVarArg())) + return Res; + + // TODO: if it's internal and only used in direct calls, we could handle this + // case too. + if (int Res = cmpNumbers(FnL->getCallingConv(), FnR->getCallingConv())) + return Res; + + if (int Res = cmpTypes(FnL->getFunctionType(), FnR->getFunctionType())) + return Res; + + assert(FnL->arg_size() == FnR->arg_size() && + "Identically typed functions have different numbers of args!"); + + // Visit the arguments so that they get enumerated in the order they're + // passed in. + for (Function::const_arg_iterator ArgLI = FnL->arg_begin(), + ArgRI = FnR->arg_begin(), + ArgLE = FnL->arg_end(); + ArgLI != ArgLE; ++ArgLI, ++ArgRI) { + if (cmpValues(&*ArgLI, &*ArgRI) != 0) + llvm_unreachable("Arguments repeat!"); + } + return 0; +} + +// Test whether the two functions have equivalent behaviour. +int FunctionComparator::compare() { + beginCompare(); + + if (int Res = compareSignature()) + return Res; + + // We do a CFG-ordered walk since the actual ordering of the blocks in the + // linked list is immaterial. Our walk starts at the entry block for both + // functions, then takes each block from each terminator in order. As an + // artifact, this also means that unreachable blocks are ignored. + SmallVector FnLBBs, FnRBBs; + SmallPtrSet VisitedBBs; // in terms of F1. + + FnLBBs.push_back(&FnL->getEntryBlock()); + FnRBBs.push_back(&FnR->getEntryBlock()); + + VisitedBBs.insert(FnLBBs[0]); + while (!FnLBBs.empty()) { + const BasicBlock *BBL = FnLBBs.pop_back_val(); + const BasicBlock *BBR = FnRBBs.pop_back_val(); + + if (int Res = cmpValues(BBL, BBR)) + return Res; + + if (int Res = cmpBasicBlocks(BBL, BBR)) + return Res; + + const Instruction *TermL = BBL->getTerminator(); + const Instruction *TermR = BBR->getTerminator(); + + assert(TermL->getNumSuccessors() == TermR->getNumSuccessors()); + for (unsigned i = 0, e = TermL->getNumSuccessors(); i != e; ++i) { + if (!VisitedBBs.insert(TermL->getSuccessor(i)).second) + continue; + + FnLBBs.push_back(TermL->getSuccessor(i)); + FnRBBs.push_back(TermR->getSuccessor(i)); + } + } + return 0; +} diff --git a/diffkemp/simpll/llvm-lib/19/FunctionComparator.h b/diffkemp/simpll/llvm-lib/19/FunctionComparator.h new file mode 100644 index 000000000..d91a63808 --- /dev/null +++ b/diffkemp/simpll/llvm-lib/19/FunctionComparator.h @@ -0,0 +1,389 @@ +//===- FunctionComparator.h - Function Comparator ---------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file defines the FunctionComparator and GlobalNumberState classes which +// are used by the MergeFunctions pass for comparing functions. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TRANSFORMS_UTILS_FUNCTIONCOMPARATOR_H +#define LLVM_TRANSFORMS_UTILS_FUNCTIONCOMPARATOR_H + +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/IR/Instructions.h" +#include "llvm/IR/Operator.h" +#include "llvm/IR/ValueMap.h" +#include "llvm/Support/AtomicOrdering.h" +#include "llvm/Support/Casting.h" +#include +#include + +namespace llvm { + +class APFloat; +class AttributeList; +class APInt; +class BasicBlock; +class Constant; +class Function; +class GlobalValue; +class InlineAsm; +class Instruction; +class MDNode; +class Type; +class Value; + +/// GlobalNumberState assigns an integer to each global value in the program, +/// which is used by the comparison routine to order references to globals. This +/// state must be preserved throughout the pass, because Functions and other +/// globals need to maintain their relative order. Globals are assigned a number +/// when they are first visited. This order is deterministic, and so the +/// assigned numbers are as well. When two functions are merged, neither number +/// is updated. If the symbols are weak, this would be incorrect. If they are +/// strong, then one will be replaced at all references to the other, and so +/// direct callsites will now see one or the other symbol, and no update is +/// necessary. Note that if we were guaranteed unique names, we could just +/// compare those, but this would not work for stripped bitcodes or for those +/// few symbols without a name. +class GlobalNumberState { + struct Config : ValueMapConfig { + enum { FollowRAUW = false }; + }; + + // Each GlobalValue is mapped to an identifier. The Config ensures when RAUW + // occurs, the mapping does not change. Tracking changes is unnecessary, and + // also problematic for weak symbols (which may be overwritten). + using ValueNumberMap = ValueMap; + ValueNumberMap GlobalNumbers; + + // The next unused serial number to assign to a global. + uint64_t NextNumber = 0; + +public: + GlobalNumberState() = default; + + virtual uint64_t getNumber(GlobalValue* Global) { + ValueNumberMap::iterator MapIter; + bool Inserted; + std::tie(MapIter, Inserted) = GlobalNumbers.insert({Global, NextNumber}); + if (Inserted) + NextNumber++; + return MapIter->second; + } + + virtual void erase(GlobalValue *Global) { + GlobalNumbers.erase(Global); + } + + virtual void clear() { + GlobalNumbers.clear(); + } +}; + +/// FunctionComparator - Compares two functions to determine whether or not +/// they will generate machine code with the same behaviour. DataLayout is +/// used if available. The comparator always fails conservatively (erring on the +/// side of claiming that two functions are different). +class FunctionComparator { +public: + FunctionComparator(const Function *F1, const Function *F2, + GlobalNumberState* GN) + : FnL(F1), FnR(F2), GlobalNumbers(GN) {} + + /// Test whether the two functions have equivalent behaviour. + virtual int compare(); + +protected: + /// Start the comparison. + virtual void beginCompare() { + sn_mapL.clear(); + sn_mapR.clear(); + } + + /// Compares the signature and other general attributes of the two functions. + virtual int compareSignature() const; + + /// Test whether two basic blocks have equivalent behaviour. + virtual int cmpBasicBlocks(const BasicBlock *BBL, const BasicBlock *BBR) const; + + /// Constants comparison. + /// Its analog to lexicographical comparison between hypothetical numbers + /// of next format: + /// + /// + /// 1. Bitcastability. + /// Check whether L's type could be losslessly bitcasted to R's type. + /// On this stage method, in case when lossless bitcast is not possible + /// method returns -1 or 1, thus also defining which type is greater in + /// context of bitcastability. + /// Stage 0: If types are equal in terms of cmpTypes, then we can go straight + /// to the contents comparison. + /// If types differ, remember types comparison result and check + /// whether we still can bitcast types. + /// Stage 1: Types that satisfies isFirstClassType conditions are always + /// greater then others. + /// Stage 2: Vector is greater then non-vector. + /// If both types are vectors, then vector with greater bitwidth is + /// greater. + /// If both types are vectors with the same bitwidth, then types + /// are bitcastable, and we can skip other stages, and go to contents + /// comparison. + /// Stage 3: Pointer types are greater than non-pointers. If both types are + /// pointers of the same address space - go to contents comparison. + /// Different address spaces: pointer with greater address space is + /// greater. + /// Stage 4: Types are neither vectors, nor pointers. And they differ. + /// We don't know how to bitcast them. So, we better don't do it, + /// and return types comparison result (so it determines the + /// relationship among constants we don't know how to bitcast). + /// + /// Just for clearance, let's see how the set of constants could look + /// on single dimension axis: + /// + /// [NFCT], [FCT, "others"], [FCT, pointers], [FCT, vectors] + /// Where: NFCT - Not a FirstClassType + /// FCT - FirstClassTyp: + /// + /// 2. Compare raw contents. + /// It ignores types on this stage and only compares bits from L and R. + /// Returns 0, if L and R has equivalent contents. + /// -1 or 1 if values are different. + /// Pretty trivial: + /// 2.1. If contents are numbers, compare numbers. + /// Ints with greater bitwidth are greater. Ints with same bitwidths + /// compared by their contents. + /// 2.2. "And so on". Just to avoid discrepancies with comments + /// perhaps it would be better to read the implementation itself. + /// 3. And again about overall picture. Let's look back at how the ordered set + /// of constants will look like: + /// [NFCT], [FCT, "others"], [FCT, pointers], [FCT, vectors] + /// + /// Now look, what could be inside [FCT, "others"], for example: + /// [FCT, "others"] = + /// [ + /// [double 0.1], [double 1.23], + /// [i32 1], [i32 2], + /// { double 1.0 }, ; StructTyID, NumElements = 1 + /// { i32 1 }, ; StructTyID, NumElements = 1 + /// { double 1, i32 1 }, ; StructTyID, NumElements = 2 + /// { i32 1, double 1 } ; StructTyID, NumElements = 2 + /// ] + /// + /// Let's explain the order. Float numbers will be less than integers, just + /// because of cmpType terms: FloatTyID < IntegerTyID. + /// Floats (with same fltSemantics) are sorted according to their value. + /// Then you can see integers, and they are, like a floats, + /// could be easy sorted among each others. + /// The structures. Structures are grouped at the tail, again because of their + /// TypeID: StructTyID > IntegerTyID > FloatTyID. + /// Structures with greater number of elements are greater. Structures with + /// greater elements going first are greater. + /// The same logic with vectors, arrays and other possible complex types. + /// + /// Bitcastable constants. + /// Let's assume, that some constant, belongs to some group of + /// "so-called-equal" values with different types, and at the same time + /// belongs to another group of constants with equal types + /// and "really" equal values. + /// + /// Now, prove that this is impossible: + /// + /// If constant A with type TyA is bitcastable to B with type TyB, then: + /// 1. All constants with equal types to TyA, are bitcastable to B. Since + /// those should be vectors (if TyA is vector), pointers + /// (if TyA is pointer), or else (if TyA equal to TyB), those types should + /// be equal to TyB. + /// 2. All constants with non-equal, but bitcastable types to TyA, are + /// bitcastable to B. + /// Once again, just because we allow it to vectors and pointers only. + /// This statement could be expanded as below: + /// 2.1. All vectors with equal bitwidth to vector A, has equal bitwidth to + /// vector B, and thus bitcastable to B as well. + /// 2.2. All pointers of the same address space, no matter what they point to, + /// bitcastable. So if C is pointer, it could be bitcasted to A and to B. + /// So any constant equal or bitcastable to A is equal or bitcastable to B. + /// QED. + /// + /// In another words, for pointers and vectors, we ignore top-level type and + /// look at their particular properties (bit-width for vectors, and + /// address space for pointers). + /// If these properties are equal - compare their contents. + virtual int cmpConstants(const Constant *L, const Constant *R) const; + + /// Compares two global values by number. Uses the GlobalNumbersState to + /// identify the same gobals across function calls. + virtual int cmpGlobalValues(GlobalValue *L, GlobalValue *R) const; + + /// Assign or look up previously assigned numbers for the two values, and + /// return whether the numbers are equal. Numbers are assigned in the order + /// visited. + /// Comparison order: + /// Stage 0: Value that is function itself is always greater then others. + /// If left and right values are references to their functions, then + /// they are equal. + /// Stage 1: Constants are greater than non-constants. + /// If both left and right are constants, then the result of + /// cmpConstants is used as cmpValues result. + /// Stage 2: InlineAsm instances are greater than others. If both left and + /// right are InlineAsm instances, InlineAsm* pointers casted to + /// integers and compared as numbers. + /// Stage 3: For all other cases we compare order we meet these values in + /// their functions. If right value was met first during scanning, + /// then left value is greater. + /// In another words, we compare serial numbers, for more details + /// see comments for sn_mapL and sn_mapR. + virtual int cmpValues(const Value *L, const Value *R) const; + + /// Compare two Instructions for equivalence, similar to + /// Instruction::isSameOperationAs. + /// + /// Stages are listed in "most significant stage first" order: + /// On each stage below, we do comparison between some left and right + /// operation parts. If parts are non-equal, we assign parts comparison + /// result to the operation comparison result and exit from method. + /// Otherwise we proceed to the next stage. + /// Stages: + /// 1. Operations opcodes. Compared as numbers. + /// 2. Number of operands. + /// 3. Operation types. Compared with cmpType method. + /// 4. Compare operation subclass optional data as stream of bytes: + /// just convert it to integers and call cmpNumbers. + /// 5. Compare in operation operand types with cmpType in + /// most significant operand first order. + /// 6. Last stage. Check operations for some specific attributes. + /// For example, for Load it would be: + /// 6.1.Load: volatile (as boolean flag) + /// 6.2.Load: alignment (as integer numbers) + /// 6.3.Load: ordering (as underlying enum class value) + /// 6.4.Load: synch-scope (as integer numbers) + /// 6.5.Load: range metadata (as integer ranges) + /// On this stage its better to see the code, since its not more than 10-15 + /// strings for particular instruction, and could change sometimes. + /// + /// Sets \p needToCmpOperands to true if the operands of the instructions + /// still must be compared afterwards. In this case it's already guaranteed + /// that both instructions have the same number of operands. + virtual int cmpOperations(const Instruction *L, const Instruction *R, + bool &needToCmpOperands) const; + + /// cmpType - compares two types, + /// defines total ordering among the types set. + /// + /// Return values: + /// 0 if types are equal, + /// -1 if Left is less than Right, + /// +1 if Left is greater than Right. + /// + /// Description: + /// Comparison is broken onto stages. Like in lexicographical comparison + /// stage coming first has higher priority. + /// On each explanation stage keep in mind total ordering properties. + /// + /// 0. Before comparison we coerce pointer types of 0 address space to + /// integer. + /// We also don't bother with same type at left and right, so + /// just return 0 in this case. + /// + /// 1. If types are of different kind (different type IDs). + /// Return result of type IDs comparison, treating them as numbers. + /// 2. If types are integers, check that they have the same width. If they + /// are vectors, check that they have the same count and subtype. + /// 3. Types have the same ID, so check whether they are one of: + /// * Void + /// * Float + /// * Double + /// * X86_FP80 + /// * FP128 + /// * PPC_FP128 + /// * Label + /// * Metadata + /// We can treat these types as equal whenever their IDs are same. + /// 4. If Left and Right are pointers, return result of address space + /// comparison (numbers comparison). We can treat pointer types of same + /// address space as equal. + /// 5. If types are complex. + /// Then both Left and Right are to be expanded and their element types will + /// be checked with the same way. If we get Res != 0 on some stage, return it. + /// Otherwise return 0. + /// 6. For all other cases put llvm_unreachable. + virtual int cmpTypes(Type *TyL, Type *TyR) const; + + virtual int cmpNumbers(uint64_t L, uint64_t R) const; + virtual int cmpAligns(Align L, Align R) const; + virtual int cmpAPInts(const APInt &L, const APInt &R) const; + virtual int cmpAPFloats(const APFloat &L, const APFloat &R) const; + virtual int cmpMem(StringRef L, StringRef R) const; + + // The two functions undergoing comparison. + const Function *FnL, *FnR; + + virtual int cmpOrderings(AtomicOrdering L, AtomicOrdering R) const; + virtual int cmpInlineAsm(const InlineAsm *L, const InlineAsm *R) const; + virtual int cmpAttrs(const AttributeList L, const AttributeList R) const; + virtual int cmpMDNode(const MDNode *L, const MDNode *R) const; + virtual int cmpMetadata(const Metadata *L, const Metadata *R) const; + virtual int cmpInstMetadata(Instruction const *L, Instruction const *R) const; + virtual int cmpOperandBundlesSchema(const CallBase &LCS, const CallBase &RCS) const; + + /// Compare two GEPs for equivalent pointer arithmetic. + /// Parts to be compared for each comparison stage, + /// most significant stage first: + /// 1. Address space. As numbers. + /// 2. Constant offset, (using GEPOperator::accumulateConstantOffset method). + /// 3. Pointer operand type (using cmpType method). + /// 4. Number of operands. + /// 5. Compare operands, using cmpValues method. + virtual int cmpGEPs(const GEPOperator *GEPL, const GEPOperator *GEPR) const; + virtual int cmpGEPs(const GetElementPtrInst *GEPL, + const GetElementPtrInst *GEPR) const { + return cmpGEPs(cast(GEPL), cast(GEPR)); + } + + /// Assign serial numbers to values from left function, and values from + /// right function. + /// Explanation: + /// Being comparing functions we need to compare values we meet at left and + /// right sides. + /// Its easy to sort things out for external values. It just should be + /// the same value at left and right. + /// But for local values (those were introduced inside function body) + /// we have to ensure they were introduced at exactly the same place, + /// and plays the same role. + /// Let's assign serial number to each value when we meet it first time. + /// Values that were met at same place will be with same serial numbers. + /// In this case it would be good to explain few points about values assigned + /// to BBs and other ways of implementation (see below). + /// + /// 1. Safety of BB reordering. + /// It's safe to change the order of BasicBlocks in function. + /// Relationship with other functions and serial numbering will not be + /// changed in this case. + /// As follows from FunctionComparator::compare(), we do CFG walk: we start + /// from the entry, and then take each terminator. So it doesn't matter how in + /// fact BBs are ordered in function. And since cmpValues are called during + /// this walk, the numbering depends only on how BBs located inside the CFG. + /// So the answer is - yes. We will get the same numbering. + /// + /// 2. Impossibility to use dominance properties of values. + /// If we compare two instruction operands: first is usage of local + /// variable AL from function FL, and second is usage of local variable AR + /// from FR, we could compare their origins and check whether they are + /// defined at the same place. + /// But, we are still not able to compare operands of PHI nodes, since those + /// could be operands from further BBs we didn't scan yet. + /// So it's impossible to use dominance properties in general. + mutable DenseMap sn_mapL, sn_mapR; + + // The global state we will use + GlobalNumberState* GlobalNumbers; +}; + +} // end namespace llvm + +#endif // LLVM_TRANSFORMS_UTILS_FUNCTIONCOMPARATOR_H diff --git a/diffkemp/simpll/passes/CalledFunctionsAnalysis.cpp b/diffkemp/simpll/passes/CalledFunctionsAnalysis.cpp index 1c217d44c..0e1ad6841 100644 --- a/diffkemp/simpll/passes/CalledFunctionsAnalysis.cpp +++ b/diffkemp/simpll/passes/CalledFunctionsAnalysis.cpp @@ -14,6 +14,8 @@ #include "CalledFunctionsAnalysis.h" #include "Utils.h" + +#include #include #include #include diff --git a/diffkemp/simpll/passes/FunctionAbstractionsGenerator.cpp b/diffkemp/simpll/passes/FunctionAbstractionsGenerator.cpp index 23f8cbbd2..2cfb43740 100644 --- a/diffkemp/simpll/passes/FunctionAbstractionsGenerator.cpp +++ b/diffkemp/simpll/passes/FunctionAbstractionsGenerator.cpp @@ -14,12 +14,14 @@ #include "FunctionAbstractionsGenerator.h" #include "CalledFunctionsAnalysis.h" +#include "Config.h" #include "Logger.h" #include "Utils.h" -#include + #include #include #include +#include AnalysisKey FunctionAbstractionsGenerator::Key; diff --git a/diffkemp/simpll/passes/FunctionAbstractionsGenerator.h b/diffkemp/simpll/passes/FunctionAbstractionsGenerator.h index 7a6b6d976..2964d4f81 100644 --- a/diffkemp/simpll/passes/FunctionAbstractionsGenerator.h +++ b/diffkemp/simpll/passes/FunctionAbstractionsGenerator.h @@ -15,7 +15,10 @@ #ifndef DIFFKEMP_SIMPLL_FUNCTIONABSTRACTIONSGENERATOR_H #define DIFFKEMP_SIMPLL_FUNCTIONABSTRACTIONSGENERATOR_H +#include #include +#include + #include #include diff --git a/diffkemp/simpll/passes/MergeNumberedFunctionsPass.cpp b/diffkemp/simpll/passes/MergeNumberedFunctionsPass.cpp index 5e8e9e50c..d8651ae2a 100644 --- a/diffkemp/simpll/passes/MergeNumberedFunctionsPass.cpp +++ b/diffkemp/simpll/passes/MergeNumberedFunctionsPass.cpp @@ -12,10 +12,11 @@ //===----------------------------------------------------------------------===// #include "MergeNumberedFunctionsPass.h" - -#include "CalledFunctionsAnalysis.h" #include "FunctionAbstractionsGenerator.h" #include "Utils.h" + +#include + #include PreservedAnalyses diff --git a/diffkemp/simpll/passes/RemoveLifetimeCallsPass.cpp b/diffkemp/simpll/passes/RemoveLifetimeCallsPass.cpp index 95017c83d..738f9fe7f 100644 --- a/diffkemp/simpll/passes/RemoveLifetimeCallsPass.cpp +++ b/diffkemp/simpll/passes/RemoveLifetimeCallsPass.cpp @@ -12,7 +12,9 @@ //===----------------------------------------------------------------------===// #include "RemoveLifetimeCallsPass.h" + #include +#include PreservedAnalyses RemoveLifetimeCallsPass::run(Module &Mod, diff --git a/diffkemp/simpll/passes/SimplifyKernelGlobalsPass.cpp b/diffkemp/simpll/passes/SimplifyKernelGlobalsPass.cpp index 6d623f27d..8e552f910 100644 --- a/diffkemp/simpll/passes/SimplifyKernelGlobalsPass.cpp +++ b/diffkemp/simpll/passes/SimplifyKernelGlobalsPass.cpp @@ -25,6 +25,13 @@ #include "Utils.h" #include +#include +#include +#include + +#if LLVM_VERSION_MAJOR >= 19 +#include +#endif /// Check if a global variable with the given name is supported to be merged in /// case multiple instances of the same variable with different suffices exist. diff --git a/diffkemp/simpll/passes/StructureDebugInfoAnalysis.cpp b/diffkemp/simpll/passes/StructureDebugInfoAnalysis.cpp index cdabd32f3..1dd4fc007 100644 --- a/diffkemp/simpll/passes/StructureDebugInfoAnalysis.cpp +++ b/diffkemp/simpll/passes/StructureDebugInfoAnalysis.cpp @@ -12,8 +12,9 @@ //===----------------------------------------------------------------------===// #include "StructureDebugInfoAnalysis.h" + #include -#include +#include AnalysisKey StructureDebugInfoAnalysis::Key; diff --git a/diffkemp/simpll/passes/StructureDebugInfoAnalysis.h b/diffkemp/simpll/passes/StructureDebugInfoAnalysis.h index 0c321c04f..9abc9be7e 100644 --- a/diffkemp/simpll/passes/StructureDebugInfoAnalysis.h +++ b/diffkemp/simpll/passes/StructureDebugInfoAnalysis.h @@ -14,7 +14,9 @@ #ifndef DIFFKEMP_SIMPLL_STRUCTUREDEBUGINFOANALYSIS_H #define DIFFKEMP_SIMPLL_STRUCTUREDEBUGINFOANALYSIS_H +#include #include + #include #include diff --git a/diffkemp/simpll/passes/StructureSizeAnalysis.cpp b/diffkemp/simpll/passes/StructureSizeAnalysis.cpp index 3e309d4d8..540c16c01 100644 --- a/diffkemp/simpll/passes/StructureSizeAnalysis.cpp +++ b/diffkemp/simpll/passes/StructureSizeAnalysis.cpp @@ -12,7 +12,10 @@ //===----------------------------------------------------------------------===// #include "StructureSizeAnalysis.h" -#include "llvm/IR/TypeFinder.h" + +#include +#include +#include AnalysisKey StructureSizeAnalysis::Key; diff --git a/flake.nix b/flake.nix index db0e88c17..46ada55e3 100644 --- a/flake.nix +++ b/flake.nix @@ -13,7 +13,7 @@ pkgs = import nixpkgs { inherit system; }; llvmVersionMin = 12; - llvmVersionMax = 18; + llvmVersionMax = 19; llvmVersions = pkgs.lib.lists.range llvmVersionMin llvmVersionMax; mkDiffkemp = diff --git a/tests/unit_tests/simpll/DifferentialFunctionComparatorTest.cpp b/tests/unit_tests/simpll/DifferentialFunctionComparatorTest.cpp index d0c8b0640..4735d225e 100644 --- a/tests/unit_tests/simpll/DifferentialFunctionComparatorTest.cpp +++ b/tests/unit_tests/simpll/DifferentialFunctionComparatorTest.cpp @@ -410,8 +410,8 @@ TEST_F(DifferentialFunctionComparatorTest, CmpAllocs) { DIExpression *exprR = builderR.createExpression(); DILocation *locL = DILocation::get(DSubL->getContext(), 0, 0, DSubL); DILocation *locR = DILocation::get(DSubR->getContext(), 0, 0, DSubR); - builderL.insertDbgValueIntrinsic(CL, varL, exprL, locL, BBL); - builderR.insertDbgValueIntrinsic(CR, varR, exprR, locR, BBR); + builderL.insertDbgValueIntrinsic(CL, varL, exprL, locL, CL); + builderR.insertDbgValueIntrinsic(CR, varR, exprR, locR, CR); ASSERT_EQ(DiffComp->testCmpAllocs(CL, CR), 0); @@ -478,8 +478,8 @@ TEST_F(DifferentialFunctionComparatorTest, CmpAllocs) { exprR = builderR.createExpression(); locL = DILocation::get(DSubL->getContext(), 0, 0, DSubL); locR = DILocation::get(DSubR->getContext(), 0, 0, DSubR); - builderL.insertDbgValueIntrinsic(CL, varL, exprL, locL, BBL); - builderR.insertDbgValueIntrinsic(CR, varR, exprR, locR, BBR); + builderL.insertDbgValueIntrinsic(CL, varL, exprL, locL, CL); + builderR.insertDbgValueIntrinsic(CR, varR, exprR, locR, CR); ASSERT_EQ(DiffComp->testCmpAllocs(CL, CR), 0); // Repeat the test again, but now with different structure types. @@ -511,7 +511,7 @@ TEST_F(DifferentialFunctionComparatorTest, CmpAllocs) { PointerTypeR = builderR.createPointerType(StructTypeR, 64); varR = builderR.createAutoVariable( FunTypeR, "var", nullptr, 0, PointerTypeR); - builderR.insertDbgValueIntrinsic(CR, varR, exprR, locR, BBR); + builderR.insertDbgValueIntrinsic(CR, varR, exprR, locR, CR); ASSERT_EQ(DiffComp->testCmpAllocs(CL, CR), 1); } @@ -614,8 +614,8 @@ TEST_F(DifferentialFunctionComparatorTest, CmpMemsets) { DIExpression *exprR = builderR.createExpression(); DILocation *locL = DILocation::get(DSubL->getContext(), 0, 0, DSubL); DILocation *locR = DILocation::get(DSubR->getContext(), 0, 0, DSubR); - builderL.insertDbgValueIntrinsic(AllL, varL, exprL, locL, BBL); - builderR.insertDbgValueIntrinsic(AllR, varR, exprR, locR, BBR); + builderL.insertDbgValueIntrinsic(AllL, varL, exprL, locL, AllL); + builderR.insertDbgValueIntrinsic(AllR, varR, exprR, locR, AllR); ASSERT_EQ(DiffComp->testCmpMemset(CL, CR), -1); @@ -635,8 +635,8 @@ TEST_F(DifferentialFunctionComparatorTest, CmpMemsets) { ConstantInt::get(Type::getInt32Ty(CtxR), STyRSize)}, "", BBR); - builderL.insertDbgValueIntrinsic(AllL, varL, exprL, locL, BBL); - builderR.insertDbgValueIntrinsic(AllR, varR, exprR, locR, BBR); + builderL.insertDbgValueIntrinsic(AllL, varL, exprL, locL, AllL); + builderR.insertDbgValueIntrinsic(AllR, varR, exprR, locR, AllR); ASSERT_EQ(DiffComp->testCmpMemset(CL, CR), 0); } @@ -745,8 +745,8 @@ TEST_F(DifferentialFunctionComparatorTest, CmpMemsetsMultipleDebugMetadata) { DIExpression *exprR = builderR.createExpression(); DILocation *locL = DILocation::get(DSubL->getContext(), 0, 0, DSubL); DILocation *locR = DILocation::get(DSubR->getContext(), 0, 0, DSubR); - builderL.insertDbgValueIntrinsic(AllL, varL, exprL, locL, BBL); - builderR.insertDbgValueIntrinsic(AllR, varR, exprR, locR, BBR); + builderL.insertDbgValueIntrinsic(AllL, varL, exprL, locL, AllR); + builderR.insertDbgValueIntrinsic(AllR, varR, exprR, locR, AllL); // Debug metadata describing var from the scope of memset function. DIFile *MemsetUnitL = builderL.createFile("memset", "stdlib"); @@ -762,9 +762,9 @@ TEST_F(DifferentialFunctionComparatorTest, CmpMemsetsMultipleDebugMetadata) { DILocalVariable *memsetVarR = builderR.createAutoVariable( MemsetTypeR, "__dest", nullptr, 0, MemsetPointerTypeR); builderL.insertDbgValueIntrinsic( - AllL, memsetVarL, builderL.createExpression(), locL, BBL); + AllL, memsetVarL, builderL.createExpression(), locL, AllL); builderR.insertDbgValueIntrinsic( - AllR, memsetVarR, builderR.createExpression(), locR, BBR); + AllR, memsetVarR, builderR.createExpression(), locR, AllR); ASSERT_EQ(DiffComp->testCmpMemset(CL, CR), 0); } @@ -837,8 +837,8 @@ TEST_F(DifferentialFunctionComparatorTest, CmpMemsetsVoidPtrType) { DIExpression *exprR = builderR.createExpression(); DILocation *locL = DILocation::get(DSubL->getContext(), 0, 0, DSubL); DILocation *locR = DILocation::get(DSubR->getContext(), 0, 0, DSubR); - builderL.insertDbgValueIntrinsic(AllL, varL, exprL, locL, BBL); - builderR.insertDbgValueIntrinsic(AllR, varR, exprR, locR, BBR); + builderL.insertDbgValueIntrinsic(AllL, varL, exprL, locL, AllL); + builderR.insertDbgValueIntrinsic(AllR, varR, exprR, locR, AllR); ASSERT_NE(DiffComp->testCmpMemset(CL, CR), 0); } @@ -970,8 +970,8 @@ TEST_F(DifferentialFunctionComparatorTest, CmpMemsetsOfTypedef) { DIExpression *exprR = builderR.createExpression(); DILocation *locL = DILocation::get(DSubL->getContext(), 0, 0, DSubL); DILocation *locR = DILocation::get(DSubR->getContext(), 0, 0, DSubR); - builderL.insertDbgValueIntrinsic(AllL, varL, exprL, locL, BBL); - builderR.insertDbgValueIntrinsic(AllR, varR, exprR, locR, BBR); + builderL.insertDbgValueIntrinsic(AllL, varL, exprL, locL, AllL); + builderR.insertDbgValueIntrinsic(AllR, varR, exprR, locR, AllR); ASSERT_EQ(DiffComp->testCmpMemset(CL, CR), 0); }