From 7959e851bd81978942ca9e8562ea9b02467c77cb Mon Sep 17 00:00:00 2001 From: Magnus Sjalander Date: Sat, 12 Sep 2026 22:12:10 +0200 Subject: [PATCH 1/6] MLIR: Support for BitCompareOperation --- jlm/mlir/RvsdgRoundtripTests.cpp | 1 + jlm/mlir/backend/JlmToMlirConverter.cpp | 2 + jlm/mlir/frontend/MlirToJlmConverter.cpp | 87 ++++++++++++++++++++++++ 3 files changed, 90 insertions(+) diff --git a/jlm/mlir/RvsdgRoundtripTests.cpp b/jlm/mlir/RvsdgRoundtripTests.cpp index 29c9db8b5..45f90945e 100644 --- a/jlm/mlir/RvsdgRoundtripTests.cpp +++ b/jlm/mlir/RvsdgRoundtripTests.cpp @@ -679,6 +679,7 @@ ROUNDTRIP_TEST(TestFreeNull, ::jlm::llvm::FreeNullTest) ROUNDTRIP_TEST(TestVariadicFunctionTest1, ::jlm::llvm::VariadicFunctionTest1) ROUNDTRIP_TEST(TestVariadicFunctionTest2, ::jlm::llvm::VariadicFunctionTest2) ROUNDTRIP_TEST(TestGamma, ::jlm::llvm::GammaTest) +ROUNDTRIP_TEST(TestGamma2, ::jlm::llvm::GammaTest2) ROUNDTRIP_TEST(TestImport, ::jlm::llvm::ImportTest) // NAllocaNodesTest is parameterized by the number of allocas, so it cannot use the diff --git a/jlm/mlir/backend/JlmToMlirConverter.cpp b/jlm/mlir/backend/JlmToMlirConverter.cpp index ff9d3f1d5..d18a80d15 100644 --- a/jlm/mlir/backend/JlmToMlirConverter.cpp +++ b/jlm/mlir/backend/JlmToMlirConverter.cpp @@ -546,6 +546,8 @@ JlmToMlirConverter::ConvertSimpleNode( else if (jlm::rvsdg::is(operation)) { MlirOp = BitCompareNode(operation, inputs); + // Set jlm.op_category attribute so it converts back to BitCompareOperation + MlirOp->setAttr("jlm.op_category", Builder_->getStringAttr("bitcmp")); } else if (auto fpCmpOp = dynamic_cast(&operation)) { diff --git a/jlm/mlir/frontend/MlirToJlmConverter.cpp b/jlm/mlir/frontend/MlirToJlmConverter.cpp index 4f07e96a1..f106a8bfb 100644 --- a/jlm/mlir/frontend/MlirToJlmConverter.cpp +++ b/jlm/mlir/frontend/MlirToJlmConverter.cpp @@ -191,6 +191,73 @@ MlirToJlmConverter::ConvertBlock(::mlir::Block & block, rvsdg::Region & rvsdgReg return GetConvertedInputs(*terminator, outputMap); } +/** + * Creates a bitstring comparison operation from an MLIR arith::CmpIOp predicate. + * + * The MLIR roundtrip encodes JLM bitstring comparisons (\ref rvsdg::biteq_op, etc.) + * as arith::CmpIOp with a "jlm.op_category" = "bitcmp" attribute. This reconstructs + * the corresponding bitstring comparison operation from the CmpIOp predicate, as + * opposed to \ref MlirToJlmConverter::ConvertCmpIOp, which reconstructs the integer + * comparison operations. + * @param predicate the comparison predicate of the CmpIOp + * @param nbits the bit width of the comparison operands + * @param op0 the first comparison operand + * @param op1 the second comparison operand + * @return the output of the created bitstring comparison node + */ +static rvsdg::Output * +CreateBitCompareNode( + ::mlir::arith::CmpIPredicate predicate, + size_t nbits, + rvsdg::Output * op0, + rvsdg::Output * op1) +{ + if (predicate == ::mlir::arith::CmpIPredicate::eq) + { + return rvsdg::biteq_op::create(nbits, op0, op1); + } + else if (predicate == ::mlir::arith::CmpIPredicate::ne) + { + return rvsdg::bitne_op::create(nbits, op0, op1); + } + else if (predicate == ::mlir::arith::CmpIPredicate::sge) + { + return rvsdg::bitsge_op::create(nbits, op0, op1); + } + else if (predicate == ::mlir::arith::CmpIPredicate::sgt) + { + return rvsdg::bitsgt_op::create(nbits, op0, op1); + } + else if (predicate == ::mlir::arith::CmpIPredicate::sle) + { + return rvsdg::bitsle_op::create(nbits, op0, op1); + } + else if (predicate == ::mlir::arith::CmpIPredicate::slt) + { + return rvsdg::bitslt_op::create(nbits, op0, op1); + } + else if (predicate == ::mlir::arith::CmpIPredicate::uge) + { + return rvsdg::bituge_op::create(nbits, op0, op1); + } + else if (predicate == ::mlir::arith::CmpIPredicate::ugt) + { + return rvsdg::bitugt_op::create(nbits, op0, op1); + } + else if (predicate == ::mlir::arith::CmpIPredicate::ule) + { + return rvsdg::bitule_op::create(nbits, op0, op1); + } + else if (predicate == ::mlir::arith::CmpIPredicate::ult) + { + return rvsdg::bitult_op::create(nbits, op0, op1); + } + else + { + JLM_UNREACHABLE("frontend : Unknown comparison predicate for bit comparison."); + } +} + rvsdg::Node * MlirToJlmConverter::ConvertCmpIOp( ::mlir::arith::CmpIOp & CompOp, @@ -683,6 +750,26 @@ MlirToJlmConverter::ConvertOperation( else if (auto ComOp = ::mlir::dyn_cast<::mlir::arith::CmpIOp>(&mlirOperation)) { auto type = ComOp.getOperandTypes()[0]; + + // Check for jlm.op_category attribute to determine operation type + auto opCategoryAttr = mlirOperation.getAttr("jlm.op_category"); + bool isBitComparison = false; + if (opCategoryAttr) + { + auto attrStr = opCategoryAttr.cast<::mlir::StringAttr>(); + if (attrStr.getValue() == "bitcmp") + { + isBitComparison = true; + } + } + + // Bit-string comparison: reconstruct the corresponding bitstring compare op + if (isBitComparison && inputs.size() > 0 && rvsdg::is(inputs[0]->Type())) + { + auto st = std::dynamic_pointer_cast(inputs[0]->Type()); + return { CreateBitCompareNode(ComOp.getPredicate(), st->nbits(), inputs[0], inputs[1]) }; + } + if (type.isa<::mlir::IntegerType>()) { auto integerType = ::mlir::cast<::mlir::IntegerType>(type); From 72f0c66c7fbecb5dc432c5b1b3565b6bf3ce99a7 Mon Sep 17 00:00:00 2001 From: Magnus Sjalander Date: Sun, 13 Sep 2026 20:37:14 +0200 Subject: [PATCH 2/6] PR comments --- jlm/mlir/backend/JlmToMlirConverter.cpp | 2 - jlm/mlir/frontend/MlirToJlmConverter.cpp | 87 ------------------------ 2 files changed, 89 deletions(-) diff --git a/jlm/mlir/backend/JlmToMlirConverter.cpp b/jlm/mlir/backend/JlmToMlirConverter.cpp index a68fa9515..b5e841e89 100644 --- a/jlm/mlir/backend/JlmToMlirConverter.cpp +++ b/jlm/mlir/backend/JlmToMlirConverter.cpp @@ -546,8 +546,6 @@ JlmToMlirConverter::ConvertSimpleNode( else if (jlm::rvsdg::is(operation)) { MlirOp = BitCompareNode(operation, inputs); - // Set jlm.op_category attribute so it converts back to BitCompareOperation - MlirOp->setAttr("jlm.op_category", Builder_->getStringAttr("bitcmp")); } else if (auto fpCmpOp = dynamic_cast(&operation)) { diff --git a/jlm/mlir/frontend/MlirToJlmConverter.cpp b/jlm/mlir/frontend/MlirToJlmConverter.cpp index 09d9409ae..790aec456 100644 --- a/jlm/mlir/frontend/MlirToJlmConverter.cpp +++ b/jlm/mlir/frontend/MlirToJlmConverter.cpp @@ -191,73 +191,6 @@ MlirToJlmConverter::ConvertBlock(::mlir::Block & block, rvsdg::Region & rvsdgReg return GetConvertedInputs(*terminator, outputMap); } -/** - * Creates a bitstring comparison operation from an MLIR arith::CmpIOp predicate. - * - * The MLIR roundtrip encodes JLM bitstring comparisons (\ref rvsdg::biteq_op, etc.) - * as arith::CmpIOp with a "jlm.op_category" = "bitcmp" attribute. This reconstructs - * the corresponding bitstring comparison operation from the CmpIOp predicate, as - * opposed to \ref MlirToJlmConverter::ConvertCmpIOp, which reconstructs the integer - * comparison operations. - * @param predicate the comparison predicate of the CmpIOp - * @param nbits the bit width of the comparison operands - * @param op0 the first comparison operand - * @param op1 the second comparison operand - * @return the output of the created bitstring comparison node - */ -static rvsdg::Output * -CreateBitCompareNode( - ::mlir::arith::CmpIPredicate predicate, - size_t nbits, - rvsdg::Output * op0, - rvsdg::Output * op1) -{ - if (predicate == ::mlir::arith::CmpIPredicate::eq) - { - return rvsdg::biteq_op::create(nbits, op0, op1); - } - else if (predicate == ::mlir::arith::CmpIPredicate::ne) - { - return rvsdg::bitne_op::create(nbits, op0, op1); - } - else if (predicate == ::mlir::arith::CmpIPredicate::sge) - { - return rvsdg::bitsge_op::create(nbits, op0, op1); - } - else if (predicate == ::mlir::arith::CmpIPredicate::sgt) - { - return rvsdg::bitsgt_op::create(nbits, op0, op1); - } - else if (predicate == ::mlir::arith::CmpIPredicate::sle) - { - return rvsdg::bitsle_op::create(nbits, op0, op1); - } - else if (predicate == ::mlir::arith::CmpIPredicate::slt) - { - return rvsdg::bitslt_op::create(nbits, op0, op1); - } - else if (predicate == ::mlir::arith::CmpIPredicate::uge) - { - return rvsdg::bituge_op::create(nbits, op0, op1); - } - else if (predicate == ::mlir::arith::CmpIPredicate::ugt) - { - return rvsdg::bitugt_op::create(nbits, op0, op1); - } - else if (predicate == ::mlir::arith::CmpIPredicate::ule) - { - return rvsdg::bitule_op::create(nbits, op0, op1); - } - else if (predicate == ::mlir::arith::CmpIPredicate::ult) - { - return rvsdg::bitult_op::create(nbits, op0, op1); - } - else - { - JLM_UNREACHABLE("frontend : Unknown comparison predicate for bit comparison."); - } -} - rvsdg::Node * MlirToJlmConverter::ConvertCmpIOp( ::mlir::arith::CmpIOp & CompOp, @@ -750,26 +683,6 @@ MlirToJlmConverter::ConvertOperation( else if (auto ComOp = ::mlir::dyn_cast<::mlir::arith::CmpIOp>(&mlirOperation)) { auto type = ComOp.getOperandTypes()[0]; - - // Check for jlm.op_category attribute to determine operation type - auto opCategoryAttr = mlirOperation.getAttr("jlm.op_category"); - bool isBitComparison = false; - if (opCategoryAttr) - { - auto attrStr = opCategoryAttr.cast<::mlir::StringAttr>(); - if (attrStr.getValue() == "bitcmp") - { - isBitComparison = true; - } - } - - // Bit-string comparison: reconstruct the corresponding bitstring compare op - if (isBitComparison && inputs.size() > 0 && rvsdg::is(inputs[0]->Type())) - { - auto st = std::dynamic_pointer_cast(inputs[0]->Type()); - return { CreateBitCompareNode(ComOp.getPredicate(), st->nbits(), inputs[0], inputs[1]) }; - } - if (type.isa<::mlir::IntegerType>()) { auto integerType = ::mlir::cast<::mlir::IntegerType>(type); From b1b49a40cc7b393a1dd17e58fe0e763da657fff6 Mon Sep 17 00:00:00 2001 From: Magnus Sjalander Date: Sun, 13 Sep 2026 18:33:27 +0200 Subject: [PATCH 3/6] MLIR: Add support for funcToPtr and ptrToFunc operations (#1887) --- jlm/mlir/RvsdgRoundtripTests.cpp | 8 +++++--- jlm/mlir/backend/JlmToMlirConverter.cpp | 14 ++++++++++++++ jlm/mlir/frontend/MlirToJlmConverter.cpp | 15 +++++++++++++++ scripts/build-mlir.sh | 2 +- 4 files changed, 35 insertions(+), 4 deletions(-) diff --git a/jlm/mlir/RvsdgRoundtripTests.cpp b/jlm/mlir/RvsdgRoundtripTests.cpp index 3cd42179a..83d9f0d77 100644 --- a/jlm/mlir/RvsdgRoundtripTests.cpp +++ b/jlm/mlir/RvsdgRoundtripTests.cpp @@ -666,9 +666,11 @@ ROUNDTRIP_TEST(TestLoadFromUndef, ::jlm::llvm::LoadFromUndefTest) ROUNDTRIP_TEST(TestGetElementPtr, ::jlm::llvm::GetElementPtrTest) ROUNDTRIP_TEST(TestBitCast, ::jlm::llvm::BitCastTest) ROUNDTRIP_TEST(TestConstantPointerNull, ::jlm::llvm::ConstantPointerNullTest) -ROUNDTRIP_TEST(TestCallTest1, ::jlm::llvm::CallTest1) -ROUNDTRIP_TEST(TestExternalCallTest1, ::jlm::llvm::ExternalCallTest1) -ROUNDTRIP_TEST(TestExternalCallTest2, ::jlm::llvm::ExternalCallTest2) +ROUNDTRIP_TEST(TestCall1, ::jlm::llvm::CallTest1) +ROUNDTRIP_TEST(TestExternalCall1, ::jlm::llvm::ExternalCallTest1) +ROUNDTRIP_TEST(TestExternalCall2, ::jlm::llvm::ExternalCallTest2) +ROUNDTRIP_TEST(TestIndirectCall1, ::jlm::llvm::IndirectCallTest1) +ROUNDTRIP_TEST(TestIndirectCall2, ::jlm::llvm::IndirectCallTest2) ROUNDTRIP_TEST(TestDeltaTest1, ::jlm::llvm::DeltaTest1) ROUNDTRIP_TEST(TestDeltaTest2, ::jlm::llvm::DeltaTest2) ROUNDTRIP_TEST(TestExternalMemory, ::jlm::llvm::ExternalMemoryTest) diff --git a/jlm/mlir/backend/JlmToMlirConverter.cpp b/jlm/mlir/backend/JlmToMlirConverter.cpp index b5e841e89..9aecfe208 100644 --- a/jlm/mlir/backend/JlmToMlirConverter.cpp +++ b/jlm/mlir/backend/JlmToMlirConverter.cpp @@ -635,6 +635,20 @@ JlmToMlirConverter::ConvertSimpleNode( JLM_UNREACHABLE(message.c_str()); } } + else if (dynamic_cast(&operation)) + { + MlirOp = Builder_->create<::mlir::jlm::FuncToPtr>( + Builder_->getUnknownLoc(), + Builder_->getType<::mlir::LLVM::LLVMPointerType>(), + inputs[0]); + } + else if (auto ptrToFnOp = dynamic_cast(&operation)) + { + MlirOp = Builder_->create<::mlir::jlm::PtrToFunc>( + Builder_->getUnknownLoc(), + ConvertType(*ptrToFnOp->result(0)), + inputs[0]); + } // ** region structural nodes ** else if (auto ctlOp = dynamic_cast(&operation)) { diff --git a/jlm/mlir/frontend/MlirToJlmConverter.cpp b/jlm/mlir/frontend/MlirToJlmConverter.cpp index 790aec456..912a37c16 100644 --- a/jlm/mlir/frontend/MlirToJlmConverter.cpp +++ b/jlm/mlir/frontend/MlirToJlmConverter.cpp @@ -927,6 +927,21 @@ MlirToJlmConverter::ConvertOperation( } JLM_UNREACHABLE("Unsupported bitcast type combination in BitcastOp."); } + else if (::mlir::isa<::mlir::jlm::FuncToPtr>(&mlirOperation)) + { + auto srcFnType = std::dynamic_pointer_cast(inputs[0]->Type()); + JLM_ASSERT(srcFnType); + auto & node = rvsdg::CreateOpNode({ inputs[0] }, srcFnType); + return { node.output(0) }; + } + else if (::mlir::isa<::mlir::jlm::PtrToFunc>(&mlirOperation)) + { + auto dstFnType = std::dynamic_pointer_cast( + ConvertType(mlirOperation.getResult(0).getType())); + JLM_ASSERT(dstFnType); + return rvsdg::outputs( + &rvsdg::CreateOpNode({ inputs[0] }, dstFnType)); + } // * region Structural nodes ** else if (auto MlirCtrlConst = ::mlir::dyn_cast<::mlir::rvsdg::ConstantCtrl>(&mlirOperation)) { diff --git a/scripts/build-mlir.sh b/scripts/build-mlir.sh index 9df852ff6..d20805a7f 100755 --- a/scripts/build-mlir.sh +++ b/scripts/build-mlir.sh @@ -2,7 +2,7 @@ set -eu GIT_REPOSITORY=https://github.com/EECS-NTNU/mlir_rvsdg.git -GIT_COMMIT=d4d1184c5da9f83356de5fd42b09251fe15a2e13 +GIT_COMMIT=d559e3f03e07f5d8f4b5a1b8eb3cc329fc267704 # Get the absolute path to this script and set default build and install paths SCRIPT_DIR="$(dirname "$(realpath "$0")")" From e40acc91d2ff9542349099202280e5fbed24c16e Mon Sep 17 00:00:00 2001 From: Nico Reissmann Date: Mon, 14 Sep 2026 08:07:25 +0200 Subject: [PATCH 4/6] IVR: add theta output constant redirection (#1889) --- jlm/llvm/opt/InvariantValueRedirection.cpp | 23 ++++++ .../opt/InvariantValueRedirectionTests.cpp | 79 +++++++++++++++++++ 2 files changed, 102 insertions(+) diff --git a/jlm/llvm/opt/InvariantValueRedirection.cpp b/jlm/llvm/opt/InvariantValueRedirection.cpp index 6266c3840..3f0d3f920 100644 --- a/jlm/llvm/opt/InvariantValueRedirection.cpp +++ b/jlm/llvm/opt/InvariantValueRedirection.cpp @@ -305,6 +305,22 @@ InvariantValueRedirection::redirectGammaOutputConstants(rvsdg::GammaNode & gamma } } +static rvsdg::Node * +getConstant(const rvsdg::Output & output) +{ + const auto owner = output.GetOwner(); + const auto ownerNode = std::get_if(&owner); + if (!ownerNode) + return nullptr; + + if (rvsdg::is(*ownerNode) + || rvsdg::is(*ownerNode) || rvsdg::is(*ownerNode) + || rvsdg::is(*ownerNode)) + return *ownerNode; + + return nullptr; +} + void InvariantValueRedirection::redirectThetaOutputs(rvsdg::ThetaNode & thetaNode) { @@ -316,7 +332,14 @@ InvariantValueRedirection::redirectThetaOutputs(rvsdg::ThetaNode & thetaNode) continue; if (rvsdg::ThetaLoopVarIsInvariant(loopVar)) + { loopVar.output->divert_users(loopVar.input->origin()); + } + else if (const auto constantNode = getConstant(*loopVar.post->origin())) + { + auto copiedConstantNode = constantNode->copy(thetaNode.region(), {}); + loopVar.output->divert_users(copiedConstantNode->output(0)); + } } } diff --git a/jlm/llvm/opt/InvariantValueRedirectionTests.cpp b/jlm/llvm/opt/InvariantValueRedirectionTests.cpp index 9ed9b468c..8b437bb15 100644 --- a/jlm/llvm/opt/InvariantValueRedirectionTests.cpp +++ b/jlm/llvm/opt/InvariantValueRedirectionTests.cpp @@ -424,6 +424,85 @@ TEST(InvariantValueRedirectionTests, TestTheta) EXPECT_EQ(lambdaNode->GetFunctionResults()[2]->origin(), thetaVar3.output); } +TEST(InvariantValueRedirectionTests, testThetaConstantRedirection) +{ + // Arrange + using namespace jlm::rvsdg; + + auto i32Type = BitType::Create(32); + auto ctlType = ControlType::Create(2); + auto fpType = FloatingPointType::Create(fpsize::dbl); + auto valueType = TestType::createValueType(); + auto functionType = FunctionType::Create( + { i32Type, ctlType, fpType, valueType }, + { i32Type, ctlType, fpType, valueType }); + + auto rvsdgModule = LlvmRvsdgModule::Create(jlm::util::FilePath(""), "", ""); + auto & rvsdg = rvsdgModule->Rvsdg(); + + auto lambdaNode = LambdaNode::Create( + rvsdg.GetRootRegion(), + LlvmLambdaOperation::Create(functionType, "test", Linkage::externalLinkage)); + auto lambdaArguments = lambdaNode->GetFunctionArguments(); + + auto thetaNode = ThetaNode::create(lambdaNode->subregion()); + auto thetaVar1 = thetaNode->AddLoopVar(lambdaArguments[0]); + auto thetaVar2 = thetaNode->AddLoopVar(lambdaArguments[1]); + auto thetaVar3 = thetaNode->AddLoopVar(lambdaArguments[2]); + auto thetaVar4 = thetaNode->AddLoopVar(lambdaArguments[3]); + + auto & intConstant = IntegerConstantOperation::Create(*thetaNode->subregion(), 32, 1); + auto & ctlConstant = ControlConstantOperation::createFalse(*thetaNode->subregion()); + auto & fpConstant = + ConstantFP::createNode(*thetaNode->subregion(), fpsize::dbl, ::llvm::APFloat(0.0)); + auto undefConstant = UndefValueOperation::Create(*thetaNode->subregion(), valueType); + + thetaVar1.post->divert_to(intConstant.output(0)); + thetaVar2.post->divert_to(&ctlConstant); + thetaVar3.post->divert_to(fpConstant.output(0)); + thetaVar4.post->divert_to(undefConstant); + + auto lambdaOutput = lambdaNode->finalize( + { thetaVar1.output, thetaVar2.output, thetaVar3.output, thetaVar4.output }); + + GraphExport::Create(*lambdaOutput, "test"); + + // Act + RunInvariantValueRedirection(*rvsdgModule); + + // Assert + { + auto [constantNode, constantOp] = + rvsdg::TryGetSimpleNodeAndOptionalOp( + *lambdaNode->GetFunctionResults()[0]->origin()); + EXPECT_NE(constantOp, nullptr); + EXPECT_EQ(constantOp->Representation().to_uint(), 1); + } + + { + auto [constantNode, constantOp] = + rvsdg::TryGetSimpleNodeAndOptionalOp( + *lambdaNode->GetFunctionResults()[1]->origin()); + EXPECT_NE(constantOp, nullptr); + EXPECT_EQ(constantOp->value().nalternatives(), 2); + EXPECT_EQ(constantOp->value().alternative(), 0); + } + + { + auto [constantNode, constantOp] = rvsdg::TryGetSimpleNodeAndOptionalOp( + *lambdaNode->GetFunctionResults()[2]->origin()); + EXPECT_NE(constantOp, nullptr); + EXPECT_EQ(constantOp->constant().convertToDouble(), 0.0); + } + + { + auto [constantNode, constantOp] = rvsdg::TryGetSimpleNodeAndOptionalOp( + *lambdaNode->GetFunctionResults()[3]->origin()); + EXPECT_NE(constantOp, nullptr); + EXPECT_EQ(constantOp->GetType(), *valueType); + } +} + TEST(InvariantValueRedirectionTests, TestCall) { // Arrange From c6d424e2ece37dc4da859b46ee2582ff4cfaa755 Mon Sep 17 00:00:00 2001 From: Nico Reissmann Date: Mon, 14 Sep 2026 08:52:01 +0200 Subject: [PATCH 5/6] LLVM: add theta reduction (#1890) Add a reduction that removes a theta node if it is possible to statically determine that its predicate is always false. --- jlm/llvm/opt/NodeReduction.cpp | 34 ++++++++++++++++---- jlm/llvm/opt/NodeReduction.hpp | 5 +++ jlm/rvsdg/ThetaTests.cpp | 58 ++++++++++++++++++++++++++++++++++ jlm/rvsdg/theta.cpp | 34 +++++++++++++++++++- jlm/rvsdg/theta.hpp | 10 ++++++ 5 files changed, 133 insertions(+), 8 deletions(-) diff --git a/jlm/llvm/opt/NodeReduction.cpp b/jlm/llvm/opt/NodeReduction.cpp index 9a0d6f9f3..2343c2289 100644 --- a/jlm/llvm/opt/NodeReduction.cpp +++ b/jlm/llvm/opt/NodeReduction.cpp @@ -18,6 +18,7 @@ #include #include #include +#include #include #include @@ -88,7 +89,9 @@ NodeReduction::Statistics::End(const rvsdg::Graph & graph) noexcept AddMeasurement("#GetElementPtrReductions", counters.numGetElementPtrReductions); AddMeasurement("#FCmpReductions", counters.numFCmpReductions); AddMeasurement("#BinaryReductions", counters.numBinaryReductions); + AddMeasurement("#GammaReductions", counters.numGammaReductions); + AddMeasurement("#ThetaReductions", counters.numThetaReductions); GetTimer(Label::Timer).stop(); } @@ -365,13 +368,20 @@ NodeReduction::ReduceNodesInRegion(rvsdg::Region & region) bool NodeReduction::ReduceStructuralNode(rvsdg::StructuralNode & structuralNode) { - bool reductionPerformed = false; - - // Reduce structural nodes - if (const auto gammaNode = dynamic_cast(&structuralNode)) - { - reductionPerformed |= ReduceGammaNode(*gammaNode); - } + const bool reductionPerformed = rvsdg::MatchTypeWithDefault( + structuralNode, + [this](rvsdg::GammaNode & gammaNode) + { + return ReduceGammaNode(gammaNode); + }, + [this](rvsdg::ThetaNode & thetaNode) + { + return reduceThetaNode(thetaNode); + }, + []() + { + return false; + }); if (reductionPerformed) { @@ -402,6 +412,16 @@ NodeReduction::ReduceGammaNode(rvsdg::GammaNode & gammaNode) return reductionPerformed; } +bool +NodeReduction::reduceThetaNode(rvsdg::ThetaNode & thetaNode) +{ + const bool reductionPerformed = rvsdg::ThetaNode::reduceStaticallyKnownPredicate(thetaNode); + if (reductionPerformed) + Statistics_->getReductionCounters().numThetaReductions++; + + return reductionPerformed; +} + bool NodeReduction::ReduceSimpleNode(rvsdg::SimpleNode & simpleNode) { diff --git a/jlm/llvm/opt/NodeReduction.hpp b/jlm/llvm/opt/NodeReduction.hpp index e2c82e9c1..463afb8f1 100644 --- a/jlm/llvm/opt/NodeReduction.hpp +++ b/jlm/llvm/opt/NodeReduction.hpp @@ -20,6 +20,7 @@ class Node; class Region; class Output; class StructuralNode; +class ThetaNode; } namespace jlm::llvm @@ -71,6 +72,9 @@ class NodeReduction final : public rvsdg::Transformation bool ReduceGammaNode(rvsdg::GammaNode & gammaNode); + bool + reduceThetaNode(rvsdg::ThetaNode & thetaNode); + bool ReduceSimpleNode(rvsdg::SimpleNode & simpleNode); @@ -173,6 +177,7 @@ class NodeReduction::Statistics final : public util::Statistics size_t numBinaryReductions = 0; size_t numGammaReductions = 0; + size_t numThetaReductions = 0; }; [[nodiscard]] ReductionCounters & diff --git a/jlm/rvsdg/ThetaTests.cpp b/jlm/rvsdg/ThetaTests.cpp index 65a3c36d8..33e929fb4 100644 --- a/jlm/rvsdg/ThetaTests.cpp +++ b/jlm/rvsdg/ThetaTests.cpp @@ -4,11 +4,15 @@ */ #include +#include #include #include #include +namespace jlm::rvsdg +{ + TEST(ThetaTests, TestThetaCreation) { using namespace jlm::rvsdg; @@ -81,3 +85,57 @@ TEST(ThetaTests, TestThetaLoopVarRemoval) EXPECT_EQ(loopvars[1].post, lv2.post); EXPECT_EQ(loopvars[1].output, lv2.output); } + +TEST(ThetaTests, reduceStaticallyKnownPredicate) +{ + // Arrange + Graph rvsdg; + auto valueType = TestType::createValueType(); + + auto x = &GraphImport::Create(rvsdg, valueType, "x"); + auto y = &GraphImport::Create(rvsdg, valueType, "y"); + + auto thetaNode = ThetaNode::create(&rvsdg.GetRootRegion()); + auto loopVar1 = thetaNode->AddLoopVar(x); + auto loopVar2 = thetaNode->AddLoopVar(y); + + auto testNode1 = TestOperation::createNode( + thetaNode->subregion(), + { loopVar1.pre, loopVar2.pre }, + { valueType }); + auto testNode2 = + TestOperation::createNode(thetaNode->subregion(), { loopVar2.pre }, { valueType }); + + auto & ctlConstant = ControlConstantOperation::createFalse(*thetaNode->subregion()); + + thetaNode->set_predicate(&ctlConstant); + loopVar1.post->divert_to(testNode1->output(0)); + loopVar2.post->divert_to(testNode2->output(0)); + + auto & x1 = GraphExport::Create(*loopVar1.output, ""); + auto & x2 = GraphExport::Create(*loopVar2.output, ""); + + // Act + ThetaNode::reduceStaticallyKnownPredicate(*thetaNode); + + // Assert + EXPECT_FALSE(Region::containsNodeType(rvsdg.GetRootRegion(), false)); + EXPECT_EQ(rvsdg.GetRootRegion().numNodes(), 3u); + + { + auto [node, operation] = TryGetSimpleNodeAndOptionalOp(*x1.origin()); + EXPECT_NE(operation, nullptr); + EXPECT_EQ(node->ninputs(), 2u); + EXPECT_EQ(node->input(0)->origin(), x); + EXPECT_EQ(node->input(1)->origin(), y); + } + + { + auto [node, operation] = TryGetSimpleNodeAndOptionalOp(*x2.origin()); + EXPECT_NE(operation, nullptr); + EXPECT_EQ(node->ninputs(), 1u); + EXPECT_EQ(node->input(0)->origin(), y); + } +} + +} diff --git a/jlm/rvsdg/theta.cpp b/jlm/rvsdg/theta.cpp index 63a3817ca..f21c3821b 100644 --- a/jlm/rvsdg/theta.cpp +++ b/jlm/rvsdg/theta.cpp @@ -4,9 +4,9 @@ * See COPYING for terms of redistribution. */ -#include #include #include +#include #include @@ -186,4 +186,36 @@ ThetaNode::GetLoopVars() const return loopvars; } +bool +ThetaNode::reduceStaticallyKnownPredicate(Node & node) +{ + auto thetaNode = dynamic_cast(&node); + if (!thetaNode) + return false; + + auto & tracedPredicate = traceOutput(*thetaNode->predicate()->origin(), false); + auto [constantNode, constantOp] = + TryGetSimpleNodeAndOptionalOp(tracedPredicate); + if (!constantOp) + return false; + + JLM_ASSERT(constantOp->value().nalternatives() == 2); + if (constantOp->value().alternative() != 0) + return false; + + // At this point we know that the predicate is statically known to be false and we can copy the + // subregion into the theta node's parent region + SubstitutionMap smap; + for (const auto & loopVar : thetaNode->GetLoopVars()) + smap.insert(loopVar.pre, loopVar.input->origin()); + + thetaNode->subregion()->copy(thetaNode->region(), smap); + + for (const auto & loopVar : thetaNode->GetLoopVars()) + loopVar.output->divert_users(&smap.lookup(*loopVar.post->origin())); + + remove(&node); + return true; +} + } diff --git a/jlm/rvsdg/theta.hpp b/jlm/rvsdg/theta.hpp index 80dc8a0f2..cc3476a2a 100644 --- a/jlm/rvsdg/theta.hpp +++ b/jlm/rvsdg/theta.hpp @@ -221,6 +221,16 @@ class ThetaNode final : public StructuralNode */ [[nodiscard]] std::vector GetLoopVars() const; + + /** + * Removes a \ref ThetaNode with a statically known false predicate by copying its subregion to + * the theta node's parent region. + * + * @param node The \ref ThetaNode that is supposed to be reduced. + * @return True, if transformation was successful, otherwise false. + */ + static bool + reduceStaticallyKnownPredicate(Node & node); }; static inline bool From 8ffeaeb10d2858d2e596f5ceb8392df03abb0300 Mon Sep 17 00:00:00 2001 From: Magnus Sjalander Date: Mon, 14 Sep 2026 09:35:02 +0200 Subject: [PATCH 6/6] LLVM: Replace deprecated biteq_op in RVSDG test graph (#1888) --- jlm/llvm/TestRvsdgs.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/jlm/llvm/TestRvsdgs.cpp b/jlm/llvm/TestRvsdgs.cpp index 1c74ec062..59a93290c 100644 --- a/jlm/llvm/TestRvsdgs.cpp +++ b/jlm/llvm/TestRvsdgs.cpp @@ -1544,8 +1544,8 @@ GammaTest2::SetupRvsdg() StoreNonVolatileOperation::Create(allocaZResults[0], nullPointer, { memoryState }, 4); auto zero = IntegerConstantOperation::Create(*lambda->subregion(), { 32, 0 }).output(0); - auto bitEq = rvsdg::biteq_op::create(32, cArgument, zero); - auto & predicateNode = MatchOperation::CreateNode(*bitEq, { { 0, 1 } }, 0, 2); + auto & intEq = IntegerEqOperation::createNode(32, *cArgument, *zero); + auto & predicateNode = MatchOperation::CreateNode(*intEq.output(0), { { 0, 1 } }, 0, 2); auto [gammaOutputA, gammaOutputMemoryState] = SetupGamma(predicateNode.output(0), xArgument, yArgument, allocaZResults[0], memoryState);