Tarantool development patches archive
 help / color / mirror / Atom feed
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__':

      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