diff --git a/docs/source/development.rst b/docs/source/development.rst index 9feb5b3..8f1ed1e 100644 --- a/docs/source/development.rst +++ b/docs/source/development.rst @@ -59,10 +59,9 @@ The ``PyContext::getGlobalContext()`` function here will cause problems. Accordi to observation, the an PyContext object will be generated and managed by NanoBind as long as you imported the library. When encountering memory bugs, you cannot see concrete symbol name in the stacktrace in Debug mode. For commit -`285d53db92264e55a705195df9d1a6c7a024d3b6 `_, the code above and `example/llvmir_builder.py line 25 `_ will cause -an memory bug (``free(): invalid pointer``) at the end of finishing the program, which -is seemingly irrelevant of the execution and may relate to underlying operating -principles of LLVM and NanoBind. +`285d53db92264e55a705195df9d1a6c7a024d3b6 `_, the code above, `example/llvmir_builder.py line 25 `_ and the ``print(m)`` at the end of file will cause an memory bug (``free(): invalid pointer``) +when program is finishing, which is seemingly irrelevant of the execution +and may relate to underlying operating principles of LLVM and NanoBind. As a contrast, the following went smooth. diff --git a/example/el_psy_congaroo.py b/example/el_psy_congaroo.py index 4e3796f..dd50299 100644 --- a/example/el_psy_congaroo.py +++ b/example/el_psy_congaroo.py @@ -3,5 +3,8 @@ import llvmpym.core as core -with core.Context() as c: - core.FunctionType() +c = core.Context.get_global_context() +s = r'target triple = "unknown-unknown-unknown"' +mem_buf = core.MemoryBuffer.from_str(s, "") +res = c.parse_ir(mem_buf) +print(res) diff --git a/example/llvmir_builder.py b/example/ir_builder.py similarity index 99% rename from example/llvmir_builder.py rename to example/ir_builder.py index 34c7ae5..363f9b8 100644 --- a/example/llvmir_builder.py +++ b/example/ir_builder.py @@ -38,5 +38,4 @@ builder.position_at_end(bb_exit) builder.ret(myint) - print(m) diff --git a/example/parse_ir_assmebly.py b/example/parse_ir_assmebly.py index d7974e2..0081bbd 100644 --- a/example/parse_ir_assmebly.py +++ b/example/parse_ir_assmebly.py @@ -25,9 +25,9 @@ ''' m = utils.parse_assembly(asm_str) -print(m) -# for f in m.functions: -# print(f'Function: {f.name}/`{f.type}`') -# m = f.parent -# # assert f.kind == core.ValueKind.Function +for f in m.functions: + print(f'Function: {f.name}/`{f.type}`') + m = f.parent + assert f.kind == core.ValueKind.Function + print(f"Functoin attributes: {}") diff --git a/note.typ b/note.typ index 2290cc6..c4d582f 100644 --- a/note.typ +++ b/note.typ @@ -46,7 +46,8 @@ directory(e.g. `.venv/lib/python3.12/site-packages/llvmpym/llvmpym_ext`) to corr - Doc: if document has `@see LLVMContext::setYieldCallback()`, then we need to also include documents for that function (in C++ header) - TODO change all possible unsigned return value into corresponding enum type - also parameter (like `kindID`?) -- Add cache to object properties +- LATER Add cache to object properties +- TODO delete all `destory` function == More Python Style 1. iterator: next, prev ? diff --git a/src/llvm/Core.cpp b/src/llvm/Core.cpp index 389f801..265b9c2 100644 --- a/src/llvm/Core.cpp +++ b/src/llvm/Core.cpp @@ -3375,12 +3375,12 @@ void bindOtherClasses(nb::module_ &m) { }, ":raises RuntimeError") .def_static("from_str", - [](std::string &inputData, const char *BufferName, bool RequiresNullTerminator) { + [](std::string &inputData, bool RequiresNullTerminator, const char *BufferName) { return PyMemoryBuffer(LLVMCreateMemoryBufferWithMemoryRange (inputData.c_str(), inputData.size(), BufferName, RequiresNullTerminator)); }, - "input_data"_a, "buffer_name"_a, "requires_null_terminator"_a) + "input_data"_a, "requires_null_terminator"_a, "buffer_name"_a = "") .def_static("from_str", [](const std::string &inputData, const char *BufferName) -> optional{ @@ -3388,7 +3388,7 @@ void bindOtherClasses(nb::module_ &m) { (inputData.c_str(), inputData.size(), BufferName); WRAP_OPTIONAL_RETURN(res, PyMemoryBuffer); }, - "input_data"_a, "buffer_name"_a) + "input_data"_a, "buffer_name"_a = "") .def_prop_ro("buffer_start", [](PyMemoryBuffer &self) { return LLVMGetBufferStart(self.get()); @@ -4393,23 +4393,22 @@ void bindOtherClasses(nb::module_ &m) { }, "callback"_a, "opaque_handle"_a, "Set the yield callback function for this context.") - // .def("parse_ir", - // [](PyContext &self, PyMemoryBuffer &memBuf) { - // try { - // auto res = parseIR(self.get(), memBuf.get()); - // memBuf.resetNoClean(); // We Cannot reuse the memory buffer again - // return res; - // } catch (const std::exception& ex) { - // // TODO test whether it is still available after a filed operation. - // memBuf.resetNoClean(); - // throw ex; - // } - // }, - // "memory_buffer"_a, - // "Read LLVM IR from a memory buffer and convert it into an in-memory Module" - // "object.\n\n" - // ":raises RuntimeError\n" - // "NOTE that you cannot use passed-in memory_buffer after this operation.") + .def("parse_ir", + [](PyContext &self, PyMemoryBuffer &memBuf) { + try { + auto res = parseIR(self.get(), memBuf.get()); + memBuf.reset(); // We Cannot reuse the memory buffer again + return res; + } catch (const std::exception& ex) { + memBuf.reset(); + throw ex; + } + }, + "memory_buffer"_a, + "Read LLVM IR from a memory buffer and convert it into an in-memory Module" + "object.\n\n" + ":raises RuntimeError\n" + "NOTE that you cannot use passed-in memory_buffer after this operation.") .def("create_builder", [](PyContext &self) { return PyBuilder(LLVMCreateBuilderInContext(self.get())); diff --git a/src/llvm/_types/PyContext.cpp b/src/llvm/_types/PyContext.cpp index a9f904c..4f402f7 100644 --- a/src/llvm/_types/PyContext.cpp +++ b/src/llvm/_types/PyContext.cpp @@ -42,6 +42,9 @@ LLVMContextRef PyContext::get() const { void PyContext::LLVMContextDeleter::operator()(LLVMContextRef context) const { + // NOTE the parent object is actually I think, since + // all the state in the `parent` object is the state when we created + // LLVMContextDeleter object if (context && parent && !parent->is_global_context) { LLVMContextDispose(context); diff --git a/src/llvm/_types/PyMemoryBuffer.cpp b/src/llvm/_types/PyMemoryBuffer.cpp index ef3ff70..04641ce 100644 --- a/src/llvm/_types/PyMemoryBuffer.cpp +++ b/src/llvm/_types/PyMemoryBuffer.cpp @@ -1,4 +1,5 @@ #include "PyMemoryBuffer.h" +#include std::unordered_map> PyMemoryBuffer::mb_map; @@ -10,22 +11,37 @@ LLVMMemoryBufferRef PyMemoryBuffer::get() const { return mb.get(); } +// This function doesn't call `reset` method on shared_ptr object, since +// it will then call deleter, which is undesired and will lead to crash. +void PyMemoryBuffer::reset() { + LLVMMemoryBufferRef m = mb.get(); + if (m) { + std::lock_guard lock(PyMemoryBuffer::map_mutex); + PyMemoryBuffer::mb_map.erase(m); + } +} + void PyMemoryBuffer::LLVMMemoryBufferRefDeleter::operator() -(LLVMMemoryBufferRef entries) const { - if (entries) { - LLVMDisposeMemoryBuffer(entries); - +(LLVMMemoryBufferRef mb) const { + if (mb) { std::lock_guard lock(PyMemoryBuffer::map_mutex); - PyMemoryBuffer::mb_map.erase(entries); + auto it = PyMemoryBuffer::mb_map.find(mb); + + // the logic here is specially designed for `reset` function + if (it != PyMemoryBuffer::mb_map.end()) { + LLVMDisposeMemoryBuffer(mb); + + PyMemoryBuffer::mb_map.erase(mb); + } } } std::shared_ptr PyMemoryBuffer::get_shared_mb -(LLVMMemoryBufferRef entries) { +(LLVMMemoryBufferRef mb) { std::lock_guard lock(PyMemoryBuffer::map_mutex); - auto it = PyMemoryBuffer::mb_map.find(entries); + auto it = PyMemoryBuffer::mb_map.find(mb); if (it != PyMemoryBuffer::mb_map.end()) { if (auto shared = it->second.lock()) { @@ -33,7 +49,7 @@ std::shared_ptr PyMemoryBuffer::get_shared_mb } } - auto shared = std::shared_ptr(entries, LLVMMemoryBufferRefDeleter()); - PyMemoryBuffer::mb_map[entries] = shared; + auto shared = std::shared_ptr(mb, LLVMMemoryBufferRefDeleter()); + PyMemoryBuffer::mb_map[mb] = shared; return shared; } diff --git a/src/llvm/_types/PyMemoryBuffer.h b/src/llvm/_types/PyMemoryBuffer.h index fd4869b..1acf15b 100644 --- a/src/llvm/_types/PyMemoryBuffer.h +++ b/src/llvm/_types/PyMemoryBuffer.h @@ -10,6 +10,12 @@ class PyMemoryBuffer { public: explicit PyMemoryBuffer(LLVMMemoryBufferRef mb); LLVMMemoryBufferRef get() const; + + /* + * This function reset the PyMemoryBuffer object, preventing it from + * being automatically disposed + */ + void reset(); private: std::shared_ptr mb;