#pragma once #include #include #include #include namespace torch::unwind { struct UnwindState { int64_t rip, rbp, rsp; }; struct Unwinder { Unwinder(Action rsp, Action rip, Action rbp) : kind_(rip.kind == A_UNDEFINED ? END : STANDARD), reg_(rsp.reg), off_(rsp.data), rip_off_(rip.data), rbp_off_( rbp.kind == A_UNDEFINED ? std::numeric_limits::max() : rbp.data), deref_(rsp.kind == A_REG_PLUS_DATA_DEREF) { check(rsp.reg == D_RSP || rsp.reg == D_RBP); check(rip.kind == A_UNDEFINED || rip.kind == A_LOAD_CFA_OFFSET); if (rsp.kind == A_REG_PLUS_DATA) { check(rbp.kind == A_LOAD_CFA_OFFSET || rbp.kind == A_UNDEFINED); } else if (rsp.kind == A_REG_PLUS_DATA_DEREF) { if (rbp.kind == A_REG_PLUS_DATA_DEREF) { check(rbp.reg == rsp.reg); rbp_off_ -= rsp.data; } else { check(rbp.kind == A_UNDEFINED); } } else { check(false); } } void check(bool cond) { if (!cond) { throw UnwindError("Unwinding actions do not follow supported patterns"); } } bool terminator() const { return kind_ != STANDARD; } bool isUnknown() const { return kind_ == UNKNOWN; } // unwinder representing some pattern unsupported in // current implementation static Unwinder unknown() { return Unwinder(); } UnwindState run(const UnwindState& cur) const { UnwindState r = cur; r.rsp = (reg_ == D_RSP ? cur.rsp : cur.rbp) + off_; r.rbp = rbp_off_ == std::numeric_limits::max() ? cur.rbp // NOLINTNEXTLINE(performance-no-int-to-ptr) : *(int64_t*)(r.rsp + rbp_off_); if (deref_) { // NOLINTNEXTLINE(performance-no-int-to-ptr) r.rsp = *(int64_t*)r.rsp; } // NOLINTNEXTLINE(performance-no-int-to-ptr) r.rip = *(int64_t*)(r.rsp + rip_off_); return r; } private: Unwinder() : kind_(UNKNOWN), reg_(0), off_(0), rip_off_(0), rbp_off_(0) {} enum Kind { STANDARD, END, UNKNOWN } kind_; uint32_t reg_; int64_t off_; int64_t rip_off_; int64_t rbp_off_; bool deref_{false}; }; } // namespace torch::unwind