Phân tích JIT trong LuaJIT

LuaJIT là một trình thông dịch Lua có hiệu suất cao, ngoài việc triển khai trực tiếp bằng mã máy, nó còn hỗ trợ chế độ JIT (Just-in-Time). Chế độ JIT biên dịch mã byte của LuaJIT thành mã máy mà bộ xử lý có thể thực thi trực tiếp, từ đó tăng tốc độ thực thi so với việc diễn dịch. LuaJIT bao gồm 97 lệnh byte, ví dụ như lệnh FORL đại diện cho vòng lặp for kiểu số, cùng với IFORL (thực thi ở chế độ thông dịch) và JFORL (thực thi ở chế độ JIT). Trình thông dịch cũng thực hiện việc dịch từng lệnh byte, dưới đây là ví dụ về bộ dịch X86. LuaJIT tối ưu hóa chuỗi lệnh, khi địa chỉ của một lệnh được đánh dấu là hot, nó bắt đầu theo dõi chuỗi lệnh tuyến tính và khi thoát khỏi quá trình theo dõi, chuỗi lệnh sẽ được biên dịch thành mã máy. Tuy nhiên, chỉ có các lệnh FUNCF, FORL, ITERL, LOOP được theo dõi, tức là bắt đầu của một hàm và các vòng lặp, ví dụ khi thực thi lệnh FORL ở chế độ thông dịch:
case BC_FORL:
    |.if JIT
    |  kiemtra_nutnong RB
    |.endif
    | // Tiếp tục. Giả sử BC_IFORL nằm sau và ins_AJ là thao tác không làm gì.
break;
Đầu tiên kiểm tra xem có ở chế độ JIT hay không, nếu có thì gọi khối kiemtra_nutnong để xác định điểm nóng, tương tự với lệnh FUNCF, gọi khối kiemtra_goiham:
case BC_FUNCF:
    |.if JIT
    |  kiemtra_goiham RB
    |.endif
case BC_FUNCV:  /* Chưa thực hiện: hàm biến đối số được biên dịch. */
    | // Tiếp tục. Giả sử BC_IFUNCF/BC_IFUNCV nằm sau và ins_AD là thao tác không làm gì.
break;
Khối kiemtra_nutnong được định nghĩa như sau:
|// Giảm giá trị đếm đã băm và kích hoạt ghi lại dấu vết nếu bằng không.
|.macro kiemtra_nutnong, thanh
|  mov thanh, PC
|  shr thanh, 1
|  and thanh, KHOAMASK_DEM
|  sub word [DIEUCHUYEN+thanh+GG_DIEUCHUYEN2DEM], DEM_LOOP
|  jb ->kiemtra_nutnong_them
|.endmacro
Nó dịch chuyển địa chỉ lệnh hiện tại sang phải một vị trí, thực hiện phép AND với KHOAMASK_DEM để lấy chỉ mục (phép băm), dựa vào chỉ mục này tìm giá trị đếm, trừ đi DEM_LOOP, nếu giá trị đếm nhỏ hơn 0 thì nhảy đến kiemtra_nutnong_them để tiếp tục thực thi.
|->kiemtra_nutnong_them:               // Giá trị đếm điểm nóng giảm xuống dưới 0.
|.if JIT
|  mov HAMHIENHANH:thanh, [BASE-8]           // Giống như curr_topL(L).
|  mov thanh, HAMHIENHANH:thanh->pc
|  movzx TD, byte [thanh+PC2PROTOTYPE(kichthuocnganhang)]
|  lea TD, [BASE+TD*8]
|  mov L:thanh, LUU_TRU_L
|  mov L:thanh->base, BASE
|  mov L:thanh->top, TD
|  mov THAMSO2, PC
|  lea THAMSO1, [DIEUCHUYEN+GG_DIEUCHUYEN2J]
|  mov aword [DIEUCHUYEN+DIEUCHUYEN_J(L)], L:thanhaword
|  mov LUU_TRU_PC, PC
|  call ngoaichuong_lj_trace_nutnong@8          // (trangthai_JIT *J, const BCIns *pc)
|  jmp <3
|.endif
</pre>

Đầu tiên lấy hàm hiện hành và con trỏ PC của mã byte, lấy kích thước ngăn hàng và lưu vào TD, tiếp theo lưu vị trí top vào TD, sau khi thiết lập một số tham số, gọi lj_trace_nutnong để bắt đầu quá trình theo dõi điểm nóng, hàm này nằm trong lj_trace.c:

/* Một giá trị đếm đã kích hoạt. Bắt đầu ghi lại dấu vết gốc. */
void LUU_THUC_HIEN_NHANH(lj_trangthai_JIT *J, const BCIns *pc)
{
  /* Lưu ý: pc ở đây là con trỏ PC của mã byte thông dịch, nó bị lệch 1 đơn vị. */
  LUU_LOI
  /* Đặt lại giá trị đếm. */
  datagiatriddem(J2GG(J), pc, J->thamso[JIT_P_nutnong]*DEM_LOOP);
  /* Chỉ bắt đầu một dấu vết mới nếu không đang ghi lại hoặc bên trong cuộc gọi __gc hoặc sự kiện vm. */
  if (J->trangthai == LJ_TRACE_IDLE &&
      !(J2G(J)->khoamask & (KHOA_GC|KHOA_SU_KIEN_VM))) {
    J->cha = 0;  /* Dấu vết gốc. */
    J->sokhigiai = 0;
    J->trangthai = LJ_TRACE_BATDAU;
    luu_tru_ins(J, pc-1);
  }
  PHUC_HOI_LOI
}
Sau khi đặt trạng thái thành LJ_TRACE_BATDAU, bắt đầu gọi luu_tru_ins để bắt đầu quá trình theo dõi điểm nóng:
/* Một lệnh byte chuẩn bị được thực thi. Ghi lại nó. */
void luu_tru_ins(lj_trangthai_JIT *J, const BCIns *pc)
{
  /* Lưu ý: J->L đã được thiết lập. pc ở đây là con trỏ PC chính xác của mã byte. */
  J->pc = pc;
  J->fn = ham_hienhanh(J->L);
  J->pt = la_ham_lua(J->fn) ? prototypedulieu(J->fn) : NULL;
  while (lj_vm_cpcall(J->L, NULL, (void *)J, trangthai_dauvao) != 0)
    J->trangthai = LJ_TRACE_LOI;
}
Ở đây pc trỏ đến lệnh byte, trong vòng lặp liên tục thực hiện và theo dõi, quá trình theo dõi được thực hiện thông qua hàm trangthai_dauvao, hàm này có 7 trạng thái:
/* Trạng thái máy trạng thái biên dịch dấu vết. */
typedef enum {
  LJ_TRACE_IDLE,  /* Máy trạng thái biên dịch dấu vết đang chờ. */
  LJ_TRACE_HOẠTĐỘNG = 0x10,
  LJ_TRACE_GHILOG,  /* Ghi log mã byte đang hoạt động. */
  LJ_TRACE_BATDAU, /* Dấu vết mới bắt đầu. */
  LJ_TRACE_KETTHUC,   /* Kết thúc dấu vết. */
  LJ_TRACE_THANHMA,   /* Biên dịch mã máy dấu vết. */
  LJ_TRACE_LOI    /* Dấu vết bị hủy bỏ với lỗi. */
} TrangThaiDauvao;
IDLE biểu thị trạng thái chờ, GHILOG biểu thị đang ghi log, KETTHUC biểu thị kết thúc, THANHMA biểu thị bắt đầu biên dịch mã máy, quá trình chuyển đổi trạng thái này được thực hiện như sau:
/* Máy trạng thái cho máy biên dịch dấu vết.

Thẻ: luajit JIT bytecode MachineCode optimization

Đăng vào ngày 31 tháng 5 lúc 01:21