summaryrefslogtreecommitdiff
path: root/misc
diff options
context:
space:
mode:
Diffstat (limited to 'misc')
-rw-r--r--misc/README6
-rwxr-xr-xmisc/expand_tabs.rb208
-rwxr-xr-xmisc/lldb_cruby.py718
-rw-r--r--misc/lldb_disasm.py239
-rw-r--r--misc/lldb_yjit.py47
-rw-r--r--misc/rb_optparse.bash21
-rw-r--r--misc/rb_optparse.zsh39
-rw-r--r--misc/ruby-style.el94
-rw-r--r--misc/test_lldb_cruby.rb40
-rwxr-xr-xmisc/test_yjit_asm.sh10
-rw-r--r--misc/yjit_asm_tests.c443
11 files changed, 1865 insertions, 0 deletions
diff --git a/misc/README b/misc/README
new file mode 100644
index 0000000000..1728b42700
--- /dev/null
+++ b/misc/README
@@ -0,0 +1,6 @@
+README this file
+rb_optparse.bash bash completion script
+rb_optparse.zsh zsh completion script
+ruby-style.el Ruby's C/C++ mode style for emacs
+lldb_cruby.py LLDB port of debug utility
+test_lldb_cruby.rb test file for LLDB port
diff --git a/misc/expand_tabs.rb b/misc/expand_tabs.rb
new file mode 100755
index 0000000000..a94eea5046
--- /dev/null
+++ b/misc/expand_tabs.rb
@@ -0,0 +1,208 @@
+#!/usr/bin/env ruby --disable-gems
+# Add the following line to your `.git/hooks/pre-commit`:
+#
+# $ ruby --disable-gems misc/expand_tabs.rb
+#
+
+require 'shellwords'
+require 'tmpdir'
+ENV['LC_ALL'] = 'C'
+
+class Git
+ def initialize(oldrev, newrev)
+ @oldrev = oldrev
+ @newrev = newrev
+ end
+
+ # ["foo/bar.c", "baz.h", ...]
+ def updated_paths
+ with_clean_env do
+ IO.popen(['git', 'diff', '--cached', '--name-only', @newrev], &:readlines).each(&:chomp!)
+ end
+ end
+
+ # [0, 1, 4, ...]
+ def updated_lines(file)
+ lines = []
+ revs_pattern = ("0"*40) + " "
+ with_clean_env { IO.popen(['git', 'blame', '-l', '--', file], &:readlines) }.each_with_index do |line, index|
+ if line.b.start_with?(revs_pattern)
+ lines << index
+ end
+ end
+ lines
+ end
+
+ def add(file)
+ git('add', file)
+ end
+
+ def toplevel
+ IO.popen(['git', 'rev-parse', '--show-toplevel'], &:read).chomp
+ end
+
+ private
+
+ def git(*args)
+ cmd = ['git', *args].shelljoin
+ unless with_clean_env { system(cmd) }
+ abort "Failed to run: #{cmd}"
+ end
+ end
+
+ def with_clean_env
+ git_dir = ENV.delete('GIT_DIR') # this overcomes '-C' or pwd
+ yield
+ ensure
+ ENV['GIT_DIR'] = git_dir if git_dir
+ end
+end
+
+DEFAULT_GEM_LIBS = %w[
+ abbrev
+ base64
+ benchmark
+ bundler
+ cmath
+ csv
+ debug
+ delegate
+ did_you_mean
+ drb
+ english
+ erb
+ fileutils
+ find
+ forwardable
+ getoptlong
+ ipaddr
+ irb
+ logger
+ mutex_m
+ net-http
+ net-protocol
+ observer
+ open3
+ open-uri
+ optparse
+ ostruct
+ pp
+ prettyprint
+ prime
+ pstore
+ rdoc
+ readline
+ reline
+ resolv
+ resolv-replace
+ rexml
+ rinda
+ rss
+ rubygems
+ scanf
+ securerandom
+ set
+ shellwords
+ singleton
+ tempfile
+ thwait
+ time
+ timeout
+ tmpdir
+ un
+ tsort
+ uri
+ weakref
+ yaml
+]
+
+DEFAULT_GEM_EXTS = %w[
+ bigdecimal
+ cgi
+ date
+ digest
+ etc
+ fcntl
+ fiddle
+ io-console
+ io-nonblock
+ io-wait
+ json
+ nkf
+ openssl
+ pathname
+ psych
+ racc
+ readline-ext
+ stringio
+ strscan
+ syslog
+ win32ole
+ zlib
+]
+
+EXPANDTAB_IGNORED_FILES = [
+ # default gems whose master is GitHub
+ %r{\Abin/(?!erb)\w+\z},
+ *DEFAULT_GEM_LIBS.flat_map { |lib|
+ [
+ %r{\Alib/#{lib}/},
+ %r{\Alib/#{lib}\.gemspec\z},
+ %r{\Alib/#{lib}\.rb\z},
+ %r{\Atest/#{lib}/},
+ ]
+ },
+ *DEFAULT_GEM_EXTS.flat_map { |ext|
+ [
+ %r{\Aext/#{ext}/},
+ %r{\Atest/#{ext}/},
+ ]
+ },
+
+ # vendoring (ccan)
+ %r{\Accan/},
+
+ # vendoring (onigmo)
+ %r{\Aenc/},
+ %r{\Ainclude/ruby/onigmo\.h\z},
+ %r{\Areg.+\.(c|h)\z},
+
+ # explicit or implicit `c-file-style: "linux"`
+ %r{\Aaddr2line\.c\z},
+ %r{\Amissing/},
+ %r{\Astrftime\.c\z},
+ %r{\Avsnprintf\.c\z},
+]
+
+git = Git.new('HEAD^', 'HEAD')
+
+Dir.chdir(git.toplevel) do
+ paths = git.updated_paths
+ paths.select! {|f|
+ (f.end_with?('.c') || f.end_with?('.h') || f == 'insns.def') && EXPANDTAB_IGNORED_FILES.all? { |re| !f.match(re) }
+ }
+ files = paths.select {|n| File.file?(n)}
+ exit if files.empty?
+
+ files.each do |f|
+ src = File.binread(f) rescue next
+
+ expanded = false
+ updated_lines = git.updated_lines(f)
+ unless updated_lines.empty?
+ src.gsub!(/^.*$/).with_index do |line, lineno|
+ if updated_lines.include?(lineno) && line.start_with?("\t") # last-committed line with hard tabs
+ expanded = true
+ line.sub(/\A\t+/) { |tabs| ' ' * (8 * tabs.length) }
+ else
+ line
+ end
+ end
+ end
+
+ if expanded
+ File.binwrite(f, src)
+ git.add(f)
+ end
+ end
+end
diff --git a/misc/lldb_cruby.py b/misc/lldb_cruby.py
new file mode 100755
index 0000000000..53e42b711c
--- /dev/null
+++ b/misc/lldb_cruby.py
@@ -0,0 +1,718 @@
+#!/usr/bin/env python
+#coding: utf-8
+#
+# Usage: run `command script import -r misc/lldb_cruby.py` on LLDB
+#
+# Test: misc/test_lldb_cruby.rb
+#
+
+from __future__ import print_function
+import lldb
+import os
+import shlex
+
+HEAP_PAGE_ALIGN_LOG = 14
+HEAP_PAGE_ALIGN_MASK = (~(~0 << HEAP_PAGE_ALIGN_LOG))
+HEAP_PAGE_ALIGN = (1 << HEAP_PAGE_ALIGN_LOG)
+HEAP_PAGE_SIZE = HEAP_PAGE_ALIGN
+
+class BackTrace:
+ VM_FRAME_MAGIC_METHOD = 0x11110001
+ VM_FRAME_MAGIC_BLOCK = 0x22220001
+ VM_FRAME_MAGIC_CLASS = 0x33330001
+ VM_FRAME_MAGIC_TOP = 0x44440001
+ VM_FRAME_MAGIC_CFUNC = 0x55550001
+ VM_FRAME_MAGIC_IFUNC = 0x66660001
+ VM_FRAME_MAGIC_EVAL = 0x77770001
+ VM_FRAME_MAGIC_RESCUE = 0x78880001
+ VM_FRAME_MAGIC_DUMMY = 0x79990001
+
+ VM_FRAME_MAGIC_MASK = 0x7fff0001
+
+ VM_FRAME_MAGIC_NAME = {
+ VM_FRAME_MAGIC_TOP: "TOP",
+ VM_FRAME_MAGIC_METHOD: "METHOD",
+ VM_FRAME_MAGIC_CLASS: "CLASS",
+ VM_FRAME_MAGIC_BLOCK: "BLOCK",
+ VM_FRAME_MAGIC_CFUNC: "CFUNC",
+ VM_FRAME_MAGIC_IFUNC: "IFUNC",
+ VM_FRAME_MAGIC_EVAL: "EVAL",
+ VM_FRAME_MAGIC_RESCUE: "RESCUE",
+ 0: "-----"
+ }
+
+ def __init__(self, debugger, command, result, internal_dict):
+ self.debugger = debugger
+ self.command = command
+ self.result = result
+
+ self.target = debugger.GetSelectedTarget()
+ self.process = self.target.GetProcess()
+ self.thread = self.process.GetSelectedThread()
+ self.frame = self.thread.GetSelectedFrame()
+ self.tRString = self.target.FindFirstType("struct RString").GetPointerType()
+ self.tRArray = self.target.FindFirstType("struct RArray").GetPointerType()
+
+ rb_cft_len = len("rb_control_frame_t")
+ method_type_length = sorted(map(len, self.VM_FRAME_MAGIC_NAME.values()), reverse=True)[0]
+ # cfp address, method type, function name
+ self.fmt = "%%-%ds %%-%ds %%s" % (rb_cft_len, method_type_length)
+
+ def vm_frame_magic(self, cfp):
+ ep = cfp.GetValueForExpressionPath("->ep")
+ frame_type = ep.GetChildAtIndex(0).GetValueAsUnsigned() & self.VM_FRAME_MAGIC_MASK
+ return self.VM_FRAME_MAGIC_NAME.get(frame_type, "(none)")
+
+ def rb_iseq_path_str(self, iseq):
+ tRBasic = self.target.FindFirstType("struct RBasic").GetPointerType()
+
+ pathobj = iseq.GetValueForExpressionPath("->body->location.pathobj")
+ pathobj = pathobj.Cast(tRBasic)
+ flags = pathobj.GetValueForExpressionPath("->flags").GetValueAsUnsigned()
+ flType = flags & RUBY_T_MASK
+
+ if flType == RUBY_T_ARRAY:
+ pathobj = pathobj.Cast(self.tRArray)
+
+ if flags & RUBY_FL_USER1:
+ len = ((flags & (RUBY_FL_USER3|RUBY_FL_USER4)) >> (RUBY_FL_USHIFT+3))
+ ptr = pathobj.GetValueForExpressionPath("->as.ary")
+ else:
+ len = pathobj.GetValueForExpressionPath("->as.heap.len").GetValueAsSigned()
+ ptr = pathobj.GetValueForExpressionPath("->as.heap.ptr")
+
+ pathobj = ptr.GetChildAtIndex(0)
+
+ pathobj = pathobj.Cast(self.tRString)
+ ptr, len = string2cstr(pathobj)
+ err = lldb.SBError()
+ path = self.target.process.ReadMemory(ptr, len, err)
+ if err.Success():
+ return path.decode("utf-8")
+ else:
+ return "unknown"
+
+ def dump_iseq_frame(self, cfp, iseq):
+ m = self.vm_frame_magic(cfp)
+
+ if iseq.GetValueAsUnsigned():
+ iseq_label = iseq.GetValueForExpressionPath("->body->location.label")
+ path = self.rb_iseq_path_str(iseq)
+ ptr, len = string2cstr(iseq_label.Cast(self.tRString))
+
+ err = lldb.SBError()
+ iseq_name = self.target.process.ReadMemory(ptr, len, err)
+ if err.Success():
+ iseq_name = iseq_name.decode("utf-8")
+ else:
+ iseq_name = "error!!"
+
+ else:
+ print("No iseq", file=self.result)
+
+ print(self.fmt % (("%0#12x" % cfp.GetAddress().GetLoadAddress(self.target)), m, "%s %s" % (path, iseq_name)), file=self.result)
+
+ def dump_cfunc_frame(self, cfp):
+ print(self.fmt % ("%0#12x" % (cfp.GetAddress().GetLoadAddress(self.target)), "CFUNC", ""), file=self.result)
+
+ def print_bt(self, ec):
+ tRbExecutionContext_t = self.target.FindFirstType("rb_execution_context_t")
+ ec = ec.Cast(tRbExecutionContext_t.GetPointerType())
+ vm_stack = ec.GetValueForExpressionPath("->vm_stack")
+ vm_stack_size = ec.GetValueForExpressionPath("->vm_stack_size")
+
+ last_cfp_frame = ec.GetValueForExpressionPath("->cfp")
+ cfp_type_p = last_cfp_frame.GetType()
+
+ stack_top = vm_stack.GetValueAsUnsigned() + (
+ vm_stack_size.GetValueAsUnsigned() * vm_stack.GetType().GetByteSize())
+
+ cfp_frame_size = cfp_type_p.GetPointeeType().GetByteSize()
+
+ start_cfp = stack_top
+ # Skip dummy frames
+ start_cfp -= cfp_frame_size
+ start_cfp -= cfp_frame_size
+
+ last_cfp = last_cfp_frame.GetValueAsUnsigned()
+
+ size = ((start_cfp - last_cfp) / cfp_frame_size) + 1
+
+ print(self.fmt % ("rb_control_frame_t", "TYPE", ""), file=self.result)
+
+ curr_addr = start_cfp
+
+ while curr_addr >= last_cfp:
+ cfp = self.target.CreateValueFromAddress("cfp", lldb.SBAddress(curr_addr, self.target), cfp_type_p.GetPointeeType())
+ ep = cfp.GetValueForExpressionPath("->ep")
+ iseq = cfp.GetValueForExpressionPath("->iseq")
+
+ frame_type = ep.GetChildAtIndex(0).GetValueAsUnsigned() & self.VM_FRAME_MAGIC_MASK
+
+ if iseq.GetValueAsUnsigned():
+ pc = cfp.GetValueForExpressionPath("->pc")
+ if pc.GetValueAsUnsigned():
+ self.dump_iseq_frame(cfp, iseq)
+ else:
+ if frame_type == self.VM_FRAME_MAGIC_CFUNC:
+ self.dump_cfunc_frame(cfp)
+
+ curr_addr -= cfp_frame_size
+
+def lldb_init(debugger):
+ target = debugger.GetSelectedTarget()
+ global SIZEOF_VALUE
+ SIZEOF_VALUE = target.FindFirstType("VALUE").GetByteSize()
+
+ value_types = []
+ g = globals()
+ for enum in target.FindFirstGlobalVariable('ruby_dummy_gdb_enums'):
+ enum = enum.GetType()
+ members = enum.GetEnumMembers()
+ for i in range(0, members.GetSize()):
+ member = members.GetTypeEnumMemberAtIndex(i)
+ name = member.GetName()
+ value = member.GetValueAsUnsigned()
+ g[name] = value
+
+ if name.startswith('RUBY_T_'):
+ value_types.append(name)
+ g['value_types'] = value_types
+
+def string2cstr(rstring):
+ """Returns the pointer to the C-string in the given String object"""
+ if rstring.TypeIsPointerType():
+ rstring = rstring.Dereference()
+ flags = rstring.GetValueForExpressionPath(".basic->flags").unsigned
+ if flags & RUBY_T_MASK != RUBY_T_STRING:
+ raise TypeError("not a string")
+ if flags & RUBY_FL_USER1:
+ cptr = int(rstring.GetValueForExpressionPath(".as.heap.ptr").value, 0)
+ clen = int(rstring.GetValueForExpressionPath(".as.heap.len").value, 0)
+ else:
+ cptr = int(rstring.GetValueForExpressionPath(".as.embed.ary").location, 0)
+ # clen = int(rstring.GetValueForExpressionPath(".as.embed.len").value, 0)
+ clen = (flags & RSTRING_EMBED_LEN_MASK) >> RSTRING_EMBED_LEN_SHIFT
+ return cptr, clen
+
+def output_string(debugger, result, rstring):
+ cptr, clen = string2cstr(rstring)
+ expr = "print *(const char (*)[%d])%0#x" % (clen, cptr)
+ append_command_output(debugger, expr, result)
+
+def fixnum_p(x):
+ return x & RUBY_FIXNUM_FLAG != 0
+
+def flonum_p(x):
+ return (x&RUBY_FLONUM_MASK) == RUBY_FLONUM_FLAG
+
+def static_sym_p(x):
+ return (x&~(~0<<RUBY_SPECIAL_SHIFT)) == RUBY_SYMBOL_FLAG
+
+def append_command_output(debugger, command, result):
+ output1 = result.GetOutput()
+ debugger.GetCommandInterpreter().HandleCommand(command, result)
+ output2 = result.GetOutput()
+ result.Clear()
+ result.write(output1)
+ result.write(output2)
+
+def lldb_rp(debugger, command, result, internal_dict):
+ if not ('RUBY_Qfalse' in globals()):
+ lldb_init(debugger)
+
+ target = debugger.GetSelectedTarget()
+ process = target.GetProcess()
+ thread = process.GetSelectedThread()
+ frame = thread.GetSelectedFrame()
+ if frame.IsValid():
+ val = frame.EvaluateExpression(command)
+ else:
+ val = target.EvaluateExpression(command)
+ error = val.GetError()
+ if error.Fail():
+ print(error, file=result)
+ return
+ lldb_inspect(debugger, target, result, val)
+
+def lldb_inspect(debugger, target, result, val):
+ num = val.GetValueAsSigned()
+ if num == RUBY_Qfalse:
+ print('false', file=result)
+ elif num == RUBY_Qtrue:
+ print('true', file=result)
+ elif num == RUBY_Qnil:
+ print('nil', file=result)
+ elif num == RUBY_Qundef:
+ print('undef', file=result)
+ elif fixnum_p(num):
+ print(num >> 1, file=result)
+ elif flonum_p(num):
+ append_command_output(debugger, "print rb_float_value(%0#x)" % val.GetValueAsUnsigned(), result)
+ elif static_sym_p(num):
+ if num < 128:
+ print("T_SYMBOL: %c" % num, file=result)
+ else:
+ print("T_SYMBOL: (%x)" % num, file=result)
+ append_command_output(debugger, "p rb_id2name(%0#x)" % (num >> 8), result)
+ elif num & RUBY_IMMEDIATE_MASK:
+ print('immediate(%x)' % num, file=result)
+ else:
+ tRBasic = target.FindFirstType("struct RBasic").GetPointerType()
+ tRValue = target.FindFirstType("struct RVALUE")
+
+ val = val.Cast(tRBasic)
+ flags = val.GetValueForExpressionPath("->flags").GetValueAsUnsigned()
+ flaginfo = ""
+
+ page = get_page(lldb, target, val)
+ page_type = target.FindFirstType("struct heap_page").GetPointerType()
+ page.Cast(page_type)
+
+ dump_bits(target, result, page, val.GetValueAsUnsigned())
+
+ if (flags & RUBY_FL_PROMOTED) == RUBY_FL_PROMOTED:
+ flaginfo += "[PROMOTED] "
+ if (flags & RUBY_FL_FREEZE) == RUBY_FL_FREEZE:
+ flaginfo += "[FROZEN] "
+ flType = flags & RUBY_T_MASK
+ if flType == RUBY_T_NONE:
+ print('T_NONE: %s%s' % (flaginfo, val.Dereference()), file=result)
+ elif flType == RUBY_T_NIL:
+ print('T_NIL: %s%s' % (flaginfo, val.Dereference()), file=result)
+ elif flType == RUBY_T_OBJECT:
+ result.write('T_OBJECT: %s' % flaginfo)
+ append_command_output(debugger, "print *(struct RObject*)%0#x" % val.GetValueAsUnsigned(), result)
+ elif flType == RUBY_T_CLASS or flType == RUBY_T_MODULE or flType == RUBY_T_ICLASS:
+ result.write('T_%s: %s' % ('CLASS' if flType == RUBY_T_CLASS else 'MODULE' if flType == RUBY_T_MODULE else 'ICLASS', flaginfo))
+ append_command_output(debugger, "print *(struct RClass*)%0#x" % val.GetValueAsUnsigned(), result)
+ elif flType == RUBY_T_STRING:
+ result.write('T_STRING: %s' % flaginfo)
+ encidx = ((flags & RUBY_ENCODING_MASK)>>RUBY_ENCODING_SHIFT)
+ encname = target.FindFirstType("enum ruby_preserved_encindex").GetEnumMembers().GetTypeEnumMemberAtIndex(encidx).GetName()
+ if encname is not None:
+ result.write('[%s] ' % encname[14:])
+ else:
+ result.write('[enc=%d] ' % encidx)
+ tRString = target.FindFirstType("struct RString").GetPointerType()
+ ptr, len = string2cstr(val.Cast(tRString))
+ if len == 0:
+ result.write("(empty)\n")
+ else:
+ append_command_output(debugger, "print *(const char (*)[%d])%0#x" % (len, ptr), result)
+ elif flType == RUBY_T_SYMBOL:
+ result.write('T_SYMBOL: %s' % flaginfo)
+ tRSymbol = target.FindFirstType("struct RSymbol").GetPointerType()
+ val = val.Cast(tRSymbol)
+ append_command_output(debugger, 'print (ID)%0#x ' % val.GetValueForExpressionPath("->id").GetValueAsUnsigned(), result)
+ tRString = target.FindFirstType("struct RString").GetPointerType()
+ output_string(debugger, result, val.GetValueForExpressionPath("->fstr").Cast(tRString))
+ elif flType == RUBY_T_ARRAY:
+ tRArray = target.FindFirstType("struct RArray").GetPointerType()
+ val = val.Cast(tRArray)
+ if flags & RUBY_FL_USER1:
+ len = ((flags & (RUBY_FL_USER3|RUBY_FL_USER4)) >> (RUBY_FL_USHIFT+3))
+ ptr = val.GetValueForExpressionPath("->as.ary")
+ else:
+ len = val.GetValueForExpressionPath("->as.heap.len").GetValueAsSigned()
+ ptr = val.GetValueForExpressionPath("->as.heap.ptr")
+ result.write("T_ARRAY: %slen=%d" % (flaginfo, len))
+ if flags & RUBY_FL_USER1:
+ result.write(" (embed)")
+ elif flags & RUBY_FL_USER2:
+ shared = val.GetValueForExpressionPath("->as.heap.aux.shared").GetValueAsUnsigned()
+ result.write(" (shared) shared=%016x" % shared)
+ else:
+ capa = val.GetValueForExpressionPath("->as.heap.aux.capa").GetValueAsSigned()
+ result.write(" (ownership) capa=%d" % capa)
+ if len == 0:
+ result.write(" {(empty)}\n")
+ else:
+ result.write("\n")
+ if ptr.GetValueAsSigned() == 0:
+ append_command_output(debugger, "expression -fx -- ((struct RArray*)%0#x)->as.ary" % val.GetValueAsUnsigned(), result)
+ else:
+ append_command_output(debugger, "expression -Z %d -fx -- (const VALUE*)%0#x" % (len, ptr.GetValueAsUnsigned()), result)
+ elif flType == RUBY_T_HASH:
+ result.write("T_HASH: %s" % flaginfo)
+ append_command_output(debugger, "p *(struct RHash *) %0#x" % val.GetValueAsUnsigned(), result)
+ elif flType == RUBY_T_BIGNUM:
+ tRBignum = target.FindFirstType("struct RBignum").GetPointerType()
+ val = val.Cast(tRBignum)
+ sign = '+' if (flags & RUBY_FL_USER1) != 0 else '-'
+ if flags & RUBY_FL_USER2:
+ len = ((flags & (RUBY_FL_USER3|RUBY_FL_USER4|RUBY_FL_USER5)) >> (RUBY_FL_USHIFT+3))
+ print("T_BIGNUM: sign=%s len=%d (embed)" % (sign, len), file=result)
+ append_command_output(debugger, "print ((struct RBignum *) %0#x)->as.ary" % val.GetValueAsUnsigned(), result)
+ else:
+ len = val.GetValueForExpressionPath("->as.heap.len").GetValueAsSigned()
+ print("T_BIGNUM: sign=%s len=%d" % (sign, len), file=result)
+ print(val.Dereference(), file=result)
+ append_command_output(debugger, "expression -Z %x -fx -- (const BDIGIT*)((struct RBignum*)%d)->as.heap.digits" % (len, val.GetValueAsUnsigned()), result)
+ # append_command_output(debugger, "x ((struct RBignum *) %0#x)->as.heap.digits / %d" % (val.GetValueAsUnsigned(), len), result)
+ elif flType == RUBY_T_FLOAT:
+ append_command_output(debugger, "print ((struct RFloat *)%d)->float_value" % val.GetValueAsUnsigned(), result)
+ elif flType == RUBY_T_RATIONAL:
+ tRRational = target.FindFirstType("struct RRational").GetPointerType()
+ val = val.Cast(tRRational)
+ lldb_inspect(debugger, target, result, val.GetValueForExpressionPath("->num"))
+ output = result.GetOutput()
+ result.Clear()
+ result.write("(Rational) " + output.rstrip() + " / ")
+ lldb_inspect(debugger, target, result, val.GetValueForExpressionPath("->den"))
+ elif flType == RUBY_T_COMPLEX:
+ tRComplex = target.FindFirstType("struct RComplex").GetPointerType()
+ val = val.Cast(tRComplex)
+ lldb_inspect(debugger, target, result, val.GetValueForExpressionPath("->real"))
+ real = result.GetOutput().rstrip()
+ result.Clear()
+ lldb_inspect(debugger, target, result, val.GetValueForExpressionPath("->imag"))
+ imag = result.GetOutput().rstrip()
+ result.Clear()
+ if not imag.startswith("-"):
+ imag = "+" + imag
+ print("(Complex) " + real + imag + "i", file=result)
+ elif flType == RUBY_T_REGEXP:
+ tRRegex = target.FindFirstType("struct RRegexp").GetPointerType()
+ val = val.Cast(tRRegex)
+ print("(Regex) ->src {", file=result)
+ lldb_inspect(debugger, target, result, val.GetValueForExpressionPath("->src"))
+ print("}", file=result)
+ elif flType == RUBY_T_DATA:
+ tRTypedData = target.FindFirstType("struct RTypedData").GetPointerType()
+ val = val.Cast(tRTypedData)
+ flag = val.GetValueForExpressionPath("->typed_flag")
+ if flag.GetValueAsUnsigned() == 1:
+ print("T_DATA: %s" % val.GetValueForExpressionPath("->type->wrap_struct_name"), file=result)
+ append_command_output(debugger, "p *(struct RTypedData *) %0#x" % val.GetValueAsUnsigned(), result)
+ else:
+ print("T_DATA:", file=result)
+ append_command_output(debugger, "p *(struct RData *) %0#x" % val.GetValueAsUnsigned(), result)
+ elif flType == RUBY_T_NODE:
+ tRTypedData = target.FindFirstType("struct RNode").GetPointerType()
+ nd_type = (flags & RUBY_NODE_TYPEMASK) >> RUBY_NODE_TYPESHIFT
+ append_command_output(debugger, "p (node_type) %d" % nd_type, result)
+ val = val.Cast(tRTypedData)
+ append_command_output(debugger, "p *(struct RNode *) %0#x" % val.GetValueAsUnsigned(), result)
+ elif flType == RUBY_T_MOVED:
+ tRTypedData = target.FindFirstType("struct RMoved").GetPointerType()
+ val = val.Cast(tRTypedData)
+ append_command_output(debugger, "p *(struct RMoved *) %0#x" % val.GetValueAsUnsigned(), result)
+ elif flType == RUBY_T_MATCH:
+ tRTypedData = target.FindFirstType("struct RMatch").GetPointerType()
+ val = val.Cast(tRTypedData)
+ append_command_output(debugger, "p *(struct RMatch *) %0#x" % val.GetValueAsUnsigned(), result)
+ elif flType == RUBY_T_IMEMO:
+ # I'm not sure how to get IMEMO_MASK out of lldb. It's not in globals()
+ imemo_type = (flags >> RUBY_FL_USHIFT) & 0x0F # IMEMO_MASK
+ print("T_IMEMO: ", file=result)
+ append_command_output(debugger, "p (enum imemo_type) %d" % imemo_type, result)
+ append_command_output(debugger, "p *(struct MEMO *) %0#x" % val.GetValueAsUnsigned(), result)
+ elif flType == RUBY_T_ZOMBIE:
+ tRZombie = target.FindFirstType("struct RZombie").GetPointerType()
+ val = val.Cast(tRZombie)
+ append_command_output(debugger, "p *(struct RZombie *) %0#x" % val.GetValueAsUnsigned(), result)
+ else:
+ print("Not-handled type %0#x" % flType, file=result)
+ print(val, file=result)
+
+def count_objects(debugger, command, ctx, result, internal_dict):
+ objspace = ctx.frame.EvaluateExpression("ruby_current_vm->objspace")
+ num_pages = objspace.GetValueForExpressionPath(".heap_pages.allocated_pages").unsigned
+
+ counts = {}
+ total = 0
+ for t in range(0x00, RUBY_T_MASK+1):
+ counts[t] = 0
+
+ for i in range(0, num_pages):
+ print("\rcounting... %d/%d" % (i, num_pages), end="")
+ page = objspace.GetValueForExpressionPath('.heap_pages.sorted[%d]' % i)
+ p = page.GetChildMemberWithName('start')
+ num_slots = page.GetChildMemberWithName('total_slots').unsigned
+ for j in range(0, num_slots):
+ obj = p.GetValueForExpressionPath('[%d]' % j)
+ flags = obj.GetValueForExpressionPath('.as.basic.flags').unsigned
+ obj_type = flags & RUBY_T_MASK
+ counts[obj_type] += 1
+ total += num_slots
+
+ print("\rTOTAL: %d, FREE: %d" % (total, counts[0x00]))
+ for sym in value_types:
+ print("%s: %d" % (sym, counts[globals()[sym]]))
+
+def stack_dump_raw(debugger, command, ctx, result, internal_dict):
+ ctx.frame.EvaluateExpression("rb_vmdebug_stack_dump_raw_current()")
+
+def check_bits(page, bitmap_name, bitmap_index, bitmap_bit, v):
+ bits = page.GetChildMemberWithName(bitmap_name)
+ plane = bits.GetChildAtIndex(bitmap_index).GetValueAsUnsigned()
+ if (plane & bitmap_bit) != 0:
+ return v
+ else:
+ return ' '
+
+def heap_page(debugger, command, ctx, result, internal_dict):
+ target = debugger.GetSelectedTarget()
+ process = target.GetProcess()
+ thread = process.GetSelectedThread()
+ frame = thread.GetSelectedFrame()
+
+ val = frame.EvaluateExpression(command)
+ page = get_page(lldb, target, val)
+ page_type = target.FindFirstType("struct heap_page").GetPointerType()
+ page.Cast(page_type)
+ append_command_output(debugger, "p (struct heap_page *) %0#x" % page.GetValueAsUnsigned(), result)
+ append_command_output(debugger, "p *(struct heap_page *) %0#x" % page.GetValueAsUnsigned(), result)
+
+def heap_page_body(debugger, command, ctx, result, internal_dict):
+ target = debugger.GetSelectedTarget()
+ process = target.GetProcess()
+ thread = process.GetSelectedThread()
+ frame = thread.GetSelectedFrame()
+
+ val = frame.EvaluateExpression(command)
+ page = get_page_body(lldb, target, val)
+ print("Page body address: ", page.GetAddress(), file=result)
+ print(page, file=result)
+
+def get_page_body(lldb, target, val):
+ tHeapPageBody = target.FindFirstType("struct heap_page_body")
+ addr = val.GetValueAsUnsigned()
+ page_addr = addr & ~(HEAP_PAGE_ALIGN_MASK)
+ address = lldb.SBAddress(page_addr, target)
+ return target.CreateValueFromAddress("page", address, tHeapPageBody)
+
+def get_page(lldb, target, val):
+ body = get_page_body(lldb, target, val)
+ return body.GetValueForExpressionPath("->header.page")
+
+def dump_node(debugger, command, ctx, result, internal_dict):
+ args = shlex.split(command)
+ if not args:
+ return
+ node = args[0]
+
+ dump = ctx.frame.EvaluateExpression("(struct RString*)rb_parser_dump_tree((NODE*)(%s), 0)" % node)
+ output_string(ctx, result, dump)
+
+def rb_backtrace(debugger, command, result, internal_dict):
+ bt = BackTrace(debugger, command, result, internal_dict)
+ frame = bt.frame
+
+ if command:
+ if frame.IsValid():
+ val = frame.EvaluateExpression(command)
+ else:
+ val = target.EvaluateExpression(command)
+
+ error = val.GetError()
+ if error.Fail():
+ print >> result, error
+ return
+ else:
+ print("Need an EC for now")
+
+ bt.print_bt(val)
+
+def dump_bits(target, result, page, object_address, end = "\n"):
+ tRValue = target.FindFirstType("struct RVALUE")
+ tUintPtr = target.FindFirstType("uintptr_t") # bits_t
+
+ num_in_page = (object_address & HEAP_PAGE_ALIGN_MASK) // tRValue.GetByteSize();
+ bits_bitlength = tUintPtr.GetByteSize() * 8
+ bitmap_index = num_in_page // bits_bitlength
+ bitmap_offset = num_in_page & (bits_bitlength - 1)
+ bitmap_bit = 1 << bitmap_offset
+
+ print("bits: [%s%s%s%s%s]" % (
+ check_bits(page, "uncollectible_bits", bitmap_index, bitmap_bit, "L"),
+ check_bits(page, "mark_bits", bitmap_index, bitmap_bit, "M"),
+ check_bits(page, "pinned_bits", bitmap_index, bitmap_bit, "P"),
+ check_bits(page, "marking_bits", bitmap_index, bitmap_bit, "R"),
+ check_bits(page, "wb_unprotected_bits", bitmap_index, bitmap_bit, "U"),
+ ), end=end, file=result)
+
+class HeapPageIter:
+ def __init__(self, page, target):
+ self.page = page
+ self.target = target
+ self.start = page.GetChildMemberWithName('start').GetValueAsUnsigned();
+ self.num_slots = page.GetChildMemberWithName('total_slots').unsigned
+ self.slot_size = page.GetChildMemberWithName('size_pool').GetChildMemberWithName('slot_size').unsigned
+ self.counter = 0
+ self.tRBasic = target.FindFirstType("struct RBasic")
+ self.tRValue = target.FindFirstType("struct RVALUE")
+
+ def is_valid(self):
+ heap_page_header_size = self.target.FindFirstType("struct heap_page_header").GetByteSize()
+ rvalue_size = self.slot_size
+ heap_page_obj_limit = int((HEAP_PAGE_SIZE - heap_page_header_size) / self.slot_size)
+
+ return (heap_page_obj_limit - 1) <= self.num_slots <= heap_page_obj_limit
+
+ def __iter__(self):
+ return self
+
+ def __next__(self):
+ if self.counter < self.num_slots:
+ obj_addr_i = self.start + (self.counter * self.slot_size)
+ obj_addr = lldb.SBAddress(obj_addr_i, self.target)
+ slot_info = (self.counter, obj_addr_i, self.target.CreateValueFromAddress("object", obj_addr, self.tRBasic))
+ self.counter += 1
+
+ return slot_info
+ else:
+ raise StopIteration
+
+
+def dump_page_internal(page, target, process, thread, frame, result, debugger, highlight=None):
+ if not ('RUBY_Qfalse' in globals()):
+ lldb_init(debugger)
+
+ ruby_type_map = ruby_types(debugger)
+
+ freelist = []
+ fl_start = page.GetChildMemberWithName('freelist').GetValueAsUnsigned()
+ tRVALUE = target.FindFirstType("struct RVALUE")
+
+ while fl_start > 0:
+ freelist.append(fl_start)
+ obj_addr = lldb.SBAddress(fl_start, target)
+ obj = target.CreateValueFromAddress("object", obj_addr, tRVALUE)
+ fl_start = obj.GetChildMemberWithName("as").GetChildMemberWithName("free").GetChildMemberWithName("next").GetValueAsUnsigned()
+
+
+ page_iter = HeapPageIter(page, target)
+ if page_iter.is_valid():
+ for (page_index, obj_addr, obj) in page_iter:
+ dump_bits(target, result, page, obj_addr, end= " ")
+ flags = obj.GetChildMemberWithName('flags').GetValueAsUnsigned()
+ flType = flags & RUBY_T_MASK
+
+ flidx = ' '
+ if flType == RUBY_T_NONE:
+ try:
+ flidx = "%3d" % freelist.index(obj_addr)
+ except ValueError:
+ flidx = ' '
+
+ result_str = "%s idx: [%3d] freelist_idx: {%s} Addr: %0#x (flags: %0#x)" % (rb_type(flags, ruby_type_map), page_index, flidx, obj_addr, flags)
+
+ if highlight == obj_addr:
+ result_str = ' '.join([result_str, "<<<<<"])
+
+ print(result_str, file=result)
+ else:
+ print("%s is not a valid heap page" % page, file=result)
+
+
+
+def dump_page(debugger, command, result, internal_dict):
+ target = debugger.GetSelectedTarget()
+ process = target.GetProcess()
+ thread = process.GetSelectedThread()
+ frame = thread.GetSelectedFrame()
+
+ tHeapPageP = target.FindFirstType("struct heap_page").GetPointerType()
+ page = frame.EvaluateExpression(command)
+ page = page.Cast(tHeapPageP)
+
+ dump_page_internal(page, target, process, thread, frame, result, debugger)
+
+
+def dump_page_rvalue(debugger, command, result, internal_dict):
+ target = debugger.GetSelectedTarget()
+ process = target.GetProcess()
+ thread = process.GetSelectedThread()
+ frame = thread.GetSelectedFrame()
+
+ val = frame.EvaluateExpression(command)
+ page = get_page(lldb, target, val)
+ page_type = target.FindFirstType("struct heap_page").GetPointerType()
+ page.Cast(page_type)
+
+ dump_page_internal(page, target, process, thread, frame, result, debugger, highlight=val.GetValueAsUnsigned())
+
+
+
+def rb_type(flags, ruby_types):
+ flType = flags & RUBY_T_MASK
+ return "%-10s" % (ruby_types.get(flType, ("%0#x" % flType)))
+
+def ruby_types(debugger):
+ target = debugger.GetSelectedTarget()
+
+ types = {}
+ for enum in target.FindFirstGlobalVariable('ruby_dummy_gdb_enums'):
+ enum = enum.GetType()
+ members = enum.GetEnumMembers()
+ for i in range(0, members.GetSize()):
+ member = members.GetTypeEnumMemberAtIndex(i)
+ name = member.GetName()
+ value = member.GetValueAsUnsigned()
+
+ if name.startswith('RUBY_T_'):
+ types[value] = name.replace('RUBY_', '')
+
+ return types
+
+def rb_ary_entry(target, ary, idx, result):
+ tRArray = target.FindFirstType("struct RArray").GetPointerType()
+ ary = ary.Cast(tRArray)
+ flags = ary.GetValueForExpressionPath("->flags").GetValueAsUnsigned()
+
+ if flags & RUBY_FL_USER1:
+ ptr = ary.GetValueForExpressionPath("->as.ary")
+ else:
+ ptr = ary.GetValueForExpressionPath("->as.heap.ptr")
+
+ ptr_addr = ptr.GetValueAsUnsigned() + (idx * ptr.GetType().GetByteSize())
+ return target.CreateValueFromAddress("ary_entry[%d]" % idx, lldb.SBAddress(ptr_addr, target), ptr.GetType().GetPointeeType())
+
+def rb_id_to_serial(id_val):
+ if id_val > tLAST_OP_ID:
+ return id_val >> RUBY_ID_SCOPE_SHIFT
+ else:
+ return id_val
+
+def rb_id2str(debugger, command, result, internal_dict):
+ if not ('RUBY_Qfalse' in globals()):
+ lldb_init(debugger)
+
+ target = debugger.GetSelectedTarget()
+ process = target.GetProcess()
+ thread = process.GetSelectedThread()
+ frame = thread.GetSelectedFrame()
+ global_symbols = target.FindFirstGlobalVariable("ruby_global_symbols")
+
+ id_val = frame.EvaluateExpression(command).GetValueAsUnsigned()
+ num = rb_id_to_serial(id_val)
+
+ last_id = global_symbols.GetChildMemberWithName("last_id").GetValueAsUnsigned()
+ ID_ENTRY_SIZE = 2
+ ID_ENTRY_UNIT = int(target.FindFirstGlobalVariable("ID_ENTRY_UNIT").GetValue())
+
+ ids = global_symbols.GetChildMemberWithName("ids")
+
+ if (num <= last_id):
+ idx = num // ID_ENTRY_UNIT
+ ary = rb_ary_entry(target, ids, idx, result)
+ pos = (num % ID_ENTRY_UNIT) * ID_ENTRY_SIZE
+ id_str = rb_ary_entry(target, ary, pos, result)
+ lldb_inspect(debugger, target, result, id_str)
+
+def __lldb_init_module(debugger, internal_dict):
+ debugger.HandleCommand("command script add -f lldb_cruby.lldb_rp rp")
+ debugger.HandleCommand("command script add -f lldb_cruby.count_objects rb_count_objects")
+ debugger.HandleCommand("command script add -f lldb_cruby.stack_dump_raw SDR")
+ debugger.HandleCommand("command script add -f lldb_cruby.dump_node dump_node")
+ debugger.HandleCommand("command script add -f lldb_cruby.heap_page heap_page")
+ debugger.HandleCommand("command script add -f lldb_cruby.heap_page_body heap_page_body")
+ debugger.HandleCommand("command script add -f lldb_cruby.rb_backtrace rbbt")
+ debugger.HandleCommand("command script add -f lldb_cruby.dump_page dump_page")
+ debugger.HandleCommand("command script add -f lldb_cruby.dump_page_rvalue dump_page_rvalue")
+ debugger.HandleCommand("command script add -f lldb_cruby.rb_id2str rb_id2str")
+
+ lldb_init(debugger)
+ print("lldb scripts for ruby has been installed.")
diff --git a/misc/lldb_disasm.py b/misc/lldb_disasm.py
new file mode 100644
index 0000000000..c02af52bc4
--- /dev/null
+++ b/misc/lldb_disasm.py
@@ -0,0 +1,239 @@
+#!/usr/bin/env python
+#coding: utf-8
+#
+# Usage: run `command script import -r misc/lldb_disasm.py` on LLDB
+#
+#
+# (lldb) p iseq
+# (rb_iseq_t *) $147 = 0x0000000101068400
+# (lldb) rbdisasm iseq
+# 0000 putspecialobject( 3 )
+# 0002 putnil
+# 0003 defineclass( ID: 0x560b, (rb_iseq_t *)0x1010681d0, 2 )
+# 0007 pop
+# 0008 putspecialobject( 3 )
+# 0010 putnil
+# 0011 defineclass( ID: 0x56eb, (rb_iseq_t *)0x101063b58, 2 )
+# 0015 leave
+
+
+import lldb
+import os
+import shlex
+
+class IseqDisassembler:
+ TS_VARIABLE = b'.'[0]
+ TS_CALLDATA = b'C'[0]
+ TS_CDHASH = b'H'[0]
+ TS_IC = b'K'[0]
+ TS_IVC = b'A'[0]
+ TS_ID = b'I'[0]
+ TS_ISE = b'T'[0]
+ TS_ISEQ = b'S'[0]
+ TS_OFFSET = b'O'[0]
+ TS_VALUE = b'V'[0]
+ TS_LINDEX = b'L'[0]
+ TS_FUNCPTR = b'F'[0]
+ TS_NUM = b'N'[0]
+ TS_BUILTIN = b'R'[0]
+
+ ISEQ_OPT_DISPATCH = {
+ TS_BUILTIN: "(rb_builtin_function *)%0#x",
+ TS_NUM: "%d",
+ TS_FUNCPTR: "(rb_insn_func_t) %0#x",
+ TS_LINDEX: "%d",
+ TS_VALUE: "(VALUE)%0#x",
+ TS_OFFSET: "%d",
+ TS_ISEQ: "(rb_iseq_t *)%0#x",
+ TS_ISE: "(iseq_inline_storage_entry *)%0#x",
+ TS_ID: "ID: %0#x",
+ TS_IVC: "(struct iseq_inline_iv_cache_entry *)%0#x",
+ TS_IC: "(struct iseq_inline_cache_entry *)%0#x",
+ TS_CDHASH: "CDHASH (VALUE)%0#x",
+ TS_CALLDATA: "(struct rb_call_data *)%0#x",
+ TS_VARIABLE: "VARIABLE %0#x",
+ }
+
+ def __init__(self, debugger, command, result, internal_dict):
+ self.debugger = debugger
+ self.command = command
+ self.result = result
+ self.internal_dict = internal_dict
+
+ self.target = debugger.GetSelectedTarget()
+ self.process = self.target.GetProcess()
+ self.thread = self.process.GetSelectedThread()
+ self.frame = self.thread.GetSelectedFrame()
+ self.addr2insn = self.build_addr2insn(self.target)
+ self.tChar = self.target.FindFirstType("char")
+
+ def disasm(self, val):
+ tRbISeq = self.target.FindFirstType("struct rb_iseq_struct").GetPointerType()
+ val = val.Cast(tRbISeq)
+ iseq_size = val.GetValueForExpressionPath("->body->iseq_size").GetValueAsUnsigned()
+ iseqs = val.GetValueForExpressionPath("->body->iseq_encoded")
+ idx = 0
+ print("PC IDX insn_name(operands) ", file=self.result)
+ while idx < iseq_size:
+ m = self.iseq_extract_values(self.debugger, self.target, self.process, self.result, iseqs, idx)
+ if m < 1:
+ print("Error decoding", file=self.result)
+ return
+ else:
+ idx += m
+
+ def build_addr2insn(self, target):
+ tIntPtr = target.FindFirstType("intptr_t")
+ size = target.EvaluateExpression('ruby_vminsn_type::VM_INSTRUCTION_SIZE').unsigned
+ sizeOfIntPtr = tIntPtr.GetByteSize()
+ addr_of_table = target.FindSymbols("vm_exec_core.insns_address_table")[0].GetSymbol().GetStartAddress().GetLoadAddress(target)
+
+ my_dict = {}
+
+ for insn in range(size):
+ addr_in_table = addr_of_table + (insn * sizeOfIntPtr)
+ addr = lldb.SBAddress(addr_in_table, target)
+ machine_insn = target.CreateValueFromAddress("insn", addr, tIntPtr).GetValueAsUnsigned()
+ my_dict[machine_insn] = insn
+
+ return my_dict
+
+ def rb_vm_insn_addr2insn2(self, target, result, wanted_addr):
+ return self.addr2insn.get(wanted_addr)
+
+ def iseq_extract_values(self, debugger, target, process, result, iseqs, n):
+ tValueP = target.FindFirstType("VALUE")
+ sizeofValueP = tValueP.GetByteSize()
+ pc = iseqs.unsigned + (n * sizeofValueP)
+ insn = target.CreateValueFromAddress("i", lldb.SBAddress(pc, target), tValueP)
+ addr = insn.GetValueAsUnsigned()
+ orig_insn = self.rb_vm_insn_addr2insn2(target, result, addr)
+
+ name = self.insn_name(target, process, result, orig_insn)
+ length = self.insn_len(target, orig_insn)
+ op_str = self.insn_op_types(target, process, result, orig_insn)
+ op_types = bytes(op_str, 'utf-8')
+
+ if length != (len(op_types) + 1):
+ print("error decoding iseqs", file=result)
+ return -1
+
+ print("%0#14x %04d %s" % (pc, n, name), file=result, end="")
+
+ if length == 1:
+ print("", file=result)
+ return length
+
+ print("(", end="", file=result)
+ for idx, op_type in enumerate(op_types):
+ if idx == 0:
+ print(" ", end="", file=result)
+ else:
+ print(", ", end="", file=result)
+
+ opAddr = lldb.SBAddress(iseqs.unsigned + ((n + idx + 1) * sizeofValueP), target)
+ opValue = target.CreateValueFromAddress("op", opAddr, tValueP)
+ op = opValue.GetValueAsUnsigned()
+ print(self.ISEQ_OPT_DISPATCH.get(op_type) % op, end="", file=result)
+
+ print(" )", file=result)
+ return length
+
+ def insn_len(self, target, offset):
+ size_of_char = self.tChar.GetByteSize()
+
+ symbol = target.FindSymbols("insn_len.t")[0].GetSymbol()
+ section = symbol.GetStartAddress().GetSection()
+ addr_of_table = symbol.GetStartAddress().GetOffset()
+
+ error = lldb.SBError()
+ length = section.GetSectionData().GetUnsignedInt8(error, addr_of_table + (offset * size_of_char))
+
+ if error.Success():
+ return length
+ else:
+ print("error getting length: ", error)
+
+ def insn_op_types(self, target, process, result, insn):
+ tUShort = target.FindFirstType("unsigned short")
+
+ size_of_short = tUShort.GetByteSize()
+ size_of_char = self.tChar.GetByteSize()
+
+ symbol = target.FindSymbols("insn_op_types.y")[0].GetSymbol()
+ section = symbol.GetStartAddress().GetSection()
+ addr_of_table = symbol.GetStartAddress().GetOffset()
+
+ addr_in_table = addr_of_table + (insn * size_of_short)
+
+ error = lldb.SBError()
+ offset = section.GetSectionData().GetUnsignedInt16(error, addr_in_table)
+
+ if not error.Success():
+ print("error getting op type offset: ", error)
+
+ symbol = target.FindSymbols("insn_op_types.x")[0].GetSymbol()
+ section = symbol.GetStartAddress().GetSection()
+ addr_of_table = symbol.GetStartAddress().GetOffset()
+ addr_in_name_table = addr_of_table + (offset * size_of_char)
+
+ error = lldb.SBError()
+ types = section.GetSectionData().GetString(error, addr_in_name_table)
+ if error.Success():
+ return types
+ else:
+ print("error getting op types: ", error)
+
+ def insn_name_table_offset(self, target, offset):
+ tUShort = target.FindFirstType("unsigned short")
+ size_of_short = tUShort.GetByteSize()
+
+ symbol = target.FindSymbols("insn_name.y")[0].GetSymbol()
+ section = symbol.GetStartAddress().GetSection()
+ table_offset = symbol.GetStartAddress().GetOffset()
+
+ table_offset = table_offset + (offset * size_of_short)
+
+ error = lldb.SBError()
+ offset = section.GetSectionData().GetUnsignedInt16(error, table_offset)
+
+ if error.Success():
+ return offset
+ else:
+ print("error getting insn name table offset: ", error)
+
+ def insn_name(self, target, process, result, offset):
+ symbol = target.FindSymbols("insn_name.x")[0].GetSymbol()
+ section = symbol.GetStartAddress().GetSection()
+ addr_of_table = symbol.GetStartAddress().GetOffset()
+
+ name_table_offset = self.insn_name_table_offset(target, offset)
+ addr_in_name_table = addr_of_table + name_table_offset
+
+ error = lldb.SBError()
+ name = section.GetSectionData().GetString(error, addr_in_name_table)
+
+ if error.Success():
+ return name
+ else:
+ print('error getting insn name', error)
+
+def disasm(debugger, command, result, internal_dict):
+ disassembler = IseqDisassembler(debugger, command, result, internal_dict)
+ frame = disassembler.frame
+
+ if frame.IsValid():
+ val = frame.EvaluateExpression(command)
+ else:
+ val = target.EvaluateExpression(command)
+ error = val.GetError()
+ if error.Fail():
+ print >> result, error
+ return
+
+ disassembler.disasm(val);
+
+
+def __lldb_init_module(debugger, internal_dict):
+ debugger.HandleCommand("command script add -f lldb_disasm.disasm rbdisasm")
+ print("lldb Ruby disasm installed.")
diff --git a/misc/lldb_yjit.py b/misc/lldb_yjit.py
new file mode 100644
index 0000000000..cc37b990ea
--- /dev/null
+++ b/misc/lldb_yjit.py
@@ -0,0 +1,47 @@
+#!/usr/bin/env python
+#coding: utf-8
+#
+# Usage: run `command script import -r misc/lldb_yjit.py` on LLDB
+#
+
+from __future__ import print_function
+import lldb
+import os
+import shlex
+
+def list_comments(debugger, command, result, internal_dict):
+ target = debugger.GetSelectedTarget()
+ process = target.GetProcess()
+ thread = process.GetSelectedThread()
+ frame = thread.GetSelectedFrame()
+
+ # Get the different types we need
+ rb_darray_meta_t = target.FindFirstType("rb_darray_meta_t")
+ codeblock_t = target.FindFirstType("codeblock_t")
+ yjit_comment = target.FindFirstType("yjit_comment")
+
+ # Get the global variables we need
+ comments = target.FindFirstGlobalVariable("yjit_code_comments")
+ cb = target.FindFirstGlobalVariable("cb").Cast(codeblock_t.GetPointerType())
+
+ # Get the address of the memory block we're using
+ mem_addr = cb.GetChildMemberWithName("mem_block").GetValueAsUnsigned()
+
+ # Find the size of the darray comment list
+ meta = comments.Cast(rb_darray_meta_t.GetPointerType())
+ size = meta.GetChildMemberWithName("size").GetValueAsUnsigned()
+
+ # Get the address of the block following the metadata header
+ t_offset = comments.GetValueAsUnsigned() + rb_darray_meta_t.GetByteSize()
+
+ # Loop through each comment and print
+ for t in range(0, size):
+ addr = lldb.SBAddress(t_offset + (t * yjit_comment.GetByteSize()), target)
+ comment = target.CreateValueFromAddress("yjit_comment", addr, yjit_comment)
+ string = comment.GetChildMemberWithName("comment")
+ comment_offset = mem_addr + comment.GetChildMemberWithName("offset").GetValueAsUnsigned()
+ print("%0#x %s" % (comment_offset, string.GetSummary()), file = result)
+
+
+def __lldb_init_module(debugger, internal_dict):
+ debugger.HandleCommand("command script add -f lldb_yjit.list_comments lc")
diff --git a/misc/rb_optparse.bash b/misc/rb_optparse.bash
new file mode 100644
index 0000000000..f77d937c87
--- /dev/null
+++ b/misc/rb_optparse.bash
@@ -0,0 +1,21 @@
+# -*- bash -*-
+#
+# Completion for bash:
+#
+# (1) install this file,
+#
+# (2) load the script, and
+# . ~/.profile.d/rb_optparse.bash
+#
+# (3) define completions in your .bashrc,
+# rb_optparse command_using_optparse_1
+# rb_optparse command_using_optparse_2
+
+_rb_optparse() {
+ COMPREPLY=($("${COMP_WORDS[0]}" "--*-completion-bash=${COMP_WORDS[COMP_CWORD]}"))
+ return 0
+}
+
+rb_optparse () {
+ [ $# = 0 ] || complete -o default -F _rb_optparse "$@"
+}
diff --git a/misc/rb_optparse.zsh b/misc/rb_optparse.zsh
new file mode 100644
index 0000000000..258d4f856c
--- /dev/null
+++ b/misc/rb_optparse.zsh
@@ -0,0 +1,39 @@
+# -*- zsh -*-
+#
+# Completion for zsh:
+# (based on <http://d.hatena.ne.jp/rubikitch/20071002/zshcomplete>)
+#
+# (1) install this file.
+# mkdir -p ~/.zsh.d
+# cp rb_optparse.zsh ~/.zsh.d/rb_optparse.zsh
+#
+# (2) load the script, and add a directory to fpath before compinit.
+# echo '. ~/.zsh.d/rb_optparse.zsh' >> "${ZDOTDIR:-~}/.zshrc"
+# echo 'fpath=(~/.zsh.d/Completion $fpath)' >> "${ZDOTDIR:-~}/.zshrc"
+# echo 'autoload -U compinit; compinit' >> "${ZDOTDIR:-~}/.zshrc"
+#
+# (3) restart zsh.
+#
+# (4) generate completion files once.
+# generate-complete-function/ruby/optparse COMMAND1
+# generate-complete-function/ruby/optparse COMMAND2
+#
+
+generate-complete-function/ruby/optparse ()
+{
+ local cmpl="_${1:t}"
+ mkdir -p "${ZSH_COMPLETION_DIR-$HOME/.zsh.d/Completion}"
+ $1 "--*-completion-zsh=${1:t}" >! "${ZSH_COMPLETION_DIR-$HOME/.zsh.d/Completion}/$cmpl"
+ if [[ $(type -w "$cmpl") == "${cmpl}: function" ]]; then
+ unfunction "$cmpl"
+ autoload -U "$cmpl"
+ else
+ compinit "$cmpl"
+ fi
+}
+
+compdef _command generate-complete-function/ruby/optparse
+
+for cmd in "$@"; do
+ generate-complete-function/ruby/optparse "$cmd"
+done
diff --git a/misc/ruby-style.el b/misc/ruby-style.el
new file mode 100644
index 0000000000..13aad77b3d
--- /dev/null
+++ b/misc/ruby-style.el
@@ -0,0 +1,94 @@
+;;; -*- emacs-lisp -*-
+;;;
+;;; ruby-style.el -
+;;;
+;;; C/C++ mode style for Ruby.
+;;;
+;;; $Author$
+;;; created at: Thu Apr 26 13:54:01 JST 2007
+;;;
+;;; Put this file under a directory contained in ``load-path'', and
+;;; then load it.
+;;; To switch to the "ruby" style automatically if it looks like a
+;;; source file of ruby, add ruby-style-c-mode to c-mode-hook:
+;;;
+;;; (require 'ruby-style)
+;;; (add-hook 'c-mode-hook 'ruby-style-c-mode)
+;;; (add-hook 'c++-mode-hook 'ruby-style-c-mode)
+;;;
+;;; Customize the c-default-style variable to set the default style
+;;; for each CC major mode.
+
+(defconst ruby-style-revision "$Revision$"
+ "Ruby style revision string.")
+
+(defconst ruby-style-version
+ (and
+ (string-match "[0-9.]+" ruby-style-revision)
+ (substring ruby-style-revision (match-beginning 0) (match-end 0)))
+ "Ruby style version number.")
+
+(defun ruby-style-case-indent (x)
+ (save-excursion
+ (back-to-indentation)
+ (unless (progn (backward-up-list) (back-to-indentation)
+ (> (point) (cdr x)))
+ (goto-char (cdr x))
+ (if (looking-at "\\<case\\|default\\>") '*))))
+
+(defun ruby-style-label-indent (x)
+ (save-excursion
+ (back-to-indentation)
+ (unless (progn (backward-up-list) (back-to-indentation)
+ (>= (point) (cdr x)))
+ (goto-char (cdr x))
+ (condition-case ()
+ (progn
+ (backward-up-list)
+ (backward-sexp 2)
+ (if (looking-at "\\<switch\\>") '/))
+ (error)))))
+
+(require 'cc-styles)
+(c-add-style
+ "ruby"
+ '("bsd"
+ (c-basic-offset . 4)
+ (tab-width . 8)
+ (indent-tabs-mode . nil)
+ (setq show-trailing-whitespace t)
+ (c-offsets-alist
+ (case-label . *)
+ (label . (ruby-style-label-indent *))
+ (statement-case-intro . *)
+ (statement-case-open . *)
+ (statement-block-intro . (ruby-style-case-indent +))
+ (access-label /)
+ )))
+
+;;;###autoload
+(defun ruby-style-c-mode ()
+ (interactive)
+ (if (or (let ((name (buffer-file-name))) (and name (string-match "/ruby\\>" name)))
+ (save-excursion
+ (goto-char (point-min))
+ (let ((head (progn (forward-line 100) (point)))
+ (case-fold-search nil))
+ (goto-char (point-min))
+ (re-search-forward "Copyright (C) .* Yukihiro Matsumoto" head t)))
+ (condition-case ()
+ (with-temp-buffer
+ (when (= 0 (call-process "git" nil t nil "remote" "get-url" "origin"))
+ (goto-char (point-min))
+ (looking-at ".*/ruby\\(\\.git\\)?$")))
+ (error))
+ (condition-case ()
+ (with-temp-buffer
+ (when (= 0 (call-process "svn" nil t nil "info" "--xml"))
+ (goto-char (point-min))
+ (search-forward-regexp "<root>.*/ruby</root>" nil)))
+ (error))
+ nil)
+ (c-set-style "ruby")))
+
+(provide 'ruby-style)
diff --git a/misc/test_lldb_cruby.rb b/misc/test_lldb_cruby.rb
new file mode 100644
index 0000000000..bd58619ac2
--- /dev/null
+++ b/misc/test_lldb_cruby.rb
@@ -0,0 +1,40 @@
+#!/usr/bin/env ruby
+require 'open3'
+require 'tempfile'
+require 'test/unit'
+
+class TestLLDBInit < Test::Unit::TestCase
+ def assert_rp(expr, pattern, message=nil)
+ Tempfile.create('lldb') do |tf|
+ tf.puts <<eom
+target create ./miniruby
+command script import -r misc/lldb_cruby.py
+b rb_inspect
+run -e'p #{expr}'
+rp obj
+eom
+ tf.flush
+ o, s = Open3.capture2('lldb', '-b', '-s', tf.path)
+ assert_true s.success?, message
+ assert_match /^\(lldb\) rp obj\n#{pattern}/, o, message
+ end
+ end
+
+ def test_rp_object
+ assert_rp 'Object.new', 'T_OBJECT'
+ end
+
+ def test_rp_regex
+ assert_rp '/foo/', '[(]Regex'
+ end
+
+ def test_rp_symbol
+ assert_rp ':abcde', /T_SYMBOL: \(\h+\)/
+ end
+
+ def test_rp_string
+ assert_rp '"abc"', /\(char \[\d+\]\) ary = "abc"/
+ assert_rp "\"\u3042\"", /\(char \[\d+\]\) ary = "\u3042"/
+ assert_rp '"' + "\u3042"*10 + '"', /\(RString::\(anonymous struct\)\) heap = \{/
+ end
+end
diff --git a/misc/test_yjit_asm.sh b/misc/test_yjit_asm.sh
new file mode 100755
index 0000000000..e09d83f0fb
--- /dev/null
+++ b/misc/test_yjit_asm.sh
@@ -0,0 +1,10 @@
+#!/bin/sh
+
+set -e
+set -x
+
+clang -std=gnu99 -Wall -Werror -Wno-error=unused-function -Wshorten-64-to-32 -I "${0%/*/*}" "${0%/*}/yjit_asm_tests.c" -o asm_test
+
+./asm_test
+
+rm asm_test
diff --git a/misc/yjit_asm_tests.c b/misc/yjit_asm_tests.c
new file mode 100644
index 0000000000..ccf8822bbe
--- /dev/null
+++ b/misc/yjit_asm_tests.c
@@ -0,0 +1,443 @@
+// For MAP_ANONYMOUS on GNU/Linux
+#define _GNU_SOURCE 1
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+// This test executable doesn't compile with the rest of Ruby
+// so we need to define a rb_bug().
+_Noreturn
+static void rb_bug(const char *message, ...)
+{
+ va_list args;
+ va_start(args, message);
+ vfprintf(stderr, message, args);
+ va_end(args);
+ abort();
+}
+
+#include "yjit_asm.c"
+
+// Print the bytes in a code block
+void print_bytes(codeblock_t* cb)
+{
+ for (uint32_t i = 0; i < cb->write_pos; ++i)
+ {
+ printf("%02X", (int)*cb_get_ptr(cb, i));
+ }
+
+ printf("\n");
+}
+
+// Check that the code block contains the given sequence of bytes
+void check_bytes(codeblock_t* cb, const char* bytes)
+{
+ printf("checking encoding: %s\n", bytes);
+
+ size_t len = strlen(bytes);
+ assert (len % 2 == 0);
+ size_t num_bytes = len / 2;
+
+ if (cb->write_pos != num_bytes)
+ {
+ fprintf(stderr, "incorrect encoding length, expected %ld, got %d\n",
+ num_bytes,
+ cb->write_pos
+ );
+ printf("%s\n", bytes);
+ print_bytes(cb);
+ exit(-1);
+ }
+
+ for (uint32_t i = 0; i < num_bytes; ++i)
+ {
+ char byte_str[] = {0, 0, 0, 0};
+ strncpy(byte_str, bytes + (2 * i), 2);
+ char* endptr;
+ long int byte = strtol(byte_str, &endptr, 16);
+
+ uint8_t cb_byte = *cb_get_ptr(cb, i);
+
+ if (cb_byte != byte)
+ {
+ fprintf(stderr, "incorrect encoding at position %d, expected %02X, got %02X\n",
+ i,
+ (int)byte,
+ (int)cb_byte
+ );
+ printf("%s\n", bytes);
+ print_bytes(cb);
+ exit(-1);
+ }
+ }
+}
+
+void run_assembler_tests(void)
+{
+ printf("Running assembler tests\n");
+
+ codeblock_t cb_obj;
+ codeblock_t* cb = &cb_obj;
+ uint8_t* mem_block = alloc_exec_mem(4096);
+ cb_init(cb, mem_block, 4096);
+
+ // add
+ cb_set_pos(cb, 0); add(cb, CL, imm_opnd(3)); check_bytes(cb, "80C103");
+ cb_set_pos(cb, 0); add(cb, CL, BL); check_bytes(cb, "00D9");
+ cb_set_pos(cb, 0); add(cb, CL, SPL); check_bytes(cb, "4000E1");
+ cb_set_pos(cb, 0); add(cb, CX, BX); check_bytes(cb, "6601D9");
+ cb_set_pos(cb, 0); add(cb, RAX, RBX); check_bytes(cb, "4801D8");
+ cb_set_pos(cb, 0); add(cb, ECX, EDX); check_bytes(cb, "01D1");
+ cb_set_pos(cb, 0); add(cb, RDX, R14); check_bytes(cb, "4C01F2");
+ cb_set_pos(cb, 0); add(cb, mem_opnd(64, RAX, 0), RDX); check_bytes(cb, "480110");
+ cb_set_pos(cb, 0); add(cb, RDX, mem_opnd(64, RAX, 0)); check_bytes(cb, "480310");
+ cb_set_pos(cb, 0); add(cb, RDX, mem_opnd(64, RAX, 8)); check_bytes(cb, "48035008");
+ cb_set_pos(cb, 0); add(cb, RDX, mem_opnd(64, RAX, 255)); check_bytes(cb, "480390FF000000");
+ cb_set_pos(cb, 0); add(cb, mem_opnd(64, RAX, 127), imm_opnd(255)); check_bytes(cb, "4881407FFF000000");
+ cb_set_pos(cb, 0); add(cb, mem_opnd(32, RAX, 0), EDX); check_bytes(cb, "0110");
+ cb_set_pos(cb, 0); add(cb, RSP, imm_opnd(8)); check_bytes(cb, "4883C408");
+ cb_set_pos(cb, 0); add(cb, ECX, imm_opnd(8)); check_bytes(cb, "83C108");
+ cb_set_pos(cb, 0); add(cb, ECX, imm_opnd(255)); check_bytes(cb, "81C1FF000000");
+
+ // and
+ cb_set_pos(cb, 0); and(cb, EBP, R12D); check_bytes(cb, "4421E5");
+ cb_set_pos(cb, 0); and(cb, mem_opnd(64, RAX, 0), imm_opnd(0x08)); check_bytes(cb, "48832008");
+
+ // call
+ {
+ cb_set_pos(cb, 0);
+ uint32_t fn_label = cb_new_label(cb, "foo");
+ call_label(cb, fn_label);
+ cb_link_labels(cb);
+ check_bytes(cb, "E8FBFFFFFF");
+ }
+ cb_set_pos(cb, 0); call(cb, RAX); check_bytes(cb, "FFD0");
+ cb_set_pos(cb, 0); call(cb, mem_opnd(64, RSP, 8)); check_bytes(cb, "FF542408");
+
+ // cmovcc
+ cb_set_pos(cb, 0); cmovg(cb, ESI, EDI); check_bytes(cb, "0F4FF7");
+ cb_set_pos(cb, 0); cmovg(cb, ESI, mem_opnd(32, RBP, 12)); check_bytes(cb, "0F4F750C");
+ cb_set_pos(cb, 0); cmovl(cb, EAX, ECX); check_bytes(cb, "0F4CC1");
+ cb_set_pos(cb, 0); cmovl(cb, RBX, RBP); check_bytes(cb, "480F4CDD");
+ cb_set_pos(cb, 0); cmovle(cb, ESI, mem_opnd(32, RSP, 4)); check_bytes(cb, "0F4E742404");
+
+ // cmp
+ cb_set_pos(cb, 0); cmp(cb, CL, DL); check_bytes(cb, "38D1");
+ cb_set_pos(cb, 0); cmp(cb, ECX, EDI); check_bytes(cb, "39F9");
+ cb_set_pos(cb, 0); cmp(cb, RDX, mem_opnd(64, R12, 0)); check_bytes(cb, "493B1424");
+ cb_set_pos(cb, 0); cmp(cb, RAX, imm_opnd(2)); check_bytes(cb, "4883F802");
+
+ // cqo
+ cb_set_pos(cb, 0); cqo(cb); check_bytes(cb, "4899");
+
+ // div
+ /*
+ test(
+ delegate void (CodeBlock cb) { cb.div(X86Opnd(EDX)); },
+ "F7F2"
+ );
+ test(
+ delegate void (CodeBlock cb) { cb.div(X86Opnd(32, RSP, -12)); },
+ "F77424F4"
+ );
+ */
+
+ // jcc to label
+ {
+ cb_set_pos(cb, 0);
+ uint32_t loop_label = cb_new_label(cb, "loop");
+ jge_label(cb, loop_label);
+ cb_link_labels(cb);
+ check_bytes(cb, "0F8DFAFFFFFF");
+ }
+ {
+ cb_set_pos(cb, 0);
+ uint32_t loop_label = cb_new_label(cb, "loop");
+ jo_label(cb, loop_label);
+ cb_link_labels(cb);
+ check_bytes(cb, "0F80FAFFFFFF");
+ }
+
+ // jmp to label
+ {
+ cb_set_pos(cb, 0);
+ uint32_t loop_label = cb_new_label(cb, "loop");
+ jmp_label(cb, loop_label);
+ cb_link_labels(cb);
+ check_bytes(cb, "E9FBFFFFFF");
+ }
+
+ // jmp with RM operand
+ cb_set_pos(cb, 0); jmp_rm(cb, R12); check_bytes(cb, "41FFE4");
+
+ // lea
+ cb_set_pos(cb, 0); lea(cb, RDX, mem_opnd(64, RCX, 8)); check_bytes(cb, "488D5108");
+ cb_set_pos(cb, 0); lea(cb, RAX, mem_opnd(8, RIP, 0)); check_bytes(cb, "488D0500000000");
+ cb_set_pos(cb, 0); lea(cb, RAX, mem_opnd(8, RIP, 5)); check_bytes(cb, "488D0505000000");
+ cb_set_pos(cb, 0); lea(cb, RDI, mem_opnd(8, RIP, 5)); check_bytes(cb, "488D3D05000000");
+
+ // mov
+ cb_set_pos(cb, 0); mov(cb, EAX, imm_opnd(7)); check_bytes(cb, "B807000000");
+ cb_set_pos(cb, 0); mov(cb, EAX, imm_opnd(-3)); check_bytes(cb, "B8FDFFFFFF");
+ cb_set_pos(cb, 0); mov(cb, R15, imm_opnd(3)); check_bytes(cb, "41BF03000000");
+ cb_set_pos(cb, 0); mov(cb, EAX, EBX); check_bytes(cb, "89D8");
+ cb_set_pos(cb, 0); mov(cb, EAX, ECX); check_bytes(cb, "89C8");
+ cb_set_pos(cb, 0); mov(cb, EDX, mem_opnd(32, RBX, 128)); check_bytes(cb, "8B9380000000");
+
+ // Test `mov rax, 3` => `mov eax, 3` optimization
+ cb_set_pos(cb, 0); mov(cb, R8, imm_opnd(0x34)); check_bytes(cb, "41B834000000");
+ cb_set_pos(cb, 0); mov(cb, R8, imm_opnd(0x80000000)); check_bytes(cb, "49B80000008000000000");
+ cb_set_pos(cb, 0); mov(cb, R8, imm_opnd(-1)); check_bytes(cb, "49B8FFFFFFFFFFFFFFFF");
+
+ cb_set_pos(cb, 0); mov(cb, RAX, imm_opnd(0x34)); check_bytes(cb, "B834000000");
+ cb_set_pos(cb, 0); mov(cb, RAX, imm_opnd(0x80000000)); check_bytes(cb, "48B80000008000000000");
+ cb_set_pos(cb, 0); mov(cb, RAX, imm_opnd(-52)); check_bytes(cb, "48B8CCFFFFFFFFFFFFFF");
+ cb_set_pos(cb, 0); mov(cb, RAX, imm_opnd(-1)); check_bytes(cb, "48B8FFFFFFFFFFFFFFFF");
+ /*
+ test(
+ delegate void (CodeBlock cb) { cb.mov(X86Opnd(AL), X86Opnd(8, RCX, 0, 1, RDX)); },
+ "8A0411"
+ );
+ */
+ cb_set_pos(cb, 0); mov(cb, CL, R9B); check_bytes(cb, "4488C9");
+ cb_set_pos(cb, 0); mov(cb, RBX, RAX); check_bytes(cb, "4889C3");
+ cb_set_pos(cb, 0); mov(cb, RDI, RBX); check_bytes(cb, "4889DF");
+ cb_set_pos(cb, 0); mov(cb, SIL, imm_opnd(11)); check_bytes(cb, "40B60B");
+ cb_set_pos(cb, 0); mov(cb, mem_opnd(8, RSP, 0), imm_opnd(-3)); check_bytes(cb, "C60424FD");
+ cb_set_pos(cb, 0); mov(cb, mem_opnd(64, RDI, 8), imm_opnd(1)); check_bytes(cb, "48C7470801000000");
+
+ // movsx
+ cb_set_pos(cb, 0); movsx(cb, AX, AL); check_bytes(cb, "660FBEC0");
+ cb_set_pos(cb, 0); movsx(cb, EDX, AL); check_bytes(cb, "0FBED0");
+ cb_set_pos(cb, 0); movsx(cb, RAX, BL); check_bytes(cb, "480FBEC3");
+ cb_set_pos(cb, 0); movsx(cb, ECX, AX); check_bytes(cb, "0FBFC8");
+ cb_set_pos(cb, 0); movsx(cb, R11, CL); check_bytes(cb, "4C0FBED9");
+ cb_set_pos(cb, 0); movsx(cb, R10, mem_opnd(32, RSP, 12)); check_bytes(cb, "4C6354240C");
+ cb_set_pos(cb, 0); movsx(cb, RAX, mem_opnd(8, RSP, 0)); check_bytes(cb, "480FBE0424");
+
+ // neg
+ cb_set_pos(cb, 0); neg(cb, RAX); check_bytes(cb, "48F7D8");
+
+ // nop
+ cb_set_pos(cb, 0); nop(cb, 1); check_bytes(cb, "90");
+
+ // not
+ cb_set_pos(cb, 0); not(cb, AX); check_bytes(cb, "66F7D0");
+ cb_set_pos(cb, 0); not(cb, EAX); check_bytes(cb, "F7D0");
+ cb_set_pos(cb, 0); not(cb, mem_opnd(64, R12, 0)); check_bytes(cb, "49F71424");
+ cb_set_pos(cb, 0); not(cb, mem_opnd(32, RSP, 301)); check_bytes(cb, "F794242D010000");
+ cb_set_pos(cb, 0); not(cb, mem_opnd(32, RSP, 0)); check_bytes(cb, "F71424");
+ cb_set_pos(cb, 0); not(cb, mem_opnd(32, RSP, 3)); check_bytes(cb, "F7542403");
+ cb_set_pos(cb, 0); not(cb, mem_opnd(32, RBP, 0)); check_bytes(cb, "F75500");
+ cb_set_pos(cb, 0); not(cb, mem_opnd(32, RBP, 13)); check_bytes(cb, "F7550D");
+ cb_set_pos(cb, 0); not(cb, RAX); check_bytes(cb, "48F7D0");
+ cb_set_pos(cb, 0); not(cb, R11); check_bytes(cb, "49F7D3");
+ cb_set_pos(cb, 0); not(cb, mem_opnd(32, RAX, 0)); check_bytes(cb, "F710");
+ cb_set_pos(cb, 0); not(cb, mem_opnd(32, RSI, 0)); check_bytes(cb, "F716");
+ cb_set_pos(cb, 0); not(cb, mem_opnd(32, RDI, 0)); check_bytes(cb, "F717");
+ cb_set_pos(cb, 0); not(cb, mem_opnd(32, RDX, 55)); check_bytes(cb, "F75237");
+ cb_set_pos(cb, 0); not(cb, mem_opnd(32, RDX, 1337)); check_bytes(cb, "F79239050000");
+ cb_set_pos(cb, 0); not(cb, mem_opnd(32, RDX, -55)); check_bytes(cb, "F752C9");
+ cb_set_pos(cb, 0); not(cb, mem_opnd(32, RDX, -555)); check_bytes(cb, "F792D5FDFFFF");
+ /*
+ test(
+ delegate void (CodeBlock cb) { cb.not(X86Opnd(32, RAX, 0, 1, RBX)); },
+ "F71418"
+ );
+ test(
+ delegate void (CodeBlock cb) { cb.not(X86Opnd(32, RAX, 0, 1, R12)); },
+ "42F71420"
+ );
+ test(
+ delegate void (CodeBlock cb) { cb.not(X86Opnd(32, R15, 0, 1, R12)); },
+ "43F71427"
+ );
+ test(
+ delegate void (CodeBlock cb) { cb.not(X86Opnd(32, R15, 5, 1, R12)); },
+ "43F7542705"
+ );
+ test(
+ delegate void (CodeBlock cb) { cb.not(X86Opnd(32, R15, 5, 8, R12)); },
+ "43F754E705"
+ );
+ test(
+ delegate void (CodeBlock cb) { cb.not(X86Opnd(32, R15, 5, 8, R13)); },
+ "43F754EF05"
+ );
+ test(
+ delegate void (CodeBlock cb) { cb.not(X86Opnd(32, R12, 5, 4, R9)); },
+ "43F7548C05"
+ );
+ test(
+ delegate void (CodeBlock cb) { cb.not(X86Opnd(32, R12, 301, 4, R9)); },
+ "43F7948C2D010000"
+ );
+ test(
+ delegate void (CodeBlock cb) { cb.not(X86Opnd(32, RAX, 5, 4, RDX)); },
+ "F7549005"
+ );
+ test(
+ delegate void (CodeBlock cb) { cb.not(X86Opnd(64, RAX, 0, 2, RDX)); },
+ "48F71450"
+ );
+ test(
+ delegate void (CodeBlock cb) { cb.not(X86Opnd(32, RSP, 0, 1, RBX)); },
+ "F7141C"
+ );
+ test(
+ delegate void (CodeBlock cb) { cb.not(X86Opnd(32, RSP, 3, 1, RBX)); },
+ "F7541C03"
+ );
+ test(
+ delegate void (CodeBlock cb) { cb.not(X86Opnd(32, RBP, 13, 1, RDX)); },
+ "F754150D"
+ );
+ */
+
+ // or
+ cb_set_pos(cb, 0); or(cb, EDX, ESI); check_bytes(cb, "09F2");
+
+ // pop
+ cb_set_pos(cb, 0); pop(cb, RAX); check_bytes(cb, "58");
+ cb_set_pos(cb, 0); pop(cb, RBX); check_bytes(cb, "5B");
+ cb_set_pos(cb, 0); pop(cb, RSP); check_bytes(cb, "5C");
+ cb_set_pos(cb, 0); pop(cb, RBP); check_bytes(cb, "5D");
+ cb_set_pos(cb, 0); pop(cb, R12); check_bytes(cb, "415C");
+ cb_set_pos(cb, 0); pop(cb, mem_opnd(64, RAX, 0)); check_bytes(cb, "8F00");
+ cb_set_pos(cb, 0); pop(cb, mem_opnd(64, R8, 0)); check_bytes(cb, "418F00");
+ cb_set_pos(cb, 0); pop(cb, mem_opnd(64, R8, 3)); check_bytes(cb, "418F4003");
+ cb_set_pos(cb, 0); pop(cb, mem_opnd_sib(64, RAX, RCX, 8, 3)); check_bytes(cb, "8F44C803");
+ cb_set_pos(cb, 0); pop(cb, mem_opnd_sib(64, R8, RCX, 8, 3)); check_bytes(cb, "418F44C803");
+
+ // push
+ cb_set_pos(cb, 0); push(cb, RAX); check_bytes(cb, "50");
+ cb_set_pos(cb, 0); push(cb, RBX); check_bytes(cb, "53");
+ cb_set_pos(cb, 0); push(cb, R12); check_bytes(cb, "4154");
+ cb_set_pos(cb, 0); push(cb, mem_opnd(64, RAX, 0)); check_bytes(cb, "FF30");
+ cb_set_pos(cb, 0); push(cb, mem_opnd(64, R8, 0)); check_bytes(cb, "41FF30");
+ cb_set_pos(cb, 0); push(cb, mem_opnd(64, R8, 3)); check_bytes(cb, "41FF7003");
+ cb_set_pos(cb, 0); push(cb, mem_opnd_sib(64, RAX, RCX, 8, 3)); check_bytes(cb, "FF74C803");
+ cb_set_pos(cb, 0); push(cb, mem_opnd_sib(64, R8, RCX, 8, 3)); check_bytes(cb, "41FF74C803");
+
+ // ret
+ cb_set_pos(cb, 0); ret(cb); check_bytes(cb, "C3");
+
+ // sal
+ cb_set_pos(cb, 0); sal(cb, CX, imm_opnd(1)); check_bytes(cb, "66D1E1");
+ cb_set_pos(cb, 0); sal(cb, ECX, imm_opnd(1)); check_bytes(cb, "D1E1");
+ cb_set_pos(cb, 0); sal(cb, EBP, imm_opnd(5)); check_bytes(cb, "C1E505");
+ cb_set_pos(cb, 0); sal(cb, mem_opnd(32, RSP, 68), imm_opnd(1)); check_bytes(cb, "D1642444");
+
+ // sar
+ cb_set_pos(cb, 0); sar(cb, EDX, imm_opnd(1)); check_bytes(cb, "D1FA");
+
+ // shr
+ cb_set_pos(cb, 0); shr(cb, R14, imm_opnd(7)); check_bytes(cb, "49C1EE07");
+
+ /*
+ // sqrtsd
+ test(
+ delegate void (CodeBlock cb) { cb.sqrtsd(X86Opnd(XMM2), X86Opnd(XMM6)); },
+ "F20F51D6"
+ );
+ */
+
+ // sub
+ cb_set_pos(cb, 0); sub(cb, EAX, imm_opnd(1)); check_bytes(cb, "83E801");
+ cb_set_pos(cb, 0); sub(cb, RAX, imm_opnd(2)); check_bytes(cb, "4883E802");
+
+ // test
+ cb_set_pos(cb, 0); test(cb, AL, AL); check_bytes(cb, "84C0");
+ cb_set_pos(cb, 0); test(cb, AX, AX); check_bytes(cb, "6685C0");
+ cb_set_pos(cb, 0); test(cb, CL, imm_opnd(8)); check_bytes(cb, "F6C108");
+ cb_set_pos(cb, 0); test(cb, DL, imm_opnd(7)); check_bytes(cb, "F6C207");
+ cb_set_pos(cb, 0); test(cb, RCX, imm_opnd(8)); check_bytes(cb, "F6C108");
+ cb_set_pos(cb, 0); test(cb, mem_opnd(8, RDX, 8), imm_opnd(8)); check_bytes(cb, "F6420808");
+ cb_set_pos(cb, 0); test(cb, mem_opnd(8, RDX, 8), imm_opnd(255)); check_bytes(cb, "F64208FF");
+ cb_set_pos(cb, 0); test(cb, DX, imm_opnd(0xFFFF)); check_bytes(cb, "66F7C2FFFF");
+ cb_set_pos(cb, 0); test(cb, mem_opnd(16, RDX, 8), imm_opnd(0xFFFF)); check_bytes(cb, "66F74208FFFF");
+ cb_set_pos(cb, 0); test(cb, mem_opnd(8, RSI, 0), imm_opnd(1)); check_bytes(cb, "F60601");
+ cb_set_pos(cb, 0); test(cb, mem_opnd(8, RSI, 16), imm_opnd(1)); check_bytes(cb, "F6461001");
+ cb_set_pos(cb, 0); test(cb, mem_opnd(8, RSI, -16), imm_opnd(1)); check_bytes(cb, "F646F001");
+ cb_set_pos(cb, 0); test(cb, mem_opnd(32, RSI, 64), EAX); check_bytes(cb, "854640");
+ cb_set_pos(cb, 0); test(cb, mem_opnd(64, RDI, 42), RAX); check_bytes(cb, "4885472A");
+ cb_set_pos(cb, 0); test(cb, RAX, RAX); check_bytes(cb, "4885C0");
+ cb_set_pos(cb, 0); test(cb, RAX, RSI); check_bytes(cb, "4885F0");
+ cb_set_pos(cb, 0); test(cb, mem_opnd(64, RSI, 64), imm_opnd(~0x08)); check_bytes(cb, "48F74640F7FFFFFF");
+
+ // xchg
+ cb_set_pos(cb, 0); xchg(cb, RAX, RCX); check_bytes(cb, "4891");
+ cb_set_pos(cb, 0); xchg(cb, RAX, R13); check_bytes(cb, "4995");
+ cb_set_pos(cb, 0); xchg(cb, RCX, RBX); check_bytes(cb, "4887D9");
+ cb_set_pos(cb, 0); xchg(cb, R9, R15); check_bytes(cb, "4D87F9");
+
+ // xor
+ cb_set_pos(cb, 0); xor(cb, EAX, EAX); check_bytes(cb, "31C0");
+
+ printf("Assembler tests done\n");
+}
+
+void assert_equal(int expected, int actual)
+{
+ if (expected != actual) {
+ fprintf(stderr, "expected %d, got %d\n", expected, actual);
+ exit(-1);
+ }
+}
+
+void run_runtime_tests(void)
+{
+ printf("Running runtime tests\n");
+
+ codeblock_t codeblock;
+ codeblock_t* cb = &codeblock;
+
+ uint8_t* mem_block = alloc_exec_mem(4096);
+ cb_init(cb, mem_block, 4096);
+
+ int (*function)(void);
+ function = (int (*)(void))mem_block;
+
+ #define TEST(BODY) cb_set_pos(cb, 0); BODY ret(cb); cb_mark_all_executable(cb); assert_equal(7, function());
+
+ // add
+ TEST({ mov(cb, RAX, imm_opnd(0)); add(cb, RAX, imm_opnd(7)); })
+ TEST({ mov(cb, RAX, imm_opnd(0)); mov(cb, RCX, imm_opnd(7)); add(cb, RAX, RCX); })
+
+ // and
+ TEST({ mov(cb, RAX, imm_opnd(31)); and(cb, RAX, imm_opnd(7)); })
+ TEST({ mov(cb, RAX, imm_opnd(31)); mov(cb, RCX, imm_opnd(7)); and(cb, RAX, RCX); })
+
+ // or
+ TEST({ mov(cb, RAX, imm_opnd(3)); or(cb, RAX, imm_opnd(4)); })
+ TEST({ mov(cb, RAX, imm_opnd(3)); mov(cb, RCX, imm_opnd(4)); or(cb, RAX, RCX); })
+
+ // push/pop
+ TEST({ mov(cb, RCX, imm_opnd(7)); push(cb, RCX); pop(cb, RAX); })
+
+ // shr
+ TEST({ mov(cb, RAX, imm_opnd(31)); shr(cb, RAX, imm_opnd(2)); })
+
+ // sub
+ TEST({ mov(cb, RAX, imm_opnd(12)); sub(cb, RAX, imm_opnd(5)); })
+ TEST({ mov(cb, RAX, imm_opnd(12)); mov(cb, RCX, imm_opnd(5)); sub(cb, RAX, RCX); })
+
+ // xor
+ TEST({ mov(cb, RAX, imm_opnd(13)); xor(cb, RAX, imm_opnd(10)); })
+ TEST({ mov(cb, RAX, imm_opnd(13)); mov(cb, RCX, imm_opnd(10)); xor(cb, RAX, RCX); })
+
+ #undef TEST
+
+ printf("Runtime tests done\n");
+}
+
+int main(int argc, char** argv)
+{
+ run_assembler_tests();
+ run_runtime_tests();
+
+ return 0;
+}