Re: How come simple "closures" aren't compiled?

  • From: Luke Gorrie <luke@xxxxxxxx>
  • To: luajit@xxxxxxxxxxxxx
  • Date: Sun, 4 Dec 2016 15:43:43 +0100

On 3 December 2016 at 11:50, Las <lasssafin@xxxxxxxxx> wrote:


For example:

local n = 0;
local f = function() return 1 end
n = f();

Couldn't f be compiled pretty easily, since it doesn't depend on any
upvalues?


This code can JIT but there are a couple of reasons it did not here:

The JIT waits until a function has been called often enough to become "hot"
before it bothers to compile it;

In practice most functions are not compiled in isolation but rather inlined
into a larger "trace" and compiled together with their callers.


If you want to force the JIT to compile the f() function in isolation then
you could change the example to call it in a loop and also ensure that the
loop itself is not compiled (because then it would inline f() instead of
compiling it separately):

-- simple.lua
local n = 0;
local f = function() return 1 end

-- Create a function that will call f() in a loop
local loop = function ()
   for i = 1, 1000 do
      n = f()
   end
end

-- Disable JIT for the loop (to avoid noise.)
jit.off(loop)

-- Run the loop.
loop()


This will generate machine code for f():

$ ./luajit -jdump simple.lua
---- TRACE 1 start simple.lua:2
0001  KSHORT   0   1
0002  RET1     0   2
---- TRACE 1 IR
---- TRACE 1 mcode 42
0bcbffcf  mov dword [0x405ed410], 0x1
0bcbffda  movsd xmm7, [0x40604d68]
0bcbffe3  movsd [rdx], xmm7
0bcbffe7  xor eax, eax
0bcbffe9  mov ebx, 0x405f5300
0bcbffee  mov r14d, 0x405edfe0
0bcbfff4  jmp 0x00420eb9
---- TRACE 1 stop -> return


You could make the example slightly more juicy by instead calling f() in a
loop, summing the return values, and allowing the JIT to operate on the
loop:

-- simple2.lua
local n = 0;
local f = function() return 1 end
for i = 1, 100 do n = n + f() end


Then the whole loop, including the inlined call to f(), will compile into
these four instructions:

->LOOP:
0bcbffe0  addsd xmm7, xmm0
0bcbffe4  add ebp, +0x01
0bcbffe7  cmp ebp, 0x3b9aca00
0bcbffed  jle 0x0bcbffe0 ->LOOP

where the first instruction (addsd xmm7, xmm0) is the entire code for the
statement n = n + f().

That is not bad: calling your function, returning its result, and adding it
to the accumulator all with one instruction :-)

Cheers,
-Luke

Other related posts: