From: Sergey Bronnikov via Tarantool-patches <tarantool-patches@dev.tarantool.org>
To: Mikhail Elhimov <m.elhimov@vk.team>,
Sergey Kaplun <skaplun@tarantool.org>,
Evgeniy Temirgaleev <e.temirgaleev@tarantool.org>
Cc: tarantool-patches@dev.tarantool.org
Subject: Re: [Tarantool-patches] [PATCH luajit v3] dbg: avoid hardcoded enums (get them from target)
Date: Thu, 8 Oct 2026 14:08:30 +0300 [thread overview]
Message-ID: <d11536fb-8bca-42e4-9e3b-84d007c766d4@tarantool.org> (raw)
In-Reply-To: <20261007221837.123140-1-m.elhimov@vk.team>
Hi, Mikhail,
thanks for the patch! LGTM
Sergey
On 10/8/26 01:18, Mikhail Elhimov wrote:
> Besides reducing lines of code this way the extension becomes compatible
> with various versions of LuaJIT because different versions might use
> different sets of enum members (a newer version might introduce
> additional BC/IR/etc.)
>
> Prior to this patch, the string was used as a debugger-agnostic way to
> specify type, but this way might not work in the case of an enum because
> debugging information might be optimized out if no variable of such an
> enum type is declared and its members are used only as predefined
> constants. The type of enum is needed to be able to get human-readable
> value (member name instead of number). The alternative way to get enum
> type is to get it from the value, i.e. somehow obtain the value that is
> of enum type and then use this type to convert the numbers to the member
> names.
>
> Creating the value of the given enum member is quite different in GDB
> and LLDB, so separate method `create_enum_value` was introduced in
> Debugger API. Also new method `cast_typeof` was introduced to perform
> value-based cast, i.e. it casts to the same type as a reference value.
>
> Also this patch fixes the mapping of FPMATHOP to a human-readable form,
> because prior to this patch, IRFPMS contained the incorrect entry
> 'exp2'. IRFPMS is a human-readable form of enum IRFPMathOp, but there is
> no 'exp2' enum member actually (see IRFPMDEF). It looks like it was
> added by mistake initially (correspoding tests to check mapping of
> FPMATHOP were added).
>
> Other adjustments:
> * [gdb] in 'eval' method dropped check of the value returned by
> gdb.parse_and_eval() as it would fail also for expression like '0',
> i.e. affects all the involved enums (looks like a kind of experimental
> code that was left by mistake)
> * [lldb] 'eval' adjusted to return object of the same type as 'cast'
> method (this improves consistency)
> * [gdb/lldb] renamed eval argument to reflect its meaning (it is
> an expression rather than a command)
>
> Resolves tarantool/tarantool#13094
> Resolves tarantool/tarantool#13159
> ---
> Changes in v3:
> * Adjusted the way number is casted to enum type.
> For unknown reason sometimes in LLDB `gdb.eval(str(i))` did evaluate
> any number to zero value, and looks like it depends on the internal
> state of LLDB because it might work correctly being executed later
> after some other command. The updated way looks more "stable", at
> least it works correctly in all tests.
>
> Changes in v2:
> * Switched to the `cast_typeof` approach
> * Added tests that checks FPMATHOP mapping as this patch fixes this problem as a side effect
> * Typos correction
>
> Branch: https://github.com/tarantool/luajit/tree/elhimov/gh-13094-dbg-use-enums-from-inferior
> Related issues:
> https://github.com/tarantool/tarantool/issues/13094
> https://github.com/tarantool/tarantool/issues/13159
>
> src/luajit_dbg.py | 632 ++++--------------
> .../debug-extension-tests.py | 58 +-
> 2 files changed, 205 insertions(+), 485 deletions(-)
>
> diff --git a/src/luajit_dbg.py b/src/luajit_dbg.py
> index 80057a4e..7c2d085f 100644
> --- a/src/luajit_dbg.py
> +++ b/src/luajit_dbg.py
> @@ -115,6 +115,13 @@ class Debugger(object):
> '''Cast the value to the required C type.'''
> pass
>
> + @abc.abstractmethod
> + def cast_typeof(self, ref_val, val):
> + '''Cast the value to the type of the given object
> + It is used mostly for casting when the string name of the type isn't
> + accessible (for example, anonymous enums).'''
> + pass
> +
> @abc.abstractmethod
> def sizeof(self, typestr):
> '''Return the size of the given type in bytes.'''
> @@ -146,8 +153,8 @@ class Debugger(object):
> pass
>
> @abc.abstractmethod
> - def eval(self, command):
> - '''Parse and evaluate the given debugger command.'''
> + def eval(self, expr):
> + '''Parse and evaluate the given debugger expression.'''
> pass
>
> @abc.abstractmethod
> @@ -187,6 +194,16 @@ class Debugger(object):
> '''Register the command with the corresponding name.'''
> pass
>
> + # Creating the value of the given enum member is quite
> + # different in GDB and LLDB, so this method is needed to hide
> + # the implementation details.
> + @abc.abstractmethod
> + def create_enum_value(self, enum_name, enum_member_name):
> + '''Return debugger-specific value object that represents
> + the given enum member.
> + '''
> + pass
> +
> @abc.abstractproperty
> def LJBase(self):
> '''Base command class.
> @@ -214,6 +231,9 @@ class _GDBDebugger(Debugger):
> def cast(self, typestr, val):
> return gdb.Value(val).cast(self._dbgtype(typestr))
>
> + def cast_typeof(self, ref_val, val):
> + return gdb.Value(val).cast(ref_val.type)
> +
> def sizeof(self, typestr):
> return self._dbgtype(typestr).sizeof
>
> @@ -268,14 +288,11 @@ class _GDBDebugger(Debugger):
> else:
> return None
>
> - def eval(self, command):
> - if not command:
> + def eval(self, expr):
> + if not expr:
> return None
>
> - ret = gdb.parse_and_eval(command)
> - if not ret:
> - raise gdb.GdbError('table argument empty')
> - return ret
> + return gdb.parse_and_eval(expr)
>
> def detect_arch(self):
> if hasattr(self, 'arch'):
> @@ -340,6 +357,11 @@ class _GDBDebugger(Debugger):
> def register_command(self, command, name):
> command(name)
>
> + def create_enum_value(self, enum_name, enum_member_name):
> + val = self.eval(enum_member_name)
> + assert val.type.code == gdb.TYPE_CODE_ENUM
> + return val
> +
> class LJBase(gdb and gdb.Command or object):
> def __init__(ljbase, name):
> # XXX Fragile: Though the command initialization looks
> @@ -378,6 +400,10 @@ class _LLDBDebugger(Debugger):
> lldb.eBasicTypeInt128
> ]
>
> + def _lldb_tp_isenum(self, tp):
> + return tp.GetCanonicalType().GetTypeClass() == \
> + lldb.eTypeClassEnumeration
> +
> def _lldb_value_from_raw(self, raw_value, size, tp):
> isfp = self._lldb_tp_isfp(tp)
> if isfp:
> @@ -491,8 +517,7 @@ class _LLDBDebugger(Debugger):
> # Instead of default GetSummary.
> if not lldbval.sbvalue.TypeIsPointerType():
> tp = lldbval.sbvalue.GetType()
> - is_float = self._lldb_tp_isfp(tp)
> - if is_float:
> + if self._lldb_tp_isfp(tp) or self._lldb_tp_isenum(tp):
> return lldbval.sbvalue.GetValue()
> else:
> return str(int(lldbval))
> @@ -568,11 +593,10 @@ class _LLDBDebugger(Debugger):
> self.dbgtype_cache[typestr] = dbgtype
> return dbgtype
>
> - def cast(self, typestr, val):
> + def _cast(self, tp, val):
> if isinstance(val, lldb.value):
> val = val.sbvalue
> elif type(val) is int:
> - tp = self._dbgtype(typestr)
> return self._lldb_value_from_raw(val, tp.GetByteSize(), tp)
> elif not isinstance(val, lldb.SBValue):
> raise Exception(
> @@ -582,7 +606,6 @@ class _LLDBDebugger(Debugger):
> # XXX: Simply SBValue.Cast() works incorrectly since it
> # may take the 8 bytes of memory instead of 4, before the
> # cast. Construct the value on the fly.
> - tp = self._dbgtype(typestr)
> if self._lldb_tp_isfp(tp):
> rawval = float(val.GetValue())
> elif self._lldb_tp_issigned(tp):
> @@ -591,6 +614,12 @@ class _LLDBDebugger(Debugger):
> rawval = val.GetValueAsUnsigned()
> return self._lldb_value_from_raw(rawval, val.GetByteSize(), tp)
>
> + def cast(self, typestr, val):
> + return self._cast(self._dbgtype(typestr), val)
> +
> + def cast_typeof(self, ref_val, val):
> + return self._cast(ref_val.sbvalue.type, val)
> +
> def sizeof(self, typestr):
> return self._dbgtype(typestr).GetByteSize()
>
> @@ -670,15 +699,14 @@ class _LLDBDebugger(Debugger):
> else:
> return None
>
> - def eval(self, command):
> - if not command:
> + def eval(self, expr):
> + if not expr:
> return None
>
> process = self.target.GetProcess()
> thread = process.GetSelectedThread()
> frame = thread.GetSelectedFrame()
> - ret = frame.EvaluateExpression(command)
> - return ret
> + return lldb.value(frame.EvaluateExpression(expr))
>
> def detect_arch(self):
> if hasattr(self, 'arch'):
> @@ -724,6 +752,47 @@ class _LLDBDebugger(Debugger):
> )
> )
>
> + def create_enum_value(self, enum_name, enum_member_name):
> + # In LLDB an enum member has to be specified in the form:
> + # <enum_name>::<enum_member_name>
> + val = self.eval(enum_name + "::" + enum_member_name)
> + # Be aware that SBValue.IsValid() is necessary, but not
> + # sufficient as it only indicates that the object does
> + # contain the relevant data (including potential error),
> + # so it is necessary to check additionally that no error
> + # has occurred while evaluating the expression.
> + if val.sbvalue.IsValid() and val.sbvalue.error.Success():
> + assert self._lldb_tp_isenum(val.sbvalue.GetType())
> + return val
> +
> + # LLDB uses the enum name in the expression above but
> + # debugging information about the enum name might be
> + # optimized out if no variable of the given enum type is
> + # declared and its members are only used as the predefined
> + # constants (like in case of the enum IRFieldID).
> +
> + # In this case the above method doesn't work so trying to
> + # discover the enum type by the given enum member.
> +
> + def find_enum_type_member(enum_type, enum_member_name):
> + # SBTypeEnumMemberList supports members iteration and
> + # [] access (both by index and by member name) only
> + # starting from lldb-12 so this implementation is used
> + # to handle earlier versions.
> + members = enum_type.GetEnumMembers()
> + for i in range(members.GetSize()):
> + item = members.GetTypeEnumMemberAtIndex(i)
> + if item.name == enum_member_name:
> + return item
> + return None
> +
> + for m in self.target.modules:
> + for et in m.GetTypes(lldb.eTypeClassEnumeration):
> + et_member = find_enum_type_member(et, enum_member_name)
> + if et_member is not None:
> + return self._cast(et, et_member.unsigned)
> + return None
> +
> class LJBase(object):
> # Ignore given parameters by LLDB.
> def __init__(ljbase, debugger, unused):
> @@ -800,6 +869,45 @@ def strx64(val):
> return re.sub('L?$', '', hex(int(tou64(val))))
>
>
> +class EnumBasedList(object):
> + def __init__(self, enum_name, max_enum_member, map_func=None,
> + *map_func_extra_args):
> + self.__enum_name = enum_name
> + self.__max_enum_member = max_enum_member
> + self.__map_func = map_func
> + self.__map_func_extra_args = map_func_extra_args
> + # Lazy initialization (see __get_items) as the required
> + # information might be unavailable at this moment.
> + self.__items = None
> +
> + def __iter__(self):
> + return iter(self.__get_items())
> +
> + def __getitem__(self, key):
> + return self.__get_items()[key]
> +
> + def __len__(self):
> + return len(self.__get_items())
> +
> + def __get_items(self):
> + if self.__items is None:
> + max_enum_value = dbg.create_enum_value(
> + self.__enum_name, self.__max_enum_member
> + )
> + items = []
> + for i in range(max_enum_value):
> + item = str(dbg.cast_typeof(max_enum_value, i))
> + if self.__map_func:
> + item = self.__map_func(item, *self.__map_func_extra_args)
> + items.append(item)
> + self.__items = items
> + return self.__items
> +
> +
> +def cut_prefix(s, prefix):
> + return s[len(prefix):] if s.startswith(prefix) else s
> +
> +
> # Types and TValues.
>
>
> @@ -877,10 +985,7 @@ def bc_d(ins):
> return int(ins) >> 16
>
>
> -BCMODE = [
> - 'none', 'dst', 'base', 'var', 'rbase', 'uv',
> - 'lit', 'lits', 'pri', 'num', 'str', 'tab', 'func', 'jump', 'cdata',
> -]
> +BCMODE = EnumBasedList('BCMode', 'BCM_max', cut_prefix, 'BCM')
>
>
> lj_bc_mode_ = None
> @@ -906,136 +1011,7 @@ def bcmode_cd(op):
> return int((lj_bc_mode()[op] >> 7) & 15)
>
>
> -# Unfortunately, there is no place in the VM except the generated
> -# Lua table, where the bytecode names are stored. So duplicate
> -# them here.
> -BYTECODES = [
> - # Comparison ops. ORDER OPR.
> - 'ISLT',
> - 'ISGE',
> - 'ISLE',
> - 'ISGT',
> -
> - 'ISEQV',
> - 'ISNEV',
> - 'ISEQS',
> - 'ISNES',
> - 'ISEQN',
> - 'ISNEN',
> - 'ISEQP',
> - 'ISNEP',
> -
> - # Unary test and copy ops.
> - 'ISTC',
> - 'ISFC',
> - 'IST',
> - 'ISF',
> - 'ISTYPE',
> - 'ISNUM',
> - 'MOV',
> - 'NOT',
> - 'UNM',
> - 'LEN',
> - 'ADDVN',
> - 'SUBVN',
> - 'MULVN',
> - 'DIVVN',
> - 'MODVN',
> -
> - # Binary ops. ORDER OPR.
> - 'ADDNV',
> - 'SUBNV',
> - 'MULNV',
> - 'DIVNV',
> - 'MODNV',
> -
> - 'ADDVV',
> - 'SUBVV',
> - 'MULVV',
> - 'DIVVV',
> - 'MODVV',
> -
> - 'POW',
> - 'CAT',
> -
> - # Constant ops.
> - 'KSTR',
> - 'KCDATA',
> - 'KSHORT',
> - 'KNUM',
> - 'KPRI',
> - 'KNIL',
> -
> - # Upvalue and function ops.
> - 'UGET',
> - 'USETV',
> - 'USETS',
> - 'USETN',
> - 'USETP',
> - 'UCLO',
> - 'FNEW',
> -
> - # Table ops.
> - 'TNEW',
> - 'TDUP',
> - 'GGET',
> - 'GSET',
> - 'TGETV',
> - 'TGETS',
> - 'TGETB',
> - 'TGETR',
> - 'TSETV',
> - 'TSETS',
> - 'TSETB',
> - 'TSETM',
> - 'TSETR',
> -
> - # Calls and vararg handling. T = tail call.
> - 'CALLM',
> - 'CALL',
> - 'CALLMT',
> - 'CALLT',
> - 'ITERC',
> - 'ITERN',
> - 'VARG',
> - 'ISNEXT',
> -
> - # Returns.
> - 'RETM',
> - 'RET',
> - 'RET0',
> - 'RET1',
> -
> - # Loops and branches. I/J = interp/JIT.
> - # I/C/L = init/call/loop.
> - 'FORI',
> - 'JFORI',
> -
> - 'FORL',
> - 'IFORL',
> - 'JFORL',
> -
> - 'ITERL',
> - 'IITERL',
> - 'JITERL',
> -
> - 'LOOP',
> - 'ILOOP',
> - 'JLOOP',
> -
> - 'JMP',
> -
> - # Function headers. I/J = interp/JIT.
> - # F/V/C = fixarg/vararg/C func.
> - 'FUNCF',
> - 'IFUNCF',
> - 'JFUNCF',
> - 'FUNCV',
> - 'IFUNCV',
> - 'JFUNCV',
> - 'FUNCC',
> - 'FUNCCW',
> -]
> +BYTECODES = EnumBasedList('BCOp', 'BC__MAX', cut_prefix, 'BC_')
>
>
> def proto_bc(proto):
> @@ -1190,42 +1166,16 @@ def J(g):
>
>
> # Matched `MMDEF(_)`.
> -MM_NAMES = [
> - 'index',
> - 'newindex',
> - 'gc',
> - 'mode',
> - 'eq',
> - 'len',
> - 'lt',
> - 'le',
> - 'concat',
> - 'call',
> - 'add',
> - 'sub',
> - 'mul',
> - 'div',
> - 'mod',
> - 'pow',
> - 'unm',
> - 'metatable',
> - 'tostring',
> - # TODO: depends on LJ_HASFFI, see `MMDEF_FFI(_)`.
> - 'new',
> - # TODO: depends on LJ_52 || LJ_HASFFI, see `MMDEF_PAIRS(_)`.
> - 'pairs',
> - 'ipairs',
> -]
> -
> -
> -GCROOT_MMNAME = 0
> -GCROOT_BASEMT = GCROOT_MMNAME + len(MM_NAMES)
> -GCROOT_IO_INPUT = GCROOT_BASEMT + i2notu32(LJ_T['NUMX']) + 1
> -GCROOT_IO_OUTPUT = GCROOT_IO_INPUT + 1
> +MM_NAMES = EnumBasedList('MMS', 'MM__MAX', cut_prefix, 'MM_')
>
>
> # Get the name of the index in the predefined arrays.
> def idx_name(field_name):
> + GCROOT_MMNAME = 0
> + GCROOT_BASEMT = GCROOT_MMNAME + len(MM_NAMES)
> + GCROOT_IO_INPUT = GCROOT_BASEMT + i2notu32(LJ_T['NUMX']) + 1
> + GCROOT_IO_OUTPUT = GCROOT_IO_INPUT + 1
> +
> # Don't use **{ to be compatible with Python 2.
> gcroot = {}
> gcroot.update({
> @@ -1477,140 +1427,7 @@ def cdataptr(cd):
> # JIT engine.
>
>
> -IRS = [
> - # Guarded assertions.
> - 'LT',
> - 'GE',
> - 'LE',
> - 'GT',
> -
> - 'ULT',
> - 'UGE',
> - 'ULE',
> - 'UGT',
> -
> - 'EQ',
> - 'NE',
> -
> - 'ABC',
> - 'RETF',
> -
> - # Miscellaneous ops.
> - 'NOP',
> - 'BASE',
> - 'PVAL',
> - 'GCSTEP',
> - 'HIOP',
> - 'LOOP',
> - 'USE',
> - 'PHI',
> - 'RENAME',
> - 'PROF',
> -
> - # Constants.
> - 'KPRI',
> - 'KINT',
> - 'KGC',
> - 'KPTR',
> - 'KKPTR',
> - 'KNULL',
> - 'KNUM',
> - 'KINT64',
> - 'KSLOT',
> -
> - # Bit ops.
> - 'BNOT',
> - 'BSWAP',
> - 'BAND',
> - 'BOR',
> - 'BXOR',
> - 'BSHL',
> - 'BSHR',
> - 'BSAR',
> - 'BROL',
> - 'BROR',
> -
> - # Arithmetic ops. ORDER ARITH
> - 'ADD',
> - 'SUB',
> - 'MUL',
> - 'DIV',
> - 'MOD',
> - 'POW',
> - 'NEG',
> -
> - 'ABS',
> - 'LDEXP',
> - 'MIN',
> - 'MAX',
> - 'FPMATH',
> -
> - # Overflow-checking arithmetic ops.
> - 'ADDOV',
> - 'SUBOV',
> - 'MULOV',
> -
> - # Memory ops. A = array, H = hash, U = upvalue, F = field,
> - # S = stack.
> -
> - # Memory references.
> - 'AREF',
> - 'HREFK',
> - 'HREF',
> - 'NEWREF',
> - 'UREFO',
> - 'UREFC',
> - 'FREF',
> - 'STRREF',
> - 'LREF',
> -
> - # Loads and Stores. These must be in the same order.
> - 'ALOAD',
> - 'HLOAD',
> - 'ULOAD',
> - 'FLOAD',
> - 'XLOAD',
> - 'SLOAD',
> - 'VLOAD',
> -
> - 'ASTORE',
> - 'HSTORE',
> - 'USTORE',
> - 'FSTORE',
> - 'XSTORE',
> -
> - # Allocations.
> - 'SNEW',
> - 'XSNEW',
> - 'TNEW',
> - 'TDUP',
> - 'CNEW',
> - 'CNEWI',
> -
> - # Buffer operations.
> - 'BUFHDR',
> - 'BUFPUT',
> - 'BUFSTR',
> -
> - # Barriers.
> - 'TBAR',
> - 'OBAR',
> - 'XBAR',
> -
> - # Type conversions.
> - 'CONV',
> - 'TOBIT',
> - 'TOSTR',
> - 'STRTO',
> -
> - # Calls.
> - 'CALLN',
> - 'CALLA',
> - 'CALLL',
> - 'CALLS',
> - 'CALLXS',
> - 'CARG',
> -]
> +IRS = EnumBasedList('IROp', 'IR__MAX', cut_prefix, 'IR_')
>
>
> # Mode bits: Commutative, {Normal/Ref, Alloc, Load, Store},
> @@ -1662,71 +1479,21 @@ def ir_mode(op):
> return mode
>
>
> -IRTYPES = [
> - 'nil',
> - 'fal',
> - 'tru',
> - 'lud',
> - 'str',
> - 'p32',
> - 'thr',
> - 'pro',
> - 'fun',
> - 'p64',
> - 'cdt',
> - 'tab',
> - 'udt',
> - 'flt',
> - 'num',
> - 'i8 ',
> - 'u8 ',
> - 'i16',
> - 'u16',
> - 'int',
> - 'u32',
> - 'i64',
> - 'u64',
> - 'sfp',
> -]
> +IRTYPES = EnumBasedList('IRType', 'IRT__MAX', lambda x: {
> + 'IRT_CDATA': 'cdt',
> + 'IRT_FLOAT': 'flt',
> + 'IRT_LIGHTUD': 'lud',
> + 'IRT_SOFTFP': 'sfp',
> + 'IRT_UDATA': 'udt',
> + }.get(x, cut_prefix(x, 'IRT_')[:3].ljust(3).lower()))
>
>
> -IRT_NUM = 14
> -assert IRTYPES[IRT_NUM] == 'num', 'incorrect IRT_NUM definition'
> -
> -
> -IRFIELDS = [
> - 'str.len',
> - 'func.env',
> - 'func.pc',
> - 'func.ffid',
> - 'thread.env',
> - 'tab.meta',
> - 'tab.array',
> - 'tab.node',
> - 'tab.asize',
> - 'tab.hmask',
> - 'tab.nomm',
> - 'udata.meta',
> - 'udata.udtype',
> - 'udata.file',
> - 'cdata.ctypeid',
> - 'cdata.ptr',
> - 'cdata.int',
> - 'cdata.int64',
> - 'cdata.int64_4',
> -]
> +IRFIELDS = EnumBasedList('IRFieldID', 'IRFL__MAX', lambda x:
> + cut_prefix(x, 'IRFL_').lower().replace('_', '.', 1))
>
>
> -IRFPMS = [
> - 'floor',
> - 'ceil',
> - 'trunc',
> - 'sqrt',
> - 'exp2',
> - 'log',
> - 'log2',
> - 'other'
> -]
> +IRFPMS = EnumBasedList('IRFPMathOp', 'IRFPM__MAX',
> + lambda x: cut_prefix(x, 'IRFPM_').lower())
>
>
> # Don't use *[ to be compatible with Python 2.
> @@ -1755,112 +1522,7 @@ REGISTERS = {
> }
>
>
> -IR_CALLS = [
> - 'lj_str_cmp',
> - 'lj_str_find',
> - 'lj_str_new',
> - 'lj_strscan_num',
> - 'lj_strfmt_int',
> - 'lj_strfmt_num',
> - 'lj_strfmt_char',
> - 'lj_strfmt_putint',
> - 'lj_strfmt_putnum',
> - 'lj_strfmt_putquoted',
> - 'lj_strfmt_putfxint',
> - 'lj_strfmt_putfnum_int',
> - 'lj_strfmt_putfnum_uint',
> - 'lj_strfmt_putfnum',
> - 'lj_strfmt_putfstr',
> - 'lj_strfmt_putfchar',
> - 'lj_buf_putmem',
> - 'lj_buf_putstr',
> - 'lj_buf_putchar',
> - 'lj_buf_putstr_reverse',
> - 'lj_buf_putstr_lower',
> - 'lj_buf_putstr_upper',
> - 'lj_buf_putstr_rep',
> - 'lj_buf_puttab',
> - 'lj_buf_tostr',
> - 'lj_tab_new_ah',
> - 'lj_tab_new1',
> - 'lj_tab_dup',
> - 'lj_tab_clear',
> - 'lj_tab_newkey',
> - 'lj_tab_len',
> - 'lj_gc_step_jit',
> - 'lj_gc_barrieruv',
> - 'lj_mem_newgco',
> - 'lj_math_random_step',
> - 'lj_vm_modi',
> - 'log10',
> - 'exp',
> - 'sin',
> - 'cos',
> - 'tan',
> - 'asin',
> - 'acos',
> - 'atan',
> - 'sinh',
> - 'cosh',
> - 'tanh',
> - 'fputc',
> - 'fwrite',
> - 'fflush',
> - 'lj_vm_floor',
> - 'lj_vm_ceil',
> - 'lj_vm_trunc',
> - 'sqrt',
> - 'log',
> - 'lj_vm_log2',
> - 'pow',
> - 'atan2',
> - 'ldexp',
> - 'lj_vm_tobit',
> - 'softfp_add',
> - 'softfp_sub',
> - 'softfp_mul',
> - 'softfp_div',
> - 'softfp_cmp',
> - 'softfp_i2d',
> - 'softfp_d2i',
> - 'lj_vm_sfmin',
> - 'lj_vm_sfmax',
> - 'lj_vm_tointg',
> - 'softfp_ui2d',
> - 'softfp_f2d',
> - 'softfp_d2ui',
> - 'softfp_d2f',
> - 'softfp_i2f',
> - 'softfp_ui2f',
> - 'softfp_f2i',
> - 'softfp_f2ui',
> - 'fp64_l2d',
> - 'fp64_ul2d',
> - 'fp64_l2f',
> - 'fp64_ul2f',
> - 'fp64_d2l',
> - 'fp64_d2ul',
> - 'fp64_f2l',
> - 'fp64_f2ul',
> - 'lj_carith_divi64',
> - 'lj_carith_divu64',
> - 'lj_carith_modi64',
> - 'lj_carith_modu64',
> - 'lj_carith_powi64',
> - 'lj_carith_powu64',
> - 'lj_cdata_newv',
> - 'lj_cdata_setfin',
> - 'strlen',
> - 'memcpy',
> - 'memset',
> - 'lj_vm_errno',
> - 'lj_carith_mul64',
> - 'lj_carith_shl64',
> - 'lj_carith_shr64',
> - 'lj_carith_sar64',
> - 'lj_carith_rol64',
> - 'lj_carith_ror64',
> -]
> +IR_CALLS = EnumBasedList('IRCallID', 'IRCALL__MAX', cut_prefix, 'IRCALL_')
>
>
> def regname(reg_number):
> @@ -1996,6 +1658,8 @@ def irt_isguard(t):
>
>
> def irt_toitype(irt):
> + IRT_NUM = 14
> + assert IRTYPES[IRT_NUM] == 'num', 'incorrect IRT_NUM definition'
> t = irt_type(irt)
> if LJ_DUALNUM and t > IRT_NUM:
> return LJ_T['NUMX']
> diff --git a/test/tarantool-debugger-tests/debug-extension-tests.py b/test/tarantool-debugger-tests/debug-extension-tests.py
> index 895171a4..5e369f00 100644
> --- a/test/tarantool-debugger-tests/debug-extension-tests.py
> +++ b/test/tarantool-debugger-tests/debug-extension-tests.py
> @@ -830,6 +830,52 @@ class TestLJIRCallXSCType(TestCaseBase):
> )
>
>
> +# Base class to check FPMATHOP mapping in FPMATH IR.
> +class TestLJIRFPMathOpBase(TestCaseBase):
> + location = 'lj_cf_print'
> + extension_cmds = 'lj-trace &((GG_State *)L)->J->cur'
> +
> + @classmethod
> + def setUpClass(cls):
> + cls.lua_script = (
> + 'jit.opt.start("hotloop=1")\n'
> + 'local function trace(a)\n'
> + ' local x = {}\n'
> + ' return x\n'
> + 'end\n'
> + 'trace(1.1)\n'
> + 'trace(1.1)\n'
> + 'print()\n'
> + ).format(cls.lua_expr)
> + cls.pattern = r'num FPMATH .* ref: ' + RX_IRN + r' lit: ' + cls.op
> + super(TestLJIRFPMathOpBase, cls).setUpClass()
> +
> +
> +class TestLJIRFPMathFloor(TestLJIRFPMathOpBase):
> + lua_expr = 'math.floor(a)'
> + op = 'floor'
> +
> +
> +class TestLJIRFPMathCeil(TestLJIRFPMathOpBase):
> + lua_expr = 'math.ceil(a)'
> + op = 'ceil'
> +
> +
> +class TestLJIRFPMathSqrt(TestLJIRFPMathOpBase):
> + lua_expr = 'math.sqrt(a)'
> + op = 'sqrt'
> +
> +
> +class TestLJIRFPMathLog(TestLJIRFPMathOpBase):
> + lua_expr = 'math.log(a)'
> + op = 'log'
> +
> +
> +class TestLJIRFPMathLog2(TestLJIRFPMathOpBase):
> + lua_expr = 'math.log(a, 3)'
> + op = 'log2'
> +
> +
> class TestLJJSlotsBase(TestCaseBase):
> location = 'trace_stop'
> extension_cmds = (
> @@ -1051,7 +1097,17 @@ class TestLJCTypeBase(TestCaseBase):
> pattern = r'\[\d+\] <int>'
>
>
> -for test_cls in TestCaseBase.__subclasses__():
> +def get_leaf_subclasses(cls):
> + subclasses = cls.__subclasses__()
> + if not subclasses:
> + yield cls
> + else:
> + for sub in subclasses:
> + for leaf in get_leaf_subclasses(sub):
> + yield leaf
> +
> +
> +for test_cls in get_leaf_subclasses(TestCaseBase):
> test_cls.test = lambda self: self.check()
>
> if __name__ == '__main__':
prev parent reply other threads:[~2026-10-08 11:08 UTC|newest]
Thread overview: 10+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-09-09 8:49 [Tarantool-patches] [PATCH luajit] " Mikhail Elhimov via Tarantool-patches
2026-09-17 9:52 ` Sergey Bronnikov via Tarantool-patches
2026-09-17 22:42 ` Mikhail Elhimov via Tarantool-patches
2026-09-17 22:42 ` Mikhail Elhimov via Tarantool-patches
2026-09-21 18:47 ` Sergey Kaplun via Tarantool-patches
2026-09-23 22:09 ` Mikhail Elhimov via Tarantool-patches
2026-09-23 22:27 ` [Tarantool-patches] [PATCH luajit v2] " Mikhail Elhimov via Tarantool-patches
2026-09-24 17:33 ` [Tarantool-patches] [PATCH luajit v3] dbg: display fast function name along with ffid Mikhail Elhimov via Tarantool-patches
2026-10-07 22:18 ` [Tarantool-patches] [PATCH luajit v3] dbg: avoid hardcoded enums (get them from target) Mikhail Elhimov via Tarantool-patches
2026-10-08 11:08 ` Sergey Bronnikov via Tarantool-patches [this message]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=d11536fb-8bca-42e4-9e3b-84d007c766d4@tarantool.org \
--to=tarantool-patches@dev.tarantool.org \
--cc=e.temirgaleev@tarantool.org \
--cc=m.elhimov@vk.team \
--cc=sergeyb@tarantool.org \
--cc=skaplun@tarantool.org \
--subject='Re: [Tarantool-patches] [PATCH luajit v3] dbg: avoid hardcoded enums (get them from target)' \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox