forked from OSchip/llvm-project
[ArgPromotion] Remove legacy PM support
Support for the legacy pass manager in ArgPromotion causes complications in D125485. As the legacy pass manager for middle-end optimizations is unsupported, drop ArgPromotion from the legacy pipeline, rather than introducing additional complexity to deal with it. Differential Revision: https://reviews.llvm.org/D128536
This commit is contained in:
parent
ca2933f3f8
commit
217e85761c
|
@ -25,7 +25,6 @@ func boolToUnsigned(b bool) C.unsigned {
|
|||
return 0
|
||||
}
|
||||
|
||||
func (pm PassManager) AddArgumentPromotionPass() { C.LLVMAddArgumentPromotionPass(pm.C) }
|
||||
func (pm PassManager) AddConstantMergePass() { C.LLVMAddConstantMergePass(pm.C) }
|
||||
func (pm PassManager) AddDeadArgEliminationPass() { C.LLVMAddDeadArgEliminationPass(pm.C) }
|
||||
func (pm PassManager) AddFunctionAttrsPass() { C.LLVMAddFunctionAttrsPass(pm.C) }
|
||||
|
|
|
@ -19,12 +19,6 @@
|
|||
#include "caml/mlvalues.h"
|
||||
#include "caml/misc.h"
|
||||
|
||||
/* [`Module] Llvm.PassManager.t -> unit */
|
||||
value llvm_add_argument_promotion(LLVMPassManagerRef PM) {
|
||||
LLVMAddArgumentPromotionPass(PM);
|
||||
return Val_unit;
|
||||
}
|
||||
|
||||
/* [`Module] Llvm.PassManager.t -> unit */
|
||||
value llvm_add_constant_merge(LLVMPassManagerRef PM) {
|
||||
LLVMAddConstantMergePass(PM);
|
||||
|
|
|
@ -6,9 +6,6 @@
|
|||
*
|
||||
*===----------------------------------------------------------------------===*)
|
||||
|
||||
external add_argument_promotion
|
||||
: [ `Module ] Llvm.PassManager.t -> unit
|
||||
= "llvm_add_argument_promotion"
|
||||
external add_constant_merge
|
||||
: [ `Module ] Llvm.PassManager.t -> unit
|
||||
= "llvm_add_constant_merge"
|
||||
|
|
|
@ -11,11 +11,6 @@
|
|||
This interface provides an OCaml API for LLVM interprocedural optimizations, the
|
||||
classes in the [LLVMIPO] library. *)
|
||||
|
||||
(** See the [llvm::createAddArgumentPromotionPass] function. *)
|
||||
external add_argument_promotion
|
||||
: [ `Module ] Llvm.PassManager.t -> unit
|
||||
= "llvm_add_argument_promotion"
|
||||
|
||||
(** See the [llvm::createConstantMergePass] function. *)
|
||||
external add_constant_merge
|
||||
: [ `Module ] Llvm.PassManager.t -> unit
|
||||
|
|
|
@ -27,9 +27,6 @@ LLVM_C_EXTERN_C_BEGIN
|
|||
* @{
|
||||
*/
|
||||
|
||||
/** See llvm::createArgumentPromotionPass function. */
|
||||
void LLVMAddArgumentPromotionPass(LLVMPassManagerRef PM);
|
||||
|
||||
/** See llvm::createConstantMergePass function. */
|
||||
void LLVMAddConstantMergePass(LLVMPassManagerRef PM);
|
||||
|
||||
|
|
|
@ -71,7 +71,6 @@ void initializeAssumeBuilderPassLegacyPassPass(PassRegistry &);
|
|||
void initializeAnnotation2MetadataLegacyPass(PassRegistry &);
|
||||
void initializeAnnotationRemarksLegacyPass(PassRegistry &);
|
||||
void initializeOpenMPOptCGSCCLegacyPassPass(PassRegistry &);
|
||||
void initializeArgPromotionPass(PassRegistry&);
|
||||
void initializeAssumptionCacheTrackerPass(PassRegistry&);
|
||||
void initializeAtomicExpandPass(PassRegistry&);
|
||||
void initializeAttributorLegacyPassPass(PassRegistry&);
|
||||
|
|
|
@ -75,7 +75,6 @@ namespace {
|
|||
(void) llvm::createAggressiveInstCombinerPass();
|
||||
(void) llvm::createBitTrackingDCEPass();
|
||||
(void)llvm::createOpenMPOptCGSCCLegacyPass();
|
||||
(void) llvm::createArgumentPromotionPass();
|
||||
(void) llvm::createAlignmentFromAssumptionsPass();
|
||||
(void) llvm::createBasicAAWrapperPass();
|
||||
(void) llvm::createSCEVAAWrapperPass();
|
||||
|
|
|
@ -151,13 +151,6 @@ ModulePass *createDeadArgEliminationPass();
|
|||
/// bugpoint.
|
||||
ModulePass *createDeadArgHackingPass();
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
/// createArgumentPromotionPass - This pass promotes "by reference" arguments to
|
||||
/// be passed by value if the number of elements passed is smaller or
|
||||
/// equal to maxElements (maxElements == 0 means always promote).
|
||||
///
|
||||
Pass *createArgumentPromotionPass(unsigned maxElements = 3);
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
/// createOpenMPOptLegacyPass - OpenMP specific optimizations.
|
||||
Pass *createOpenMPOptCGSCCLegacyPass();
|
||||
|
|
|
@ -970,113 +970,3 @@ PreservedAnalyses ArgumentPromotionPass::run(LazyCallGraph::SCC &C,
|
|||
PA.preserveSet<AllAnalysesOn<Function>>();
|
||||
return PA;
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
/// ArgPromotion - The 'by reference' to 'by value' argument promotion pass.
|
||||
struct ArgPromotion : public CallGraphSCCPass {
|
||||
// Pass identification, replacement for typeid
|
||||
static char ID;
|
||||
|
||||
explicit ArgPromotion(unsigned MaxElements = 3)
|
||||
: CallGraphSCCPass(ID), MaxElements(MaxElements) {
|
||||
initializeArgPromotionPass(*PassRegistry::getPassRegistry());
|
||||
}
|
||||
|
||||
void getAnalysisUsage(AnalysisUsage &AU) const override {
|
||||
AU.addRequired<AssumptionCacheTracker>();
|
||||
AU.addRequired<TargetLibraryInfoWrapperPass>();
|
||||
AU.addRequired<TargetTransformInfoWrapperPass>();
|
||||
getAAResultsAnalysisUsage(AU);
|
||||
CallGraphSCCPass::getAnalysisUsage(AU);
|
||||
}
|
||||
|
||||
bool runOnSCC(CallGraphSCC &SCC) override;
|
||||
|
||||
private:
|
||||
using llvm::Pass::doInitialization;
|
||||
|
||||
bool doInitialization(CallGraph &CG) override;
|
||||
|
||||
/// The maximum number of elements to expand, or 0 for unlimited.
|
||||
unsigned MaxElements;
|
||||
};
|
||||
|
||||
} // end anonymous namespace
|
||||
|
||||
char ArgPromotion::ID = 0;
|
||||
|
||||
INITIALIZE_PASS_BEGIN(ArgPromotion, "argpromotion",
|
||||
"Promote 'by reference' arguments to scalars", false,
|
||||
false)
|
||||
INITIALIZE_PASS_DEPENDENCY(AssumptionCacheTracker)
|
||||
INITIALIZE_PASS_DEPENDENCY(CallGraphWrapperPass)
|
||||
INITIALIZE_PASS_DEPENDENCY(TargetLibraryInfoWrapperPass)
|
||||
INITIALIZE_PASS_DEPENDENCY(TargetTransformInfoWrapperPass)
|
||||
INITIALIZE_PASS_END(ArgPromotion, "argpromotion",
|
||||
"Promote 'by reference' arguments to scalars", false, false)
|
||||
|
||||
Pass *llvm::createArgumentPromotionPass(unsigned MaxElements) {
|
||||
return new ArgPromotion(MaxElements);
|
||||
}
|
||||
|
||||
bool ArgPromotion::runOnSCC(CallGraphSCC &SCC) {
|
||||
if (skipSCC(SCC))
|
||||
return false;
|
||||
|
||||
// Get the callgraph information that we need to update to reflect our
|
||||
// changes.
|
||||
CallGraph &CG = getAnalysis<CallGraphWrapperPass>().getCallGraph();
|
||||
|
||||
LegacyAARGetter AARGetter(*this);
|
||||
|
||||
bool Changed = false, LocalChange;
|
||||
|
||||
// Iterate until we stop promoting from this SCC.
|
||||
do {
|
||||
LocalChange = false;
|
||||
// Attempt to promote arguments from all functions in this SCC.
|
||||
bool IsRecursive = SCC.size() > 1;
|
||||
for (CallGraphNode *OldNode : SCC) {
|
||||
Function *OldF = OldNode->getFunction();
|
||||
if (!OldF)
|
||||
continue;
|
||||
|
||||
auto ReplaceCallSite = [&](CallBase &OldCS, CallBase &NewCS) {
|
||||
Function *Caller = OldCS.getParent()->getParent();
|
||||
CallGraphNode *NewCalleeNode =
|
||||
CG.getOrInsertFunction(NewCS.getCalledFunction());
|
||||
CallGraphNode *CallerNode = CG[Caller];
|
||||
CallerNode->replaceCallEdge(cast<CallBase>(OldCS),
|
||||
cast<CallBase>(NewCS), NewCalleeNode);
|
||||
};
|
||||
|
||||
const TargetTransformInfo &TTI =
|
||||
getAnalysis<TargetTransformInfoWrapperPass>().getTTI(*OldF);
|
||||
if (Function *NewF =
|
||||
promoteArguments(OldF, AARGetter, MaxElements, {ReplaceCallSite},
|
||||
TTI, IsRecursive)) {
|
||||
LocalChange = true;
|
||||
|
||||
// Update the call graph for the newly promoted function.
|
||||
CallGraphNode *NewNode = CG.getOrInsertFunction(NewF);
|
||||
NewNode->stealCalledFunctionsFrom(OldNode);
|
||||
if (OldNode->getNumReferences() == 0)
|
||||
delete CG.removeFunctionFromModule(OldNode);
|
||||
else
|
||||
OldF->setLinkage(Function::ExternalLinkage);
|
||||
|
||||
// And update the SCC we're iterating as well.
|
||||
SCC.ReplaceNode(OldNode, NewNode);
|
||||
}
|
||||
}
|
||||
// Remember that we changed something.
|
||||
Changed |= LocalChange;
|
||||
} while (LocalChange);
|
||||
|
||||
return Changed;
|
||||
}
|
||||
|
||||
bool ArgPromotion::doInitialization(CallGraph &CG) {
|
||||
return CallGraphSCCPass::doInitialization(CG);
|
||||
}
|
||||
|
|
|
@ -24,7 +24,6 @@ using namespace llvm;
|
|||
|
||||
void llvm::initializeIPO(PassRegistry &Registry) {
|
||||
initializeOpenMPOptCGSCCLegacyPassPass(Registry);
|
||||
initializeArgPromotionPass(Registry);
|
||||
initializeAnnotation2MetadataLegacyPass(Registry);
|
||||
initializeCalledValuePropagationLegacyPassPass(Registry);
|
||||
initializeConstantMergeLegacyPassPass(Registry);
|
||||
|
@ -70,10 +69,6 @@ void LLVMInitializeIPO(LLVMPassRegistryRef R) {
|
|||
initializeIPO(*unwrap(R));
|
||||
}
|
||||
|
||||
void LLVMAddArgumentPromotionPass(LLVMPassManagerRef PM) {
|
||||
unwrap(PM)->add(createArgumentPromotionPass());
|
||||
}
|
||||
|
||||
void LLVMAddCalledValuePropagationPass(LLVMPassManagerRef PM) {
|
||||
unwrap(PM)->add(createCalledValuePropagationPass());
|
||||
}
|
||||
|
|
|
@ -732,8 +732,6 @@ void PassManagerBuilder::populateModulePassManager(
|
|||
MPM.add(createOpenMPOptCGSCCLegacyPass());
|
||||
|
||||
MPM.add(createPostOrderFunctionAttrsLegacyPass());
|
||||
if (OptLevel > 2)
|
||||
MPM.add(createArgumentPromotionPass()); // Scalarize uninlined fn args
|
||||
|
||||
addExtensionsToPM(EP_CGSCCOptimizerLate, MPM);
|
||||
addFunctionSimplificationPasses(MPM);
|
||||
|
@ -1003,10 +1001,6 @@ void PassManagerBuilder::addLTOOptimizationPasses(legacy::PassManagerBase &PM) {
|
|||
PM.add(createGlobalOptimizerPass());
|
||||
PM.add(createGlobalDCEPass()); // Remove dead functions.
|
||||
|
||||
// If we didn't decide to inline a function, check to see if we can
|
||||
// transform it to pass arguments by value instead of by reference.
|
||||
PM.add(createArgumentPromotionPass());
|
||||
|
||||
// The IPO passes may leave cruft around. Clean up after them.
|
||||
PM.add(createInstructionCombiningPass());
|
||||
addExtensionsToPM(EP_Peephole, PM);
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
; RUN: opt < %s -tbaa -basic-aa -argpromotion -mem2reg -S | FileCheck %s
|
||||
; RUN: opt < %s -passes=argpromotion,mem2reg -S | FileCheck %s
|
||||
|
||||
target datalayout = "E-p:64:64:64"
|
||||
|
||||
|
|
|
@ -47,7 +47,6 @@ let test_transforms () =
|
|||
end;
|
||||
|
||||
ignore (PassManager.create ()
|
||||
++ add_argument_promotion
|
||||
++ add_constant_merge
|
||||
++ add_dead_arg_elimination
|
||||
++ add_function_attrs
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
|
||||
; RUN: opt -S -argpromotion -mtriple=powerpc64le-unknown-linux-gnu < %s | FileCheck %s
|
||||
; RUN: opt -S -passes=argpromotion -mtriple=powerpc64le-unknown-linux-gnu < %s | FileCheck %s
|
||||
|
||||
; Test to check that we do not promote arguments when the
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
; RUN: opt < %s -argpromotion
|
||||
; RUN: opt < %s -passes=argpromotion
|
||||
|
||||
declare void @llvm.gcroot(ptr, ptr)
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature --scrub-attributes
|
||||
; RUN: opt < %s -inline -argpromotion -disable-output
|
||||
; RUN: opt < %s -passes=inline,argpromotion -disable-output
|
||||
|
||||
define internal fastcc i32 @hash(i32* %ts, i32 %mod) nounwind {
|
||||
entry:
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature --scrub-attributes
|
||||
; RUN: opt -S -argpromotion < %s | FileCheck %s
|
||||
; RUN: opt -S -passes=argpromotion < %s | FileCheck %s
|
||||
; Test that we only promote arguments when the caller/callee have compatible
|
||||
; function attrubtes.
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature --scrub-attributes
|
||||
; RUN: opt -S -argpromotion < %s | FileCheck %s
|
||||
; RUN: opt -S -passes=argpromotion < %s | FileCheck %s
|
||||
; Test that we only promote arguments when the caller/callee have compatible
|
||||
; function attrubtes.
|
||||
|
|
|
@ -4,8 +4,8 @@
|
|||
; we don't do that anymore. It also verifies that the combination of
|
||||
; globalopt and argpromotion is able to optimize the call safely.
|
||||
;
|
||||
; RUN: opt -S -argpromotion %s | FileCheck %s --check-prefix=ARGPROMOTION
|
||||
; RUN: opt -S -globalopt -argpromotion %s | FileCheck %s --check-prefix=GLOBALOPT_ARGPROMOTION
|
||||
; RUN: opt -S -passes=argpromotion %s | FileCheck %s --check-prefix=ARGPROMOTION
|
||||
; RUN: opt -S -passes=globalopt,argpromotion %s | FileCheck %s --check-prefix=GLOBALOPT_ARGPROMOTION
|
||||
|
||||
target datalayout = "e-m:x-p:32:32-i64:64-f80:32-n8:16:32-a:0:32-S32"
|
||||
target triple = "i386-pc-windows-msvc19.11.0"
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature --scrub-attributes
|
||||
; RUN: opt -S -argpromotion < %s | FileCheck %s
|
||||
; RUN: opt -S -passes=argpromotion < %s | FileCheck %s
|
||||
|
||||
define internal i32 @callee_must_exec(i32* %p) {
|
||||
; CHECK-LABEL: define {{[^@]+}}@callee_must_exec
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature --scrub-attributes
|
||||
; RUN: opt < %s -basic-aa -argpromotion -mem2reg -S | FileCheck %s
|
||||
; RUN: opt < %s -passes=argpromotion,mem2reg -S | FileCheck %s
|
||||
target datalayout = "E-p:64:64:64-a0:0:8-f32:32:32-f64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:64-v64:64:64-v128:128:128"
|
||||
|
||||
define internal i32 @test(i32* %X, i32* %Y) {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature --scrub-attributes
|
||||
; RUN: opt -S -argpromotion < %s | FileCheck %s
|
||||
; RUN: opt -S -passes=argpromotion < %s | FileCheck %s
|
||||
|
||||
; Test argument promotion involving bitcasts.
|
||||
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature --scrub-attributes
|
||||
; RUN: opt -S < %s -inline -argpromotion | FileCheck %s --check-prefixes=ARGPROMOTION,ALL_OLDPM --allow-unused-prefixes
|
||||
; RUN: opt -S < %s -passes=inline,argpromotion | FileCheck %s --check-prefixes=ARGPROMOTION,ALL_NEWPM --allow-unused-prefixes
|
||||
; RUN: opt -S < %s -passes=inline,argpromotion | FileCheck %s --check-prefix=ARGPROMOTION
|
||||
|
||||
%S = type { %S* }
|
||||
|
||||
|
@ -55,10 +54,6 @@ bb:
|
|||
}
|
||||
|
||||
define internal i1 @barney(i8* %arg) {
|
||||
; ALL_OLDPM-LABEL: define {{[^@]+}}@barney() {
|
||||
; ALL_OLDPM-NEXT: bb:
|
||||
; ALL_OLDPM-NEXT: ret i1 undef
|
||||
;
|
||||
bb:
|
||||
ret i1 undef
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature --scrub-attributes
|
||||
; RUN: opt -S -argpromotion < %s | FileCheck %s
|
||||
; RUN: opt -S -passes=argpromotion < %s | FileCheck %s
|
||||
|
||||
declare void @may_not_return()
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature --scrub-attributes
|
||||
; RUN: opt < %s -argpromotion -S | FileCheck %s
|
||||
; RUN: opt < %s -passes=argpromotion -S | FileCheck %s
|
||||
|
||||
declare void @use.i32(i32)
|
||||
declare void @use.p32(i32*)
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature --scrub-attributes
|
||||
; RUN: opt -S -argpromotion -opaque-pointers < %s | FileCheck %s
|
||||
; RUN: opt -S -passes=argpromotion < %s | FileCheck %s
|
||||
|
||||
define internal i32 @callee_basic(ptr %p) {
|
||||
; CHECK-LABEL: define {{[^@]+}}@callee_basic
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature --scrub-attributes
|
||||
; RUN: opt < %s -argpromotion -S | FileCheck %s
|
||||
; RUN: opt < %s -passes=argpromotion -S | FileCheck %s
|
||||
; PR 32917
|
||||
|
||||
@b = common local_unnamed_addr global i32 0, align 4
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature
|
||||
; RUN: opt -S < %s -argpromotion | FileCheck %s
|
||||
; RUN: opt -S < %s -passes=argpromotion | FileCheck %s
|
||||
|
||||
; This shouldn't get infinitely promoted.
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature --scrub-attributes
|
||||
; RUN: opt -argpromotion -mem2reg -S < %s | FileCheck %s
|
||||
; RUN: opt -passes=argpromotion,mem2reg -S < %s | FileCheck %s
|
||||
target datalayout = "E-p:64:64:64-a0:0:8-f32:32:32-f64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:64-v64:64:64-v128:128:128"
|
||||
|
||||
; Checks if !prof metadata is corret in deadargelim.
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
|
||||
; RUN: opt -S -argpromotion < %s | FileCheck %s
|
||||
; RUN: opt -S -passes=argpromotion < %s | FileCheck %s
|
||||
|
||||
; Make sure volatile and atomic loads are not promoted.
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
; RUN: opt < %s -inline -loop-simplify -loop-extract -S | FileCheck %s
|
||||
; RUN: opt < %s -argpromotion -loop-simplify -loop-extract -S | FileCheck %s
|
||||
; RUN: opt < %s -passes='cgscc(inline,loop-simplify),loop-extract' -S | FileCheck %s
|
||||
; RUN: opt < %s -passes='cgscc(argpromotion,loop-simplify),loop-extract' -S | FileCheck %s
|
||||
|
||||
; This test used to trigger an assert (PR8929).
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
; RUN: opt < %s -inline -argpromotion -disable-output
|
||||
; RUN: opt < %s -passes=inline,argpromotion -disable-output
|
||||
; ModuleID = '<stdin>'
|
||||
target datalayout = "e-p:32:32:32-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:64-f32:32:32-f64:32:64-v64:64:64-v128:128:128-a0:0:64-f80:128:128"
|
||||
target triple = "i386-apple-darwin9.6"
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
; RUN: opt < %s -inline -argpromotion -instcombine -disable-output
|
||||
; RUN: opt < %s -passes=inline,argpromotion,instcombine -disable-output
|
||||
|
||||
; This test was failing because the inliner would inline @list_DeleteElement
|
||||
; into @list_DeleteDuplicates and then into @inf_GetBackwardPartnerLits,
|
||||
|
|
Loading…
Reference in New Issue