20
20
#include " nanobind/nanobind.h"
21
21
#include " llvm/ADT/ArrayRef.h"
22
22
#include " llvm/ADT/SmallVector.h"
23
- #include " llvm/Support/raw_ostream.h"
24
23
25
24
#include < optional>
26
- #include < system_error>
27
- #include < utility>
28
25
29
26
namespace nb = nanobind;
30
27
using namespace nb ::literals;
@@ -1523,7 +1520,7 @@ nb::object PyOperation::create(std::string_view name,
1523
1520
llvm::ArrayRef<MlirValue> operands,
1524
1521
std::optional<nb::dict> attributes,
1525
1522
std::optional<std::vector<PyBlock *>> successors,
1526
- int regions, DefaultingPyLocation location,
1523
+ int regions, PyLocation & location,
1527
1524
const nb::object &maybeIp, bool inferType) {
1528
1525
llvm::SmallVector<MlirType, 4 > mlirResults;
1529
1526
llvm::SmallVector<MlirBlock, 4 > mlirSuccessors;
@@ -1627,7 +1624,7 @@ nb::object PyOperation::create(std::string_view name,
1627
1624
if (!operation.ptr )
1628
1625
throw nb::value_error (" Operation creation failed" );
1629
1626
PyOperationRef created =
1630
- PyOperation::createDetached (location-> getContext (), operation);
1627
+ PyOperation::createDetached (location. getContext (), operation);
1631
1628
maybeInsertOperation (created, maybeIp);
1632
1629
1633
1630
return created.getObject ();
@@ -1937,9 +1934,9 @@ nb::object PyOpView::buildGeneric(
1937
1934
std::optional<nb::list> resultTypeList, nb::list operandList,
1938
1935
std::optional<nb::dict> attributes,
1939
1936
std::optional<std::vector<PyBlock *>> successors,
1940
- std::optional<int > regions, DefaultingPyLocation location,
1937
+ std::optional<int > regions, PyLocation & location,
1941
1938
const nb::object &maybeIp) {
1942
- PyMlirContextRef context = location-> getContext ();
1939
+ PyMlirContextRef context = location. getContext ();
1943
1940
1944
1941
// Class level operation construction metadata.
1945
1942
// Operand and result segment specs are either none, which does no
@@ -2789,6 +2786,90 @@ class PyOpAttributeMap {
2789
2786
PyOperationRef operation;
2790
2787
};
2791
2788
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
+
2792
2873
} // namespace
2793
2874
2794
2875
// ------------------------------------------------------------------------------
@@ -3052,10 +3133,10 @@ void mlir::python::populateIRCore(nb::module_ &m) {
3052
3133
.def (" __eq__" , [](PyLocation &self, nb::object other) { return false ; })
3053
3134
.def_prop_ro_static (
3054
3135
" current" ,
3055
- [](nb::object & /* class*/ ) {
3136
+ [](nb::object & /* class*/ ) -> std::optional<PyLocation *> {
3056
3137
auto *loc = PyThreadContextEntry::getDefaultLocation ();
3057
3138
if (!loc)
3058
- throw nb::value_error ( " No current Location " ) ;
3139
+ return std::nullopt ;
3059
3140
return loc;
3060
3141
},
3061
3142
" Gets the Location bound to the current thread or raises ValueError" )
@@ -3240,8 +3321,9 @@ void mlir::python::populateIRCore(nb::module_ &m) {
3240
3321
kModuleParseDocstring )
3241
3322
.def_static (
3242
3323
" 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 ());
3245
3327
return PyModule::forModule (module ).releaseObject ();
3246
3328
},
3247
3329
nb::arg (" loc" ).none () = nb::none (), " Creates an empty module" )
@@ -3462,8 +3544,8 @@ void mlir::python::populateIRCore(nb::module_ &m) {
3462
3544
std::optional<std::vector<PyValue *>> operands,
3463
3545
std::optional<nb::dict> attributes,
3464
3546
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) {
3467
3549
// Unpack/validate operands.
3468
3550
llvm::SmallVector<MlirValue, 4 > mlirOperands;
3469
3551
if (operands) {
@@ -3475,8 +3557,9 @@ void mlir::python::populateIRCore(nb::module_ &m) {
3475
3557
}
3476
3558
}
3477
3559
3560
+ PyLocation pyLoc = maybeGetTracebackLocation (location);
3478
3561
return PyOperation::create (name, results, mlirOperands, attributes,
3479
- successors, regions, location , maybeIp,
3562
+ successors, regions, pyLoc , maybeIp,
3480
3563
inferType);
3481
3564
},
3482
3565
nb::arg (" name" ), nb::arg (" results" ).none () = nb::none (),
@@ -3520,12 +3603,14 @@ void mlir::python::populateIRCore(nb::module_ &m) {
3520
3603
std::optional<nb::list> resultTypeList, nb::list operandList,
3521
3604
std::optional<nb::dict> attributes,
3522
3605
std::optional<std::vector<PyBlock *>> successors,
3523
- std::optional<int > regions, DefaultingPyLocation location,
3606
+ std::optional<int > regions,
3607
+ const std::optional<PyLocation> &location,
3524
3608
const nb::object &maybeIp) {
3609
+ PyLocation pyLoc = maybeGetTracebackLocation (location);
3525
3610
new (self) PyOpView (PyOpView::buildGeneric (
3526
3611
name, opRegionSpec, operandSegmentSpecObj,
3527
3612
resultSegmentSpecObj, resultTypeList, operandList,
3528
- attributes, successors, regions, location , maybeIp));
3613
+ attributes, successors, regions, pyLoc , maybeIp));
3529
3614
},
3530
3615
nb::arg (" name" ), nb::arg (" opRegionSpec" ),
3531
3616
nb::arg (" operandSegmentSpecObj" ).none () = nb::none (),
@@ -3559,17 +3644,18 @@ void mlir::python::populateIRCore(nb::module_ &m) {
3559
3644
[](nb::handle cls, std::optional<nb::list> resultTypeList,
3560
3645
nb::list operandList, std::optional<nb::dict> attributes,
3561
3646
std::optional<std::vector<PyBlock *>> successors,
3562
- std::optional<int > regions, DefaultingPyLocation location,
3647
+ std::optional<int > regions, std::optional<PyLocation> location,
3563
3648
const nb::object &maybeIp) {
3564
3649
std::string name = nb::cast<std::string>(cls.attr (" OPERATION_NAME" ));
3565
3650
std::tuple<int , bool > opRegionSpec =
3566
3651
nb::cast<std::tuple<int , bool >>(cls.attr (" _ODS_REGIONS" ));
3567
3652
nb::object operandSegmentSpec = cls.attr (" _ODS_OPERAND_SEGMENTS" );
3568
3653
nb::object resultSegmentSpec = cls.attr (" _ODS_RESULT_SEGMENTS" );
3654
+ PyLocation pyLoc = maybeGetTracebackLocation (location);
3569
3655
return PyOpView::buildGeneric (name, opRegionSpec, operandSegmentSpec,
3570
3656
resultSegmentSpec, resultTypeList,
3571
3657
operandList, attributes, successors,
3572
- regions, location , maybeIp);
3658
+ regions, pyLoc , maybeIp);
3573
3659
},
3574
3660
nb::arg (" cls" ), nb::arg (" results" ).none () = nb::none (),
3575
3661
nb::arg (" operands" ).none () = nb::none (),
0 commit comments