Index: llvm/lib/Analysis/IRSimilarityIdentifier.cpp =================================================================== --- llvm/lib/Analysis/IRSimilarityIdentifier.cpp +++ llvm/lib/Analysis/IRSimilarityIdentifier.cpp @@ -785,11 +785,39 @@ std::make_pair(InstValA, DenseSet({InstValB}))); if (!WasInserted && !ValueMappingIt->second.contains(InstValB)) return false; + else if (ValueMappingIt->second.size() != 1) { + for (unsigned OtherVal : ValueMappingIt->second) { + if (OtherVal == InstValB) + continue; + if (ValueNumberMappingA.find(OtherVal) == ValueNumberMappingA.end()) + continue; + if (!ValueNumberMappingA[OtherVal].contains(InstValA)) + continue; + ValueNumberMappingA[OtherVal].erase(InstValA); + } + ValueNumberMappingA.erase(ValueMappingIt); + std::tie(ValueMappingIt, WasInserted) = ValueNumberMappingA.insert( + std::make_pair(InstValA, DenseSet({InstValB}))); + } std::tie(ValueMappingIt, WasInserted) = ValueNumberMappingB.insert( std::make_pair(InstValB, DenseSet({InstValA}))); if (!WasInserted && !ValueMappingIt->second.contains(InstValA)) return false; + else if (ValueMappingIt->second.size() != 1) { + for (unsigned OtherVal : ValueMappingIt->second) { + if (OtherVal == InstValA) + continue; + if (ValueNumberMappingB.find(OtherVal) == ValueNumberMappingB.end()) + continue; + if (!ValueNumberMappingB[OtherVal].contains(InstValB)) + continue; + ValueNumberMappingB[OtherVal].erase(InstValB); + } + ValueNumberMappingB.erase(ValueMappingIt); + std::tie(ValueMappingIt, WasInserted) = ValueNumberMappingB.insert( + std::make_pair(InstValB, DenseSet({InstValA}))); + } // We have different paths for commutative instructions and non-commutative // instructions since commutative instructions could allow multiple mappings Index: llvm/test/Transforms/IROutliner/outlining-larger-size-commutative.ll =================================================================== --- /dev/null +++ llvm/test/Transforms/IROutliner/outlining-larger-size-commutative.ll @@ -0,0 +1,89 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt -S -p iroutliner,verify -ir-outlining-no-cost < %s | FileCheck %s + +; This test checks that commutative instructions where the operands are +; swapped are outlined as the same function. + +; It also checks that non-commutative instructions outlined as different +; functions when the operands are swapped; + +; These are identical functions, except that in the flipped functions, +; the operands in the adds are commuted. However, since add instructions +; are commutative, we should still outline from all four as the same +; instruction. + +define void @function1(i32 %a, i32 %b) { +; CHECK-LABEL: @function1( +; CHECK-NEXT: entry: +; CHECK-NEXT: br label [[BLOCK_1:%.*]] +; CHECK: block_0: +; CHECK-NEXT: [[TMP0:%.*]] = add i32 [[A:%.*]], [[B:%.*]] +; CHECK-NEXT: [[TMP1:%.*]] = add i32 [[TMP4:%.*]], 1 +; CHECK-NEXT: [[TMP2:%.*]] = add i32 [[TMP0]], [[TMP0]] +; CHECK-NEXT: [[TMP3:%.*]] = icmp sgt i32 [[TMP0]], [[TMP0]] +; CHECK-NEXT: br i1 [[TMP3]], label [[BLOCK_1]], label [[BLOCK_2:%.*]] +; CHECK: block_1: +; CHECK-NEXT: [[TMP4]] = phi i32 [ [[TMP1]], [[BLOCK_0:%.*]] ], [ 0, [[ENTRY:%.*]] ] +; CHECK-NEXT: call void @outlined_ir_func_0(i32 [[B]]) +; CHECK-NEXT: br label [[BLOCK_0]] +; CHECK: block_2: +; CHECK-NEXT: [[TMP5:%.*]] = add i32 [[TMP2]], [[TMP2]] +; CHECK-NEXT: ret void +; +entry: + br label %block_1 + +block_0: + %0 = add i32 %a, %b + %1 = add i32 %4, 1 + %2 = add i32 %0, %0 + %3 = icmp sgt i32 %0, %0 + br i1 %3, label %block_1, label %block_2 + +block_1: + %4 = phi i32 [ %1, %block_0 ], [ 0, %entry ] + %5 = add i32 %b, %b + br label %block_0 + +block_2: + %6 = add i32 %2, %2 + ret void +} + +define void @function2(i32 %a, i32 %b) { +; CHECK-LABEL: @function2( +; CHECK-NEXT: entry: +; CHECK-NEXT: br label [[BLOCK_1:%.*]] +; CHECK: block_0: +; CHECK-NEXT: [[TMP0:%.*]] = sub i32 [[A:%.*]], [[B:%.*]] +; CHECK-NEXT: [[TMP1:%.*]] = add i32 1, [[TMP4:%.*]] +; CHECK-NEXT: [[TMP2:%.*]] = add i32 [[TMP0]], [[TMP0]] +; CHECK-NEXT: [[TMP3:%.*]] = icmp sgt i32 [[TMP0]], [[TMP0]] +; CHECK-NEXT: br i1 [[TMP3]], label [[BLOCK_1]], label [[BLOCK_2:%.*]] +; CHECK: block_1: +; CHECK-NEXT: [[TMP4]] = phi i32 [ [[TMP1]], [[BLOCK_0:%.*]] ], [ 0, [[ENTRY:%.*]] ] +; CHECK-NEXT: call void @outlined_ir_func_0(i32 [[B]]) +; CHECK-NEXT: br label [[BLOCK_0]] +; CHECK: block_2: +; CHECK-NEXT: [[TMP5:%.*]] = sub i32 [[TMP2]], [[TMP2]] +; CHECK-NEXT: ret void +; +entry: + br label %block_1 + +block_0: + %0 = sub i32 %a, %b + %1 = add i32 1, %4 + %2 = add i32 %0, %0 + %3 = icmp sgt i32 %0, %0 + br i1 %3, label %block_1, label %block_2 + +block_1: + %4 = phi i32 [ %1, %block_0 ], [ 0, %entry ] + %5 = add i32 %b, %b + br label %block_0 + +block_2: + %6 = sub i32 %2, %2 + ret void +}