2020#include " nanobind/nanobind.h"
2121#include " llvm/ADT/ArrayRef.h"
2222#include " llvm/ADT/SmallVector.h"
23- #include " llvm/Support/raw_ostream.h"
2423
2524#include < optional>
26- #include < system_error>
27- #include < utility>
2825
2926namespace nb = nanobind;
3027using namespace nb ::literals;
@@ -1523,7 +1520,7 @@ nb::object PyOperation::create(std::string_view name,
15231520 llvm::ArrayRef<MlirValue> operands,
15241521 std::optional<nb::dict> attributes,
15251522 std::optional<std::vector<PyBlock *>> successors,
1526- int regions, DefaultingPyLocation location,
1523+ int regions, PyLocation & location,
15271524 const nb::object &maybeIp, bool inferType) {
15281525 llvm::SmallVector<MlirType, 4 > mlirResults;
15291526 llvm::SmallVector<MlirBlock, 4 > mlirSuccessors;
@@ -1627,7 +1624,7 @@ nb::object PyOperation::create(std::string_view name,
16271624 if (!operation.ptr )
16281625 throw nb::value_error (" Operation creation failed" );
16291626 PyOperationRef created =
1630- PyOperation::createDetached (location-> getContext (), operation);
1627+ PyOperation::createDetached (location. getContext (), operation);
16311628 maybeInsertOperation (created, maybeIp);
16321629
16331630 return created.getObject ();
@@ -1937,9 +1934,9 @@ nb::object PyOpView::buildGeneric(
19371934 std::optional<nb::list> resultTypeList, nb::list operandList,
19381935 std::optional<nb::dict> attributes,
19391936 std::optional<std::vector<PyBlock *>> successors,
1940- std::optional<int > regions, DefaultingPyLocation location,
1937+ std::optional<int > regions, PyLocation & location,
19411938 const nb::object &maybeIp) {
1942- PyMlirContextRef context = location-> getContext ();
1939+ PyMlirContextRef context = location. getContext ();
19431940
19441941 // Class level operation construction metadata.
19451942 // Operand and result segment specs are either none, which does no
@@ -2789,6 +2786,90 @@ class PyOpAttributeMap {
27892786 PyOperationRef operation;
27902787};
27912788
2789+ MlirLocation tracebackToLocation (MlirContext ctx) {
2790+ size_t framesLimit =
2791+ PyGlobals::get ().getTracebackLoc ().locTracebackFramesLimit ();
2792+ // Use a thread_local here to avoid requiring a large amount of space.
2793+ thread_local std::array<MlirLocation, PyGlobals::TracebackLoc::kMaxFrames >
2794+ frames;
2795+ size_t count = 0 ;
2796+
2797+ nb::gil_scoped_acquire acquire;
2798+ PyThreadState *tstate = PyThreadState_GET ();
2799+ PyFrameObject *next;
2800+ PyFrameObject *pyFrame = PyThreadState_GetFrame (tstate);
2801+ // In the increment expression:
2802+ // 1. get the next prev frame;
2803+ // 2. decrement the ref count on the current frame (in order that it can get
2804+ // gc'd, along with any objects in its closure and etc);
2805+ // 3. set current = next.
2806+ for (; pyFrame != nullptr && count < framesLimit;
2807+ next = PyFrame_GetBack (pyFrame), Py_XDECREF (pyFrame), pyFrame = next) {
2808+ PyCodeObject *code = PyFrame_GetCode (pyFrame);
2809+ auto fileNameStr =
2810+ nb::cast<std::string>(nb::borrow<nb::str>(code->co_filename ));
2811+ llvm::StringRef fileName (fileNameStr);
2812+ if (!PyGlobals::get ().getTracebackLoc ().isUserTracebackFilename (fileName))
2813+ continue ;
2814+
2815+ #if PY_VERSION_HEX < 0x030b00f0
2816+ std::string name =
2817+ nb::cast<std::string>(nb::borrow<nb::str>(code->co_name ));
2818+ llvm::StringRef funcName (name);
2819+ int startLine = PyFrame_GetLineNumber (pyFrame);
2820+ MlirLocation loc =
2821+ mlirLocationFileLineColGet (ctx, wrap (fileName), startLine, 0 );
2822+ #else
2823+ // co_qualname and PyCode_Addr2Location added in py3.11
2824+ std::string name =
2825+ nb::cast<std::string>(nb::borrow<nb::str>(code->co_qualname ));
2826+ llvm::StringRef funcName (name);
2827+ int startLine, startCol, endLine, endCol;
2828+ int lasti = PyFrame_GetLasti (pyFrame);
2829+ if (!PyCode_Addr2Location (code, lasti, &startLine, &startCol, &endLine,
2830+ &endCol)) {
2831+ throw nb::python_error ();
2832+ }
2833+ MlirLocation loc = mlirLocationFileLineColRangeGet (
2834+ ctx, wrap (fileName), startLine, startCol, endLine, endCol);
2835+ #endif
2836+
2837+ frames[count] = mlirLocationNameGet (ctx, wrap (funcName), loc);
2838+ ++count;
2839+ }
2840+ // When the loop breaks (after the last iter), current frame (if non-null)
2841+ // is leaked without this.
2842+ Py_XDECREF (pyFrame);
2843+
2844+ if (count == 0 )
2845+ return mlirLocationUnknownGet (ctx);
2846+
2847+ MlirLocation callee = frames[0 ];
2848+ assert (!mlirLocationIsNull (callee) && " expected non-null callee location" );
2849+ if (count == 1 )
2850+ return callee;
2851+
2852+ MlirLocation caller = frames[count - 1 ];
2853+ assert (!mlirLocationIsNull (caller) && " expected non-null caller location" );
2854+ for (int i = count - 2 ; i >= 1 ; i--)
2855+ caller = mlirLocationCallSiteGet (frames[i], caller);
2856+
2857+ return mlirLocationCallSiteGet (callee, caller);
2858+ }
2859+
2860+ PyLocation
2861+ maybeGetTracebackLocation (const std::optional<PyLocation> &location) {
2862+ if (location.has_value ())
2863+ return location.value ();
2864+ if (!PyGlobals::get ().getTracebackLoc ().locTracebacksEnabled ())
2865+ return DefaultingPyLocation::resolve ();
2866+
2867+ PyMlirContext &ctx = DefaultingPyMlirContext::resolve ();
2868+ MlirLocation mlirLoc = tracebackToLocation (ctx.get ());
2869+ PyMlirContextRef ref = PyMlirContext::forContext (ctx.get ());
2870+ return {ref, mlirLoc};
2871+ }
2872+
27922873} // namespace
27932874
27942875// ------------------------------------------------------------------------------
@@ -3052,10 +3133,10 @@ void mlir::python::populateIRCore(nb::module_ &m) {
30523133 .def (" __eq__" , [](PyLocation &self, nb::object other) { return false ; })
30533134 .def_prop_ro_static (
30543135 " current" ,
3055- [](nb::object & /* class*/ ) {
3136+ [](nb::object & /* class*/ ) -> std::optional<PyLocation *> {
30563137 auto *loc = PyThreadContextEntry::getDefaultLocation ();
30573138 if (!loc)
3058- throw nb::value_error ( " No current Location " ) ;
3139+ return std:: nullopt ;
30593140 return loc;
30603141 },
30613142 " Gets the Location bound to the current thread or raises ValueError" )
@@ -3240,8 +3321,9 @@ void mlir::python::populateIRCore(nb::module_ &m) {
32403321 kModuleParseDocstring )
32413322 .def_static (
32423323 " create" ,
3243- [](DefaultingPyLocation loc) {
3244- MlirModule module = mlirModuleCreateEmpty (loc);
3324+ [](const std::optional<PyLocation> &loc) {
3325+ PyLocation pyLoc = maybeGetTracebackLocation (loc);
3326+ MlirModule module = mlirModuleCreateEmpty (pyLoc.get ());
32453327 return PyModule::forModule (module ).releaseObject ();
32463328 },
32473329 nb::arg (" loc" ).none () = nb::none (), " Creates an empty module" )
@@ -3462,8 +3544,8 @@ void mlir::python::populateIRCore(nb::module_ &m) {
34623544 std::optional<std::vector<PyValue *>> operands,
34633545 std::optional<nb::dict> attributes,
34643546 std::optional<std::vector<PyBlock *>> successors, int regions,
3465- DefaultingPyLocation location, const nb::object &maybeIp ,
3466- bool inferType) {
3547+ const std::optional<PyLocation> &location ,
3548+ const nb::object &maybeIp, bool inferType) {
34673549 // Unpack/validate operands.
34683550 llvm::SmallVector<MlirValue, 4 > mlirOperands;
34693551 if (operands) {
@@ -3475,8 +3557,9 @@ void mlir::python::populateIRCore(nb::module_ &m) {
34753557 }
34763558 }
34773559
3560+ PyLocation pyLoc = maybeGetTracebackLocation (location);
34783561 return PyOperation::create (name, results, mlirOperands, attributes,
3479- successors, regions, location , maybeIp,
3562+ successors, regions, pyLoc , maybeIp,
34803563 inferType);
34813564 },
34823565 nb::arg (" name" ), nb::arg (" results" ).none () = nb::none (),
@@ -3520,12 +3603,14 @@ void mlir::python::populateIRCore(nb::module_ &m) {
35203603 std::optional<nb::list> resultTypeList, nb::list operandList,
35213604 std::optional<nb::dict> attributes,
35223605 std::optional<std::vector<PyBlock *>> successors,
3523- std::optional<int > regions, DefaultingPyLocation location,
3606+ std::optional<int > regions,
3607+ const std::optional<PyLocation> &location,
35243608 const nb::object &maybeIp) {
3609+ PyLocation pyLoc = maybeGetTracebackLocation (location);
35253610 new (self) PyOpView (PyOpView::buildGeneric (
35263611 name, opRegionSpec, operandSegmentSpecObj,
35273612 resultSegmentSpecObj, resultTypeList, operandList,
3528- attributes, successors, regions, location , maybeIp));
3613+ attributes, successors, regions, pyLoc , maybeIp));
35293614 },
35303615 nb::arg (" name" ), nb::arg (" opRegionSpec" ),
35313616 nb::arg (" operandSegmentSpecObj" ).none () = nb::none (),
@@ -3559,17 +3644,18 @@ void mlir::python::populateIRCore(nb::module_ &m) {
35593644 [](nb::handle cls, std::optional<nb::list> resultTypeList,
35603645 nb::list operandList, std::optional<nb::dict> attributes,
35613646 std::optional<std::vector<PyBlock *>> successors,
3562- std::optional<int > regions, DefaultingPyLocation location,
3647+ std::optional<int > regions, std::optional<PyLocation> location,
35633648 const nb::object &maybeIp) {
35643649 std::string name = nb::cast<std::string>(cls.attr (" OPERATION_NAME" ));
35653650 std::tuple<int , bool > opRegionSpec =
35663651 nb::cast<std::tuple<int , bool >>(cls.attr (" _ODS_REGIONS" ));
35673652 nb::object operandSegmentSpec = cls.attr (" _ODS_OPERAND_SEGMENTS" );
35683653 nb::object resultSegmentSpec = cls.attr (" _ODS_RESULT_SEGMENTS" );
3654+ PyLocation pyLoc = maybeGetTracebackLocation (location);
35693655 return PyOpView::buildGeneric (name, opRegionSpec, operandSegmentSpec,
35703656 resultSegmentSpec, resultTypeList,
35713657 operandList, attributes, successors,
3572- regions, location , maybeIp);
3658+ regions, pyLoc , maybeIp);
35733659 },
35743660 nb::arg (" cls" ), nb::arg (" results" ).none () = nb::none (),
35753661 nb::arg (" operands" ).none () = nb::none (),
0 commit comments