From acdf5530b2b446da50818af3a27cc2b991ee7e2b Mon Sep 17 00:00:00 2001 From: Nico Reissmann Date: Tue, 22 Sep 2026 07:07:13 +0200 Subject: [PATCH] LLVM: add idempotent normalization for IntegerMulOperation --- jlm/llvm/ir/operators/IntegerOperations.cpp | 28 +++++++++++ jlm/llvm/ir/operators/IntegerOperations.hpp | 18 +++++++ .../ir/operators/IntegerOperationsTests.cpp | 49 +++++++++++++++++++ jlm/llvm/opt/NodeReduction.cpp | 4 +- 4 files changed, 97 insertions(+), 2 deletions(-) diff --git a/jlm/llvm/ir/operators/IntegerOperations.cpp b/jlm/llvm/ir/operators/IntegerOperations.cpp index ed628d18e..ec01c38bd 100644 --- a/jlm/llvm/ir/operators/IntegerOperations.cpp +++ b/jlm/llvm/ir/operators/IntegerOperations.cpp @@ -321,6 +321,34 @@ IntegerMulOperation::foldConstants( return foldBinaryOperationConstants(operands); } +std::optional> +IntegerMulOperation::normalizeIdempotent( + const IntegerMulOperation &, + const std::vector & operands) +{ + JLM_ASSERT(operands.size() == 2); + auto & operand1 = *operands[0]; + auto & operand2 = *operands[1]; + + const auto & tracedOperand1 = llvm::traceOutput(operand1, true); + auto [c1Node, c1Operation] = + rvsdg::TryGetSimpleNodeAndOptionalOp(tracedOperand1); + if (c1Operation && c1Operation->Representation().to_uint() == 1) + { + return std::vector({ &operand2 }); + } + + const auto & tracedOperand2 = llvm::traceOutput(operand2, true); + auto [c2Node, c2Operation] = + rvsdg::TryGetSimpleNodeAndOptionalOp(tracedOperand2); + if (c2Operation && c2Operation->Representation().to_uint() == 1) + { + return std::vector({ &operand1 }); + } + + return std::nullopt; +} + IntegerSDivOperation::~IntegerSDivOperation() noexcept = default; bool diff --git a/jlm/llvm/ir/operators/IntegerOperations.hpp b/jlm/llvm/ir/operators/IntegerOperations.hpp index c4b323d12..4fc06b693 100644 --- a/jlm/llvm/ir/operators/IntegerOperations.hpp +++ b/jlm/llvm/ir/operators/IntegerOperations.hpp @@ -268,6 +268,24 @@ class IntegerMulOperation final : public IntegerBinaryOperation foldConstants( const IntegerMulOperation & operation, const std::vector & operands); + + /** + * Performs the following normalization: + * + * v = IntegerMulOperation x 1 + * => + * v = x + * + * @param operation The \ref IntegerMulOperation on which the transformation is performed. + * @param operands The operands of the \ref IntegerMulOperation node. + * + * @return If the normalization could be applied, then the result of the \ref IntegerMulOperation + * after the transformation. Otherwise, std::nullopt. + */ + static std::optional> + normalizeIdempotent( + const IntegerMulOperation & operation, + const std::vector & operands); }; /** diff --git a/jlm/llvm/ir/operators/IntegerOperationsTests.cpp b/jlm/llvm/ir/operators/IntegerOperationsTests.cpp index 0a7cba3ec..548ae504a 100644 --- a/jlm/llvm/ir/operators/IntegerOperationsTests.cpp +++ b/jlm/llvm/ir/operators/IntegerOperationsTests.cpp @@ -204,6 +204,55 @@ TEST(IntegerMulOperationTest, foldConstants) TestFoldConstants({ -6, 32, 7, 32, -42, 32 }); } +TEST(IntegerMulOperationTest, normalizeIdempotent) +{ + using namespace jlm::rvsdg; + + // Arrange + auto i32Type = BitType::Create(32); + + Graph graph; + + auto & i0 = GraphImport::Create(graph, i32Type, "i0"); + + auto & zeroNode = IntegerConstantOperation::Create(graph.GetRootRegion(), 32, 0); + auto & oneNode = IntegerConstantOperation::Create(graph.GetRootRegion(), 32, 1); + + auto & mulNode1 = IntegerMulOperation::createNode(32, i0, *oneNode.output(0)); + auto & mulNode2 = IntegerMulOperation::createNode(32, *oneNode.output(0), i0); + auto & mulNode3 = IntegerMulOperation::createNode(32, i0, *zeroNode.output(0)); + auto & mulNode4 = IntegerMulOperation::createNode(32, *zeroNode.output(0), i0); + + auto & x1 = GraphExport::Create(*mulNode1.output(0), "x1"); + auto & x2 = GraphExport::Create(*mulNode2.output(0), "x2"); + auto & x3 = GraphExport::Create(*mulNode3.output(0), "x3"); + auto & x4 = GraphExport::Create(*mulNode4.output(0), "x4"); + + // Act + ReduceNode( + IntegerMulOperation::normalizeIdempotent, + dynamic_cast(mulNode1)); + ReduceNode( + IntegerMulOperation::normalizeIdempotent, + dynamic_cast(mulNode2)); + ReduceNode( + IntegerMulOperation::normalizeIdempotent, + dynamic_cast(mulNode3)); + ReduceNode( + IntegerMulOperation::normalizeIdempotent, + dynamic_cast(mulNode4)); + + graph.PruneNodes(); + + view(graph, stdout); + + // Assert + EXPECT_EQ(x1.origin(), &i0); + EXPECT_EQ(x2.origin(), &i0); + EXPECT_EQ(x3.origin(), mulNode3.output(0)); + EXPECT_EQ(x4.origin(), mulNode4.output(0)); +} + TEST(IntegerSDivOperationTest, foldConstants) { TestFoldConstants({ -13, 32, 3, 32, -4, 32 }); diff --git a/jlm/llvm/opt/NodeReduction.cpp b/jlm/llvm/opt/NodeReduction.cpp index 4341b0010..70f913be4 100644 --- a/jlm/llvm/opt/NodeReduction.cpp +++ b/jlm/llvm/opt/NodeReduction.cpp @@ -200,8 +200,8 @@ static std::vector> static std::vector> integerSubNormalizations( { IntegerSubOperation::normalizeAdditiveInverse, IntegerSubOperation::foldConstants }); -static std::vector> - integerMulNormalizations({ IntegerMulOperation::foldConstants }); +static std::vector> integerMulNormalizations( + { IntegerMulOperation::foldConstants, IntegerMulOperation::normalizeIdempotent }); static std::vector> integerSDivNormalizations({ IntegerSDivOperation::foldConstants });