From b4aeeef5dee768d0d50408dc308b9cb7feb5639e Mon Sep 17 00:00:00 2001 From: Nico Reissmann Date: Tue, 22 Sep 2026 06:24:15 +0200 Subject: [PATCH 1/3] LLVM: add MemoryHoistBarrierOperation --- jlm/llvm/ir/operators/IOBarrier.cpp | 22 ++++++ jlm/llvm/ir/operators/IOBarrier.hpp | 103 ++++++++++++++++++++++++++++ 2 files changed, 125 insertions(+) diff --git a/jlm/llvm/ir/operators/IOBarrier.cpp b/jlm/llvm/ir/operators/IOBarrier.cpp index e44e02f37d..1fe77d9435 100644 --- a/jlm/llvm/ir/operators/IOBarrier.cpp +++ b/jlm/llvm/ir/operators/IOBarrier.cpp @@ -4,6 +4,7 @@ */ #include +#include namespace jlm::llvm { @@ -29,4 +30,25 @@ IOBarrierOperation::copy() const return std::make_unique(*this); } +MemoryHoistBarrierOperation::~MemoryHoistBarrierOperation() noexcept = default; + +bool +MemoryHoistBarrierOperation::operator==(const Operation & other) const noexcept +{ + const auto hoistBarrier = dynamic_cast(&other); + return hoistBarrier && hoistBarrier->getDereferenceableSize() == getDereferenceableSize(); +} + +std::string +MemoryHoistBarrierOperation::debug_string() const +{ + return util::strfmt("MemoryHoistBarrier[", getDereferenceableSize(), "]"); +} + +std::unique_ptr +MemoryHoistBarrierOperation::copy() const +{ + return std::make_unique(*this); +} + } diff --git a/jlm/llvm/ir/operators/IOBarrier.hpp b/jlm/llvm/ir/operators/IOBarrier.hpp index de85e57959..174d0f47e9 100644 --- a/jlm/llvm/ir/operators/IOBarrier.hpp +++ b/jlm/llvm/ir/operators/IOBarrier.hpp @@ -90,6 +90,109 @@ class IOBarrierOperation final : public rvsdg::SimpleOperation } }; +/** + * A \ref MemoryHoistBarrierOperation is used to sequentialize memory operations, such as + * \ref LoadNonVolatileOperation or \ref StoreNonVolatileOperation, after other IO state operations. + * It has no equivalent in LLVM. + * + * Example: + * + * \code{.c} + * int f(int * x) + * { + * opaque(); //calls internally exit(0) + * return *x; + * } + * \endcode + * + * The above code is valid C code and not undefined even if x is null. + * The reason for this is that the function opaque() invokes exit(0), and the load operation is + * never performed at runtime. In the RVSDG, the load operation might have no dependency on the + * function call to opaque() and therefore it can happen that it is sequentialized before the call + * operation, transforming the valid program to an undefined program. + * + * The \ref MemoryHoistBarrierOperation ensures a sequentialization of these two operations by + * routing the address operand through it along with an I/O state as additional operand. The + * load operation consumes then the result value of the \ref MemoryHoistBarrierOperation, + * effectively sequentializing the load after the barrier and with that after the call operation: + * + * ... io = Call opaque .... + * ptr2 = MemoryHoistBarrierOperation ptr io + * ... = LoadNonVolatileOperation ptr2 ... + * + * The \ref MemoryHoistBarrierOperation has a \ref MemoryHoistBarrierOperation::dereferenceableSize + * attribute, which determines the number of bytes that its input address is known to be + * dereferenceable. + */ +class MemoryHoistBarrierOperation final : public rvsdg::SimpleOperation +{ +public: + ~MemoryHoistBarrierOperation() noexcept override; + + explicit MemoryHoistBarrierOperation(const std::size_t dereferenceableSize) + : SimpleOperation( + { PointerType::Create(), IOStateType::Create() }, + { PointerType::Create() }), + dereferenceableSize_(dereferenceableSize) + {} + + bool + operator==(const Operation & other) const noexcept override; + + std::string + debug_string() const override; + + std::unique_ptr + copy() const override; + + [[nodiscard]] std::size_t + getDereferenceableSize() const noexcept + { + return dereferenceableSize_; + } + + [[nodiscard]] static rvsdg::Input & + getAddressInput(const rvsdg::Node & node) noexcept + { + JLM_ASSERT(rvsdg::is(&node)); + const auto input = node.input(0); + JLM_ASSERT(rvsdg::is(input->Type())); + return *input; + } + + [[nodiscard]] static rvsdg::Output & + getAddressOutput(const rvsdg::Node & node) noexcept + { + JLM_ASSERT(rvsdg::is(&node)); + const auto output = node.output(0); + JLM_ASSERT(rvsdg::is(output->Type())); + return *output; + } + + [[nodiscard]] static rvsdg::Input & + getIOStateInput(const rvsdg::Node & node) noexcept + { + JLM_ASSERT(rvsdg::is(&node)); + const auto input = node.input(1); + JLM_ASSERT(rvsdg::is(input->Type())); + return *input; + } + + static rvsdg::SimpleNode & + createNode(rvsdg::Output & value, rvsdg::Output & ioState, const std::size_t dereferenceableSize) + { + return rvsdg::CreateOpNode( + { &value, &ioState }, + dereferenceableSize); + } + +private: + /** + * Dereferenceable size of the memory input in bytes. + */ + std::size_t dereferenceableSize_; +}; + } #endif // JLM_LLVM_IR_OPERATORS_IOBARRIER_HPP From 095b5ab96dbd87d782a2237f2fe981f1abf80d92 Mon Sep 17 00:00:00 2001 From: Nico Reissmann Date: Tue, 22 Sep 2026 06:29:00 +0200 Subject: [PATCH 2/3] fix doxygen --- jlm/llvm/ir/operators/IOBarrier.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jlm/llvm/ir/operators/IOBarrier.hpp b/jlm/llvm/ir/operators/IOBarrier.hpp index 174d0f47e9..5e16d6fda5 100644 --- a/jlm/llvm/ir/operators/IOBarrier.hpp +++ b/jlm/llvm/ir/operators/IOBarrier.hpp @@ -120,7 +120,7 @@ class IOBarrierOperation final : public rvsdg::SimpleOperation * ptr2 = MemoryHoistBarrierOperation ptr io * ... = LoadNonVolatileOperation ptr2 ... * - * The \ref MemoryHoistBarrierOperation has a \ref MemoryHoistBarrierOperation::dereferenceableSize + * The \ref MemoryHoistBarrierOperation has a \ref MemoryHoistBarrierOperation::dereferenceableSize_ * attribute, which determines the number of bytes that its input address is known to be * dereferenceable. */ From 142ee65207a750ed9a7d71d6f69ec9a33167064d Mon Sep 17 00:00:00 2001 From: Nico Reissmann Date: Tue, 22 Sep 2026 06:29:33 +0200 Subject: [PATCH 3/3] renaming --- jlm/llvm/ir/operators/IOBarrier.hpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/jlm/llvm/ir/operators/IOBarrier.hpp b/jlm/llvm/ir/operators/IOBarrier.hpp index 5e16d6fda5..cebf37882a 100644 --- a/jlm/llvm/ir/operators/IOBarrier.hpp +++ b/jlm/llvm/ir/operators/IOBarrier.hpp @@ -179,10 +179,13 @@ class MemoryHoistBarrierOperation final : public rvsdg::SimpleOperation } static rvsdg::SimpleNode & - createNode(rvsdg::Output & value, rvsdg::Output & ioState, const std::size_t dereferenceableSize) + createNode( + rvsdg::Output & address, + rvsdg::Output & ioState, + const std::size_t dereferenceableSize) { return rvsdg::CreateOpNode( - { &value, &ioState }, + { &address, &ioState }, dereferenceableSize); }