<HTML><BODY><div class="cl-1vhdty4nd7"><div>Hi, Sergey!</div><div> </div><div>Thanks for the patch. Please, see my comments.<br> </div><div data-signature-widget="container"><div data-signature-widget="content"><div>--<br>Best regards,</div><div>Evgeniy Temirgaleev</div></div></div><br><div class="mail-quote-collapse"><blockquote style="border-left:1px solid #0857A6;margin:10px;padding:0 0 0 10px"><span>От кого: Sergey Kaplun <<a href="mailto:skaplun@tarantool.org">skaplun@tarantool.org</a>><br>Кому: Sergey Bronnikov <<a href="mailto:sergeyb@tarantool.org">sergeyb@tarantool.org</a>>, Evgeniy Temirgaleev <<a href="mailto:e.temirgaleev@tarantool.org">e.temirgaleev@tarantool.org</a>><br>Копия: tarantool-patches@dev.tarantool.org, Sergey Kaplun <<a href="mailto:skaplun@tarantool.org">skaplun@tarantool.org</a>><br>Дата: Четверг, 25 июня 2026, 23:29 +03:00</span><br> <div><div id=""><div class="cl-lb60oino82"><div class="js-helper_mr_css_attr js-readmsg-msg_mr_css_attr"><div id="style_17824193842013071217_mr_css_attr"><div id="style_17824193842013071217_BODY_mr_css_attr">This patch adds dumpers for a single IR instruction (`lj-ir`), as well<br>as for all bytecodes inside one trace (`lj-trace`). Its dump is quite<br>similar to the -jdump flag but also reports types of register operands<br>(`ref`, `lit`, `cst`) and operation mode (`N`, `A`, `W`, etc.).<br>The `lj-trace` command accepts optional /rs flags to dump registers<br>associated with IR and snapshots for the trace correspondingly.<br>The `lj-ir` command can be used for dumping IR constants as well.<br>The `lj-jslots` command dumps the content of `J->slot`. It is useful to<br>simplify debugging of `rec_check_slots()` assertion failures.<br><br>For LLDB value, the `__getitem__` metamethod now accepts bool keys.<br>Also, `__index__` is set to allow lldb.value to be used as an index<br>without explicit conversion to int. Old GDB versions (below 7.12) are<br>not supported because of the gdb.Value lacks the `__index__` metamethod<br>and can't be monkey-patched. The support for these versions may be added<br>by demand.<br><br>Part of tarantool/tarantool#4808<br>---<br>src/luajit_dbg.py | 1216 ++++++++++++++++-<br>.../debug-extension-tests.py | 365 +++++<br>2 files changed, 1570 insertions(+), 11 deletions(-)<br><br>diff --git a/src/luajit_dbg.py b/src/luajit_dbg.py<br>index 2edb199a..fd6ca8a5 100644<br>--- a/src/luajit_dbg.py<br>+++ b/src/luajit_dbg.py<br>@@ -58,6 +58,26 @@ class Debugger(object):<br>self.LLDB = True<br>return super(Debugger, self).__new__(_LLDBDebugger)<br><br>+ def parse_flags(self, raw_flags, permitted_flags):<br>+ flags = {}<br>+ for flag in raw_flags:<br>+ if flag not in permitted_flags:<br>+ raise self.error('Unrecongnized option: "{}"'.format(flag))<br>+ flags[flag] = True<br>+ return flags<br>+<br>+ def extract_flags(self, arg, permitted_flags):<br>+ if not arg:<br>+ return None, None<br>+ flags = {}<br>+ if arg.startswith('/'):<br>+ match = re.match(r'/(\S*)\s+(.*)$', arg)<br>+ if not match:<br>+ return arg, flags<br>+ raw_flags, arg = match.group(1, 2)<br>+ flags = self.parse_flags(raw_flags, permitted_flags)<br>+ return arg, flags<br>+<br>def configure(self):<br>global PADDING, LJ_TISNUM<br>if not self.check_libluajit():<br>@@ -70,6 +90,17 @@ class Debugger(object):<br>self.write('luajit_dbg.py failed to load: '<br>'no debugging symbols found for libluajit\n')<br>return False<br>+<br>+ # Setup arch.<br>+ try:<br>+ self.arch = str(self.eval('LJ_ARCH_NAME')).split('"')[1]<br>+ except Exception:<br>+ try:<br>+ self.arch = self.detect_arch()<br>+ except Exception:<br>+ # Setup on demand if necessary.<br>+ pass<br>+<br>return True<br><br>def initialize_extension(self, commands):<br>@@ -99,21 +130,42 @@ class Debugger(object):<br>'''Return the content of the string by the given pointer.'''<br>pass<br><br>+ @abc.abstractmethod<br>+ def address(self, obj):<br>+ '''Return the address in memory of the given object.'''<br>+ pass<br>+<br>@abc.abstractmethod<br>def lookup_global(self, symbol):<br>'''Look up the global C symbol by the given name.'''<br>pass<br><br>+ @abc.abstractmethod<br>+ def member_by_offset(self, typename, offset, prev_name=None):<br>+ '''Look up the global C symbol by the given name.'''<br>+ pass<br>+<br>@abc.abstractmethod<br>def eval(self, command):<br>'''Parse and evaluate the given debugger command.'''<br>pass<br><br>+ @abc.abstractmethod<br>+ def detect_arch(self):<br>+ '''Detect the CPU architecture and canonicalize it to the LuaJIT<br>+ notation.'''<br>+ pass<br>+<br>@abc.abstractmethod<br>def write(self, msg):<br>'''Print the message.'''<br>pass<br><br>+ @abc.abstractmethod<br>+ def error(self, msg):<br>+ '''Create the error object with message.'''<br>+ pass<br>+<br>@abc.abstractmethod<br>def check_libluajit(self):<br>'''Check that libluajit is loaded.<br>@@ -172,10 +224,50 @@ class _GDBDebugger(Debugger):<br># A string is printed with a pointer to it. Just strip it.<br>return re.sub(r'^0x[a-f0-9]+\s+(?=")', '', str(strptr))<br><br>+ def address(self, obj):<br>+ return obj.address<br>+<br>def lookup_global(self, symbol):<br>variable, _ = gdb.lookup_symbol(symbol)<br>return variable.value() if variable else None<br><br>+ def member_by_offset(self, tp, offset, prev_name=None):<br>+ if isinstance(tp, str):<br>+ tp = self._dbgtype(tp)<br>+ assert offset < tp.sizeof, 'offset is bigger than object size'<br>+ if tp.code == gdb.TYPE_CODE_TYPEDEF:<br>+ tp = tp.strip_typedefs()<br>+ if tp.code == gdb.TYPE_CODE_STRUCT:<br>+ fields = tp.fields()<br>+ for n_field in range(len(fields)):<br>+ islast = n_field == (len(fields) - 1)<br>+ field = fields[n_field]<br>+ start_field = field.bitpos / 8<br>+ end_field = fields[n_field + 1].bitpos / 8 if not islast \<br>+ else tp.sizeof<br>+ if start_field <= offset and offset < end_field:<br>+ next_name = self.member_by_offset(<br>+ field.type,<br>+ offset - start_field,<br>+ prev_name=field.name<br>+ )<br>+ return '.{field}{suffix}'.format(<br>+ field=field.name,<br>+ suffix=next_name if next_name else ''<br>+ )<br>+ elif tp.code == gdb.TYPE_CODE_ARRAY:<br>+ # Get array field type.<br>+ target = tp.target()<br>+ tsize = target.sizeof<br>+ idx = int(offset // tsize)<br>+ next_name = self.member_by_offset(target, offset - idx * tsize)<br>+ idxname = idx_name(prev_name)<br>+ if idxname and idx in idxname:<br>+ idx = idxname[idx]<br>+ return '[{}]{}'.format(idx, next_name if next_name else '')<br>+ else:<br>+ return None<br>+<br>def eval(self, command):<br>if not command:<br>return None<br>@@ -185,9 +277,23 @@ class _GDBDebugger(Debugger):<br>raise gdb.GdbError('table argument empty')<br>return ret<br><br>+ def detect_arch(self):<br>+ if hasattr(self, 'arch'):<br>+ return self.arch<br>+ target = str(gdb.execute('info target', False, True))<br>+ if re.match('.*x86-64.*', target, flags=re.DOTALL):<br>+ return 'x64'<br>+ elif re.match('.*aarch64.*', target, flags=re.DOTALL):<br>+ return 'arm64'<br>+ else:<br>+ return ''<br>+<br>def write(self, msg):<br>gdb.write(msg)<br><br>+ def error(self, errmsg):<br>+ return gdb.GdbError(errmsg)<br>+<br>def check_libluajit(self):<br># XXX Fragile: Though connecting the callback looks bad,<br># it respects both Python 2 and Python 3 (see #4828).<br>@@ -322,8 +428,26 @@ class _LLDBDebugger(Debugger):<br>def lldb__getitem__(lldbval, key):<br>if type(key) is lldb.value:<br>key = int(key)<br>+ if type(key) is bool:<br>+ key = int(key)<br>if type(key) is int:<br># Allow array access.<br>+ ltp = lldbval.sbvalue.GetType()<br>+ # XXX: LLDB in versions 17 - 19 can't use an array<br>+ # object as the initializer for `lldb.value` since<br>+ # `GetValue()` for it returns `None` leading to<br>+ # the invalid result. See<br>+ # <a href="https://github.com/llvm/llvm-project/pull/90144">https://github.com/llvm/llvm-project/pull/90144</a>.<br>+ if (self.version < 17 or self.version > 19) or \<br>+ ltp.GetTypeClass() != lldb.eTypeClassArray:<br>+ pass<br>+ else:<br>+ ptr_tp = ltp.GetArrayElementType().GetPointerType()<br>+ lldbval = self._lldb_value_from_raw(<br>+ lldbval.sbvalue.GetLoadAddress(),<br>+ ptr_tp.GetByteSize(),<br>+ ptr_tp<br>+ )<br>if key >= 0 and not lldbval.sbvalue.TypeIsPointerType():<br>return lldb.value(<br>lldbval.sbvalue.GetValueForExpressionPath('[%i]' % key)<br>@@ -349,6 +473,9 @@ class _LLDBDebugger(Debugger):<br>def lldb__gt__(lldbval, other):<br>return int(lldbval) > int(other)<br><br>+ def lldb__index__(lldbval):<br>+ return int(lldbval)<br>+<br>def lldb__le__(lldbval, other):<br>return int(lldbval) <= int(other)<br><br>@@ -406,6 +533,7 @@ class _LLDBDebugger(Debugger):<br>lldb.value.__ge__ = lldb__ge__<br>lldb.value.__getitem__ = lldb__getitem__<br>lldb.value.__gt__ = lldb__gt__<br>+ lldb.value.__index__ = lldb__index__<br>lldb.value.__le__ = lldb__le__<br>lldb.value.__lt__ = lldb__lt__<br>lldb.value.__str__ = lldb__str__<br>@@ -474,6 +602,9 @@ class _LLDBDebugger(Debugger):<br>def cstr(self, strptr):<br>return strptr.sbvalue.summary<br><br>+ def address(self, obj):<br>+ return lldb.value(obj.sbvalue.address_of)<br>+<br>def lookup_global(self, symbol):<br>sbvalue = self.target.FindFirstGlobalVariable(symbol)<br>tp = sbvalue.GetType()<br>@@ -492,6 +623,46 @@ class _LLDBDebugger(Debugger):<br>ptr_tp<br>)<br><br>+ def member_by_offset(self, tp, offset, prev_name=None):<br>+ if isinstance(tp, str):<br>+ tp = self._dbgtype(tp)<br>+ assert offset < tp.GetByteSize(), 'offset is bigger than object size'<br>+ tp = tp.GetCanonicalType()<br>+ if tp.GetTypeClass() == lldb.eTypeClassStruct:<br>+ len_fields = tp.GetNumberOfFields()<br>+ for n_field in range(len_fields):<br>+ islast = n_field == (len_fields - 1)<br>+ field = tp.GetFieldAtIndex(n_field)<br>+ start_field = field.GetOffsetInBytes()<br>+ if not islast:<br>+ end_field = tp.GetFieldAtIndex(<br>+ n_field + 1<br>+ ).GetOffsetInBytes()<br>+ else:<br>+ end_field = tp.GetByteSize()<br>+ if start_field <= offset and offset < end_field:<br>+ next_name = self.member_by_offset(<br>+ field.GetType(),<br>+ offset - start_field,<br>+ prev_name=field.GetName()<br>+ )<br>+ return '.{field}{suffix}'.format(<br>+ field=field.GetName(),<br>+ suffix=next_name if next_name else ''<br>+ )<br>+ if tp.GetTypeClass() == lldb.eTypeClassArray:</div></div></div></div></div></div></blockquote></div></div><div>Typo?: elif</div><div class="cl-1vhdty4nd7"><div class="mail-quote-collapse"><blockquote style="border-left:1px solid #0857A6;margin:10px;padding:0 0 0 10px"><div><div><div class="cl-lb60oino82"><div class="js-helper_mr_css_attr js-readmsg-msg_mr_css_attr"><div><div>+ # Get array field type.<br>+ target = tp.GetArrayElementType()<br>+ tsize = target.GetByteSize()<br>+ idx = int(offset // tsize)<br>+ next_name = self.member_by_offset(target, offset - idx * tsize)<br>+ idxname = idx_name(prev_name)<br>+ if idxname and idx in idxname:<br>+ idx = idxname[idx]<br>+ return '[{}]{}'.format(idx, next_name if next_name else '')<br>+ else:<br>+ return None<br>+<br>def eval(self, command):<br>if not command:<br>return None<br>@@ -502,9 +673,23 @@ class _LLDBDebugger(Debugger):<br>ret = frame.EvaluateExpression(command)<br>return ret<br><br>+ def detect_arch(self):<br>+ if hasattr(self, 'arch'):<br>+ return self.arch<br>+ target = self.target.GetTriple().split('-')[0]<br>+ if target == 'x86_64':<br>+ return 'x64'<br>+ elif target == 'arm64' or target == 'aarch64':<br>+ return 'arm64'<br>+ else:<br>+ return ''<br>+<br>def write(self, msg):<br>sys.stdout.write(msg)<br><br>+ def error(self, errmsg):<br>+ return Exception(errmsg)<br>+<br>def check_libluajit(self):<br># TODO: Implement postpone loading for LLDB too.<br>return True<br>@@ -997,6 +1182,86 @@ def J(g):<br>return dbg.cast('jit_State *', dbg.cast('char *', g) - g_offset + J_offset)<br><br><br>+# Matched `MMDEF(_)`.<br>+MM_NAMES = [<br>+ 'index',<br>+ 'newindex',<br>+ 'gc',<br>+ 'mode',<br>+ 'eq',<br>+ 'len',<br>+ 'lt',<br>+ 'le',<br>+ 'concat',<br>+ 'call',<br>+ 'add',<br>+ 'sub',<br>+ 'mul',<br>+ 'div',<br>+ 'mod',<br>+ 'pow',<br>+ 'unm',<br>+ 'metatable',<br>+ 'tostring',<br>+ # TODO: depends on LJ_HASFFI, see `MMDEF_FFI(_)`.<br>+ 'new',<br>+ # TODO: depends on LJ_52 || LJ_HASFFI, see `MMDEF_PAIRS(_)`.<br>+ 'pairs',<br>+ 'ipairs',<br>+]<br>+<br>+<br>+GCROOT_MMNAME = 0<br>+GCROOT_BASEMT = GCROOT_MMNAME + len(MM_NAMES)<br>+GCROOT_IO_INPUT = GCROOT_BASEMT + i2notu32(LJ_T['NUMX']) + 1<br>+GCROOT_IO_OUTPUT = GCROOT_IO_INPUT + 1<br>+<br>+<br>+# Get the name of the index in the predefined arrays.<br>+def idx_name(field_name):<br>+ # Don't use **{ to be compatible with Python 2.<br>+ gcroot = {}<br>+ gcroot.update({<br>+ i: 'GCROOT_MMNAME_' + MM_NAMES[i] for i in range(len(MM_NAMES))<br>+ })<br>+ gcroot.update({<br>+ i2notu32(LJ_T[k]) + GCROOT_BASEMT: 'GCROOT_BASEMT_' + k<br>+ for k in LJ_T.keys()<br>+ })<br>+ gcroot.update({<br>+ GCROOT_IO_INPUT: 'GCROOT_IO_INPUT',<br>+ GCROOT_IO_OUTPUT: 'GCROOT_IO_OUTPUT',<br>+ })<br>+ return {<br>+ # May be one of 2 slots depending on the result address.<br>+ 'ksimd': {<br>+ 0 * 2 + 0: 'LJ_KSIMD_ABS',<br>+ 0 * 2 + 1: 'LJ_KSIMD_ABS',<br>+ 1 * 2 + 0: 'LJ_KSIMD_NEG',<br>+ 1 * 2 + 1: 'LJ_KSIMD_NEG',<br>+ },<br>+ 'gcroot': gcroot,<br>+ }.get(field_name, None)<br>+<br>+<br>+ggfname_cache = {}<br>+<br>+<br>+# Get GG field name by given offset. Use in JIT dump.<br>+def ggfname_by_offset(offset):<br>+ if offset in ggfname_cache:<br>+ return ggfname_cache[offset]<br>+<br>+ field_path = dbg.member_by_offset('GG_State', offset)<br>+ if not field_path:<br>+ return None<br>+<br>+ # Remove first '.'.<br>+ ggfname = 'offsetof(GG, {})'.format(field_path[1:])<br>+ ggfname_cache[offset] = ggfname<br>+ return ggfname<br>+<br>+<br>def vm_state(g):<br>return {<br>i2notu32(0): 'INTERP',<br>@@ -1087,6 +1352,555 @@ def lightudV(tv):<br>return gcval(tv['gcr'])<br><br><br>+# JIT engine.<br>+<br>+<br>+IRS = [<br>+ # Guarded assertions.<br>+ 'LT',<br>+ 'GE',<br>+ 'LE',<br>+ 'GT',<br>+<br>+ 'ULT',<br>+ 'UGE',<br>+ 'ULE',<br>+ 'UGT',<br>+<br>+ 'EQ',<br>+ 'NE',<br>+<br>+ 'ABC',<br>+ 'RETF',<br>+<br>+ # Miscellaneous ops.<br>+ 'NOP',<br>+ 'BASE',<br>+ 'PVAL',<br>+ 'GCSTEP',<br>+ 'HIOP',<br>+ 'LOOP',<br>+ 'USE',<br>+ 'PHI',<br>+ 'RENAME',<br>+ 'PROF',<br>+<br>+ # Constants.<br>+ 'KPRI',<br>+ 'KINT',<br>+ 'KGC',<br>+ 'KPTR',<br>+ 'KKPTR',<br>+ 'KNULL',<br>+ 'KNUM',<br>+ 'KINT64',<br>+ 'KSLOT',<br>+<br>+ # Bit ops.<br>+ 'BNOT',<br>+ 'BSWAP',<br>+ 'BAND',<br>+ 'BOR',<br>+ 'BXOR',<br>+ 'BSHL',<br>+ 'BSHR',<br>+ 'BSAR',<br>+ 'BROL',<br>+ 'BROR',<br>+<br>+ # Arithmetic ops. ORDER ARITH<br>+ 'ADD',<br>+ 'SUB',<br>+ 'MUL',<br>+ 'DIV',<br>+ 'MOD',<br>+ 'POW',<br>+ 'NEG',<br>+<br>+ 'ABS',<br>+ 'LDEXP',<br>+ 'MIN',<br>+ 'MAX',<br>+ 'FPMATH',<br>+<br>+ # Overflow-checking arithmetic ops.<br>+ 'ADDOV',<br>+ 'SUBOV',<br>+ 'MULOV',<br>+<br>+ # Memory ops. A = array, H = hash, U = upvalue, F = field,<br>+ # S = stack.<br>+<br>+ # Memory references.<br>+ 'AREF',<br>+ 'HREFK',<br>+ 'HREF',<br>+ 'NEWREF',<br>+ 'UREFO',<br>+ 'UREFC',<br>+ 'FREF',<br>+ 'STRREF',<br>+ 'LREF',<br>+<br>+ # Loads and Stores. These must be in the same order.<br>+ 'ALOAD',<br>+ 'HLOAD',<br>+ 'ULOAD',<br>+ 'FLOAD',<br>+ 'XLOAD',<br>+ 'SLOAD',<br>+ 'VLOAD',<br>+<br>+ 'ASTORE',<br>+ 'HSTORE',<br>+ 'USTORE',<br>+ 'FSTORE',<br>+ 'XSTORE',<br>+<br>+ # Allocations.<br>+ 'SNEW',<br>+ 'XSNEW',<br>+ 'TNEW',<br>+ 'TDUP',<br>+ 'CNEW',<br>+ 'CNEWI',<br>+<br>+ # Buffer operations.<br>+ 'BUFHDR',<br>+ 'BUFPUT',<br>+ 'BUFSTR',<br>+<br>+ # Barriers.<br>+ 'TBAR',<br>+ 'OBAR',<br>+ 'XBAR',<br>+<br>+ # Type conversions.<br>+ 'CONV',<br>+ 'TOBIT',<br>+ 'TOSTR',<br>+ 'STRTO',<br>+<br>+ # Calls.<br>+ 'CALLN',<br>+ 'CALLA',<br>+ 'CALLL',<br>+ 'CALLS',<br>+ 'CALLXS',<br>+ 'CARG',<br>+]<br>+<br>+<br>+# Mode bits: Commutative, {Normal/Ref, Alloc, Load, Store},<br>+# Non-weak guard. */</div></div></div></div></div></div></blockquote></div></div><div>Typo: C comment end */</div><div class="cl-1vhdty4nd7"><div class="mail-quote-collapse"><blockquote style="border-left:1px solid #0857A6;margin:10px;padding:0 0 0 10px"><div><div><div class="cl-lb60oino82"><div class="js-helper_mr_css_attr js-readmsg-msg_mr_css_attr"><div><div>+IRM_C = 0x10<br>+IRM_A = 0x20<br>+IRM_L = 0x40<br>+IRM_S = 0x60<br>+IRM_W = 0x80<br>+<br>+<br>+# IR operand mode (2 bit).<br>+IRM = [<br>+ 'ref',<br>+ 'lit',<br>+ 'cst',<br>+ '', # none<br>+]<br>+<br>+<br>+lj_ir_mode_ = None<br>+<br>+<br>+def lj_ir_mode():<br>+ global lj_ir_mode_<br>+ if lj_ir_mode_:<br>+ return lj_ir_mode_<br>+ lj_ir_mode_ = dbg.lookup_global('lj_ir_mode')<br>+ return lj_ir_mode_<br>+<br>+<br>+def ir_left(op):<br>+ return IRM[int(lj_ir_mode()[op] & 3)]</div></div></div></div></div></div></blockquote></div></div><div>May be binary constant will be more clear? xxx & 0b0011</div><div class="cl-1vhdty4nd7"><div class="mail-quote-collapse"><blockquote style="border-left:1px solid #0857A6;margin:10px;padding:0 0 0 10px"><div><div><div class="cl-lb60oino82"><div class="js-helper_mr_css_attr js-readmsg-msg_mr_css_attr"><div><div>+<br>+<br>+def ir_right(op):<br>+ return IRM[int(lj_ir_mode()[op] >> 2 & 3)]</div></div></div></div></div></div></blockquote></div></div><div>May be binary constant will be more clear? (xxx & 0b1100) >> 2</div><div class="cl-1vhdty4nd7"><div class="mail-quote-collapse"><blockquote style="border-left:1px solid #0857A6;margin:10px;padding:0 0 0 10px"><div><div><div class="cl-lb60oino82"><div class="js-helper_mr_css_attr js-readmsg-msg_mr_css_attr"><div><div>+<br>+<br>+def ir_mode(op):<br>+ mode = ''<br>+ ir_mode = int(lj_ir_mode()[op] ^ IRM_W)</div></div></div></div></div></div></blockquote></div></div><div class="cl-1vhdty4nd7"><div class="mail-quote-collapse"><blockquote style="border-left:1px solid #0857A6;margin:10px;padding:0 0 0 10px"><div><div><div class="cl-lb60oino82"><div class="js-helper_mr_css_attr js-readmsg-msg_mr_css_attr"><div><div>+ if ir_mode == IRM_C:<br>+ mode = 'C'<br>+ elif ir_mode == IRM_A:<br>+ mode = 'A'<br>+ elif ir_mode == IRM_L:<br>+ mode = 'L'<br>+ elif ir_mode == IRM_S:<br>+ mode = 'S'<br>+ else:<br>+ mode = 'N'</div></div></div></div></div></div></blockquote></div></div><div class="cl-1vhdty4nd7"><div class="mail-quote-collapse"><blockquote style="border-left:1px solid #0857A6;margin:10px;padding:0 0 0 10px"><div><div><div class="cl-lb60oino82"><div class="js-helper_mr_css_attr js-readmsg-msg_mr_css_attr"><div><div>+ mode += 'W' if ir_mode & IRM_W else ''</div></div></div></div></div></div></blockquote></div></div><div>May be table with 16 items and comments will be more clear? E. g. return XXX[(lj_ir_mode()[op] & 0b11110000) >> 4]</div><div>And it will contain invalid values also.</div><div># <flag bits in a comment></div><div>XXX[0b0000] = ‘NW’ # Normal/Ref | !Non-weak guard</div><div>XXX[0b0001] = ‘CW’ # Commutative | !Non-weak guard</div><div>XXX[0b0011] = ‘Invalid’</div><div>...<br>XXX[0b1000] = ‘N’ # Normal/Ref | Non-weak guard</div><div>XXX[0b1001] = ‘C’ # Commutative | Non-weak guard</div><div>XXX[0b1011] = ‘Invalid’</div><div>...</div><div class="cl-1vhdty4nd7"><div class="mail-quote-collapse"><blockquote style="border-left:1px solid #0857A6;margin:10px;padding:0 0 0 10px"><div><div><div class="cl-lb60oino82"><div class="js-helper_mr_css_attr js-readmsg-msg_mr_css_attr"><div><div>+ return mode</div></div></div></div></div></div></blockquote></div></div><div class="cl-1vhdty4nd7"><div class="mail-quote-collapse"><blockquote style="border-left:1px solid #0857A6;margin:10px;padding:0 0 0 10px"><div><div><div class="cl-lb60oino82"><div class="js-helper_mr_css_attr js-readmsg-msg_mr_css_attr"><div><div>+<br>+<br>+IRTYPES = [<br>+ 'nil',<br>+ 'fal',<br>+ 'tru',<br>+ 'lud',<br>+ 'str',<br>+ 'p32',<br>+ 'thr',<br>+ 'pro',<br>+ 'fun',<br>+ 'p64',<br>+ 'cdt',<br>+ 'tab',<br>+ 'udt',<br>+ 'flt',<br>+ 'num',<br>+ 'i8 ',<br>+ 'u8 ',<br>+ 'i16',<br>+ 'u16',<br>+ 'int',<br>+ 'u32',<br>+ 'i64',<br>+ 'u64',<br>+ 'sfp',<br>+]<br>+<br>+<br>+IRT_NUM = 14<br>+assert IRTYPES[IRT_NUM] == 'num', 'incorrect IRT_NUM definition'<br>+<br>+<br>+IRFIELDS = [<br>+ 'str.len',<br>+ 'func.env',<br>+ 'func.pc',<br>+ 'func.ffid',<br>+ 'thread.env',<br>+ 'tab.meta',<br>+ 'tab.array',<br>+ 'tab.node',<br>+ 'tab.asize',<br>+ 'tab.hmask',<br>+ 'tab.nomm',<br>+ 'udata.meta',<br>+ 'udata.udtype',<br>+ 'udata.file',<br>+ 'cdata.ctypeid',<br>+ 'cdata.ptr',<br>+ 'cdata.int',<br>+ 'cdata.int64',<br>+ 'cdata.int64_4',<br>+]<br>+<br>+<br>+IRFPMS = [<br>+ 'floor',<br>+ 'ceil',<br>+ 'trunc',<br>+ 'sqrt',<br>+ 'exp2',<br>+ 'log',<br>+ 'log2',<br>+ 'other'<br>+]<br>+<br>+<br>+# Don't use *[ to be compatible with Python 2.<br>+REGISTERS = {'x64': [<br>+ 'rax',<br>+ 'rcx',<br>+ 'rdx',<br>+ 'rbx',<br>+ 'rsp',<br>+ 'rbp',<br>+ 'rsi',<br>+ 'rdi',<br>+] + [<br>+ 'r{}'.format(i) for i in range(8, 16) # r8 .. r15<br>+] + [<br>+ 'xmm{}'.format(i) for i in range(0, 16) # xmm0 .. xmm15<br>+], 'arm64': [<br>+ 'x{}'.format(i) for i in range(0, 31) # x0 .. x30<br>+] + ['sp'] + [ # x31<br>+ 'd{}'.format(i) for i in range(0, 32) # d0 .. d31<br>+]}</div></div></div></div></div></div></blockquote></div></div><div>It seems, the ‘arm64’ registers are missed.</div><div class="cl-1vhdty4nd7"><div class="mail-quote-collapse"><blockquote style="border-left:1px solid #0857A6;margin:10px;padding:0 0 0 10px"><div><div><div class="cl-lb60oino82"><div class="js-helper_mr_css_attr js-readmsg-msg_mr_css_attr"><div><div>+<br>+<br>+IR_CALLS = [<br>+ 'lj_str_cmp',<br>+ 'lj_str_find',<br>+ 'lj_str_new',<br>+ 'lj_strscan_num',<br>+ 'lj_strfmt_int',<br>+ 'lj_strfmt_num',<br>+ 'lj_strfmt_char',<br>+ 'lj_strfmt_putint',<br>+ 'lj_strfmt_putnum',<br>+ 'lj_strfmt_putquoted',<br>+ 'lj_strfmt_putfxint',<br>+ 'lj_strfmt_putfnum_int',<br>+ 'lj_strfmt_putfnum_uint',<br>+ 'lj_strfmt_putfnum',<br>+ 'lj_strfmt_putfstr',<br>+ 'lj_strfmt_putfchar',<br>+ 'lj_buf_putmem',<br>+ 'lj_buf_putstr',<br>+ 'lj_buf_putchar',<br>+ 'lj_buf_putstr_reverse',<br>+ 'lj_buf_putstr_lower',<br>+ 'lj_buf_putstr_upper',<br>+ 'lj_buf_putstr_rep',<br>+ 'lj_buf_puttab',<br>+ 'lj_buf_tostr',<br>+ 'lj_tab_new_ah',<br>+ 'lj_tab_new1',<br>+ 'lj_tab_dup',<br>+ 'lj_tab_clear',<br>+ 'lj_tab_newkey',<br>+ 'lj_tab_len',<br>+ 'lj_gc_step_jit',<br>+ 'lj_gc_barrieruv',<br>+ 'lj_mem_newgco',<br>+ 'lj_math_random_step',<br>+ 'lj_vm_modi',<br>+ 'log10',<br>+ 'exp',<br>+ 'sin',<br>+ 'cos',<br>+ 'tan',<br>+ 'asin',<br>+ 'acos',<br>+ 'atan',<br>+ 'sinh',<br>+ 'cosh',<br>+ 'tanh',<br>+ 'fputc',<br>+ 'fwrite',<br>+ 'fflush',<br>+ 'lj_vm_floor',<br>+ 'lj_vm_ceil',<br>+ 'lj_vm_trunc',<br>+ 'sqrt',<br>+ 'log',<br>+ 'lj_vm_log2',<br>+ 'pow',<br>+ 'atan2',<br>+ 'ldexp',<br>+ 'lj_vm_tobit',<br>+ 'softfp_add',<br>+ 'softfp_sub',<br>+ 'softfp_mul',<br>+ 'softfp_div',<br>+ 'softfp_cmp',<br>+ 'softfp_i2d',<br>+ 'softfp_d2i',<br>+ 'lj_vm_sfmin',<br>+ 'lj_vm_sfmax',<br>+ 'lj_vm_tointg',<br>+ 'softfp_ui2d',<br>+ 'softfp_f2d',<br>+ 'softfp_d2ui',<br>+ 'softfp_d2f',<br>+ 'softfp_i2f',<br>+ 'softfp_ui2f',<br>+ 'softfp_f2i',<br>+ 'softfp_f2ui',<br>+ 'fp64_l2d',<br>+ 'fp64_ul2d',<br>+ 'fp64_l2f',<br>+ 'fp64_ul2f',<br>+ 'fp64_d2l',<br>+ 'fp64_d2ul',<br>+ 'fp64_f2l',<br>+ 'fp64_f2ul',<br>+ 'lj_carith_divi64',<br>+ 'lj_carith_divu64',<br>+ 'lj_carith_modi64',<br>+ 'lj_carith_modu64',<br>+ 'lj_carith_powi64',<br>+ 'lj_carith_powu64',<br>+ 'lj_cdata_newv',<br>+ 'lj_cdata_setfin',<br>+ 'strlen',<br>+ 'memcpy',<br>+ 'memset',<br>+ 'lj_vm_errno',<br>+ 'lj_carith_mul64',<br>+ 'lj_carith_shl64',<br>+ 'lj_carith_shr64',<br>+ 'lj_carith_sar64',<br>+ 'lj_carith_rol64',<br>+ 'lj_carith_ror64',<br>+]<br>+<br>+<br>+def regname(reg_number):<br>+ if not hasattr(dbg, 'arch'):<br>+ dbg.arch = dbg.detect_arch()<br>+ return REGISTERS[dbg.arch][reg_number]<br>+<br>+<br>+def litname_sload(mode):<br>+ modes_str = ''<br>+ modes_str += 'P' if mode & 0x1 else ''<br>+ modes_str += 'F' if mode & 0x2 else ''<br>+ modes_str += 'T' if mode & 0x4 else ''<br>+ modes_str += 'C' if mode & 0x8 else ''<br>+ modes_str += 'R' if mode & 0x10 else ''<br>+ modes_str += 'I' if mode & 0x20 else ''<br>+ return modes_str<br>+<br>+<br>+def litname_xload(mode):<br>+ flags = ['-', 'R', 'V', 'RV', 'U', 'RU', 'VU', 'RVU']</div></div></div></div></div></div></blockquote></div></div><div>Does we need a range check as in litname_bufhdr()?</div><div class="cl-1vhdty4nd7"><div class="mail-quote-collapse"><blockquote style="border-left:1px solid #0857A6;margin:10px;padding:0 0 0 10px"><div><div><div class="cl-lb60oino82"><div class="js-helper_mr_css_attr js-readmsg-msg_mr_css_attr"><div><div>+ return flags[mode]<br>+<br>+<br>+def litname_conv(mode):</div></div></div></div></div></div></blockquote></div></div><div>Does we need some range checking here?</div><div class="cl-1vhdty4nd7"><div class="mail-quote-collapse"><blockquote style="border-left:1px solid #0857A6;margin:10px;padding:0 0 0 10px"><div><div><div class="cl-lb60oino82"><div class="js-helper_mr_css_attr js-readmsg-msg_mr_css_attr"><div><div>+ IRCONV_DSH = 5<br>+ IRCONV_CSH = 12<br>+ IRCONV_SEXT = 0x800<br>+ IRCONV_SRCMASK = 0x1f<br>+ conv_str = '{to}.{frm}'.format(<br>+ to=IRTYPES[(mode >> IRCONV_DSH) & IRCONV_SRCMASK],<br>+ frm=IRTYPES[mode & IRCONV_SRCMASK]<br>+ )<br>+ conv_str += ' sext' if mode & IRCONV_SEXT else ''<br>+ num2int_mode = mode >> IRCONV_CSH<br>+ if num2int_mode == 2:<br>+ conv_str += ' index'<br>+ elif num2int_mode == 3:<br>+ conv_str += ' check'<br>+ return conv_str<br>+<br>+<br>+def litname_irfield(mode):<br>+ if mode >= len(IRFIELDS):<br>+ return 'unknown irfield'<br>+ return IRFIELDS[mode]<br>+<br>+<br>+def litname_fpm(mode):<br>+ if mode >= len(IRFPMS):<br>+ return 'unknown irfpm'<br>+ return IRFPMS[mode]<br>+<br>+<br>+def litname_bufhdr(mode):<br>+ modes = ['RESET', 'APPEND']<br>+ if mode >= len(modes):<br>+ return 'unknown bufhdr mode'<br>+ return modes[mode]<br>+<br>+<br>+def litname_tostr(mode):<br>+ modes = ['INT', 'NUM', 'CHAR']<br>+ if mode >= len(modes):<br>+ return 'unknown tostr mode'<br>+ return modes[mode]<br>+<br>+<br>+IR_LITNAMES = {<br>+ 'SLOAD': litname_sload,<br>+ 'XLOAD': litname_xload,<br>+ 'CONV': litname_conv,<br>+ 'FLOAD': litname_irfield,<br>+ 'FREF': litname_irfield,<br>+ 'FPMATH': litname_fpm,<br>+ 'BUFHDR': litname_bufhdr,<br>+ 'TOSTR': litname_tostr<br>+}<br>+<br>+# Additional flags.<br>+IRT_MARK = 0x20 # Marker for misc. purposes.<br>+IRT_ISPHI = 0x40 # Instruction is left or right PHI operand.<br>+IRT_GUARD = 0x80 # Instruction is a guard.<br>+# Masks.<br>+IRT_TYPE = 0x1f<br>+<br>+RID_NONE = 0x80<br>+RID_MASK = 0x7f<br>+RID_INIT = (RID_NONE | RID_MASK)<br>+RID_SINK = (RID_INIT - 1)<br>+RID_SUNK = (RID_INIT - 2)<br>+# Spill slot 0 means no spill slot has been allocated.<br>+SPS_NONE = 0<br>+<br>+REF_BIAS = 0x8000<br>+<br>+TREF_SHIFT = 24<br>+<br>+TREF_REFMASK = 0x0000ffff<br>+TREF_FRAME = 0x00010000<br>+TREF_CONT = 0x00020000<br>+# Snapshot flags and masks.<br>+SNAP_FRAME = 0x010000<br>+SNAP_SOFTFPNUM = 0x080000<br>+<br>+<br>+def irt_type(t):<br>+ return dbg.cast('IRType', t['irt'] & IRT_TYPE)<br>+<br>+<br>+def tref_type(tr):<br>+ return dbg.cast('IRType', (tr >> TREF_SHIFT) & IRT_TYPE)<br>+<br>+<br>+def tref_ref(tr):<br>+ return int(tr & TREF_REFMASK)<br>+<br>+<br>+def irt_ismarked(t):<br>+ return t['irt'] & IRT_MARK</div></div></div></div></div></div></blockquote></div></div><div>I propose explicit bool cast (!= 0) here and below.</div><div class="cl-1vhdty4nd7"><div class="mail-quote-collapse"><blockquote style="border-left:1px solid #0857A6;margin:10px;padding:0 0 0 10px"><div><div><div class="cl-lb60oino82"><div class="js-helper_mr_css_attr js-readmsg-msg_mr_css_attr"><div><div>+<br>+<br>+def irt_isphi(t):<br>+ return t['irt'] & IRT_ISPHI<br>+<br>+<br>+def irt_isguard(t):<br>+ return t['irt'] & IRT_GUARD<br>+<br>+<br>+def irt_toitype(irt):<br>+ t = irt_type(irt)<br>+ if LJ_DUALNUM and t > IRT_NUM:<br>+ return LJ_T['NUMX']<br>+ else:<br>+ return i2notu32(t)<br>+<br>+<br>+def ir_kptr(ir):<br>+ irname = IRS[ir['o']]<br>+ assert irname == 'KPTR' or irname == 'KKPTR', 'wrong IR for ir_iptr()'<br>+ return mref('void *', dbg.cast('IRIns *', dbg.address(ir))[LJ_GC64]['ptr'])<br>+<br>+<br>+def ir_kgc(ir):<br>+ irname = IRS[ir['o']]<br>+ assert irname == 'KGC', 'wrong IR for ir_kgc()'<br>+ return gcref(dbg.cast('IRIns *', dbg.address(ir))[LJ_GC64]['gcr'])<br>+<br>+<br>+def ir_knum(ir):<br>+ irname = IRS[ir['o']]<br>+ assert irname == 'KNUM', 'wrong IR for ir_knum()'<br>+ return dbg.address(dbg.cast('IRIns *', dbg.address(ir))[1]['tv'])<br>+<br>+<br>+def ir_kint64(ir):<br>+ irname = IRS[ir['o']]<br>+ assert irname == 'KINT64', 'wrong IR for ir_knum()'<br>+ return dbg.address(dbg.cast('IRIns *', dbg.address(ir))[1]['tv'])<br>+<br>+<br># Dumpers.<br><br># GCobj dumpers.<br>@@ -1467,6 +2281,325 @@ def dump_func(func):<br>return 'fast function #{}\n'.format(int(ffid))<br><br><br>+# JIT dumpers.<br>+<br>+<br>+def dump_call_func(trace, callop):<br>+ ctype = ''<br>+ if callop > 0:<br>+ ir = trace['ir'][REF_BIAS + callop]<br>+ if IRTYPES[irt_type(ir['t'])] == 'nil': # nil == CARG(func, ctype)<br>+ callop = int(ir['op1']) - REF_BIAS<br>+ cdt_idx_irk = trace['ir'][ir['op2']]<br>+ assert IRS[cdt_idx_irk['o']] == 'KINT', \<br>+ 'unexpected IR for ctype storage'<br>+ ctype_idx = cdt_idx_irk['i']<br>+ ctype = 'ctype: {}'.format(ctype_idx)<br>+<br>+ func_str = ''<br>+ if callop < 0:<br>+ irk = trace['ir'][REF_BIAS + callop]<br>+ assert IRS[irk['o']] == 'KINT64', \<br>+ 'unexpected IR for FFI function storage'<br>+ func_addr = int(ir_kint64(irk)['u64'])<br>+ # TODO: Symbol demangling.<br>+ func_str = '[{:#x}]'.format(func_addr)<br>+ else:<br>+ func_str = '[{:04d}]'.format(callop)<br>+<br>+ return func_str, ctype<br>+<br>+<br>+def dump_call_args(trace, ins):<br>+ if ins < 0:<br>+ return '{{{}}}'.format(dump_irk(trace, ins))<br>+ else:<br>+ ir = trace['ir'][REF_BIAS + ins]<br>+ irname = IRS[ir['o']]<br>+ if irname == 'CARG':<br>+ last_arg = ''<br>+ args = dump_call_args(trace, int(ir['op1']) - REF_BIAS)<br>+ op2 = int(ir['op2']) - REF_BIAS<br>+ if op2 < 0:<br>+ last_arg = '{{{}}}'.format(dump_irk(trace, op2))<br>+ else:<br>+ last_arg = '{{{:04d}}}'.format(op2)<br>+ return args + ', ' + last_arg<br>+ else:<br>+ return '{{{:04d}}}'.format(ins)<br>+<br>+<br>+# Special FP constant.<br>+CONST_BIAS = 2 ** 52 + 2 ** 51<br>+<br>+<br>+def dump_irk(trace, idx):<br>+ ref = idx + REF_BIAS<br>+ assert ref >= trace['nk'] and ref < REF_BIAS, 'bad constant in IR dump'<br>+ irins = trace['ir'][ref]<br>+ irname = IRS[irins['o']]<br>+ slot = ''<br>+ if irname == 'KSLOT':<br>+ slot = ' KSLOT: @{}'.format(int(irins['op2']))<br>+ irins = trace['ir'][irins['op1']]<br>+ irname = IRS[irins['o']]<br>+<br>+ irtype = irins['t']<br>+ if irname == 'KPRI':<br>+ typename = typenames(irt_toitype(irtype))<br>+ # Trivial dump for primitives.<br>+ irk = tv_dumpers.get(<br>+ typename, dump_lj_tv_invalid # noqa: F821 # Generated.<br>+ )(0)<br>+ elif irname == 'KINT':<br>+ irk = 'integer {}'.format(dbg.cast('int32_t', irins['i']))<br>+ elif irname == 'KGC':<br>+ typename = typenames(irt_toitype(irtype))<br>+ irk = gco_dumpers.get(typename, dump_lj_gco_invalid)(ir_kgc(irins))<br>+ elif irname == 'KKPTR':<br>+ addr = ir_kptr(irins)<br>+ if addr == dbg.address(G(L())['nilnode']):<br>+ return '[g->nilnode]' + slot<br>+ irk = '[{}]'.format(strx64(addr))<br>+ elif irname == 'KPTR':<br>+ irk = '[{}]'.format(strx64(ir_kptr(irins)))<br>+ elif irname == 'KNULL':<br>+ irk = 'NULL'<br>+ elif irname == 'KNUM':<br>+ tv_num = ir_knum(irins)<br>+ if float(tv_num['n']) == CONST_BIAS:<br>+ return 'bias'<br>+ irk = dump_lj_tv_numx(tv_num)<br>+ elif irname == 'KINT64':<br>+ irk = 'int64_t {}'.format(dbg.cast(<br>+ 'int64_t', int(ir_kint64(irins)['u64'])<br>+ ))<br>+ else:<br>+ return 'Unknown IRK: ' + irname<br>+ return irk + slot<br>+<br>+<br>+def dump_irins(irins, trace=None):<br>+ irop = int(irins['o'])<br>+ if irop >= len(IRS):<br>+ return 'INVALID'<br>+<br>+ irname = IRS[irop]<br>+ leftop = ir_left(irop)<br>+ rightop = ir_right(irop)<br>+ irt = irins['t']<br>+ is_sinksunk = irins['r'] == RID_SINK or irins['r'] == RID_SUNK<br>+ flags = '{is_sinksunk}{is_marked}{is_guard}{is_phi}'.format(<br>+ # Sink flag should be the first to match sink slots during<br>+ # the dump of registers.<br>+ is_sinksunk='}' if is_sinksunk else ' ',<br>+ is_marked='!' if irt_ismarked(irt) else ' ',<br>+ is_guard='>' if irt_isguard(irt) else ' ',<br>+ is_phi='+' if irt_isphi(irt) else ' '<br>+ )<br>+<br>+ if not trace:<br>+ g = G(L(None))<br>+ compiling = jit_state(g) != 'IDLE'<br>+ assert compiling, 'attempt to dump IR for J.cur trace in bad VM state'<br>+ trace = J(g)['cur']<br>+<br>+ left = ''<br>+ right = ''<br>+ lisref = leftop == 'ref'<br>+ risref = rightop == 'ref'<br>+ op1 = int((irins['op1'] - REF_BIAS) if lisref else irins['op1'])<br>+ op2 = int((irins['op2'] - REF_BIAS) if risref else irins['op2'])</div></div></div></div></div></div></blockquote></div></div><div class="cl-1vhdty4nd7"><div class="mail-quote-collapse"><blockquote style="border-left:1px solid #0857A6;margin:10px;padding:0 0 0 10px"><div><div><div class="cl-lb60oino82"><div class="js-helper_mr_css_attr js-readmsg-msg_mr_css_attr"><div><div>+<br>+ skip_right = False<br>+ if re.match('CALL', irname):<br>+ ctype = ''<br>+ args = ''<br>+ if rightop == 'lit':<br>+ func = IR_CALLS[op2]<br>+ else:<br>+ func, ctype = dump_call_func(trace, op2)<br>+<br>+ if op1 != -1:<br>+ args = dump_call_args(trace, int(op1))<br>+<br>+ return '{flags} {type} {name:6} [{mode:2}] {f}({args}) {ct}\n'.format(<br>+ flags=flags,<br>+ name=irname,<br>+ mode=ir_mode(irop),<br>+ type=IRTYPES[irt_type(irt)],<br>+ ct=ctype,<br>+ args=args,<br>+ f=func,<br>+ )<br>+ elif irname == 'CNEW' and op2 == -1:<br>+ left = dump_irk(trace, op1)<br>+ skip_right = True<br>+ elif leftop:<br>+ if op1 < 0:<br>+ left = dump_irk(trace, op1)<br>+ elif leftop == 'cst':<br>+ idx = irins - dbg.address(trace['ir'][REF_BIAS])<br>+ left = dump_irk(trace, idx)<br>+ else:<br>+ left = ('{:04d}' if lisref else '#{:<3d}').format(op1)<br>+<br>+ if rightop:<br>+ if rightop == 'lit':<br>+ litname = IR_LITNAMES.get(irname, None)<br>+ if litname:<br>+ # Try to handle `lj_ir_ggfload()`.<br>+ ggfname = None<br>+ if irname == 'FLOAD' and left == 'nil' \<br>+ and op2 >= len(IRFIELDS):<br>+ ggfname = ggfname_by_offset(op2 << 2)<br>+<br>+ if ggfname:<br>+ right = ggfname<br>+ else:<br>+ right = litname(op2)<br>+ elif irname == 'UREFO' or irname == 'UREFC':<br>+ right = '#{:<3d}'.format(op2 >> 8)<br>+ else:<br>+ right = '#{:<3d}'.format(op2)<br>+ elif op2 < 0:<br>+ right = dump_irk(trace, op2)<br>+ else:<br>+ right = ('{:04d}').format(op2)<br>+<br>+ typename = ''<br>+ if irname == 'LOOP':<br>+ typename = '---'<br>+ elif irname == 'NOP':<br>+ typename = ' '<br>+ else:<br>+ typename = IRTYPES[irt_type(irt)]<br>+<br>+ return '{flags} {type} {name:6} [{mode:2}] {left:<9s} {right}\n'.format(<br>+ flags=flags,<br>+ name=irname,<br>+ mode=ir_mode(irop),<br>+ type=typename,<br>+ left=(leftop + ': ' + left) if leftop else '',<br>+ right=(rightop + ': ' + right) if rightop and not skip_right else '',<br>+ )<br>+<br>+<br>+def dump_snap(trace, snapno, snap):<br>+ dump = 'SNAP #{:<3d} ['.format(snapno)<br>+ snap_map = dbg.address(trace['snapmap'][snap['mapofs']])<br>+ snap_entry_num = 0<br>+ for slot in range(0, snap['nslots']):<br>+ dump += ' '<br>+ snap_entry = int(snap_map[snap_entry_num])<br>+ if snap_entry_num < snap['nent'] and snap_entry >> TREF_SHIFT == slot:<br>+ snap_entry_num += 1<br>+ ref = int((snap_entry & TREF_REFMASK) - REF_BIAS)<br>+ if ref < 0:<br>+ if int(snap_entry) == 0x1057fff:<br>+ dump += '----'<br>+ continue<br>+ elif (snap_entry & TREF_CONT):<br>+ dump += 'contpc'<br>+ elif (snap_entry & TREF_FRAME):<br>+ dump += 'ftsz '<br>+ else:<br>+ dump += '{{{const}}}'.format(const=dump_irk(trace, ref))<br>+ elif snap_entry & SNAP_SOFTFPNUM:<br>+ dump += '{:04d}/{:04d}'.format(ref, ref + 1)<br>+ else:<br>+ dump += '{:04d}'.format(ref)<br>+<br>+ if snap_entry & SNAP_FRAME:<br>+ dump += '|'<br>+ else:<br>+ dump += '----'<br>+<br>+ dump += ' ]\n'<br>+ return dump<br>+<br>+<br>+def dump_sink_slot(rid, spill, ins_number):<br>+ assert rid == RID_SINK or rid == RID_SUNK, 'incorrect rid in sink dump'<br>+ tp = 'sink' if rid == RID_SINK else 'sunk'<br>+ return '{{{}'.format(tp) if spill == RID_INIT or spill == SPS_NONE \<br>+ else '{{{:04d}'.format(int(ins_number - spill))<br>+<br>+<br>+def dump_regsp(irins, ins_number):<br>+ rid = irins['r']<br>+ spill = irins['s']<br>+ if rid == RID_SINK or rid == RID_SUNK:<br>+ return dump_sink_slot(rid, spill, ins_number)<br>+ elif irins['prev'] > 255:<br>+ return '[{:#05x}]'.format(int(spill * 4))<br>+ elif rid < 128:<br>+ return regname(rid)<br>+ else:<br>+ return ''<br>+<br>+<br>+def dump_trace(trace, flags):<br>+ dump = 'Trace {num} start\n\tproto: {start_pt}\n\tBC: {start_bc}\n'.format(<br>+ num=trace['traceno'],<br>+ start_pt=gcref(trace['startpt']),<br>+ start_bc=mref('BCIns *', trace['startpc']),<br>+ )<br>+<br>+ nins = trace['nins'] - REF_BIAS<br>+ dump += '---- TRACE IR\n'<br>+ nsnap = 0<br>+ snap = trace['snap'][nsnap]<br>+ snapref = snap['ref']<br>+ for irnum in range(1, nins):<br>+ irref = REF_BIAS + irnum<br>+ if 's' in flags and irref >= snapref and nsnap < trace['nsnap']:<br>+ dump += '.... '<br>+ if 'r' in flags:<br>+ dump += ' ' * 7<br>+ dump += dump_snap(trace, nsnap, snap)<br>+ nsnap += 1<br>+ snap = trace['snap'][nsnap]<br>+ snapref = snap['ref']<br>+ dump += '{:04d} '.format(irnum)<br>+ if 'r' in flags:<br>+ dump += '{:>7}'.format(dump_regsp(trace['ir'][irref], irnum))<br>+ dump += dump_irins(trace['ir'][irref], trace)<br>+ return dump<br>+<br>+<br>+def dump_tref(tref):<br>+ return '[{F}{C}] {tp} {ref:#x}'.format(<br>+ F='F' if tref & TREF_FRAME else ' ',<br>+ C='C' if tref & TREF_CONT else ' ',<br>+ tp=IRTYPES[tref_type(tref)],<br>+ ref=tref_ref(tref)<br>+ )<br>+<br>+<br>+def dump_jslots(coroutine):<br>+ lstate = L(None)<br>+ g = G(lstate or coroutine)<br>+ j = J(g)<br>+<br>+ dump = ''<br>+ maxslot = j['baseslot'] + j['maxslot']<br>+ first_base_slot = 1 + LJ_FR2<br>+ for n in reversed(range(first_base_slot, maxslot)):<br>+ tref = j['slot'][n]<br>+ ref = tref_ref(tref)<br>+ address = dbg.address(tref)<br>+ dump += '{addr} {nslot:04d} {base:1s} {tref}{const}\n'.format(<br>+ addr=address,<br>+ base='B' if address == j['base'] else ' ',<br>+ nslot=n,<br>+ tref=dump_tref(tref),<br>+ const=' ' + dump_irk(j['cur'], ref - REF_BIAS)<br>+ if ref != 0 and ref < REF_BIAS else ''<br>+ )<br>+ return dump<br>+<br>+<br># Extension commands. ############################################<br><br><br>@@ -1600,6 +2733,42 @@ error message occurs.<br>dbg.write('{}\n'.format(dump_gcobj(gcobj)))<br><br><br>+class LJDumpIR(dbg.LJBase):<br>+ '''<br>+lj-ir <IRIns *><br>+<br>+The command receives a pointer to <ir> (IRIns address) and dumps<br>+the IR type and some info related to it. The format is similar to<br>+the `jit.dump` tool but also provides information about IR mode and<br>+operands modes.<br>+<br>+For the list of IR names and modes (operand types), see:<br>+<a href="https://github.com/tarantool/tarantool/wiki/LuaJIT-SSA-IR">https://github.com/tarantool/tarantool/wiki/LuaJIT-SSA-IR</a>.<br>+ '''<br>+<br>+ def execute(self, arg):<br>+ dbg.write('{}'.format(dump_irins(dbg.cast('IRIns *', dbg.eval(arg)))))<br>+<br>+<br>+class LJDumpJSlots(dbg.LJBase):<br>+ '''<br>+lj-jslots [<lua_State *>]<br>+<br>+The command receives an optional lua_State address and dumps the<br>+slots of JIT stack map:<br>+<br>+<slot ptr> <slot number> [<FRAME|CONTINUATION>] <IR reference><br>+<br>+The lua_State pointer is optional to help in finding the VM's JIT state<br>+when there is no coroutine to be inspected in the debugged frame.<br>+ '''<br>+<br>+ def execute(self, arg):<br>+ dbg.write('{}'.format(<br>+ dump_jslots(dbg.cast('lua_State *', dbg.eval(arg)))<br>+ ))<br>+<br>+<br>class LJDumpProto(dbg.LJBase):<br>'''<br>lj-proto <GCproto *><br>@@ -1784,19 +2953,44 @@ error message occurs.<br>dbg.write('{}\n'.format(dump_tvalue(tv)))<br><br><br>+class LJDumpTrace(dbg.LJBase):<br>+ '''<br>+lj-trace [/FLAGS] <GCtrace *><br>+<br>+The command receives a pointer to <trace> (IRIns address) and dumps<br>+its number, IRs, and information about start location. The format is<br>+similar to the `jit.dump` tool but also provides information about<br>+IR mode and operands modes.<br>+<br>+Trace may be preceded with /FLAGS:<br>+* r: Dump registers associated with IR, if any.<br>+* s: Dump snapshots for the trace.<br>+ '''<br>+<br>+ def execute(self, arg):<br>+ arg, flags = dbg.extract_flags(arg, 'rs')<br>+ dbg.write('{}'.format(dump_trace(<br>+ dbg.cast('GCtrace *', dbg.eval(arg)),<br>+ flags<br>+ )))<br>+<br>+<br>def load(event=None):<br>dbg.initialize_extension({<br>- 'lj-arch': LJDumpArch,<br>- 'lj-bc': LJDumpBC,<br>- 'lj-func': LJDumpFunc,<br>- 'lj-gc': LJGC,<br>- 'lj-gco': LJDumpGCobj,<br>- 'lj-proto': LJDumpProto,<br>- 'lj-stack': LJDumpStack,<br>- 'lj-state': LJState,<br>- 'lj-str': LJDumpString,<br>- 'lj-tab': LJDumpTable,<br>- 'lj-tv': LJDumpTValue,<br>+ 'lj-arch': LJDumpArch,<br>+ 'lj-bc': LJDumpBC,<br>+ 'lj-func': LJDumpFunc,<br>+ 'lj-gc': LJGC,<br>+ 'lj-gco': LJDumpGCobj,<br>+ 'lj-ir': LJDumpIR,<br>+ 'lj-jslots': LJDumpJSlots,<br>+ 'lj-proto': LJDumpProto,<br>+ 'lj-stack': LJDumpStack,<br>+ 'lj-state': LJState,<br>+ 'lj-str': LJDumpString,<br>+ 'lj-tab': LJDumpTable,<br>+ 'lj-trace': LJDumpTrace,<br>+ 'lj-tv': LJDumpTValue,<br>})<br><br><br>diff --git a/test/tarantool-debugger-tests/debug-extension-tests.py b/test/tarantool-debugger-tests/debug-extension-tests.py<br>index 7e8ea5a2..76543daa 100644<br>--- a/test/tarantool-debugger-tests/debug-extension-tests.py<br>+++ b/test/tarantool-debugger-tests/debug-extension-tests.py<br>@@ -46,7 +46,9 @@ else:<br>RX_ADDR = r'0x[a-f0-9]+'<br>RX_HASH = RX_ADDR # The same pattern for hexademic values.<br>RX_BCN = r'00\d\d'<br>+RX_IRN = RX_BCN # The same as for the bytecodes.<br>RX_FRAME = r'\[(S|\s)(B|\s)(T|\s)(M|\s)\]'<br>+RX_IRREF = r'0x\d\d\d\d'<br><br><br>def persist(data):<br>@@ -101,6 +103,9 @@ IS_GC64 = execute_process([<br>LUAJIT_BINARY, '-e', "print(require('ffi').abi('gc64'))"<br>]).strip() == 'true'<br><br>+# Regexp for pointer type in IR.<br>+RX_P = 'p64' if IS_GC64 else 'p32'<br>+<br># If it is the guaranteed DUALNUM build (for example, on aarch64),<br># we use this regexp for the guaranteed 'integer' check and<br># 'number' for single-number build.<br>@@ -108,6 +113,18 @@ RX_INT = r'integer' if IS_DUALNUM else r'number'<br>RX_ISDUALNUM = r'True' if IS_DUALNUM else r'False'<br><br><br>+# Assume not cross-platform debugging.<br>+machine = os.uname().machine<br>+if machine == 'x86_64':<br>+ RX_GPR = r'r\w\w'<br>+ RX_FPR = r'xmm\d+'<br>+elif machine == 'arm64' or machine == 'aarch64':<br>+ RX_GPR = r'x\d+'<br>+ RX_FPR = r'd\d+'<br>+else:<br>+ raise Exception('Unknown archeticture in testing')<br>+<br>+<br>class TestCaseBase(unittest.TestCase):<br>@classmethod<br>def construct_cmds(cls):<br>@@ -193,6 +210,16 @@ def mref(arg, tp):<br>return '((' + tp + '*)(' + arg + ').ptr32)'<br><br><br>+def gcref(arg):<br>+ if SUPPORT_MACRO_EXPAND:<br>+ return 'gcref(' + arg + ')'<br>+ else:<br>+ if IS_GC64:<br>+ return '(' + arg + ').gcptr64'<br>+ else:<br>+ return '(' + arg + ').gcptr32'<br>+<br>+<br>class TestLoad(TestCaseBase):<br>extension_cmds = ''<br>location = 'lj_cf_print'<br>@@ -203,11 +230,14 @@ class TestLoad(TestCaseBase):<br>r'lj-func command initialized\n'<br>r'lj-gc command initialized\n'<br>r'lj-gco command initialized\n'<br>+ r'lj-ir command initialized\n'<br>+ r'lj-jslots command initialized\n'<br>r'lj-proto command initialized\n'<br>r'lj-stack command initialized\n'<br>r'lj-state command initialized\n'<br>r'lj-str command initialized\n'<br>r'lj-tab command initialized\n'<br>+ r'lj-trace command initialized\n'<br>r'lj-tv command initialized\n'<br>r'LuaJIT debug extension is successfully loaded'<br>)<br>@@ -473,6 +503,341 @@ class TestLJBC(TestCaseBase):<br>)<br><br><br>+# JIT engine.<br>+<br>+<br>+class TestLJTraceBase(TestCaseBase):<br>+ location = 'lj_cf_print'<br>+ extension_cmds = (<br>+ 'n\n' # Load L.<br>+ 'lj-trace ' + gcref('((GG_State *)L)->J->trace[1]')<br>+ )<br>+ lua_script = (<br>+ 'jit.opt.start("hotloop=1")\n'<br>+ 'for _ = 1, 4 do end\n'<br>+ 'print()\n'<br>+ )<br>+ pattern = (<br>+ r'Trace 1 start\n'<br>+ r'\t*proto: ' + RX_ADDR + r'\n' +<br>+ r'\t*BC: ' + RX_ADDR + r'\n' +<br>+ r'---- TRACE IR\n' +<br>+ RX_IRN + r'\s+ int SLOAD \[N \] lit: #[12] lit: C?I\n' +<br>+ RX_IRN + r'\s+ \+ int ADD \[C \] ref: ' + RX_IRN +<br>+ r' ref: integer 1\n' +<br>+ RX_IRN + r'\s+ > int LE \[N \] ref: ' + RX_IRN +<br>+ r' ref: integer 4\n' +<br>+ RX_IRN + r'\s+ > --- LOOP \[N \]\s*\n' +<br>+ RX_IRN + r'\s+ \+ int ADD \[C \] ref: ' + RX_IRN +<br>+ r' ref: integer 1\n' +<br>+ RX_IRN + r'\s+ > int LE \[N \] ref: ' + RX_IRN +<br>+ r' ref: integer 4\n' +<br>+ RX_IRN + r'\s+ int PHI \[S \] ref: ' + RX_IRN + r' ref: ' +<br>+ RX_IRN + r'\n' +<br>+ RX_IRN + r'\s+ NOP \[N \]\s*\n'<br>+ )<br>+<br>+<br>+# Check the IR enumeration correcness by test the lowest (LT) and<br>+# the highest (CARG) IRs. Also, checks CALL* occasionally.<br>+class TestLJTraceIRRange(TestCaseBase):<br>+ location = 'lj_cf_print'<br>+ extension_cmds = (<br>+ 'n\n' # Load L.<br>+ 'lj-trace ' + gcref('((GG_State *)L)->J->trace[1]')<br>+ )<br>+ lua_script = (<br>+ 'local ffi = require("ffi")\n'<br>+ 'ffi.cdef[[int getpid(int, int);]]\n' # Use argument for testing.<br>+ 'jit.opt.start("hotloop=1")\n'<br>+ 'for i = 1, 4 do\n'<br>+ ' if i < 100 then\n' # LT.<br>+ ' ffi.C.getpid(i, 1LL)\n' # CARG and CALLXS.<br>+ ' end\n'<br>+ 'end\n'<br>+ 'print()\n'<br>+ )<br>+ # IRs from variant part of the trace.<br>+ pattern = (<br>+ RX_IRN + r'\s+ > int LT \[N \] ref: ' +<br>+ RX_IRN + r' ref: integer 100\n' +<br>+ RX_IRN + r'\s+ nil CARG \[N \] ref: ' +<br>+ RX_IRN + r' ref: integer 1\n' +<br>+ RX_IRN + r'\s+ int CALLXS \[S \] \[' + RX_ADDR +<br>+ r'\]\(\{' + RX_IRN + r'\}, \{integer 1\}\)'<br>+ )<br>+<br>+<br>+# Test /rs flags.<br>+class TestLJTraceFlags(TestCaseBase):<br>+ location = 'lj_cf_print'<br>+ extension_cmds = (<br>+ 'n\n' # Load L.<br>+ 'lj-trace /rs ' + gcref('((GG_State *)L)->J->trace[1]')<br>+ )<br>+ lua_script = (<br>+ 'jit.opt.start("hotloop=1")\n'<br>+ 'local r = 0.1\n'<br>+ 'for i = 1, 4 do\n'<br>+ ' r = i + r\n'<br>+ 'end\n'<br>+ 'print()\n'<br>+ )<br>+ # IRs and snapshot from variant part of the trace.<br>+ pattern = (<br>+ RX_IRN + r'\s+' + RX_FPR + r'\s* \+ num ADD.*\n' +<br>+ RX_IRN + r'\s+' + RX_GPR + r'\s* \+ int ADD.*\n' +<br>+ r'\.\.\.\.\s* SNAP #\d \[ (---- )*' + RX_IRN + r' \]'<br>+ )<br>+<br>+<br>+class TestLJIRConst(TestCaseBase):<br>+ location = 'trace_stop'<br>+<br>+ # No narrowing of 42.<br>+ if IS_DUALNUM:<br>+ # KNUM occupies 2 slots.<br>+ _knum_irnum = '6'<br>+ _kgc_irnum = '8' if IS_GC64 else '7'<br>+ _kptr_irnum = '10' if IS_GC64 else '8'<br>+ else:<br>+ # KNUM occupies 2 slots.<br>+ _knum_irnum = '8'<br>+ _kgc_irnum = '10' if IS_GC64 else '9'<br>+ _kptr_irnum = '12' if IS_GC64 else '10'<br>+ extension_cmds = (<br>+ 'n\n' # Load J.<br>+ 'lj-ir &J->cur.ir[0x8000 - 0]\n'<br>+ 'lj-ir &J->cur.ir[0x8000 - 1]\n'<br>+ 'lj-ir &J->cur.ir[0x8000 - 2]\n'<br>+ 'lj-ir &J->cur.ir[0x8000 - 3]\n'<br>+ 'lj-ir &J->cur.ir[0x8000 - 4]\n'<br>+ # Skip non-DUALNUM narrowed value.<br>+ 'lj-ir &J->cur.ir[0x8000 - ' + _knum_irnum + ']\n'<br>+ 'lj-ir &J->cur.ir[0x8000 - ' + _kgc_irnum + ']\n'<br>+ 'lj-ir &J->cur.ir[0x8000 - ' + _kptr_irnum + ']\n'<br>+ )<br>+ lua_script = (<br>+ 'jit.opt.start("hotloop=1")\n'<br>+ 'local function trace(x)\n'<br>+ ' return x + 42, x + 0.5, x .. "1"\n'<br>+ 'end\n'<br>+ 'trace(1)\n'<br>+ 'trace(1)\n'<br>+ )<br>+ pattern = (<br>+ RX_P + r' BASE.*\n' +<br>+ r'\s* nil KPRI.*\n'<br>+ r'\s* fal KPRI.*\n'<br>+ r'\s* tru KPRI.*\n'<br>+ r'\s* int KINT.*cst: integer 42\s*\n'<br>+ r'\s* num KNUM.*cst: number 0.5\s*\n'<br>+ r'\s* str KGC.*cst: string "1".*\n' +<br>+ r'\s*' + RX_P + r' KPTR.*cst: \[' + RX_ADDR + r'\]'<br>+ )<br>+<br>+<br>+class TestLJIRFloadNeg(TestCaseBase):<br>+ location = 'lj_cf_print'<br>+ extension_cmds = (<br>+ 'n\n' # Load L.<br>+ 'lj-trace ' + gcref('((GG_State *)L)->J->trace[1]')<br>+ )<br>+ lua_script = (<br>+ 'jit.opt.start("hotloop=1")\n'<br>+ 'local function trace(a)\n'<br>+ ' local x = -a\n'<br>+ ' return x\n'<br>+ 'end\n'<br>+ 'trace(1.1)\n'<br>+ 'trace(1.1)\n'<br>+ 'print()\n'<br>+ )<br>+ pattern = (<br>+ r'num FLOAD .* ref: nil lit: offsetof\(GG, J\.ksimd\[LJ_KSIMD_NEG\]\)'<br>+ )<br>+<br>+<br>+class TestLJIRFloadAbs(TestCaseBase):<br>+ location = 'lj_cf_print'<br>+ extension_cmds = (<br>+ 'n\n' # Load L.<br>+ 'lj-trace ' + gcref('((GG_State *)L)->J->trace[1]')<br>+ )<br>+ lua_script = (<br>+ 'jit.opt.start("hotloop=1")\n'<br>+ 'local math_abs = math.abs\n'<br>+ 'local function trace(a)\n'<br>+ ' local x = math_abs(a)\n'<br>+ ' return x\n'<br>+ 'end\n'<br>+ 'trace(1)\n'<br>+ 'trace(1)\n'<br>+ 'print()\n'<br>+ )<br>+ pattern = (<br>+ r'num FLOAD .* ref: nil lit: offsetof\(GG, J\.ksimd\[LJ_KSIMD_ABS\]\)'<br>+ )<br>+<br>+<br>+# XXX: Implemented only for GC64 in LuaJIT until backporting the<br>+# corresponding commit.<br>+if IS_GC64:<br>+ class TestLJIRFloadGCRootBaseMT(TestCaseBase):<br>+ location = 'lj_cf_print'<br>+ extension_cmds = (<br>+ 'n\n' # Load L.<br>+ 'lj-trace ' + gcref('((GG_State *)L)->J->trace[1]')<br>+ )<br>+ lua_script = (<br>+ 'jit.opt.start("hotloop=1")\n'<br>+ 'local function trace(a)\n'<br>+ 'local x = a.sub(1, 2)\n'<br>+ ' return x\n'<br>+ 'end\n'<br>+ 'trace("12")\n'<br>+ 'trace("12")\n'<br>+ 'print()\n'<br>+ )<br>+ pattern = (<br>+ r'tab FLOAD .* ref: nil lit: '<br>+ r'offsetof\(GG, g\.gcroot\[GCROOT_BASEMT_STR\]\.gcptr64\)'<br>+ )<br>+<br>+ class TestLJIRFloadGCRootIO(TestCaseBase):<br>+ location = 'lj_cf_print'<br>+ extension_cmds = (<br>+ 'n\n' # Load L.<br>+ 'lj-trace ' + gcref('((GG_State *)L)->J->trace[1]')<br>+ )<br>+ lua_script = (<br>+ 'jit.opt.start("hotloop=1")\n'<br>+ 'local io_flush = io.flush\n'<br>+ 'local function trace()\n'<br>+ ' io_flush()\n'<br>+ 'end\n'<br>+ 'trace()\n'<br>+ 'trace()\n'<br>+ 'print()\n'<br>+ )<br>+ pattern = (<br>+ r'udt FLOAD .* ref: nil lit: '<br>+ r'offsetof\(GG, g\.gcroot\[GCROOT_IO_OUTPUT\]\.gcptr64\)'<br>+ )<br>+<br>+<br>+# Some IRs related to tables.<br>+class TestLJIRTable(TestCaseBase):<br>+ location = 'lj_cf_print'<br>+ extension_cmds = (<br>+ 'n\n' # Load L.<br>+ 'lj-trace ' + gcref('((GG_State *)L)->J->trace[1]')<br>+ )<br>+ lua_script = (<br>+ 'jit.opt.start("hotloop=1")\n'<br>+ 'local function trace(t)\n'<br>+ ' t.a = nil\n'<br>+ ' t.b = 1\n'<br>+ ' return t\n'<br>+ 'end\n'<br>+ 'trace({a = 1})\n'<br>+ 'trace({a = 1})\n'<br>+ 'print()\n'<br>+ )<br>+ pattern = (<br>+ r'(?s)int FLOAD .* tab\.hmask\n'<br>+ r'.*' + RX_P + r' FLOAD .* tab\.node\n'<br>+ r'.*' + RX_P + r' HREFK .* string "a" @ ' + RX_ADDR +<br>+ r' KSLOT: @\d\n'<br>+ r'.*' + RX_P + r' HREF .* string "b" @ ' + RX_ADDR + r'\s*\n'<br>+ r'.*' + RX_P + r' EQ .* \[g->nilnode\]'<br>+ )<br>+<br>+<br>+class TestLJIRUref(TestCaseBase):<br>+ location = 'lj_cf_print'<br>+ extension_cmds = (<br>+ 'n\n' # Load L.<br>+ 'lj-trace ' + gcref('((GG_State *)L)->J->trace[1]')<br>+ )<br>+ lua_script = (<br>+ 'jit.opt.start("hotloop=1")\n'<br>+ 'local uv = 0\n'<br>+ 'local function trace(a)\n'<br>+ ' uv = a\n'<br>+ ' return uv\n'<br>+ 'end\n'<br>+ 'trace(1)\n'<br>+ 'trace(1)\n'<br>+ 'print()\n'<br>+ )<br>+ pattern = r'UREFO .* lit: #0'<br>+<br>+<br>+# Check border values (that always avalable) of CALL IRs.<br>+class TestLJIRCall(TestCaseBase):<br>+ location = 'lj_cf_print'<br>+ extension_cmds = (<br>+ 'n\n' # Load L.<br>+ 'lj-trace ' + gcref('((GG_State *)L)->J->trace[1]')<br>+ )<br>+ lua_script = (<br>+ 'local ffi = require("ffi")\n'<br>+ 'jit.opt.start("hotloop=1")\n'<br>+ 'local function trace(a, b)\n'<br>+ ' return a < b, ffi.errno()\n'<br>+ 'end\n'<br>+ 'trace("abc", "abd")\n'<br>+ 'trace("abc", "abd")\n'<br>+ 'print(1)\n'<br>+ )<br>+ pattern = (<br>+ r'(?s)int CALLN .* '<br>+ r'lj_str_cmp\(\{' + RX_IRN + r'\}, \{' + RX_IRN + r'\}\)'<br>+ r'.*int CALLS .* lj_vm_errno\(\)'<br>+ )<br>+<br>+<br>+# Test ffi call with ctype stored in CARG.<br>+class TestLJIRCallXSCType(TestCaseBase):<br>+ location = 'lj_cf_print'<br>+ extension_cmds = (<br>+ 'n\n' # Load L.<br>+ 'lj-trace ' + gcref('((GG_State *)L)->J->trace[1]')<br>+ )<br>+ lua_script = (<br>+ 'local ffi = require("ffi")\n'<br>+ 'ffi.cdef[[int printf(const char *fmt, ...);]]\n'<br>+ 'jit.opt.start("hotloop=1")\n'<br>+ 'local function trace()\n'<br>+ ' local t = ffi.C.printf("")\n'<br>+ ' return t\n'<br>+ 'end\n'<br>+ 'trace()\n'<br>+ 'trace()\n'<br>+ 'print()\n'<br>+ )<br>+ pattern = r'int CALLXS .* [' + RX_ADDR + r'\]\(.*\) ctype: \d+'<br>+<br>+<br>+class TestLJJSlotsBase(TestCaseBase):<br>+ location = 'trace_stop'<br>+ extension_cmds = (<br>+ 'n\n' # Load J.<br>+ 'lj-jslots J->L\n'<br>+ )<br>+ lua_script = (<br>+ 'jit.opt.start("hotloop=1")\n'<br>+ 'for _ = 1, 4 do end\n'<br>+ )<br>+ pattern = (<br>+ r'(?s)(.*' +<br>+ RX_ADDR + ' ' + RX_IRN + r' (B|\s) \[(F|\s)(C|\s)\] \w\w\w ' +<br>+ RX_IRREF +<br>+ r'.*)+'<br>+ )<br>+<br>+<br>for test_cls in TestCaseBase.__subclasses__():<br>test_cls.test = lambda self: self.check()<br><br>--<br>2.54.0</div></div></div></div></div></div></blockquote></div></div></BODY></HTML>