Tarantool development patches archive
 help / color / mirror / Atom feed
* [Tarantool-patches] [PATCH luajit] Consider slots used by upvalues in use-def analysis.
@ 2024-02-07 14:51 Sergey Kaplun via Tarantool-patches
  2024-02-08 11:11 ` Sergey Bronnikov via Tarantool-patches
                   ` (2 more replies)
  0 siblings, 3 replies; 6+ messages in thread
From: Sergey Kaplun via Tarantool-patches @ 2024-02-07 14:51 UTC (permalink / raw)
  To: Maxim Kokryashkin, Sergey Bronnikov; +Cc: tarantool-patches

From: Mike Pall <mike>

Reported by XmiliaH.

(cherry picked from commit 3a654999c6f00de4cb9e61232d23579442e544a0)

`snap_usedef()` analysis doesn't check slots for child upvalues of the
currentlly recorded function in use-def analysis. Hence, such slots may
be considered unused and not stored in the snapshot. So on snapshot
restoration, values from these slots may be lost.

This patch adds a marking for all such local upvalues.

Sergey Kaplun:
* added the description and the test for the problem

Part of tarantool/tarantool#9595
---

Branch: https://github.com/tarantool/luajit/tree/skaplun/lj-737-snap-usedef-upvalues
Tarantool PR: https://github.com/tarantool/tarantool/pull/9662
Related issues:
* https://github.com/tarantool/tarantool/issues/9595
* https://github.com/LuaJIT/LuaJIT/issues/737

 src/lj_snap.c                                 | 35 +++++++++++-
 .../lj-737-snap-use-def-upvalues.test.lua     | 55 +++++++++++++++++++
 2 files changed, 87 insertions(+), 3 deletions(-)
 create mode 100644 test/tarantool-tests/lj-737-snap-use-def-upvalues.test.lua

diff --git a/src/lj_snap.c b/src/lj_snap.c
index 26352080..1ac9296a 100644
--- a/src/lj_snap.c
+++ b/src/lj_snap.c
@@ -302,6 +302,31 @@ static BCReg snap_usedef(jit_State *J, uint8_t *udf,
   return 0;  /* unreachable */
 }
 
+/* Mark slots used by upvalues of child prototypes as used. */
+void snap_useuv(GCproto *pt, uint8_t *udf)
+{
+  /* This is a coarse check, because it's difficult to correlate the lifetime
+  ** of slots and closures. But the number of false positives is quite low.
+  ** A false positive may cause a slot not to be purged, which is just
+  ** a missed optimization.
+  */
+  if ((pt->flags & PROTO_CHILD)) {
+    ptrdiff_t i, j, n = pt->sizekgc;
+    GCRef *kr = mref(pt->k, GCRef) - 1;
+    for (i = 0; i < n; i++, kr--) {
+      GCobj *o = gcref(*kr);
+      if (o->gch.gct == ~LJ_TPROTO) {
+	for (j = 0; j < gco2pt(o)->sizeuv; j++) {
+	  uint32_t v = proto_uv(gco2pt(o))[j];
+	  if ((v & PROTO_UV_LOCAL)) {
+	    udf[(v & 0xff)] = 0;
+	  }
+	}
+      }
+    }
+  }
+}
+
 /* Purge dead slots before the next snapshot. */
 void lj_snap_purge(jit_State *J)
 {
@@ -310,9 +335,12 @@ void lj_snap_purge(jit_State *J)
   if (bc_op(*J->pc) == BC_FUNCV && maxslot > J->pt->numparams)
     maxslot = J->pt->numparams;
   s = snap_usedef(J, udf, J->pc, maxslot);
-  for (; s < maxslot; s++)
-    if (udf[s] != 0)
-      J->base[s] = 0;  /* Purge dead slots. */
+  if (s < maxslot) {
+    snap_useuv(J->pt, udf);
+    for (; s < maxslot; s++)
+      if (udf[s] != 0)
+	J->base[s] = 0;  /* Purge dead slots. */
+  }
 }
 
 /* Shrink last snapshot. */
@@ -325,6 +353,7 @@ void lj_snap_shrink(jit_State *J)
   BCReg maxslot = J->maxslot;
   BCReg baseslot = J->baseslot;
   BCReg minslot = snap_usedef(J, udf, snap_pc(&map[nent]), maxslot);
+  if (minslot < maxslot) snap_useuv(J->pt, udf);
   maxslot += baseslot;
   minslot += baseslot;
   snap->nslots = (uint8_t)maxslot;
diff --git a/test/tarantool-tests/lj-737-snap-use-def-upvalues.test.lua b/test/tarantool-tests/lj-737-snap-use-def-upvalues.test.lua
new file mode 100644
index 00000000..8535f9f6
--- /dev/null
+++ b/test/tarantool-tests/lj-737-snap-use-def-upvalues.test.lua
@@ -0,0 +1,55 @@
+local tap = require('tap')
+
+-- Test file to demonstrate LuaJIT misbehaviour in use-def
+-- snapshot analysis for local upvalues.
+-- See also https://github.com/LuaJIT/LuaJIT/issues/737.
+
+local test = tap.test('lj-737-snap-use-def-upvalues'):skipcond({
+  ['Test requires JIT enabled'] = not jit.status(),
+})
+
+test:plan(1)
+
+-- XXX: simplify `jit.dump()` output.
+local fmod = math.fmod
+
+local EXPECTED = 'expected'
+
+jit.opt.start('hotloop=1')
+
+local function wrapped_trace(create_closure)
+  local local_upvalue, closure
+  if create_closure then
+    closure = function() return local_upvalue end
+  end
+  for i = 1, 4 do
+    -- On the second iteration, the trace is recorded.
+    if i == 2 then
+      -- Before the patch, this slot was considered unused by
+      -- use-def analysis in the `snap_usedef()` since there are
+      -- no open unpvalues for `closure()` on recording (1st call).
+      local_upvalue = EXPECTED
+      -- luacheck: ignore
+      -- Emit an additional snapshot after setting the
+      -- upvalue.
+      if i == 0 then end
+      -- Stitching ends the trace here.
+      fmod(1,1)
+      return closure
+    end
+  end
+end
+
+-- Compile the trace.
+local func_with_uv = wrapped_trace(false)
+assert(func_with_uv == nil, 'no function is returned on the first call')
+
+-- Now run this trace when `closure()` is defined and has an open
+-- local upvalue.
+func_with_uv = wrapped_trace(true)
+assert(type(func_with_uv) == 'function',
+       'function is returned after the second call')
+
+test:is(func_with_uv(), EXPECTED, 'correct result of the closure call')
+
+test:done(true)
-- 
2.43.0


^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [Tarantool-patches] [PATCH luajit] Consider slots used by upvalues in use-def analysis.
  2024-02-07 14:51 [Tarantool-patches] [PATCH luajit] Consider slots used by upvalues in use-def analysis Sergey Kaplun via Tarantool-patches
@ 2024-02-08 11:11 ` Sergey Bronnikov via Tarantool-patches
  2024-02-08 11:24   ` Sergey Kaplun via Tarantool-patches
  2024-02-09 16:54 ` Maxim Kokryashkin via Tarantool-patches
  2024-02-15 13:48 ` Igor Munkin via Tarantool-patches
  2 siblings, 1 reply; 6+ messages in thread
From: Sergey Bronnikov via Tarantool-patches @ 2024-02-08 11:11 UTC (permalink / raw)
  To: Sergey Kaplun, Maxim Kokryashkin; +Cc: tarantool-patches

Hi, Sergey

thanks for the patch! LGTM with a minor comment

On 2/7/24 17:51, Sergey Kaplun wrote:
> From: Mike Pall <mike>
>
> Reported by XmiliaH.
>
> (cherry picked from commit 3a654999c6f00de4cb9e61232d23579442e544a0)
>
> `snap_usedef()` analysis doesn't check slots for child upvalues of the
> currentlly recorded function in use-def analysis. Hence, such slots may
typo: currentlly
> be considered unused and not stored in the snapshot. So on snapshot
> restoration, values from these slots may be lost.
>
> This patch adds a marking for all such local upvalues.
>
> Sergey Kaplun:
> * added the description and the test for the problem
>
> Part of tarantool/tarantool#9595
> ---
>
> Branch: https://github.com/tarantool/luajit/tree/skaplun/lj-737-snap-usedef-upvalues
> Tarantool PR: https://github.com/tarantool/tarantool/pull/9662
> Related issues:
> * https://github.com/tarantool/tarantool/issues/9595
> * https://github.com/LuaJIT/LuaJIT/issues/737
>
>   src/lj_snap.c                                 | 35 +++++++++++-
>   .../lj-737-snap-use-def-upvalues.test.lua     | 55 +++++++++++++++++++
>   2 files changed, 87 insertions(+), 3 deletions(-)
>   create mode 100644 test/tarantool-tests/lj-737-snap-use-def-upvalues.test.lua
>
> diff --git a/src/lj_snap.c b/src/lj_snap.c
> index 26352080..1ac9296a 100644
> --- a/src/lj_snap.c
> +++ b/src/lj_snap.c
> @@ -302,6 +302,31 @@ static BCReg snap_usedef(jit_State *J, uint8_t *udf,
>     return 0;  /* unreachable */
>   }
>   
> +/* Mark slots used by upvalues of child prototypes as used. */
> +void snap_useuv(GCproto *pt, uint8_t *udf)
> +{
> +  /* This is a coarse check, because it's difficult to correlate the lifetime
> +  ** of slots and closures. But the number of false positives is quite low.
> +  ** A false positive may cause a slot not to be purged, which is just
> +  ** a missed optimization.
> +  */
> +  if ((pt->flags & PROTO_CHILD)) {
> +    ptrdiff_t i, j, n = pt->sizekgc;
> +    GCRef *kr = mref(pt->k, GCRef) - 1;
> +    for (i = 0; i < n; i++, kr--) {
> +      GCobj *o = gcref(*kr);
> +      if (o->gch.gct == ~LJ_TPROTO) {
> +	for (j = 0; j < gco2pt(o)->sizeuv; j++) {
> +	  uint32_t v = proto_uv(gco2pt(o))[j];
> +	  if ((v & PROTO_UV_LOCAL)) {
> +	    udf[(v & 0xff)] = 0;
> +	  }
> +	}
> +      }
> +    }
> +  }
> +}
> +
>   /* Purge dead slots before the next snapshot. */
>   void lj_snap_purge(jit_State *J)
>   {
> @@ -310,9 +335,12 @@ void lj_snap_purge(jit_State *J)
>     if (bc_op(*J->pc) == BC_FUNCV && maxslot > J->pt->numparams)
>       maxslot = J->pt->numparams;
>     s = snap_usedef(J, udf, J->pc, maxslot);
> -  for (; s < maxslot; s++)
> -    if (udf[s] != 0)
> -      J->base[s] = 0;  /* Purge dead slots. */
> +  if (s < maxslot) {
> +    snap_useuv(J->pt, udf);
> +    for (; s < maxslot; s++)
> +      if (udf[s] != 0)
> +	J->base[s] = 0;  /* Purge dead slots. */
> +  }
>   }
>   
>   /* Shrink last snapshot. */
> @@ -325,6 +353,7 @@ void lj_snap_shrink(jit_State *J)
>     BCReg maxslot = J->maxslot;
>     BCReg baseslot = J->baseslot;
>     BCReg minslot = snap_usedef(J, udf, snap_pc(&map[nent]), maxslot);
> +  if (minslot < maxslot) snap_useuv(J->pt, udf);
>     maxslot += baseslot;
>     minslot += baseslot;
>     snap->nslots = (uint8_t)maxslot;
> diff --git a/test/tarantool-tests/lj-737-snap-use-def-upvalues.test.lua b/test/tarantool-tests/lj-737-snap-use-def-upvalues.test.lua
> new file mode 100644
> index 00000000..8535f9f6
> --- /dev/null
> +++ b/test/tarantool-tests/lj-737-snap-use-def-upvalues.test.lua
> @@ -0,0 +1,55 @@
> +local tap = require('tap')
> +
> +-- Test file to demonstrate LuaJIT misbehaviour in use-def
> +-- snapshot analysis for local upvalues.
> +-- See also https://github.com/LuaJIT/LuaJIT/issues/737.
> +
> +local test = tap.test('lj-737-snap-use-def-upvalues'):skipcond({
> +  ['Test requires JIT enabled'] = not jit.status(),
> +})
> +
> +test:plan(1)
> +
> +-- XXX: simplify `jit.dump()` output.
> +local fmod = math.fmod
> +
> +local EXPECTED = 'expected'
> +
> +jit.opt.start('hotloop=1')
> +
> +local function wrapped_trace(create_closure)
> +  local local_upvalue, closure
> +  if create_closure then
> +    closure = function() return local_upvalue end
> +  end
> +  for i = 1, 4 do
> +    -- On the second iteration, the trace is recorded.
> +    if i == 2 then
> +      -- Before the patch, this slot was considered unused by
> +      -- use-def analysis in the `snap_usedef()` since there are
> +      -- no open unpvalues for `closure()` on recording (1st call).
> +      local_upvalue = EXPECTED
> +      -- luacheck: ignore
> +      -- Emit an additional snapshot after setting the
> +      -- upvalue.
> +      if i == 0 then end
> +      -- Stitching ends the trace here.
> +      fmod(1,1)
> +      return closure
> +    end
> +  end
> +end
> +
> +-- Compile the trace.
> +local func_with_uv = wrapped_trace(false)
> +assert(func_with_uv == nil, 'no function is returned on the first call')
> +
> +-- Now run this trace when `closure()` is defined and has an open
> +-- local upvalue.
> +func_with_uv = wrapped_trace(true)
> +assert(type(func_with_uv) == 'function',
> +       'function is returned after the second call')
> +
> +test:is(func_with_uv(), EXPECTED, 'correct result of the closure call')
> +
> +test:done(true)

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [Tarantool-patches] [PATCH luajit] Consider slots used by upvalues in use-def analysis.
  2024-02-08 11:11 ` Sergey Bronnikov via Tarantool-patches
@ 2024-02-08 11:24   ` Sergey Kaplun via Tarantool-patches
  2024-02-08 12:49     ` Sergey Bronnikov via Tarantool-patches
  0 siblings, 1 reply; 6+ messages in thread
From: Sergey Kaplun via Tarantool-patches @ 2024-02-08 11:24 UTC (permalink / raw)
  To: Sergey Bronnikov; +Cc: tarantool-patches

Hi, Sergey!
Thanks for the review!
Fixed your comment and force-pushed the branch.

On 08.02.24, Sergey Bronnikov wrote:
> Hi, Sergey
> 
> thanks for the patch! LGTM with a minor comment
> 
> On 2/7/24 17:51, Sergey Kaplun wrote:
> > From: Mike Pall <mike>
> >
> > Reported by XmiliaH.
> >
> > (cherry picked from commit 3a654999c6f00de4cb9e61232d23579442e544a0)
> >
> > `snap_usedef()` analysis doesn't check slots for child upvalues of the
> > currentlly recorded function in use-def analysis. Hence, such slots may
> typo: currentlly

Fixed, thanks!
The new commit message is the following:

| Consider slots used by upvalues in use-def analysis.
|
| Reported by XmiliaH.
|
| (cherry picked from commit 3a654999c6f00de4cb9e61232d23579442e544a0)
|
| `snap_usedef()` analysis doesn't check slots for child upvalues of the
| currently recorded function in use-def analysis. Hence, such slots may
| be considered unused and not stored in the snapshot. So on snapshot
| restoration, values from these slots may be lost.
|
| This patch adds a marking for all such local upvalues.
|
| Sergey Kaplun:
| * added the description and the test for the problem
|
| Part of tarantool/tarantool#9595

> > be considered unused and not stored in the snapshot. So on snapshot
> > restoration, values from these slots may be lost.
> >
> > This patch adds a marking for all such local upvalues.
> >
> > Sergey Kaplun:
> > * added the description and the test for the problem
> >
> > Part of tarantool/tarantool#9595
> > ---

-- 
Best regards,
Sergey Kaplun

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [Tarantool-patches] [PATCH luajit] Consider slots used by upvalues in use-def analysis.
  2024-02-08 11:24   ` Sergey Kaplun via Tarantool-patches
@ 2024-02-08 12:49     ` Sergey Bronnikov via Tarantool-patches
  0 siblings, 0 replies; 6+ messages in thread
From: Sergey Bronnikov via Tarantool-patches @ 2024-02-08 12:49 UTC (permalink / raw)
  To: Sergey Kaplun; +Cc: tarantool-patches


On 2/8/24 14:24, Sergey Kaplun wrote:
> Hi, Sergey!
> Thanks for the review!
> Fixed your comment and force-pushed the branch.
Hi, thanks for the fix. LGTM
<snipped>

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [Tarantool-patches] [PATCH luajit] Consider slots used by upvalues in use-def analysis.
  2024-02-07 14:51 [Tarantool-patches] [PATCH luajit] Consider slots used by upvalues in use-def analysis Sergey Kaplun via Tarantool-patches
  2024-02-08 11:11 ` Sergey Bronnikov via Tarantool-patches
@ 2024-02-09 16:54 ` Maxim Kokryashkin via Tarantool-patches
  2024-02-15 13:48 ` Igor Munkin via Tarantool-patches
  2 siblings, 0 replies; 6+ messages in thread
From: Maxim Kokryashkin via Tarantool-patches @ 2024-02-09 16:54 UTC (permalink / raw)
  To: Sergey Kaplun; +Cc: tarantool-patches

Hi, Sergey!
Thanks for the patch!
LGTM

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [Tarantool-patches] [PATCH luajit] Consider slots used by upvalues in use-def analysis.
  2024-02-07 14:51 [Tarantool-patches] [PATCH luajit] Consider slots used by upvalues in use-def analysis Sergey Kaplun via Tarantool-patches
  2024-02-08 11:11 ` Sergey Bronnikov via Tarantool-patches
  2024-02-09 16:54 ` Maxim Kokryashkin via Tarantool-patches
@ 2024-02-15 13:48 ` Igor Munkin via Tarantool-patches
  2 siblings, 0 replies; 6+ messages in thread
From: Igor Munkin via Tarantool-patches @ 2024-02-15 13:48 UTC (permalink / raw)
  To: Sergey Kaplun; +Cc: tarantool-patches

Sergey,

I've checked the patchset into all long-term branches in
tarantool/luajit and bumped a new version in master, release/3.0 and
release/2.11.

On 07.02.24, Sergey Kaplun via Tarantool-patches wrote:
> From: Mike Pall <mike>
> 
> Reported by XmiliaH.
> 
> (cherry picked from commit 3a654999c6f00de4cb9e61232d23579442e544a0)
> 
> `snap_usedef()` analysis doesn't check slots for child upvalues of the
> currentlly recorded function in use-def analysis. Hence, such slots may
> be considered unused and not stored in the snapshot. So on snapshot
> restoration, values from these slots may be lost.
> 
> This patch adds a marking for all such local upvalues.
> 
> Sergey Kaplun:
> * added the description and the test for the problem
> 
> Part of tarantool/tarantool#9595
> ---
> 
> Branch: https://github.com/tarantool/luajit/tree/skaplun/lj-737-snap-usedef-upvalues
> Tarantool PR: https://github.com/tarantool/tarantool/pull/9662
> Related issues:
> * https://github.com/tarantool/tarantool/issues/9595
> * https://github.com/LuaJIT/LuaJIT/issues/737
> 
>  src/lj_snap.c                                 | 35 +++++++++++-
>  .../lj-737-snap-use-def-upvalues.test.lua     | 55 +++++++++++++++++++
>  2 files changed, 87 insertions(+), 3 deletions(-)
>  create mode 100644 test/tarantool-tests/lj-737-snap-use-def-upvalues.test.lua
> 

<snipped>

> -- 
> 2.43.0
> 

-- 
Best regards,
IM

^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2024-02-15 14:00 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2024-02-07 14:51 [Tarantool-patches] [PATCH luajit] Consider slots used by upvalues in use-def analysis Sergey Kaplun via Tarantool-patches
2024-02-08 11:11 ` Sergey Bronnikov via Tarantool-patches
2024-02-08 11:24   ` Sergey Kaplun via Tarantool-patches
2024-02-08 12:49     ` Sergey Bronnikov via Tarantool-patches
2024-02-09 16:54 ` Maxim Kokryashkin via Tarantool-patches
2024-02-15 13:48 ` Igor Munkin via Tarantool-patches

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox