From ac4805702b1350148c41d2ccbc785742be691477 Mon Sep 17 00:00:00 2001 From: Nico Reissmann Date: Mon, 7 Sep 2026 07:21:54 +0200 Subject: [PATCH] LLVM: add idempotent normalization for IntegerOrOperation --- 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 dbcfc2937..e964f815a 100644 --- a/jlm/llvm/ir/operators/IntegerOperations.cpp +++ b/jlm/llvm/ir/operators/IntegerOperations.cpp @@ -762,6 +762,34 @@ IntegerOrOperation::foldConstants( return foldBinaryOperationConstants(operands); } +std::optional> +IntegerOrOperation::normalizeIdempotent( + const IntegerOrOperation &, + const std::vector & operands) +{ + JLM_ASSERT(operands.size() == 2); + auto & operand1 = *operands[0]; + auto & operand2 = *operands[1]; + + const auto & tracedOperand1 = llvm::traceOutput(operand1); + auto [c1Node, c1Operation] = + rvsdg::TryGetSimpleNodeAndOptionalOp(tracedOperand1); + if (c1Operation && c1Operation->Representation().to_uint() == 0) + { + return std::vector({ &operand2 }); + } + + const auto & tracedOperand2 = llvm::traceOutput(operand2); + auto [c2Node, c2Operation] = + rvsdg::TryGetSimpleNodeAndOptionalOp(tracedOperand2); + if (c2Operation && c2Operation->Representation().to_uint() == 0) + { + return std::vector({ &operand1 }); + } + + return std::nullopt; +} + IntegerXorOperation::~IntegerXorOperation() noexcept = default; bool diff --git a/jlm/llvm/ir/operators/IntegerOperations.hpp b/jlm/llvm/ir/operators/IntegerOperations.hpp index 42b5c3d63..ddeaa00df 100644 --- a/jlm/llvm/ir/operators/IntegerOperations.hpp +++ b/jlm/llvm/ir/operators/IntegerOperations.hpp @@ -772,6 +772,24 @@ class IntegerOrOperation final : public IntegerBinaryOperation foldConstants( const IntegerOrOperation & operation, const std::vector & operands); + + /** + * Performs the following normalization: + * + * v = IntegerOrOperation x 0 + * => + * v = x + * + * @param operation The \ref IntegerOrOperation on which the transformation is performed. + * @param operands The operands of the \ref IntegerOrOperation node. + * + * @return If the normalization could be applied, then the result of the \ref IntegerOrOperation + * after the transformation. Otherwise, std::nullopt. + */ + static std::optional> + normalizeIdempotent( + const IntegerOrOperation & operation, + const std::vector & operands); }; /** diff --git a/jlm/llvm/ir/operators/IntegerOperationsTests.cpp b/jlm/llvm/ir/operators/IntegerOperationsTests.cpp index 7e3832f14..3d738e094 100644 --- a/jlm/llvm/ir/operators/IntegerOperationsTests.cpp +++ b/jlm/llvm/ir/operators/IntegerOperationsTests.cpp @@ -245,4 +245,53 @@ TEST(IntegerSubOperationTests, normalizeAdditiveInverse) } } +TEST(IntegerOrOperationTest, 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 & subNode1 = IntegerOrOperation::createNode(32, i0, *zeroNode.output(0)); + auto & subNode2 = IntegerOrOperation::createNode(32, *zeroNode.output(0), i0); + auto & subNode3 = IntegerOrOperation::createNode(32, i0, *oneNode.output(0)); + auto & subNode4 = IntegerOrOperation::createNode(32, *oneNode.output(0), i0); + + auto & x1 = GraphExport::Create(*subNode1.output(0), "x1"); + auto & x2 = GraphExport::Create(*subNode2.output(0), "x2"); + auto & x3 = GraphExport::Create(*subNode3.output(0), "x3"); + auto & x4 = GraphExport::Create(*subNode4.output(0), "x4"); + + // Act + ReduceNode( + IntegerOrOperation::normalizeIdempotent, + dynamic_cast(subNode1)); + ReduceNode( + IntegerOrOperation::normalizeIdempotent, + dynamic_cast(subNode2)); + ReduceNode( + IntegerOrOperation::normalizeIdempotent, + dynamic_cast(subNode3)); + ReduceNode( + IntegerOrOperation::normalizeIdempotent, + dynamic_cast(subNode4)); + + graph.PruneNodes(); + + view(graph, stdout); + + // Assert + EXPECT_EQ(x1.origin(), &i0); + EXPECT_EQ(x2.origin(), &i0); + EXPECT_EQ(x3.origin(), subNode3.output(0)); + EXPECT_EQ(x4.origin(), subNode4.output(0)); +} + } diff --git a/jlm/llvm/opt/NodeReduction.cpp b/jlm/llvm/opt/NodeReduction.cpp index 4957c954e..903152b09 100644 --- a/jlm/llvm/opt/NodeReduction.cpp +++ b/jlm/llvm/opt/NodeReduction.cpp @@ -213,8 +213,8 @@ static std::vector> static std::vector> integerAndNormalizations({ IntegerAndOperation::foldConstants }); -static std::vector> - integerOrNormalizations({ IntegerOrOperation::foldConstants }); +static std::vector> integerOrNormalizations( + { IntegerOrOperation::foldConstants, IntegerOrOperation::normalizeIdempotent }); static std::vector> integerXorNormalizations({ IntegerXorOperation::foldConstants });