Skip to content

Instantly share code, notes, and snippets.

@razielgn
Created March 28, 2014 19:19
Show Gist options
  • Save razielgn/9840867 to your computer and use it in GitHub Desktop.
Save razielgn/9840867 to your computer and use it in GitHub Desktop.
From 06e2d0b5ab731d099e1a914c2761a7ab094d69cf Mon Sep 17 00:00:00 2001
From: Federico Ravasio <ravasio.federico@gmail.com>
Date: Fri, 28 Mar 2014 20:19:14 +0100
Subject: [PATCH] Partial blog post.
---
_posts/2014-02-22-jit-debugging-video.markdown | 81 ++++++++++++++++++++++++++
1 file changed, 81 insertions(+)
create mode 100644 _posts/2014-02-22-jit-debugging-video.markdown
diff --git a/_posts/2014-02-22-jit-debugging-video.markdown b/_posts/2014-02-22-jit-debugging-video.markdown
new file mode 100644
index 0000000..6fb5455
--- /dev/null
+++ b/_posts/2014-02-22-jit-debugging-video.markdown
@@ -0,0 +1,81 @@
+---
+layout: post
+title: A JIT Debugging session (with video)
+author: Federico Ravasio
+---
+
+Rubinius 2 features a just-in-time compiler (JIT): while your application is running, it tracks which methods are used more often and compiles them down into machine code. The fantastic news is they will run several times faster than their original bytecode version.
+
+Sometimes though, the JIT code may miscompile some bytecode instructions, causing subtle bugs to show up only after a number of executions of any method.
+
+A few days ago, a similar issue [has been reported on the issue tracker](https://github.com/rubinius/rubinius/issues/2926).
+Here's a (valid) semplification of the script manifesting the bug:
+
+``` ruby
+loop do
+ value = nil
+ ary = *value
+ if ary == [nil]
+ raise "error"
+ end
+end
+```
+
+The exposing code is ran in an infinite loop, so that the JIT would eventually have to compile it.
+There was something not very obvious to me at first:
+
+``` ruby
+ary = *nil
+# => ary == []
+```
+
+But after the JIT pass, suddenly `ary == [nil]`.
+
+How would that happen? I've been contributing to Rubinius for the past 6-7 months, trust me when I say the JIT code section looks like the scariest one. :)
+
+At some point, Dirkjan acknowledged he'd identified the problem and offered to pair with someone who was interested enough to dive a bit into the scary JIT code section. So a little more than a week ago, we met for an hangout, so he could patiently lead me down the JIT rabbit hole.
+
+First bytcode: `rbx compile -B bug.rb`
+
+```
+0000: push_nil
+0001: set_local 0
+0003: pop
+0004: push_local 0
+0006: cast_array
+0007: set_local 1
+0009: pop
+0010: push_local 1
+0012: push_nil
+0013: make_array 1
+0015: send_stack :==, 1
+0018: goto_if_false 0030:
+0020: push_self
+0021: push_literal "error"
+0023: string_dup
+0024: allow_private
+0025: send_stack :raise, 1
+0028: goto 0031:
+0030: push_nil
+0031: ret
+```
+
+The relevant line is `0006`, the instruction is `cast_array`.
+
+``` cpp
+instruction cast_array() [ value -- array ]
+ Object* t1 = stack_top();
+
+ if(t1->nil_p()) {
+ t1 = Array::create_dirty(state, 0);
+ } else if(Tuple* tup = try_as<Tuple>(t1)) {
+ ...
+ } else if(!kind_of<Array>(t1)) {
+ ...
+ }
+```
+
+As you can see, the first check after popping "nil" from the stack, is actually to check if "nil" was popped. Nothing's wrong here, just as a reference to better understand the JIT'd version.
+
+The JIT version is a bit harder to follow, let me explain:
+
--
1.8.1.2
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment