From 7f32dc3140cd4c283d837f98bd6cf8c36c15c62b Mon Sep 17 00:00:00 2001 From: Andy Belle-Isle Date: Sun, 25 Aug 2019 15:23:18 -0400 Subject: [PATCH] Added LuaJIT library files --- lib/LuaJIT/.gitignore | 7 + lib/LuaJIT/Makefile | 721 +++ lib/LuaJIT/Makefile.dep | 246 + lib/LuaJIT/host/.gitignore | 3 + lib/LuaJIT/host/README | 4 + lib/LuaJIT/host/buildvm.c | 518 +++ lib/LuaJIT/host/buildvm.h | 105 + lib/LuaJIT/host/buildvm.o | Bin 0 -> 88800 bytes lib/LuaJIT/host/buildvm_asm.c | 359 ++ lib/LuaJIT/host/buildvm_asm.o | Bin 0 -> 6768 bytes lib/LuaJIT/host/buildvm_fold.c | 229 + lib/LuaJIT/host/buildvm_fold.o | Bin 0 -> 9568 bytes lib/LuaJIT/host/buildvm_lib.c | 457 ++ lib/LuaJIT/host/buildvm_lib.o | Bin 0 -> 18776 bytes lib/LuaJIT/host/buildvm_libbc.h | 56 + lib/LuaJIT/host/buildvm_peobj.c | 392 ++ lib/LuaJIT/host/buildvm_peobj.o | Bin 0 -> 5264 bytes lib/LuaJIT/host/genlibbc.lua | 197 + lib/LuaJIT/host/genminilua.lua | 429 ++ lib/LuaJIT/host/minilua.c | 7770 +++++++++++++++++++++++++++++++ lib/LuaJIT/host/minilua.o | Bin 0 -> 162968 bytes lib/LuaJIT/jit/.gitignore | 1 + lib/LuaJIT/jit/bc.lua | 190 + lib/LuaJIT/jit/bcsave.lua | 661 +++ lib/LuaJIT/jit/dis_arm.lua | 689 +++ lib/LuaJIT/jit/dis_arm64.lua | 1216 +++++ lib/LuaJIT/jit/dis_arm64be.lua | 12 + lib/LuaJIT/jit/dis_mips.lua | 443 ++ lib/LuaJIT/jit/dis_mips64.lua | 17 + lib/LuaJIT/jit/dis_mips64el.lua | 17 + lib/LuaJIT/jit/dis_mipsel.lua | 17 + lib/LuaJIT/jit/dis_ppc.lua | 591 +++ lib/LuaJIT/jit/dis_x64.lua | 17 + lib/LuaJIT/jit/dis_x86.lua | 953 ++++ lib/LuaJIT/jit/dump.lua | 712 +++ lib/LuaJIT/jit/p.lua | 311 ++ lib/LuaJIT/jit/v.lua | 170 + lib/LuaJIT/jit/zone.lua | 45 + lib/LuaJIT/lauxlib.h | 161 + lib/LuaJIT/lib_aux.c | 374 ++ lib/LuaJIT/lib_aux.o | Bin 0 -> 10632 bytes lib/LuaJIT/lib_aux_dyn.o | Bin 0 -> 10520 bytes lib/LuaJIT/lib_base.c | 679 +++ lib/LuaJIT/lib_base.o | Bin 0 -> 19608 bytes lib/LuaJIT/lib_base_dyn.o | Bin 0 -> 19584 bytes lib/LuaJIT/lib_bit.c | 180 + lib/LuaJIT/lib_bit.o | Bin 0 -> 4808 bytes lib/LuaJIT/lib_bit_dyn.o | Bin 0 -> 4808 bytes lib/LuaJIT/lib_debug.c | 405 ++ lib/LuaJIT/lib_debug.o | Bin 0 -> 16248 bytes lib/LuaJIT/lib_debug_dyn.o | Bin 0 -> 16216 bytes lib/LuaJIT/lib_ffi.c | 872 ++++ lib/LuaJIT/lib_ffi.o | Bin 0 -> 22984 bytes lib/LuaJIT/lib_ffi_dyn.o | Bin 0 -> 22984 bytes lib/LuaJIT/lib_init.c | 55 + lib/LuaJIT/lib_init.o | Bin 0 -> 3496 bytes lib/LuaJIT/lib_init_dyn.o | Bin 0 -> 3496 bytes lib/LuaJIT/lib_io.c | 539 +++ lib/LuaJIT/lib_io.o | Bin 0 -> 15360 bytes lib/LuaJIT/lib_io_dyn.o | Bin 0 -> 15360 bytes lib/LuaJIT/lib_jit.c | 777 ++++ lib/LuaJIT/lib_jit.o | Bin 0 -> 19776 bytes lib/LuaJIT/lib_jit_dyn.o | Bin 0 -> 19776 bytes lib/LuaJIT/lib_math.c | 226 + lib/LuaJIT/lib_math.o | Bin 0 -> 5296 bytes lib/LuaJIT/lib_math_dyn.o | Bin 0 -> 5296 bytes lib/LuaJIT/lib_os.c | 292 ++ lib/LuaJIT/lib_os.o | Bin 0 -> 10304 bytes lib/LuaJIT/lib_os_dyn.o | Bin 0 -> 10304 bytes lib/LuaJIT/lib_package.c | 623 +++ lib/LuaJIT/lib_package.o | Bin 0 -> 18456 bytes lib/LuaJIT/lib_package_dyn.o | Bin 0 -> 18456 bytes lib/LuaJIT/lib_string.c | 748 +++ lib/LuaJIT/lib_string.o | Bin 0 -> 16344 bytes lib/LuaJIT/lib_string_dyn.o | Bin 0 -> 16344 bytes lib/LuaJIT/lib_table.c | 327 ++ lib/LuaJIT/lib_table.o | Bin 0 -> 8808 bytes lib/LuaJIT/lib_table_dyn.o | Bin 0 -> 8808 bytes lib/LuaJIT/libluajit.a | Bin 0 -> 866936 bytes lib/LuaJIT/libluajit.so | Bin 0 -> 503336 bytes lib/LuaJIT/lj.supp | 41 + lib/LuaJIT/lj_alloc.c | 1490 ++++++ lib/LuaJIT/lj_alloc.h | 17 + lib/LuaJIT/lj_alloc.o | Bin 0 -> 13248 bytes lib/LuaJIT/lj_alloc_dyn.o | Bin 0 -> 13248 bytes lib/LuaJIT/lj_api.c | 1292 +++++ lib/LuaJIT/lj_api.o | Bin 0 -> 25440 bytes lib/LuaJIT/lj_api_dyn.o | Bin 0 -> 25480 bytes lib/LuaJIT/lj_arch.h | 599 +++ lib/LuaJIT/lj_asm.c | 2414 ++++++++++ lib/LuaJIT/lj_asm.h | 17 + lib/LuaJIT/lj_asm.o | Bin 0 -> 57712 bytes lib/LuaJIT/lj_asm_arm.h | 2210 +++++++++ lib/LuaJIT/lj_asm_arm64.h | 2031 ++++++++ lib/LuaJIT/lj_asm_dyn.o | Bin 0 -> 57712 bytes lib/LuaJIT/lj_asm_mips.h | 2659 +++++++++++ lib/LuaJIT/lj_asm_ppc.h | 2251 +++++++++ lib/LuaJIT/lj_asm_x86.h | 3121 +++++++++++++ lib/LuaJIT/lj_bc.c | 14 + lib/LuaJIT/lj_bc.h | 265 ++ lib/LuaJIT/lj_bc.o | Bin 0 -> 1704 bytes lib/LuaJIT/lj_bc_dyn.o | Bin 0 -> 1704 bytes lib/LuaJIT/lj_bcdump.h | 68 + lib/LuaJIT/lj_bcread.c | 457 ++ lib/LuaJIT/lj_bcread.o | Bin 0 -> 7152 bytes lib/LuaJIT/lj_bcread_dyn.o | Bin 0 -> 7152 bytes lib/LuaJIT/lj_bcwrite.c | 361 ++ lib/LuaJIT/lj_bcwrite.o | Bin 0 -> 5520 bytes lib/LuaJIT/lj_bcwrite_dyn.o | Bin 0 -> 5520 bytes lib/LuaJIT/lj_buf.c | 232 + lib/LuaJIT/lj_buf.h | 103 + lib/LuaJIT/lj_buf.o | Bin 0 -> 5624 bytes lib/LuaJIT/lj_buf_dyn.o | Bin 0 -> 5624 bytes lib/LuaJIT/lj_carith.c | 437 ++ lib/LuaJIT/lj_carith.h | 38 + lib/LuaJIT/lj_carith.o | Bin 0 -> 8584 bytes lib/LuaJIT/lj_carith_dyn.o | Bin 0 -> 8584 bytes lib/LuaJIT/lj_ccall.c | 1183 +++++ lib/LuaJIT/lj_ccall.h | 194 + lib/LuaJIT/lj_ccall.o | Bin 0 -> 5928 bytes lib/LuaJIT/lj_ccall_dyn.o | Bin 0 -> 5928 bytes lib/LuaJIT/lj_ccallback.c | 787 ++++ lib/LuaJIT/lj_ccallback.h | 25 + lib/LuaJIT/lj_ccallback.o | Bin 0 -> 4920 bytes lib/LuaJIT/lj_ccallback_dyn.o | Bin 0 -> 4920 bytes lib/LuaJIT/lj_cconv.c | 754 +++ lib/LuaJIT/lj_cconv.h | 70 + lib/LuaJIT/lj_cconv.o | Bin 0 -> 13280 bytes lib/LuaJIT/lj_cconv_dyn.o | Bin 0 -> 13280 bytes lib/LuaJIT/lj_cdata.c | 299 ++ lib/LuaJIT/lj_cdata.h | 78 + lib/LuaJIT/lj_cdata.o | Bin 0 -> 4864 bytes lib/LuaJIT/lj_cdata_dyn.o | Bin 0 -> 4864 bytes lib/LuaJIT/lj_char.c | 43 + lib/LuaJIT/lj_char.h | 42 + lib/LuaJIT/lj_char.o | Bin 0 -> 1304 bytes lib/LuaJIT/lj_char_dyn.o | Bin 0 -> 1304 bytes lib/LuaJIT/lj_clib.c | 430 ++ lib/LuaJIT/lj_clib.h | 29 + lib/LuaJIT/lj_clib.o | Bin 0 -> 4880 bytes lib/LuaJIT/lj_clib_dyn.o | Bin 0 -> 4880 bytes lib/LuaJIT/lj_cparse.c | 1898 ++++++++ lib/LuaJIT/lj_cparse.h | 65 + lib/LuaJIT/lj_cparse.o | Bin 0 -> 31520 bytes lib/LuaJIT/lj_cparse_dyn.o | Bin 0 -> 31520 bytes lib/LuaJIT/lj_crecord.c | 1892 ++++++++ lib/LuaJIT/lj_crecord.h | 38 + lib/LuaJIT/lj_crecord.o | Bin 0 -> 38208 bytes lib/LuaJIT/lj_crecord_dyn.o | Bin 0 -> 38208 bytes lib/LuaJIT/lj_ctype.c | 637 +++ lib/LuaJIT/lj_ctype.h | 461 ++ lib/LuaJIT/lj_ctype.o | Bin 0 -> 13016 bytes lib/LuaJIT/lj_ctype_dyn.o | Bin 0 -> 13016 bytes lib/LuaJIT/lj_debug.c | 699 +++ lib/LuaJIT/lj_debug.h | 65 + lib/LuaJIT/lj_debug.o | Bin 0 -> 14336 bytes lib/LuaJIT/lj_debug_dyn.o | Bin 0 -> 14336 bytes lib/LuaJIT/lj_def.h | 361 ++ lib/LuaJIT/lj_dispatch.c | 557 +++ lib/LuaJIT/lj_dispatch.h | 156 + lib/LuaJIT/lj_dispatch.o | Bin 0 -> 8328 bytes lib/LuaJIT/lj_dispatch_dyn.o | Bin 0 -> 8328 bytes lib/LuaJIT/lj_emit_arm.h | 357 ++ lib/LuaJIT/lj_emit_arm64.h | 419 ++ lib/LuaJIT/lj_emit_mips.h | 295 ++ lib/LuaJIT/lj_emit_ppc.h | 238 + lib/LuaJIT/lj_emit_x86.h | 573 +++ lib/LuaJIT/lj_err.c | 854 ++++ lib/LuaJIT/lj_err.h | 41 + lib/LuaJIT/lj_err.o | Bin 0 -> 15272 bytes lib/LuaJIT/lj_err_dyn.o | Bin 0 -> 15344 bytes lib/LuaJIT/lj_errmsg.h | 190 + lib/LuaJIT/lj_ff.h | 18 + lib/LuaJIT/lj_ffrecord.c | 1226 +++++ lib/LuaJIT/lj_ffrecord.h | 24 + lib/LuaJIT/lj_ffrecord.o | Bin 0 -> 33592 bytes lib/LuaJIT/lj_ffrecord_dyn.o | Bin 0 -> 33592 bytes lib/LuaJIT/lj_frame.h | 297 ++ lib/LuaJIT/lj_func.c | 187 + lib/LuaJIT/lj_func.h | 24 + lib/LuaJIT/lj_func.o | Bin 0 -> 3400 bytes lib/LuaJIT/lj_func_dyn.o | Bin 0 -> 3400 bytes lib/LuaJIT/lj_gc.c | 854 ++++ lib/LuaJIT/lj_gc.h | 134 + lib/LuaJIT/lj_gc.o | Bin 0 -> 11912 bytes lib/LuaJIT/lj_gc_dyn.o | Bin 0 -> 11912 bytes lib/LuaJIT/lj_gdbjit.c | 817 ++++ lib/LuaJIT/lj_gdbjit.h | 22 + lib/LuaJIT/lj_gdbjit.o | Bin 0 -> 920 bytes lib/LuaJIT/lj_gdbjit_dyn.o | Bin 0 -> 920 bytes lib/LuaJIT/lj_ir.c | 494 ++ lib/LuaJIT/lj_ir.h | 590 +++ lib/LuaJIT/lj_ir.o | Bin 0 -> 14160 bytes lib/LuaJIT/lj_ir_dyn.o | Bin 0 -> 14160 bytes lib/LuaJIT/lj_ircall.h | 358 ++ lib/LuaJIT/lj_iropt.h | 162 + lib/LuaJIT/lj_jit.h | 505 ++ lib/LuaJIT/lj_lex.c | 509 ++ lib/LuaJIT/lj_lex.h | 86 + lib/LuaJIT/lj_lex.o | Bin 0 -> 19424 bytes lib/LuaJIT/lj_lex_dyn.o | Bin 0 -> 19424 bytes lib/LuaJIT/lj_lib.c | 303 ++ lib/LuaJIT/lj_lib.h | 115 + lib/LuaJIT/lj_lib.o | Bin 0 -> 7248 bytes lib/LuaJIT/lj_lib_dyn.o | Bin 0 -> 7248 bytes lib/LuaJIT/lj_load.c | 168 + lib/LuaJIT/lj_load.o | Bin 0 -> 5552 bytes lib/LuaJIT/lj_load_dyn.o | Bin 0 -> 5552 bytes lib/LuaJIT/lj_mcode.c | 381 ++ lib/LuaJIT/lj_mcode.h | 30 + lib/LuaJIT/lj_mcode.o | Bin 0 -> 3960 bytes lib/LuaJIT/lj_mcode_dyn.o | Bin 0 -> 3960 bytes lib/LuaJIT/lj_meta.c | 477 ++ lib/LuaJIT/lj_meta.h | 38 + lib/LuaJIT/lj_meta.o | Bin 0 -> 10112 bytes lib/LuaJIT/lj_meta_dyn.o | Bin 0 -> 10112 bytes lib/LuaJIT/lj_obj.c | 50 + lib/LuaJIT/lj_obj.h | 991 ++++ lib/LuaJIT/lj_obj.o | Bin 0 -> 2768 bytes lib/LuaJIT/lj_obj_dyn.o | Bin 0 -> 2768 bytes lib/LuaJIT/lj_opt_dce.c | 78 + lib/LuaJIT/lj_opt_dce.o | Bin 0 -> 1888 bytes lib/LuaJIT/lj_opt_dce_dyn.o | Bin 0 -> 1888 bytes lib/LuaJIT/lj_opt_fold.c | 2555 ++++++++++ lib/LuaJIT/lj_opt_fold.o | Bin 0 -> 47336 bytes lib/LuaJIT/lj_opt_fold_dyn.o | Bin 0 -> 47336 bytes lib/LuaJIT/lj_opt_loop.c | 449 ++ lib/LuaJIT/lj_opt_loop.o | Bin 0 -> 5072 bytes lib/LuaJIT/lj_opt_loop_dyn.o | Bin 0 -> 5072 bytes lib/LuaJIT/lj_opt_mem.c | 935 ++++ lib/LuaJIT/lj_opt_mem.o | Bin 0 -> 10968 bytes lib/LuaJIT/lj_opt_mem_dyn.o | Bin 0 -> 10968 bytes lib/LuaJIT/lj_opt_narrow.c | 654 +++ lib/LuaJIT/lj_opt_narrow.o | Bin 0 -> 9808 bytes lib/LuaJIT/lj_opt_narrow_dyn.o | Bin 0 -> 9808 bytes lib/LuaJIT/lj_opt_sink.c | 251 + lib/LuaJIT/lj_opt_sink.o | Bin 0 -> 5696 bytes lib/LuaJIT/lj_opt_sink_dyn.o | Bin 0 -> 5696 bytes lib/LuaJIT/lj_opt_split.c | 870 ++++ lib/LuaJIT/lj_opt_split.o | Bin 0 -> 920 bytes lib/LuaJIT/lj_opt_split_dyn.o | Bin 0 -> 920 bytes lib/LuaJIT/lj_parse.c | 2728 +++++++++++ lib/LuaJIT/lj_parse.h | 18 + lib/LuaJIT/lj_parse.o | Bin 0 -> 36792 bytes lib/LuaJIT/lj_parse_dyn.o | Bin 0 -> 36792 bytes lib/LuaJIT/lj_profile.c | 368 ++ lib/LuaJIT/lj_profile.h | 21 + lib/LuaJIT/lj_profile.o | Bin 0 -> 4168 bytes lib/LuaJIT/lj_profile_dyn.o | Bin 0 -> 4168 bytes lib/LuaJIT/lj_record.c | 2649 +++++++++++ lib/LuaJIT/lj_record.h | 45 + lib/LuaJIT/lj_record.o | Bin 0 -> 46768 bytes lib/LuaJIT/lj_record_dyn.o | Bin 0 -> 46768 bytes lib/LuaJIT/lj_snap.c | 916 ++++ lib/LuaJIT/lj_snap.h | 34 + lib/LuaJIT/lj_snap.o | Bin 0 -> 13408 bytes lib/LuaJIT/lj_snap_dyn.o | Bin 0 -> 13408 bytes lib/LuaJIT/lj_state.c | 300 ++ lib/LuaJIT/lj_state.h | 35 + lib/LuaJIT/lj_state.o | Bin 0 -> 5864 bytes lib/LuaJIT/lj_state_dyn.o | Bin 0 -> 5864 bytes lib/LuaJIT/lj_str.c | 197 + lib/LuaJIT/lj_str.h | 27 + lib/LuaJIT/lj_str.o | Bin 0 -> 3880 bytes lib/LuaJIT/lj_str_dyn.o | Bin 0 -> 3880 bytes lib/LuaJIT/lj_strfmt.c | 472 ++ lib/LuaJIT/lj_strfmt.h | 125 + lib/LuaJIT/lj_strfmt.o | Bin 0 -> 11696 bytes lib/LuaJIT/lj_strfmt_dyn.o | Bin 0 -> 11696 bytes lib/LuaJIT/lj_strfmt_num.c | 592 +++ lib/LuaJIT/lj_strfmt_num.o | Bin 0 -> 10856 bytes lib/LuaJIT/lj_strfmt_num_dyn.o | Bin 0 -> 10856 bytes lib/LuaJIT/lj_strscan.c | 547 +++ lib/LuaJIT/lj_strscan.h | 39 + lib/LuaJIT/lj_strscan.o | Bin 0 -> 6952 bytes lib/LuaJIT/lj_strscan_dyn.o | Bin 0 -> 6952 bytes lib/LuaJIT/lj_tab.c | 688 +++ lib/LuaJIT/lj_tab.h | 73 + lib/LuaJIT/lj_tab.o | Bin 0 -> 9120 bytes lib/LuaJIT/lj_tab_dyn.o | Bin 0 -> 9120 bytes lib/LuaJIT/lj_target.h | 164 + lib/LuaJIT/lj_target_arm.h | 270 ++ lib/LuaJIT/lj_target_arm64.h | 332 ++ lib/LuaJIT/lj_target_mips.h | 377 ++ lib/LuaJIT/lj_target_ppc.h | 280 ++ lib/LuaJIT/lj_target_x86.h | 362 ++ lib/LuaJIT/lj_trace.c | 909 ++++ lib/LuaJIT/lj_trace.h | 55 + lib/LuaJIT/lj_trace.o | Bin 0 -> 13264 bytes lib/LuaJIT/lj_trace_dyn.o | Bin 0 -> 13264 bytes lib/LuaJIT/lj_traceerr.h | 61 + lib/LuaJIT/lj_udata.c | 34 + lib/LuaJIT/lj_udata.h | 14 + lib/LuaJIT/lj_udata.o | Bin 0 -> 1544 bytes lib/LuaJIT/lj_udata_dyn.o | Bin 0 -> 1544 bytes lib/LuaJIT/lj_vm.h | 120 + lib/LuaJIT/lj_vm.o | Bin 0 -> 29328 bytes lib/LuaJIT/lj_vm_dyn.o | Bin 0 -> 29328 bytes lib/LuaJIT/lj_vmevent.c | 58 + lib/LuaJIT/lj_vmevent.h | 59 + lib/LuaJIT/lj_vmevent.o | Bin 0 -> 2760 bytes lib/LuaJIT/lj_vmevent_dyn.o | Bin 0 -> 2712 bytes lib/LuaJIT/lj_vmmath.c | 152 + lib/LuaJIT/lj_vmmath.o | Bin 0 -> 4632 bytes lib/LuaJIT/lj_vmmath_dyn.o | Bin 0 -> 4632 bytes lib/LuaJIT/ljamalg.c | 97 + lib/LuaJIT/lua.h | 402 ++ lib/LuaJIT/lua.hpp | 9 + lib/LuaJIT/luaconf.h | 152 + lib/LuaJIT/luajit.c | 587 +++ lib/LuaJIT/luajit.h | 79 + lib/LuaJIT/luajit.o | Bin 0 -> 20448 bytes lib/LuaJIT/lualib.h | 43 + lib/LuaJIT/msvcbuild.bat | 122 + lib/LuaJIT/ps4build.bat | 123 + lib/LuaJIT/psvitabuild.bat | 93 + lib/LuaJIT/vm_arm.dasc | 4593 ++++++++++++++++++ lib/LuaJIT/vm_arm64.dasc | 3988 ++++++++++++++++ lib/LuaJIT/vm_mips.dasc | 5264 +++++++++++++++++++++ lib/LuaJIT/vm_mips64.dasc | 5112 ++++++++++++++++++++ lib/LuaJIT/vm_ppc.dasc | 6053 ++++++++++++++++++++++++ lib/LuaJIT/vm_x64.dasc | 4909 +++++++++++++++++++ lib/LuaJIT/vm_x86.dasc | 5780 +++++++++++++++++++++++ lib/LuaJIT/xb1build.bat | 101 + lib/LuaJIT/xedkbuild.bat | 92 + 325 files changed, 123032 insertions(+) create mode 100644 lib/LuaJIT/.gitignore create mode 100644 lib/LuaJIT/Makefile create mode 100644 lib/LuaJIT/Makefile.dep create mode 100644 lib/LuaJIT/host/.gitignore create mode 100644 lib/LuaJIT/host/README create mode 100644 lib/LuaJIT/host/buildvm.c create mode 100644 lib/LuaJIT/host/buildvm.h create mode 100644 lib/LuaJIT/host/buildvm.o create mode 100644 lib/LuaJIT/host/buildvm_asm.c create mode 100644 lib/LuaJIT/host/buildvm_asm.o create mode 100644 lib/LuaJIT/host/buildvm_fold.c create mode 100644 lib/LuaJIT/host/buildvm_fold.o create mode 100644 lib/LuaJIT/host/buildvm_lib.c create mode 100644 lib/LuaJIT/host/buildvm_lib.o create mode 100644 lib/LuaJIT/host/buildvm_libbc.h create mode 100644 lib/LuaJIT/host/buildvm_peobj.c create mode 100644 lib/LuaJIT/host/buildvm_peobj.o create mode 100644 lib/LuaJIT/host/genlibbc.lua create mode 100644 lib/LuaJIT/host/genminilua.lua create mode 100644 lib/LuaJIT/host/minilua.c create mode 100644 lib/LuaJIT/host/minilua.o create mode 100644 lib/LuaJIT/jit/.gitignore create mode 100644 lib/LuaJIT/jit/bc.lua create mode 100644 lib/LuaJIT/jit/bcsave.lua create mode 100644 lib/LuaJIT/jit/dis_arm.lua create mode 100644 lib/LuaJIT/jit/dis_arm64.lua create mode 100644 lib/LuaJIT/jit/dis_arm64be.lua create mode 100644 lib/LuaJIT/jit/dis_mips.lua create mode 100644 lib/LuaJIT/jit/dis_mips64.lua create mode 100644 lib/LuaJIT/jit/dis_mips64el.lua create mode 100644 lib/LuaJIT/jit/dis_mipsel.lua create mode 100644 lib/LuaJIT/jit/dis_ppc.lua create mode 100644 lib/LuaJIT/jit/dis_x64.lua create mode 100644 lib/LuaJIT/jit/dis_x86.lua create mode 100644 lib/LuaJIT/jit/dump.lua create mode 100644 lib/LuaJIT/jit/p.lua create mode 100644 lib/LuaJIT/jit/v.lua create mode 100644 lib/LuaJIT/jit/zone.lua create mode 100644 lib/LuaJIT/lauxlib.h create mode 100644 lib/LuaJIT/lib_aux.c create mode 100644 lib/LuaJIT/lib_aux.o create mode 100644 lib/LuaJIT/lib_aux_dyn.o create mode 100644 lib/LuaJIT/lib_base.c create mode 100644 lib/LuaJIT/lib_base.o create mode 100644 lib/LuaJIT/lib_base_dyn.o create mode 100644 lib/LuaJIT/lib_bit.c create mode 100644 lib/LuaJIT/lib_bit.o create mode 100644 lib/LuaJIT/lib_bit_dyn.o create mode 100644 lib/LuaJIT/lib_debug.c create mode 100644 lib/LuaJIT/lib_debug.o create mode 100644 lib/LuaJIT/lib_debug_dyn.o create mode 100644 lib/LuaJIT/lib_ffi.c create mode 100644 lib/LuaJIT/lib_ffi.o create mode 100644 lib/LuaJIT/lib_ffi_dyn.o create mode 100644 lib/LuaJIT/lib_init.c create mode 100644 lib/LuaJIT/lib_init.o create mode 100644 lib/LuaJIT/lib_init_dyn.o create mode 100644 lib/LuaJIT/lib_io.c create mode 100644 lib/LuaJIT/lib_io.o create mode 100644 lib/LuaJIT/lib_io_dyn.o create mode 100644 lib/LuaJIT/lib_jit.c create mode 100644 lib/LuaJIT/lib_jit.o create mode 100644 lib/LuaJIT/lib_jit_dyn.o create mode 100644 lib/LuaJIT/lib_math.c create mode 100644 lib/LuaJIT/lib_math.o create mode 100644 lib/LuaJIT/lib_math_dyn.o create mode 100644 lib/LuaJIT/lib_os.c create mode 100644 lib/LuaJIT/lib_os.o create mode 100644 lib/LuaJIT/lib_os_dyn.o create mode 100644 lib/LuaJIT/lib_package.c create mode 100644 lib/LuaJIT/lib_package.o create mode 100644 lib/LuaJIT/lib_package_dyn.o create mode 100644 lib/LuaJIT/lib_string.c create mode 100644 lib/LuaJIT/lib_string.o create mode 100644 lib/LuaJIT/lib_string_dyn.o create mode 100644 lib/LuaJIT/lib_table.c create mode 100644 lib/LuaJIT/lib_table.o create mode 100644 lib/LuaJIT/lib_table_dyn.o create mode 100644 lib/LuaJIT/libluajit.a create mode 100755 lib/LuaJIT/libluajit.so create mode 100644 lib/LuaJIT/lj.supp create mode 100644 lib/LuaJIT/lj_alloc.c create mode 100644 lib/LuaJIT/lj_alloc.h create mode 100644 lib/LuaJIT/lj_alloc.o create mode 100644 lib/LuaJIT/lj_alloc_dyn.o create mode 100644 lib/LuaJIT/lj_api.c create mode 100644 lib/LuaJIT/lj_api.o create mode 100644 lib/LuaJIT/lj_api_dyn.o create mode 100644 lib/LuaJIT/lj_arch.h create mode 100644 lib/LuaJIT/lj_asm.c create mode 100644 lib/LuaJIT/lj_asm.h create mode 100644 lib/LuaJIT/lj_asm.o create mode 100644 lib/LuaJIT/lj_asm_arm.h create mode 100644 lib/LuaJIT/lj_asm_arm64.h create mode 100644 lib/LuaJIT/lj_asm_dyn.o create mode 100644 lib/LuaJIT/lj_asm_mips.h create mode 100644 lib/LuaJIT/lj_asm_ppc.h create mode 100644 lib/LuaJIT/lj_asm_x86.h create mode 100644 lib/LuaJIT/lj_bc.c create mode 100644 lib/LuaJIT/lj_bc.h create mode 100644 lib/LuaJIT/lj_bc.o create mode 100644 lib/LuaJIT/lj_bc_dyn.o create mode 100644 lib/LuaJIT/lj_bcdump.h create mode 100644 lib/LuaJIT/lj_bcread.c create mode 100644 lib/LuaJIT/lj_bcread.o create mode 100644 lib/LuaJIT/lj_bcread_dyn.o create mode 100644 lib/LuaJIT/lj_bcwrite.c create mode 100644 lib/LuaJIT/lj_bcwrite.o create mode 100644 lib/LuaJIT/lj_bcwrite_dyn.o create mode 100644 lib/LuaJIT/lj_buf.c create mode 100644 lib/LuaJIT/lj_buf.h create mode 100644 lib/LuaJIT/lj_buf.o create mode 100644 lib/LuaJIT/lj_buf_dyn.o create mode 100644 lib/LuaJIT/lj_carith.c create mode 100644 lib/LuaJIT/lj_carith.h create mode 100644 lib/LuaJIT/lj_carith.o create mode 100644 lib/LuaJIT/lj_carith_dyn.o create mode 100644 lib/LuaJIT/lj_ccall.c create mode 100644 lib/LuaJIT/lj_ccall.h create mode 100644 lib/LuaJIT/lj_ccall.o create mode 100644 lib/LuaJIT/lj_ccall_dyn.o create mode 100644 lib/LuaJIT/lj_ccallback.c create mode 100644 lib/LuaJIT/lj_ccallback.h create mode 100644 lib/LuaJIT/lj_ccallback.o create mode 100644 lib/LuaJIT/lj_ccallback_dyn.o create mode 100644 lib/LuaJIT/lj_cconv.c create mode 100644 lib/LuaJIT/lj_cconv.h create mode 100644 lib/LuaJIT/lj_cconv.o create mode 100644 lib/LuaJIT/lj_cconv_dyn.o create mode 100644 lib/LuaJIT/lj_cdata.c create mode 100644 lib/LuaJIT/lj_cdata.h create mode 100644 lib/LuaJIT/lj_cdata.o create mode 100644 lib/LuaJIT/lj_cdata_dyn.o create mode 100644 lib/LuaJIT/lj_char.c create mode 100644 lib/LuaJIT/lj_char.h create mode 100644 lib/LuaJIT/lj_char.o create mode 100644 lib/LuaJIT/lj_char_dyn.o create mode 100644 lib/LuaJIT/lj_clib.c create mode 100644 lib/LuaJIT/lj_clib.h create mode 100644 lib/LuaJIT/lj_clib.o create mode 100644 lib/LuaJIT/lj_clib_dyn.o create mode 100644 lib/LuaJIT/lj_cparse.c create mode 100644 lib/LuaJIT/lj_cparse.h create mode 100644 lib/LuaJIT/lj_cparse.o create mode 100644 lib/LuaJIT/lj_cparse_dyn.o create mode 100644 lib/LuaJIT/lj_crecord.c create mode 100644 lib/LuaJIT/lj_crecord.h create mode 100644 lib/LuaJIT/lj_crecord.o create mode 100644 lib/LuaJIT/lj_crecord_dyn.o create mode 100644 lib/LuaJIT/lj_ctype.c create mode 100644 lib/LuaJIT/lj_ctype.h create mode 100644 lib/LuaJIT/lj_ctype.o create mode 100644 lib/LuaJIT/lj_ctype_dyn.o create mode 100644 lib/LuaJIT/lj_debug.c create mode 100644 lib/LuaJIT/lj_debug.h create mode 100644 lib/LuaJIT/lj_debug.o create mode 100644 lib/LuaJIT/lj_debug_dyn.o create mode 100644 lib/LuaJIT/lj_def.h create mode 100644 lib/LuaJIT/lj_dispatch.c create mode 100644 lib/LuaJIT/lj_dispatch.h create mode 100644 lib/LuaJIT/lj_dispatch.o create mode 100644 lib/LuaJIT/lj_dispatch_dyn.o create mode 100644 lib/LuaJIT/lj_emit_arm.h create mode 100644 lib/LuaJIT/lj_emit_arm64.h create mode 100644 lib/LuaJIT/lj_emit_mips.h create mode 100644 lib/LuaJIT/lj_emit_ppc.h create mode 100644 lib/LuaJIT/lj_emit_x86.h create mode 100644 lib/LuaJIT/lj_err.c create mode 100644 lib/LuaJIT/lj_err.h create mode 100644 lib/LuaJIT/lj_err.o create mode 100644 lib/LuaJIT/lj_err_dyn.o create mode 100644 lib/LuaJIT/lj_errmsg.h create mode 100644 lib/LuaJIT/lj_ff.h create mode 100644 lib/LuaJIT/lj_ffrecord.c create mode 100644 lib/LuaJIT/lj_ffrecord.h create mode 100644 lib/LuaJIT/lj_ffrecord.o create mode 100644 lib/LuaJIT/lj_ffrecord_dyn.o create mode 100644 lib/LuaJIT/lj_frame.h create mode 100644 lib/LuaJIT/lj_func.c create mode 100644 lib/LuaJIT/lj_func.h create mode 100644 lib/LuaJIT/lj_func.o create mode 100644 lib/LuaJIT/lj_func_dyn.o create mode 100644 lib/LuaJIT/lj_gc.c create mode 100644 lib/LuaJIT/lj_gc.h create mode 100644 lib/LuaJIT/lj_gc.o create mode 100644 lib/LuaJIT/lj_gc_dyn.o create mode 100644 lib/LuaJIT/lj_gdbjit.c create mode 100644 lib/LuaJIT/lj_gdbjit.h create mode 100644 lib/LuaJIT/lj_gdbjit.o create mode 100644 lib/LuaJIT/lj_gdbjit_dyn.o create mode 100644 lib/LuaJIT/lj_ir.c create mode 100644 lib/LuaJIT/lj_ir.h create mode 100644 lib/LuaJIT/lj_ir.o create mode 100644 lib/LuaJIT/lj_ir_dyn.o create mode 100644 lib/LuaJIT/lj_ircall.h create mode 100644 lib/LuaJIT/lj_iropt.h create mode 100644 lib/LuaJIT/lj_jit.h create mode 100644 lib/LuaJIT/lj_lex.c create mode 100644 lib/LuaJIT/lj_lex.h create mode 100644 lib/LuaJIT/lj_lex.o create mode 100644 lib/LuaJIT/lj_lex_dyn.o create mode 100644 lib/LuaJIT/lj_lib.c create mode 100644 lib/LuaJIT/lj_lib.h create mode 100644 lib/LuaJIT/lj_lib.o create mode 100644 lib/LuaJIT/lj_lib_dyn.o create mode 100644 lib/LuaJIT/lj_load.c create mode 100644 lib/LuaJIT/lj_load.o create mode 100644 lib/LuaJIT/lj_load_dyn.o create mode 100644 lib/LuaJIT/lj_mcode.c create mode 100644 lib/LuaJIT/lj_mcode.h create mode 100644 lib/LuaJIT/lj_mcode.o create mode 100644 lib/LuaJIT/lj_mcode_dyn.o create mode 100644 lib/LuaJIT/lj_meta.c create mode 100644 lib/LuaJIT/lj_meta.h create mode 100644 lib/LuaJIT/lj_meta.o create mode 100644 lib/LuaJIT/lj_meta_dyn.o create mode 100644 lib/LuaJIT/lj_obj.c create mode 100644 lib/LuaJIT/lj_obj.h create mode 100644 lib/LuaJIT/lj_obj.o create mode 100644 lib/LuaJIT/lj_obj_dyn.o create mode 100644 lib/LuaJIT/lj_opt_dce.c create mode 100644 lib/LuaJIT/lj_opt_dce.o create mode 100644 lib/LuaJIT/lj_opt_dce_dyn.o create mode 100644 lib/LuaJIT/lj_opt_fold.c create mode 100644 lib/LuaJIT/lj_opt_fold.o create mode 100644 lib/LuaJIT/lj_opt_fold_dyn.o create mode 100644 lib/LuaJIT/lj_opt_loop.c create mode 100644 lib/LuaJIT/lj_opt_loop.o create mode 100644 lib/LuaJIT/lj_opt_loop_dyn.o create mode 100644 lib/LuaJIT/lj_opt_mem.c create mode 100644 lib/LuaJIT/lj_opt_mem.o create mode 100644 lib/LuaJIT/lj_opt_mem_dyn.o create mode 100644 lib/LuaJIT/lj_opt_narrow.c create mode 100644 lib/LuaJIT/lj_opt_narrow.o create mode 100644 lib/LuaJIT/lj_opt_narrow_dyn.o create mode 100644 lib/LuaJIT/lj_opt_sink.c create mode 100644 lib/LuaJIT/lj_opt_sink.o create mode 100644 lib/LuaJIT/lj_opt_sink_dyn.o create mode 100644 lib/LuaJIT/lj_opt_split.c create mode 100644 lib/LuaJIT/lj_opt_split.o create mode 100644 lib/LuaJIT/lj_opt_split_dyn.o create mode 100644 lib/LuaJIT/lj_parse.c create mode 100644 lib/LuaJIT/lj_parse.h create mode 100644 lib/LuaJIT/lj_parse.o create mode 100644 lib/LuaJIT/lj_parse_dyn.o create mode 100644 lib/LuaJIT/lj_profile.c create mode 100644 lib/LuaJIT/lj_profile.h create mode 100644 lib/LuaJIT/lj_profile.o create mode 100644 lib/LuaJIT/lj_profile_dyn.o create mode 100644 lib/LuaJIT/lj_record.c create mode 100644 lib/LuaJIT/lj_record.h create mode 100644 lib/LuaJIT/lj_record.o create mode 100644 lib/LuaJIT/lj_record_dyn.o create mode 100644 lib/LuaJIT/lj_snap.c create mode 100644 lib/LuaJIT/lj_snap.h create mode 100644 lib/LuaJIT/lj_snap.o create mode 100644 lib/LuaJIT/lj_snap_dyn.o create mode 100644 lib/LuaJIT/lj_state.c create mode 100644 lib/LuaJIT/lj_state.h create mode 100644 lib/LuaJIT/lj_state.o create mode 100644 lib/LuaJIT/lj_state_dyn.o create mode 100644 lib/LuaJIT/lj_str.c create mode 100644 lib/LuaJIT/lj_str.h create mode 100644 lib/LuaJIT/lj_str.o create mode 100644 lib/LuaJIT/lj_str_dyn.o create mode 100644 lib/LuaJIT/lj_strfmt.c create mode 100644 lib/LuaJIT/lj_strfmt.h create mode 100644 lib/LuaJIT/lj_strfmt.o create mode 100644 lib/LuaJIT/lj_strfmt_dyn.o create mode 100644 lib/LuaJIT/lj_strfmt_num.c create mode 100644 lib/LuaJIT/lj_strfmt_num.o create mode 100644 lib/LuaJIT/lj_strfmt_num_dyn.o create mode 100644 lib/LuaJIT/lj_strscan.c create mode 100644 lib/LuaJIT/lj_strscan.h create mode 100644 lib/LuaJIT/lj_strscan.o create mode 100644 lib/LuaJIT/lj_strscan_dyn.o create mode 100644 lib/LuaJIT/lj_tab.c create mode 100644 lib/LuaJIT/lj_tab.h create mode 100644 lib/LuaJIT/lj_tab.o create mode 100644 lib/LuaJIT/lj_tab_dyn.o create mode 100644 lib/LuaJIT/lj_target.h create mode 100644 lib/LuaJIT/lj_target_arm.h create mode 100644 lib/LuaJIT/lj_target_arm64.h create mode 100644 lib/LuaJIT/lj_target_mips.h create mode 100644 lib/LuaJIT/lj_target_ppc.h create mode 100644 lib/LuaJIT/lj_target_x86.h create mode 100644 lib/LuaJIT/lj_trace.c create mode 100644 lib/LuaJIT/lj_trace.h create mode 100644 lib/LuaJIT/lj_trace.o create mode 100644 lib/LuaJIT/lj_trace_dyn.o create mode 100644 lib/LuaJIT/lj_traceerr.h create mode 100644 lib/LuaJIT/lj_udata.c create mode 100644 lib/LuaJIT/lj_udata.h create mode 100644 lib/LuaJIT/lj_udata.o create mode 100644 lib/LuaJIT/lj_udata_dyn.o create mode 100644 lib/LuaJIT/lj_vm.h create mode 100644 lib/LuaJIT/lj_vm.o create mode 100644 lib/LuaJIT/lj_vm_dyn.o create mode 100644 lib/LuaJIT/lj_vmevent.c create mode 100644 lib/LuaJIT/lj_vmevent.h create mode 100644 lib/LuaJIT/lj_vmevent.o create mode 100644 lib/LuaJIT/lj_vmevent_dyn.o create mode 100644 lib/LuaJIT/lj_vmmath.c create mode 100644 lib/LuaJIT/lj_vmmath.o create mode 100644 lib/LuaJIT/lj_vmmath_dyn.o create mode 100644 lib/LuaJIT/ljamalg.c create mode 100644 lib/LuaJIT/lua.h create mode 100644 lib/LuaJIT/lua.hpp create mode 100644 lib/LuaJIT/luaconf.h create mode 100644 lib/LuaJIT/luajit.c create mode 100644 lib/LuaJIT/luajit.h create mode 100644 lib/LuaJIT/luajit.o create mode 100644 lib/LuaJIT/lualib.h create mode 100644 lib/LuaJIT/msvcbuild.bat create mode 100644 lib/LuaJIT/ps4build.bat create mode 100644 lib/LuaJIT/psvitabuild.bat create mode 100644 lib/LuaJIT/vm_arm.dasc create mode 100644 lib/LuaJIT/vm_arm64.dasc create mode 100644 lib/LuaJIT/vm_mips.dasc create mode 100644 lib/LuaJIT/vm_mips64.dasc create mode 100644 lib/LuaJIT/vm_ppc.dasc create mode 100644 lib/LuaJIT/vm_x64.dasc create mode 100644 lib/LuaJIT/vm_x86.dasc create mode 100644 lib/LuaJIT/xb1build.bat create mode 100644 lib/LuaJIT/xedkbuild.bat diff --git a/lib/LuaJIT/.gitignore b/lib/LuaJIT/.gitignore new file mode 100644 index 0000000..1a30573 --- /dev/null +++ b/lib/LuaJIT/.gitignore @@ -0,0 +1,7 @@ +luajit +lj_bcdef.h +lj_ffdef.h +lj_libdef.h +lj_recdef.h +lj_folddef.h +lj_vm.[sS] diff --git a/lib/LuaJIT/Makefile b/lib/LuaJIT/Makefile new file mode 100644 index 0000000..d22eb73 --- /dev/null +++ b/lib/LuaJIT/Makefile @@ -0,0 +1,721 @@ +############################################################################## +# LuaJIT Makefile. Requires GNU Make. +# +# Please read doc/install.html before changing any variables! +# +# Suitable for POSIX platforms (Linux, *BSD, OSX etc.). +# Also works with MinGW and Cygwin on Windows. +# Please check msvcbuild.bat for building with MSVC on Windows. +# +# Copyright (C) 2005-2017 Mike Pall. See Copyright Notice in luajit.h +############################################################################## + +MAJVER= 2 +MINVER= 1 +RELVER= 0 +ABIVER= 5.1 +NODOTABIVER= 51 + +############################################################################## +############################# COMPILER OPTIONS ############################# +############################################################################## +# These options mainly affect the speed of the JIT compiler itself, not the +# speed of the JIT-compiled code. Turn any of the optional settings on by +# removing the '#' in front of them. Make sure you force a full recompile +# with "make clean", followed by "make" if you change any options. +# +DEFAULT_CC = gcc +# +# LuaJIT builds as a native 32 or 64 bit binary by default. +CC= $(DEFAULT_CC) +# +# Use this if you want to force a 32 bit build on a 64 bit multilib OS. +#CC= $(DEFAULT_CC) -m32 +# +# Since the assembler part does NOT maintain a frame pointer, it's pointless +# to slow down the C part by not omitting it. Debugging, tracebacks and +# unwinding are not affected -- the assembler part has frame unwind +# information and GCC emits it where needed (x64) or with -g (see CCDEBUG). +CCOPT= -O2 -fomit-frame-pointer +# Use this if you want to generate a smaller binary (but it's slower): +#CCOPT= -Os -fomit-frame-pointer +# Note: it's no longer recommended to use -O3 with GCC 4.x. +# The I-Cache bloat usually outweighs the benefits from aggressive inlining. +# +# Target-specific compiler options: +# +# x86/x64 only: For GCC 4.2 or higher and if you don't intend to distribute +# the binaries to a different machine you could also use: -march=native +# +CCOPT_x86= -march=i686 -msse -msse2 -mfpmath=sse +CCOPT_x64= +CCOPT_arm= +CCOPT_arm64= +CCOPT_ppc= +CCOPT_mips= +# +CCDEBUG= +# Uncomment the next line to generate debug information: +#CCDEBUG= -g +# +CCWARN= -Wall +# Uncomment the next line to enable more warnings: +#CCWARN+= -Wextra -Wdeclaration-after-statement -Wredundant-decls -Wshadow -Wpointer-arith +# +############################################################################## + +############################################################################## +################################ BUILD MODE ################################ +############################################################################## +# The default build mode is mixed mode on POSIX. On Windows this is the same +# as dynamic mode. +# +# Mixed mode creates a static + dynamic library and a statically linked luajit. +BUILDMODE= mixed +# +# Static mode creates a static library and a statically linked luajit. +#BUILDMODE= static +# +# Dynamic mode creates a dynamic library and a dynamically linked luajit. +# Note: this executable will only run when the library is installed! +#BUILDMODE= dynamic +# +############################################################################## + +############################################################################## +################################# FEATURES ################################# +############################################################################## +# Enable/disable these features as needed, but make sure you force a full +# recompile with "make clean", followed by "make". +XCFLAGS= +# +# Permanently disable the FFI extension to reduce the size of the LuaJIT +# executable. But please consider that the FFI library is compiled-in, +# but NOT loaded by default. It only allocates any memory, if you actually +# make use of it. +#XCFLAGS+= -DLUAJIT_DISABLE_FFI +# +# Features from Lua 5.2 that are unlikely to break existing code are +# enabled by default. Some other features that *might* break some existing +# code (e.g. __pairs or os.execute() return values) can be enabled here. +# Note: this does not provide full compatibility with Lua 5.2 at this time. +#XCFLAGS+= -DLUAJIT_ENABLE_LUA52COMPAT +# +# Disable the JIT compiler, i.e. turn LuaJIT into a pure interpreter. +#XCFLAGS+= -DLUAJIT_DISABLE_JIT +# +# Some architectures (e.g. PPC) can use either single-number (1) or +# dual-number (2) mode. Uncomment one of these lines to override the +# default mode. Please see LJ_ARCH_NUMMODE in lj_arch.h for details. +#XCFLAGS+= -DLUAJIT_NUMMODE=1 +#XCFLAGS+= -DLUAJIT_NUMMODE=2 +# +# Enable GC64 mode for x64. +#XCFLAGS+= -DLUAJIT_ENABLE_GC64 +# +############################################################################## + +############################################################################## +############################ DEBUGGING SUPPORT ############################# +############################################################################## +# Enable these options as needed, but make sure you force a full recompile +# with "make clean", followed by "make". +# Note that most of these are NOT suitable for benchmarking or release mode! +# +# Use the system provided memory allocator (realloc) instead of the +# bundled memory allocator. This is slower, but sometimes helpful for +# debugging. This option cannot be enabled on x64 without GC64, since +# realloc usually doesn't return addresses in the right address range. +# OTOH this option is mandatory for Valgrind's memcheck tool on x64 and +# the only way to get useful results from it for all other architectures. +#XCFLAGS+= -DLUAJIT_USE_SYSMALLOC +# +# This define is required to run LuaJIT under Valgrind. The Valgrind +# header files must be installed. You should enable debug information, too. +# Use --suppressions=lj.supp to avoid some false positives. +#XCFLAGS+= -DLUAJIT_USE_VALGRIND +# +# This is the client for the GDB JIT API. GDB 7.0 or higher is required +# to make use of it. See lj_gdbjit.c for details. Enabling this causes +# a non-negligible overhead, even when not running under GDB. +#XCFLAGS+= -DLUAJIT_USE_GDBJIT +# +# Turn on assertions for the Lua/C API to debug problems with lua_* calls. +# This is rather slow -- use only while developing C libraries/embeddings. +#XCFLAGS+= -DLUA_USE_APICHECK +# +# Turn on assertions for the whole LuaJIT VM. This significantly slows down +# everything. Use only if you suspect a problem with LuaJIT itself. +#XCFLAGS+= -DLUA_USE_ASSERT +# +############################################################################## +# You probably don't need to change anything below this line! +############################################################################## + +############################################################################## +# Host system detection. +############################################################################## + +ifeq (Windows,$(findstring Windows,$(OS))$(MSYSTEM)$(TERM)) + HOST_SYS= Windows + HOST_RM= del +else + HOST_SYS:= $(shell uname -s) + ifneq (,$(findstring MINGW,$(HOST_SYS))) + HOST_SYS= Windows + HOST_MSYS= mingw + endif + ifneq (,$(findstring MSYS,$(HOST_SYS))) + HOST_SYS= Windows + HOST_MSYS= mingw + endif + ifneq (,$(findstring CYGWIN,$(HOST_SYS))) + HOST_SYS= Windows + HOST_MSYS= cygwin + endif +endif + +############################################################################## +# Flags and options for host and target. +############################################################################## + +# You can override the following variables at the make command line: +# CC HOST_CC STATIC_CC DYNAMIC_CC +# CFLAGS HOST_CFLAGS TARGET_CFLAGS +# LDFLAGS HOST_LDFLAGS TARGET_LDFLAGS TARGET_SHLDFLAGS +# LIBS HOST_LIBS TARGET_LIBS +# CROSS HOST_SYS TARGET_SYS TARGET_FLAGS +# +# Cross-compilation examples: +# make HOST_CC="gcc -m32" CROSS=i586-mingw32msvc- TARGET_SYS=Windows +# make HOST_CC="gcc -m32" CROSS=powerpc-linux-gnu- + +ASOPTIONS= $(CCOPT) $(CCWARN) $(XCFLAGS) $(CFLAGS) +CCOPTIONS= $(CCDEBUG) $(ASOPTIONS) +LDOPTIONS= $(CCDEBUG) $(LDFLAGS) + +HOST_CC= $(CC) +HOST_RM?= rm -f +# If left blank, minilua is built and used. You can supply an installed +# copy of (plain) Lua 5.1 or 5.2, plus Lua BitOp. E.g. with: HOST_LUA=lua +HOST_LUA= + +HOST_XCFLAGS= -I. +HOST_XLDFLAGS= +HOST_XLIBS= +HOST_ACFLAGS= $(CCOPTIONS) $(HOST_XCFLAGS) $(TARGET_ARCH) $(HOST_CFLAGS) +HOST_ALDFLAGS= $(LDOPTIONS) $(HOST_XLDFLAGS) $(HOST_LDFLAGS) +HOST_ALIBS= $(HOST_XLIBS) $(LIBS) $(HOST_LIBS) + +STATIC_CC = $(CROSS)$(CC) +DYNAMIC_CC = $(CROSS)$(CC) -fPIC +TARGET_CC= $(STATIC_CC) +TARGET_STCC= $(STATIC_CC) +TARGET_DYNCC= $(DYNAMIC_CC) +TARGET_LD= $(CROSS)$(CC) +TARGET_AR= $(CROSS)ar rcus +TARGET_STRIP= $(CROSS)strip + +TARGET_LIBPATH= $(or $(PREFIX),/usr/local)/$(or $(MULTILIB),lib) +TARGET_SONAME= libluajit-$(ABIVER).so.$(MAJVER) +TARGET_DYLIBNAME= libluajit-$(ABIVER).$(MAJVER).dylib +TARGET_DYLIBPATH= $(TARGET_LIBPATH)/$(TARGET_DYLIBNAME) +TARGET_DLLNAME= lua$(NODOTABIVER).dll +TARGET_XSHLDFLAGS= -shared -fPIC -Wl,-soname,$(TARGET_SONAME) +TARGET_DYNXLDOPTS= + +TARGET_LFSFLAGS= -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE +TARGET_XCFLAGS= $(TARGET_LFSFLAGS) -U_FORTIFY_SOURCE +TARGET_XLDFLAGS= +TARGET_XLIBS= -lm +TARGET_TCFLAGS= $(CCOPTIONS) $(TARGET_XCFLAGS) $(TARGET_FLAGS) $(TARGET_CFLAGS) +TARGET_ACFLAGS= $(CCOPTIONS) $(TARGET_XCFLAGS) $(TARGET_FLAGS) $(TARGET_CFLAGS) +TARGET_ASFLAGS= $(ASOPTIONS) $(TARGET_XCFLAGS) $(TARGET_FLAGS) $(TARGET_CFLAGS) +TARGET_ALDFLAGS= $(LDOPTIONS) $(TARGET_XLDFLAGS) $(TARGET_FLAGS) $(TARGET_LDFLAGS) +TARGET_ASHLDFLAGS= $(LDOPTIONS) $(TARGET_XSHLDFLAGS) $(TARGET_FLAGS) $(TARGET_SHLDFLAGS) +TARGET_ALIBS= $(TARGET_XLIBS) $(LIBS) $(TARGET_LIBS) + +TARGET_TESTARCH=$(shell $(TARGET_CC) $(TARGET_TCFLAGS) -E lj_arch.h -dM) +ifneq (,$(findstring LJ_TARGET_X64 ,$(TARGET_TESTARCH))) + TARGET_LJARCH= x64 +else +ifneq (,$(findstring LJ_TARGET_X86 ,$(TARGET_TESTARCH))) + TARGET_LJARCH= x86 +else +ifneq (,$(findstring LJ_TARGET_ARM ,$(TARGET_TESTARCH))) + TARGET_LJARCH= arm +else +ifneq (,$(findstring LJ_TARGET_ARM64 ,$(TARGET_TESTARCH))) + ifneq (,$(findstring __AARCH64EB__ ,$(TARGET_TESTARCH))) + TARGET_ARCH= -D__AARCH64EB__=1 + endif + TARGET_LJARCH= arm64 +else +ifneq (,$(findstring LJ_TARGET_PPC ,$(TARGET_TESTARCH))) + ifneq (,$(findstring LJ_LE 1,$(TARGET_TESTARCH))) + TARGET_ARCH= -DLJ_ARCH_ENDIAN=LUAJIT_LE + else + TARGET_ARCH= -DLJ_ARCH_ENDIAN=LUAJIT_BE + endif + TARGET_LJARCH= ppc +else +ifneq (,$(findstring LJ_TARGET_MIPS ,$(TARGET_TESTARCH))) + ifneq (,$(findstring MIPSEL ,$(TARGET_TESTARCH))) + TARGET_ARCH= -D__MIPSEL__=1 + endif + ifneq (,$(findstring LJ_TARGET_MIPS64 ,$(TARGET_TESTARCH))) + TARGET_LJARCH= mips64 + else + TARGET_LJARCH= mips + endif +else + $(error Unsupported target architecture) +endif +endif +endif +endif +endif +endif + +ifneq (,$(findstring LJ_TARGET_PS3 1,$(TARGET_TESTARCH))) + TARGET_SYS= PS3 + TARGET_ARCH+= -D__CELLOS_LV2__ + TARGET_XCFLAGS+= -DLUAJIT_USE_SYSMALLOC + TARGET_XLIBS+= -lpthread +endif + +TARGET_XCFLAGS+= $(CCOPT_$(TARGET_LJARCH)) +TARGET_ARCH+= $(patsubst %,-DLUAJIT_TARGET=LUAJIT_ARCH_%,$(TARGET_LJARCH)) + +ifneq (,$(PREFIX)) +ifneq (/usr/local,$(PREFIX)) + TARGET_XCFLAGS+= -DLUA_ROOT=\"$(PREFIX)\" + ifneq (/usr,$(PREFIX)) + TARGET_DYNXLDOPTS= -Wl,-rpath,$(TARGET_LIBPATH) + endif +endif +endif +ifneq (,$(MULTILIB)) + TARGET_XCFLAGS+= -DLUA_MULTILIB=\"$(MULTILIB)\" +endif +ifneq (,$(LMULTILIB)) + TARGET_XCFLAGS+= -DLUA_LMULTILIB=\"$(LMULTILIB)\" +endif + +############################################################################## +# Target system detection. +############################################################################## + +TARGET_SYS?= $(HOST_SYS) +ifeq (Windows,$(TARGET_SYS)) + TARGET_STRIP+= --strip-unneeded + TARGET_XSHLDFLAGS= -shared + TARGET_DYNXLDOPTS= +else + TARGET_AR+= 2>/dev/null +ifeq (,$(shell $(TARGET_CC) -o /dev/null -c -x c /dev/null -fno-stack-protector 2>/dev/null || echo 1)) + TARGET_XCFLAGS+= -fno-stack-protector +endif +ifeq (Darwin,$(TARGET_SYS)) + ifeq (,$(MACOSX_DEPLOYMENT_TARGET)) + export MACOSX_DEPLOYMENT_TARGET=10.4 + endif + TARGET_STRIP+= -x + TARGET_XSHLDFLAGS= -dynamiclib -single_module -undefined dynamic_lookup -fPIC + TARGET_DYNXLDOPTS= + TARGET_XSHLDFLAGS+= -install_name $(TARGET_DYLIBPATH) -compatibility_version $(MAJVER).$(MINVER) -current_version $(MAJVER).$(MINVER).$(RELVER) + ifeq (x64,$(TARGET_LJARCH)) + TARGET_XLDFLAGS+= -pagezero_size 10000 -image_base 100000000 + TARGET_XSHLDFLAGS+= -image_base 7fff04c4a000 + endif +else +ifeq (iOS,$(TARGET_SYS)) + TARGET_STRIP+= -x + TARGET_XSHLDFLAGS= -dynamiclib -single_module -undefined dynamic_lookup -fPIC + TARGET_DYNXLDOPTS= + TARGET_XSHLDFLAGS+= -install_name $(TARGET_DYLIBPATH) -compatibility_version $(MAJVER).$(MINVER) -current_version $(MAJVER).$(MINVER).$(RELVER) + ifeq (arm64,$(TARGET_LJARCH)) + TARGET_XCFLAGS+= -fno-omit-frame-pointer + endif +else + ifneq (SunOS,$(TARGET_SYS)) + ifneq (PS3,$(TARGET_SYS)) + TARGET_XLDFLAGS+= -Wl,-E + endif + endif + ifeq (Linux,$(TARGET_SYS)) + TARGET_XLIBS+= -ldl + endif + ifeq (GNU/kFreeBSD,$(TARGET_SYS)) + TARGET_XLIBS+= -ldl + endif +endif +endif +endif + +ifneq ($(HOST_SYS),$(TARGET_SYS)) + ifeq (Windows,$(TARGET_SYS)) + HOST_XCFLAGS+= -malign-double -DLUAJIT_OS=LUAJIT_OS_WINDOWS + else + ifeq (Linux,$(TARGET_SYS)) + HOST_XCFLAGS+= -DLUAJIT_OS=LUAJIT_OS_LINUX + else + ifeq (Darwin,$(TARGET_SYS)) + HOST_XCFLAGS+= -DLUAJIT_OS=LUAJIT_OS_OSX + else + ifeq (iOS,$(TARGET_SYS)) + HOST_XCFLAGS+= -DLUAJIT_OS=LUAJIT_OS_OSX + else + HOST_XCFLAGS+= -DLUAJIT_OS=LUAJIT_OS_OTHER + endif + endif + endif + endif +endif + +ifneq (,$(CCDEBUG)) + TARGET_STRIP= @: +endif + +############################################################################## +# Files and pathnames. +############################################################################## + +MINILUA_O= host/minilua.o +MINILUA_LIBS= -lm +MINILUA_T= host/minilua +MINILUA_X= $(MINILUA_T) + +ifeq (,$(HOST_LUA)) + HOST_LUA= $(MINILUA_X) + DASM_DEP= $(MINILUA_T) +endif + +DASM_DIR= ../dynasm +DASM= $(HOST_LUA) $(DASM_DIR)/dynasm.lua +DASM_XFLAGS= +DASM_AFLAGS= +DASM_ARCH= $(TARGET_LJARCH) + +ifneq (,$(findstring LJ_LE 1,$(TARGET_TESTARCH))) + DASM_AFLAGS+= -D ENDIAN_LE +else + DASM_AFLAGS+= -D ENDIAN_BE +endif +ifneq (,$(findstring LJ_ARCH_BITS 64,$(TARGET_TESTARCH))) + DASM_AFLAGS+= -D P64 +endif +ifneq (,$(findstring LJ_HASJIT 1,$(TARGET_TESTARCH))) + DASM_AFLAGS+= -D JIT +endif +ifneq (,$(findstring LJ_HASFFI 1,$(TARGET_TESTARCH))) + DASM_AFLAGS+= -D FFI +endif +ifneq (,$(findstring LJ_DUALNUM 1,$(TARGET_TESTARCH))) + DASM_AFLAGS+= -D DUALNUM +endif +ifneq (,$(findstring LJ_ARCH_HASFPU 1,$(TARGET_TESTARCH))) + DASM_AFLAGS+= -D FPU + TARGET_ARCH+= -DLJ_ARCH_HASFPU=1 +else + TARGET_ARCH+= -DLJ_ARCH_HASFPU=0 +endif +ifeq (,$(findstring LJ_ABI_SOFTFP 1,$(TARGET_TESTARCH))) + DASM_AFLAGS+= -D HFABI + TARGET_ARCH+= -DLJ_ABI_SOFTFP=0 +else + TARGET_ARCH+= -DLJ_ABI_SOFTFP=1 +endif +ifneq (,$(findstring LJ_NO_UNWIND 1,$(TARGET_TESTARCH))) + DASM_AFLAGS+= -D NO_UNWIND + TARGET_ARCH+= -DLUAJIT_NO_UNWIND +endif +DASM_AFLAGS+= -D VER=$(subst LJ_ARCH_VERSION_,,$(filter LJ_ARCH_VERSION_%,$(subst LJ_ARCH_VERSION ,LJ_ARCH_VERSION_,$(TARGET_TESTARCH)))) +ifeq (Windows,$(TARGET_SYS)) + DASM_AFLAGS+= -D WIN +endif +ifeq (x64,$(TARGET_LJARCH)) + ifeq (,$(findstring LJ_FR2 1,$(TARGET_TESTARCH))) + DASM_ARCH= x86 + endif +else +ifeq (arm,$(TARGET_LJARCH)) + ifeq (iOS,$(TARGET_SYS)) + DASM_AFLAGS+= -D IOS + endif +else +ifeq (ppc,$(TARGET_LJARCH)) + ifneq (,$(findstring LJ_ARCH_SQRT 1,$(TARGET_TESTARCH))) + DASM_AFLAGS+= -D SQRT + endif + ifneq (,$(findstring LJ_ARCH_ROUND 1,$(TARGET_TESTARCH))) + DASM_AFLAGS+= -D ROUND + endif + ifneq (,$(findstring LJ_ARCH_PPC32ON64 1,$(TARGET_TESTARCH))) + DASM_AFLAGS+= -D GPR64 + endif + ifeq (PS3,$(TARGET_SYS)) + DASM_AFLAGS+= -D PPE -D TOC + endif + ifneq (,$(findstring LJ_ARCH_PPC64 ,$(TARGET_TESTARCH))) + DASM_ARCH= ppc64 + endif +endif +endif +endif + +DASM_FLAGS= $(DASM_XFLAGS) $(DASM_AFLAGS) +DASM_DASC= vm_$(DASM_ARCH).dasc + +BUILDVM_O= host/buildvm.o host/buildvm_asm.o host/buildvm_peobj.o \ + host/buildvm_lib.o host/buildvm_fold.o +BUILDVM_T= host/buildvm +BUILDVM_X= $(BUILDVM_T) + +HOST_O= $(MINILUA_O) $(BUILDVM_O) +HOST_T= $(MINILUA_T) $(BUILDVM_T) + +LJVM_S= lj_vm.S +LJVM_O= lj_vm.o +LJVM_BOUT= $(LJVM_S) +LJVM_MODE= elfasm + +LJLIB_O= lib_base.o lib_math.o lib_bit.o lib_string.o lib_table.o \ + lib_io.o lib_os.o lib_package.o lib_debug.o lib_jit.o lib_ffi.o +LJLIB_C= $(LJLIB_O:.o=.c) + +LJCORE_O= lj_gc.o lj_err.o lj_char.o lj_bc.o lj_obj.o lj_buf.o \ + lj_str.o lj_tab.o lj_func.o lj_udata.o lj_meta.o lj_debug.o \ + lj_state.o lj_dispatch.o lj_vmevent.o lj_vmmath.o lj_strscan.o \ + lj_strfmt.o lj_strfmt_num.o lj_api.o lj_profile.o \ + lj_lex.o lj_parse.o lj_bcread.o lj_bcwrite.o lj_load.o \ + lj_ir.o lj_opt_mem.o lj_opt_fold.o lj_opt_narrow.o \ + lj_opt_dce.o lj_opt_loop.o lj_opt_split.o lj_opt_sink.o \ + lj_mcode.o lj_snap.o lj_record.o lj_crecord.o lj_ffrecord.o \ + lj_asm.o lj_trace.o lj_gdbjit.o \ + lj_ctype.o lj_cdata.o lj_cconv.o lj_ccall.o lj_ccallback.o \ + lj_carith.o lj_clib.o lj_cparse.o \ + lj_lib.o lj_alloc.o lib_aux.o \ + $(LJLIB_O) lib_init.o + +LJVMCORE_O= $(LJVM_O) $(LJCORE_O) +LJVMCORE_DYNO= $(LJVMCORE_O:.o=_dyn.o) + +LIB_VMDEF= jit/vmdef.lua +LIB_VMDEFP= $(LIB_VMDEF) + +LUAJIT_O= luajit.o +LUAJIT_A= libluajit.a +LUAJIT_SO= libluajit.so +LUAJIT_T= luajit + +ALL_T= $(LUAJIT_T) $(LUAJIT_A) $(LUAJIT_SO) $(HOST_T) +ALL_HDRGEN= lj_bcdef.h lj_ffdef.h lj_libdef.h lj_recdef.h lj_folddef.h \ + host/buildvm_arch.h +ALL_GEN= $(LJVM_S) $(ALL_HDRGEN) $(LIB_VMDEFP) +WIN_RM= *.obj *.lib *.exp *.dll *.exe *.manifest *.pdb *.ilk +ALL_RM= $(ALL_T) $(ALL_GEN) *.o host/*.o $(WIN_RM) + +############################################################################## +# Build mode handling. +############################################################################## + +# Mixed mode defaults. +TARGET_O= $(LUAJIT_A) +TARGET_T= $(LUAJIT_T) $(LUAJIT_SO) +TARGET_DEP= $(LIB_VMDEF) $(LUAJIT_SO) + +ifeq (Windows,$(TARGET_SYS)) + TARGET_DYNCC= $(STATIC_CC) + LJVM_MODE= peobj + LJVM_BOUT= $(LJVM_O) + LUAJIT_T= luajit.exe + ifeq (cygwin,$(HOST_MSYS)) + LUAJIT_SO= cyg$(TARGET_DLLNAME) + else + LUAJIT_SO= $(TARGET_DLLNAME) + endif + # Mixed mode is not supported on Windows. And static mode doesn't work well. + # C modules cannot be loaded, because they bind to lua51.dll. + ifneq (static,$(BUILDMODE)) + BUILDMODE= dynamic + TARGET_XCFLAGS+= -DLUA_BUILD_AS_DLL + endif +endif +ifeq (Darwin,$(TARGET_SYS)) + LJVM_MODE= machasm +endif +ifeq (iOS,$(TARGET_SYS)) + LJVM_MODE= machasm +endif +ifeq (SunOS,$(TARGET_SYS)) + BUILDMODE= static +endif +ifeq (PS3,$(TARGET_SYS)) + BUILDMODE= static +endif + +ifeq (Windows,$(HOST_SYS)) + MINILUA_T= host/minilua.exe + BUILDVM_T= host/buildvm.exe + ifeq (,$(HOST_MSYS)) + MINILUA_X= host\minilua + BUILDVM_X= host\buildvm + ALL_RM:= $(subst /,\,$(ALL_RM)) + endif +endif + +ifeq (static,$(BUILDMODE)) + TARGET_DYNCC= @: + TARGET_T= $(LUAJIT_T) + TARGET_DEP= $(LIB_VMDEF) +else +ifeq (dynamic,$(BUILDMODE)) + ifneq (Windows,$(TARGET_SYS)) + TARGET_CC= $(DYNAMIC_CC) + endif + TARGET_DYNCC= @: + LJVMCORE_DYNO= $(LJVMCORE_O) + TARGET_O= $(LUAJIT_SO) + TARGET_XLDFLAGS+= $(TARGET_DYNXLDOPTS) +else +ifeq (Darwin,$(TARGET_SYS)) + TARGET_DYNCC= @: + LJVMCORE_DYNO= $(LJVMCORE_O) +endif +ifeq (iOS,$(TARGET_SYS)) + TARGET_DYNCC= @: + LJVMCORE_DYNO= $(LJVMCORE_O) +endif +endif +endif + +Q= @ +E= @echo +#Q= +#E= @: + +############################################################################## +# Make targets. +############################################################################## + +default all: $(TARGET_T) + +amalg: + @grep "^[+|]" ljamalg.c + $(MAKE) all "LJCORE_O=ljamalg.o" + +clean: + $(HOST_RM) $(ALL_RM) + +libbc: + ./$(LUAJIT_T) host/genlibbc.lua -o host/buildvm_libbc.h $(LJLIB_C) + $(MAKE) all + +depend: + @for file in $(ALL_HDRGEN); do \ + test -f $$file || touch $$file; \ + done + @$(HOST_CC) $(HOST_ACFLAGS) -MM *.c host/*.c | \ + sed -e "s| [^ ]*/dasm_\S*\.h||g" \ + -e "s|^\([^l ]\)|host/\1|" \ + -e "s| lj_target_\S*\.h| lj_target_*.h|g" \ + -e "s| lj_emit_\S*\.h| lj_emit_*.h|g" \ + -e "s| lj_asm_\S*\.h| lj_asm_*.h|g" >Makefile.dep + @for file in $(ALL_HDRGEN); do \ + test -s $$file || $(HOST_RM) $$file; \ + done + +.PHONY: default all amalg clean libbc depend + +############################################################################## +# Rules for generated files. +############################################################################## + +$(MINILUA_T): $(MINILUA_O) + $(E) "HOSTLINK $@" + $(Q)$(HOST_CC) $(HOST_ALDFLAGS) -o $@ $(MINILUA_O) $(MINILUA_LIBS) $(HOST_ALIBS) + +host/buildvm_arch.h: $(DASM_DASC) $(DASM_DEP) $(DASM_DIR)/*.lua + $(E) "DYNASM $@" + $(Q)$(DASM) $(DASM_FLAGS) -o $@ $(DASM_DASC) + +host/buildvm.o: $(DASM_DIR)/dasm_*.h + +$(BUILDVM_T): $(BUILDVM_O) + $(E) "HOSTLINK $@" + $(Q)$(HOST_CC) $(HOST_ALDFLAGS) -o $@ $(BUILDVM_O) $(HOST_ALIBS) + +$(LJVM_BOUT): $(BUILDVM_T) + $(E) "BUILDVM $@" + $(Q)$(BUILDVM_X) -m $(LJVM_MODE) -o $@ + +lj_bcdef.h: $(BUILDVM_T) $(LJLIB_C) + $(E) "BUILDVM $@" + $(Q)$(BUILDVM_X) -m bcdef -o $@ $(LJLIB_C) + +lj_ffdef.h: $(BUILDVM_T) $(LJLIB_C) + $(E) "BUILDVM $@" + $(Q)$(BUILDVM_X) -m ffdef -o $@ $(LJLIB_C) + +lj_libdef.h: $(BUILDVM_T) $(LJLIB_C) + $(E) "BUILDVM $@" + $(Q)$(BUILDVM_X) -m libdef -o $@ $(LJLIB_C) + +lj_recdef.h: $(BUILDVM_T) $(LJLIB_C) + $(E) "BUILDVM $@" + $(Q)$(BUILDVM_X) -m recdef -o $@ $(LJLIB_C) + +$(LIB_VMDEF): $(BUILDVM_T) $(LJLIB_C) + $(E) "BUILDVM $@" + $(Q)$(BUILDVM_X) -m vmdef -o $(LIB_VMDEFP) $(LJLIB_C) + +lj_folddef.h: $(BUILDVM_T) lj_opt_fold.c + $(E) "BUILDVM $@" + $(Q)$(BUILDVM_X) -m folddef -o $@ lj_opt_fold.c + +############################################################################## +# Object file rules. +############################################################################## + +%.o: %.c + $(E) "CC $@" + $(Q)$(TARGET_DYNCC) $(TARGET_ACFLAGS) -c -o $(@:.o=_dyn.o) $< + $(Q)$(TARGET_CC) $(TARGET_ACFLAGS) -c -o $@ $< + +%.o: %.S + $(E) "ASM $@" + $(Q)$(TARGET_DYNCC) $(TARGET_ASFLAGS) -c -o $(@:.o=_dyn.o) $< + $(Q)$(TARGET_CC) $(TARGET_ASFLAGS) -c -o $@ $< + +$(LUAJIT_O): + $(E) "CC $@" + $(Q)$(TARGET_STCC) $(TARGET_ACFLAGS) -c -o $@ $< + +$(HOST_O): %.o: %.c + $(E) "HOSTCC $@" + $(Q)$(HOST_CC) $(HOST_ACFLAGS) -c -o $@ $< + +include Makefile.dep + +############################################################################## +# Target file rules. +############################################################################## + +$(LUAJIT_A): $(LJVMCORE_O) + $(E) "AR $@" + $(Q)$(TARGET_AR) $@ $(LJVMCORE_O) + +# The dependency on _O, but linking with _DYNO is intentional. +$(LUAJIT_SO): $(LJVMCORE_O) + $(E) "DYNLINK $@" + $(Q)$(TARGET_LD) $(TARGET_ASHLDFLAGS) -o $@ $(LJVMCORE_DYNO) $(TARGET_ALIBS) + $(Q)$(TARGET_STRIP) $@ + +$(LUAJIT_T): $(TARGET_O) $(LUAJIT_O) $(TARGET_DEP) + $(E) "LINK $@" + $(Q)$(TARGET_LD) $(TARGET_ALDFLAGS) -o $@ $(LUAJIT_O) $(TARGET_O) $(TARGET_ALIBS) + $(Q)$(TARGET_STRIP) $@ + $(E) "OK Successfully built LuaJIT" + +############################################################################## diff --git a/lib/LuaJIT/Makefile.dep b/lib/LuaJIT/Makefile.dep new file mode 100644 index 0000000..2b1cb5e --- /dev/null +++ b/lib/LuaJIT/Makefile.dep @@ -0,0 +1,246 @@ +lib_aux.o: lib_aux.c lua.h luaconf.h lauxlib.h lj_obj.h lj_def.h \ + lj_arch.h lj_err.h lj_errmsg.h lj_state.h lj_trace.h lj_jit.h lj_ir.h \ + lj_dispatch.h lj_bc.h lj_traceerr.h lj_lib.h lj_alloc.h +lib_base.o: lib_base.c lua.h luaconf.h lauxlib.h lualib.h lj_obj.h \ + lj_def.h lj_arch.h lj_gc.h lj_err.h lj_errmsg.h lj_debug.h lj_str.h \ + lj_tab.h lj_meta.h lj_state.h lj_frame.h lj_bc.h lj_ctype.h lj_cconv.h \ + lj_ff.h lj_ffdef.h lj_dispatch.h lj_jit.h lj_ir.h lj_char.h lj_strscan.h \ + lj_strfmt.h lj_lib.h lj_libdef.h +lib_bit.o: lib_bit.c lua.h luaconf.h lauxlib.h lualib.h lj_obj.h lj_def.h \ + lj_arch.h lj_err.h lj_errmsg.h lj_buf.h lj_gc.h lj_str.h lj_strscan.h \ + lj_strfmt.h lj_ctype.h lj_cdata.h lj_cconv.h lj_carith.h lj_ff.h \ + lj_ffdef.h lj_lib.h lj_libdef.h +lib_debug.o: lib_debug.c lua.h luaconf.h lauxlib.h lualib.h lj_obj.h \ + lj_def.h lj_arch.h lj_gc.h lj_err.h lj_errmsg.h lj_debug.h lj_lib.h \ + lj_libdef.h +lib_ffi.o: lib_ffi.c lua.h luaconf.h lauxlib.h lualib.h lj_obj.h lj_def.h \ + lj_arch.h lj_gc.h lj_err.h lj_errmsg.h lj_str.h lj_tab.h lj_meta.h \ + lj_ctype.h lj_cparse.h lj_cdata.h lj_cconv.h lj_carith.h lj_ccall.h \ + lj_ccallback.h lj_clib.h lj_strfmt.h lj_ff.h lj_ffdef.h lj_lib.h \ + lj_libdef.h +lib_init.o: lib_init.c lua.h luaconf.h lauxlib.h lualib.h lj_arch.h +lib_io.o: lib_io.c lua.h luaconf.h lauxlib.h lualib.h lj_obj.h lj_def.h \ + lj_arch.h lj_gc.h lj_err.h lj_errmsg.h lj_buf.h lj_str.h lj_state.h \ + lj_strfmt.h lj_ff.h lj_ffdef.h lj_lib.h lj_libdef.h +lib_jit.o: lib_jit.c lua.h luaconf.h lauxlib.h lualib.h lj_obj.h lj_def.h \ + lj_arch.h lj_gc.h lj_err.h lj_errmsg.h lj_debug.h lj_str.h lj_tab.h \ + lj_state.h lj_bc.h lj_ctype.h lj_ir.h lj_jit.h lj_ircall.h lj_iropt.h \ + lj_target.h lj_target_*.h lj_trace.h lj_dispatch.h lj_traceerr.h \ + lj_vm.h lj_vmevent.h lj_lib.h luajit.h lj_libdef.h +lib_math.o: lib_math.c lua.h luaconf.h lauxlib.h lualib.h lj_obj.h \ + lj_def.h lj_arch.h lj_lib.h lj_vm.h lj_libdef.h +lib_os.o: lib_os.c lua.h luaconf.h lauxlib.h lualib.h lj_obj.h lj_def.h \ + lj_arch.h lj_gc.h lj_err.h lj_errmsg.h lj_buf.h lj_str.h lj_lib.h \ + lj_libdef.h +lib_package.o: lib_package.c lua.h luaconf.h lauxlib.h lualib.h lj_obj.h \ + lj_def.h lj_arch.h lj_err.h lj_errmsg.h lj_lib.h +lib_string.o: lib_string.c lua.h luaconf.h lauxlib.h lualib.h lj_obj.h \ + lj_def.h lj_arch.h lj_gc.h lj_err.h lj_errmsg.h lj_buf.h lj_str.h \ + lj_tab.h lj_meta.h lj_state.h lj_ff.h lj_ffdef.h lj_bcdump.h lj_lex.h \ + lj_char.h lj_strfmt.h lj_lib.h lj_libdef.h +lib_table.o: lib_table.c lua.h luaconf.h lauxlib.h lualib.h lj_obj.h \ + lj_def.h lj_arch.h lj_gc.h lj_err.h lj_errmsg.h lj_buf.h lj_str.h \ + lj_tab.h lj_ff.h lj_ffdef.h lj_lib.h lj_libdef.h +lj_alloc.o: lj_alloc.c lj_def.h lua.h luaconf.h lj_arch.h lj_alloc.h +lj_api.o: lj_api.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h lj_gc.h \ + lj_err.h lj_errmsg.h lj_debug.h lj_str.h lj_tab.h lj_func.h lj_udata.h \ + lj_meta.h lj_state.h lj_bc.h lj_frame.h lj_trace.h lj_jit.h lj_ir.h \ + lj_dispatch.h lj_traceerr.h lj_vm.h lj_strscan.h lj_strfmt.h +lj_asm.o: lj_asm.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h lj_gc.h \ + lj_str.h lj_tab.h lj_frame.h lj_bc.h lj_ctype.h lj_ir.h lj_jit.h \ + lj_ircall.h lj_iropt.h lj_mcode.h lj_trace.h lj_dispatch.h lj_traceerr.h \ + lj_snap.h lj_asm.h lj_vm.h lj_target.h lj_target_*.h lj_emit_*.h \ + lj_asm_*.h +lj_bc.o: lj_bc.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h lj_bc.h \ + lj_bcdef.h +lj_bcread.o: lj_bcread.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \ + lj_gc.h lj_err.h lj_errmsg.h lj_buf.h lj_str.h lj_tab.h lj_bc.h \ + lj_ctype.h lj_cdata.h lualib.h lj_lex.h lj_bcdump.h lj_state.h \ + lj_strfmt.h +lj_bcwrite.o: lj_bcwrite.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \ + lj_gc.h lj_buf.h lj_str.h lj_bc.h lj_ctype.h lj_dispatch.h lj_jit.h \ + lj_ir.h lj_strfmt.h lj_bcdump.h lj_lex.h lj_err.h lj_errmsg.h lj_vm.h +lj_buf.o: lj_buf.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h lj_gc.h \ + lj_err.h lj_errmsg.h lj_buf.h lj_str.h lj_tab.h lj_strfmt.h +lj_carith.o: lj_carith.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \ + lj_gc.h lj_err.h lj_errmsg.h lj_tab.h lj_meta.h lj_ir.h lj_ctype.h \ + lj_cconv.h lj_cdata.h lj_carith.h lj_strscan.h +lj_ccall.o: lj_ccall.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \ + lj_gc.h lj_err.h lj_errmsg.h lj_tab.h lj_ctype.h lj_cconv.h lj_cdata.h \ + lj_ccall.h lj_trace.h lj_jit.h lj_ir.h lj_dispatch.h lj_bc.h \ + lj_traceerr.h +lj_ccallback.o: lj_ccallback.c lj_obj.h lua.h luaconf.h lj_def.h \ + lj_arch.h lj_gc.h lj_err.h lj_errmsg.h lj_tab.h lj_state.h lj_frame.h \ + lj_bc.h lj_ctype.h lj_cconv.h lj_ccall.h lj_ccallback.h lj_target.h \ + lj_target_*.h lj_mcode.h lj_jit.h lj_ir.h lj_trace.h lj_dispatch.h \ + lj_traceerr.h lj_vm.h +lj_cconv.o: lj_cconv.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \ + lj_err.h lj_errmsg.h lj_tab.h lj_ctype.h lj_gc.h lj_cdata.h lj_cconv.h \ + lj_ccallback.h +lj_cdata.o: lj_cdata.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \ + lj_gc.h lj_err.h lj_errmsg.h lj_tab.h lj_ctype.h lj_cconv.h lj_cdata.h +lj_char.o: lj_char.c lj_char.h lj_def.h lua.h luaconf.h +lj_clib.o: lj_clib.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h lj_gc.h \ + lj_err.h lj_errmsg.h lj_tab.h lj_str.h lj_udata.h lj_ctype.h lj_cconv.h \ + lj_cdata.h lj_clib.h lj_strfmt.h +lj_cparse.o: lj_cparse.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \ + lj_gc.h lj_err.h lj_errmsg.h lj_buf.h lj_str.h lj_ctype.h lj_cparse.h \ + lj_frame.h lj_bc.h lj_vm.h lj_char.h lj_strscan.h lj_strfmt.h +lj_crecord.o: lj_crecord.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \ + lj_err.h lj_errmsg.h lj_tab.h lj_frame.h lj_bc.h lj_ctype.h lj_gc.h \ + lj_cdata.h lj_cparse.h lj_cconv.h lj_carith.h lj_clib.h lj_ccall.h \ + lj_ff.h lj_ffdef.h lj_ir.h lj_jit.h lj_ircall.h lj_iropt.h lj_trace.h \ + lj_dispatch.h lj_traceerr.h lj_record.h lj_ffrecord.h lj_snap.h \ + lj_crecord.h lj_strfmt.h +lj_ctype.o: lj_ctype.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \ + lj_gc.h lj_err.h lj_errmsg.h lj_str.h lj_tab.h lj_strfmt.h lj_ctype.h \ + lj_ccallback.h lj_buf.h +lj_debug.o: lj_debug.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \ + lj_err.h lj_errmsg.h lj_debug.h lj_buf.h lj_gc.h lj_str.h lj_tab.h \ + lj_state.h lj_frame.h lj_bc.h lj_strfmt.h lj_jit.h lj_ir.h +lj_dispatch.o: lj_dispatch.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \ + lj_err.h lj_errmsg.h lj_buf.h lj_gc.h lj_str.h lj_func.h lj_tab.h \ + lj_meta.h lj_debug.h lj_state.h lj_frame.h lj_bc.h lj_ff.h lj_ffdef.h \ + lj_strfmt.h lj_jit.h lj_ir.h lj_ccallback.h lj_ctype.h lj_trace.h \ + lj_dispatch.h lj_traceerr.h lj_profile.h lj_vm.h luajit.h +lj_err.o: lj_err.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h lj_err.h \ + lj_errmsg.h lj_debug.h lj_str.h lj_func.h lj_state.h lj_frame.h lj_bc.h \ + lj_ff.h lj_ffdef.h lj_trace.h lj_jit.h lj_ir.h lj_dispatch.h \ + lj_traceerr.h lj_vm.h lj_strfmt.h +lj_ffrecord.o: lj_ffrecord.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \ + lj_err.h lj_errmsg.h lj_str.h lj_tab.h lj_frame.h lj_bc.h lj_ff.h \ + lj_ffdef.h lj_ir.h lj_jit.h lj_ircall.h lj_iropt.h lj_trace.h \ + lj_dispatch.h lj_traceerr.h lj_record.h lj_ffrecord.h lj_crecord.h \ + lj_vm.h lj_strscan.h lj_strfmt.h lj_recdef.h +lj_func.o: lj_func.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h lj_gc.h \ + lj_func.h lj_trace.h lj_jit.h lj_ir.h lj_dispatch.h lj_bc.h \ + lj_traceerr.h lj_vm.h +lj_gc.o: lj_gc.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h lj_gc.h \ + lj_err.h lj_errmsg.h lj_buf.h lj_str.h lj_tab.h lj_func.h lj_udata.h \ + lj_meta.h lj_state.h lj_frame.h lj_bc.h lj_ctype.h lj_cdata.h lj_trace.h \ + lj_jit.h lj_ir.h lj_dispatch.h lj_traceerr.h lj_vm.h +lj_gdbjit.o: lj_gdbjit.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \ + lj_gc.h lj_err.h lj_errmsg.h lj_debug.h lj_frame.h lj_bc.h lj_buf.h \ + lj_str.h lj_strfmt.h lj_jit.h lj_ir.h lj_dispatch.h +lj_ir.o: lj_ir.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h lj_gc.h \ + lj_buf.h lj_str.h lj_tab.h lj_ir.h lj_jit.h lj_ircall.h lj_iropt.h \ + lj_trace.h lj_dispatch.h lj_bc.h lj_traceerr.h lj_ctype.h lj_cdata.h \ + lj_carith.h lj_vm.h lj_strscan.h lj_strfmt.h lj_lib.h +lj_lex.o: lj_lex.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h lj_gc.h \ + lj_err.h lj_errmsg.h lj_buf.h lj_str.h lj_tab.h lj_ctype.h lj_cdata.h \ + lualib.h lj_state.h lj_lex.h lj_parse.h lj_char.h lj_strscan.h \ + lj_strfmt.h +lj_lib.o: lj_lib.c lauxlib.h lua.h luaconf.h lj_obj.h lj_def.h lj_arch.h \ + lj_gc.h lj_err.h lj_errmsg.h lj_str.h lj_tab.h lj_func.h lj_bc.h \ + lj_dispatch.h lj_jit.h lj_ir.h lj_vm.h lj_strscan.h lj_strfmt.h lj_lex.h \ + lj_bcdump.h lj_lib.h +lj_load.o: lj_load.c lua.h luaconf.h lauxlib.h lj_obj.h lj_def.h \ + lj_arch.h lj_gc.h lj_err.h lj_errmsg.h lj_buf.h lj_str.h lj_func.h \ + lj_frame.h lj_bc.h lj_vm.h lj_lex.h lj_bcdump.h lj_parse.h +lj_mcode.o: lj_mcode.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \ + lj_gc.h lj_err.h lj_errmsg.h lj_jit.h lj_ir.h lj_mcode.h lj_trace.h \ + lj_dispatch.h lj_bc.h lj_traceerr.h lj_vm.h +lj_meta.o: lj_meta.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h lj_gc.h \ + lj_err.h lj_errmsg.h lj_buf.h lj_str.h lj_tab.h lj_meta.h lj_frame.h \ + lj_bc.h lj_vm.h lj_strscan.h lj_strfmt.h lj_lib.h +lj_obj.o: lj_obj.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h +lj_opt_dce.o: lj_opt_dce.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \ + lj_ir.h lj_jit.h lj_iropt.h +lj_opt_fold.o: lj_opt_fold.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \ + lj_buf.h lj_gc.h lj_str.h lj_tab.h lj_ir.h lj_jit.h lj_ircall.h \ + lj_iropt.h lj_trace.h lj_dispatch.h lj_bc.h lj_traceerr.h lj_ctype.h \ + lj_carith.h lj_vm.h lj_strscan.h lj_strfmt.h lj_folddef.h +lj_opt_loop.o: lj_opt_loop.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \ + lj_err.h lj_errmsg.h lj_buf.h lj_gc.h lj_str.h lj_ir.h lj_jit.h \ + lj_iropt.h lj_trace.h lj_dispatch.h lj_bc.h lj_traceerr.h lj_snap.h \ + lj_vm.h +lj_opt_mem.o: lj_opt_mem.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \ + lj_tab.h lj_ir.h lj_jit.h lj_iropt.h lj_ircall.h +lj_opt_narrow.o: lj_opt_narrow.c lj_obj.h lua.h luaconf.h lj_def.h \ + lj_arch.h lj_bc.h lj_ir.h lj_jit.h lj_iropt.h lj_trace.h lj_dispatch.h \ + lj_traceerr.h lj_vm.h lj_strscan.h +lj_opt_sink.o: lj_opt_sink.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \ + lj_ir.h lj_jit.h lj_iropt.h lj_target.h lj_target_*.h +lj_opt_split.o: lj_opt_split.c lj_obj.h lua.h luaconf.h lj_def.h \ + lj_arch.h lj_err.h lj_errmsg.h lj_buf.h lj_gc.h lj_str.h lj_ir.h \ + lj_jit.h lj_ircall.h lj_iropt.h lj_dispatch.h lj_bc.h lj_vm.h +lj_parse.o: lj_parse.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \ + lj_gc.h lj_err.h lj_errmsg.h lj_debug.h lj_buf.h lj_str.h lj_tab.h \ + lj_func.h lj_state.h lj_bc.h lj_ctype.h lj_strfmt.h lj_lex.h lj_parse.h \ + lj_vm.h lj_vmevent.h +lj_profile.o: lj_profile.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \ + lj_buf.h lj_gc.h lj_str.h lj_frame.h lj_bc.h lj_debug.h lj_dispatch.h \ + lj_jit.h lj_ir.h lj_trace.h lj_traceerr.h lj_profile.h luajit.h +lj_record.o: lj_record.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \ + lj_err.h lj_errmsg.h lj_str.h lj_tab.h lj_meta.h lj_frame.h lj_bc.h \ + lj_ctype.h lj_gc.h lj_ff.h lj_ffdef.h lj_debug.h lj_ir.h lj_jit.h \ + lj_ircall.h lj_iropt.h lj_trace.h lj_dispatch.h lj_traceerr.h \ + lj_record.h lj_ffrecord.h lj_snap.h lj_vm.h +lj_snap.o: lj_snap.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h lj_gc.h \ + lj_tab.h lj_state.h lj_frame.h lj_bc.h lj_ir.h lj_jit.h lj_iropt.h \ + lj_trace.h lj_dispatch.h lj_traceerr.h lj_snap.h lj_target.h \ + lj_target_*.h lj_ctype.h lj_cdata.h +lj_state.o: lj_state.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \ + lj_gc.h lj_err.h lj_errmsg.h lj_buf.h lj_str.h lj_tab.h lj_func.h \ + lj_meta.h lj_state.h lj_frame.h lj_bc.h lj_ctype.h lj_trace.h lj_jit.h \ + lj_ir.h lj_dispatch.h lj_traceerr.h lj_vm.h lj_lex.h lj_alloc.h luajit.h +lj_str.o: lj_str.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h lj_gc.h \ + lj_err.h lj_errmsg.h lj_str.h lj_char.h +lj_strfmt.o: lj_strfmt.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \ + lj_buf.h lj_gc.h lj_str.h lj_state.h lj_char.h lj_strfmt.h +lj_strfmt_num.o: lj_strfmt_num.c lj_obj.h lua.h luaconf.h lj_def.h \ + lj_arch.h lj_buf.h lj_gc.h lj_str.h lj_strfmt.h +lj_strscan.o: lj_strscan.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \ + lj_char.h lj_strscan.h +lj_tab.o: lj_tab.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h lj_gc.h \ + lj_err.h lj_errmsg.h lj_tab.h +lj_trace.o: lj_trace.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \ + lj_gc.h lj_err.h lj_errmsg.h lj_debug.h lj_str.h lj_frame.h lj_bc.h \ + lj_state.h lj_ir.h lj_jit.h lj_iropt.h lj_mcode.h lj_trace.h \ + lj_dispatch.h lj_traceerr.h lj_snap.h lj_gdbjit.h lj_record.h lj_asm.h \ + lj_vm.h lj_vmevent.h lj_target.h lj_target_*.h +lj_udata.o: lj_udata.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \ + lj_gc.h lj_udata.h +lj_vmevent.o: lj_vmevent.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \ + lj_str.h lj_tab.h lj_state.h lj_dispatch.h lj_bc.h lj_jit.h lj_ir.h \ + lj_vm.h lj_vmevent.h +lj_vmmath.o: lj_vmmath.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \ + lj_ir.h lj_vm.h +ljamalg.o: ljamalg.c lua.h luaconf.h lauxlib.h lj_gc.c lj_obj.h lj_def.h \ + lj_arch.h lj_gc.h lj_err.h lj_errmsg.h lj_buf.h lj_str.h lj_tab.h \ + lj_func.h lj_udata.h lj_meta.h lj_state.h lj_frame.h lj_bc.h lj_ctype.h \ + lj_cdata.h lj_trace.h lj_jit.h lj_ir.h lj_dispatch.h lj_traceerr.h \ + lj_vm.h lj_err.c lj_debug.h lj_ff.h lj_ffdef.h lj_strfmt.h lj_char.c \ + lj_char.h lj_bc.c lj_bcdef.h lj_obj.c lj_buf.c lj_str.c lj_tab.c \ + lj_func.c lj_udata.c lj_meta.c lj_strscan.h lj_lib.h lj_debug.c \ + lj_state.c lj_lex.h lj_alloc.h luajit.h lj_dispatch.c lj_ccallback.h \ + lj_profile.h lj_vmevent.c lj_vmevent.h lj_vmmath.c lj_strscan.c \ + lj_strfmt.c lj_strfmt_num.c lj_api.c lj_profile.c lj_lex.c lualib.h \ + lj_parse.h lj_parse.c lj_bcread.c lj_bcdump.h lj_bcwrite.c lj_load.c \ + lj_ctype.c lj_cdata.c lj_cconv.h lj_cconv.c lj_ccall.c lj_ccall.h \ + lj_ccallback.c lj_target.h lj_target_*.h lj_mcode.h lj_carith.c \ + lj_carith.h lj_clib.c lj_clib.h lj_cparse.c lj_cparse.h lj_lib.c lj_ir.c \ + lj_ircall.h lj_iropt.h lj_opt_mem.c lj_opt_fold.c lj_folddef.h \ + lj_opt_narrow.c lj_opt_dce.c lj_opt_loop.c lj_snap.h lj_opt_split.c \ + lj_opt_sink.c lj_mcode.c lj_snap.c lj_record.c lj_record.h lj_ffrecord.h \ + lj_crecord.c lj_crecord.h lj_ffrecord.c lj_recdef.h lj_asm.c lj_asm.h \ + lj_emit_*.h lj_asm_*.h lj_trace.c lj_gdbjit.h lj_gdbjit.c lj_alloc.c \ + lib_aux.c lib_base.c lj_libdef.h lib_math.c lib_string.c lib_table.c \ + lib_io.c lib_os.c lib_package.c lib_debug.c lib_bit.c lib_jit.c \ + lib_ffi.c lib_init.c +luajit.o: luajit.c lua.h luaconf.h lauxlib.h lualib.h luajit.h lj_arch.h +host/buildvm.o: host/buildvm.c host/buildvm.h lj_def.h lua.h luaconf.h \ + lj_arch.h lj_obj.h lj_def.h lj_arch.h lj_gc.h lj_obj.h lj_bc.h lj_ir.h \ + lj_ircall.h lj_ir.h lj_jit.h lj_frame.h lj_bc.h lj_dispatch.h lj_ctype.h \ + lj_gc.h lj_ccall.h lj_ctype.h luajit.h \ + host/buildvm_arch.h lj_traceerr.h +host/buildvm_asm.o: host/buildvm_asm.c host/buildvm.h lj_def.h lua.h luaconf.h \ + lj_arch.h lj_bc.h lj_def.h lj_arch.h +host/buildvm_fold.o: host/buildvm_fold.c host/buildvm.h lj_def.h lua.h \ + luaconf.h lj_arch.h lj_obj.h lj_def.h lj_arch.h lj_ir.h lj_obj.h +host/buildvm_lib.o: host/buildvm_lib.c host/buildvm.h lj_def.h lua.h luaconf.h \ + lj_arch.h lj_obj.h lj_def.h lj_arch.h lj_bc.h lj_lib.h lj_obj.h \ + host/buildvm_libbc.h +host/buildvm_peobj.o: host/buildvm_peobj.c host/buildvm.h lj_def.h lua.h \ + luaconf.h lj_arch.h lj_bc.h lj_def.h lj_arch.h +host/minilua.o: host/minilua.c diff --git a/lib/LuaJIT/host/.gitignore b/lib/LuaJIT/host/.gitignore new file mode 100644 index 0000000..762ac2a --- /dev/null +++ b/lib/LuaJIT/host/.gitignore @@ -0,0 +1,3 @@ +minilua +buildvm +buildvm_arch.h diff --git a/lib/LuaJIT/host/README b/lib/LuaJIT/host/README new file mode 100644 index 0000000..abfcdaa --- /dev/null +++ b/lib/LuaJIT/host/README @@ -0,0 +1,4 @@ +The files in this directory are only used during the build process of LuaJIT. +For cross-compilation, they must be executed on the host, not on the target. + +These files should NOT be installed! diff --git a/lib/LuaJIT/host/buildvm.c b/lib/LuaJIT/host/buildvm.c new file mode 100644 index 0000000..de23fab --- /dev/null +++ b/lib/LuaJIT/host/buildvm.c @@ -0,0 +1,518 @@ +/* +** LuaJIT VM builder. +** Copyright (C) 2005-2017 Mike Pall. See Copyright Notice in luajit.h +** +** This is a tool to build the hand-tuned assembler code required for +** LuaJIT's bytecode interpreter. It supports a variety of output formats +** to feed different toolchains (see usage() below). +** +** This tool is not particularly optimized because it's only used while +** _building_ LuaJIT. There's no point in distributing or installing it. +** Only the object code generated by this tool is linked into LuaJIT. +** +** Caveat: some memory is not free'd, error handling is lazy. +** It's a one-shot tool -- any effort fixing this would be wasted. +*/ + +#include "buildvm.h" +#include "lj_obj.h" +#include "lj_gc.h" +#include "lj_bc.h" +#include "lj_ir.h" +#include "lj_ircall.h" +#include "lj_frame.h" +#include "lj_dispatch.h" +#if LJ_HASFFI +#include "lj_ctype.h" +#include "lj_ccall.h" +#endif +#include "luajit.h" + +#if defined(_WIN32) +#include +#include +#endif + +/* ------------------------------------------------------------------------ */ + +/* DynASM glue definitions. */ +#define Dst ctx +#define Dst_DECL BuildCtx *ctx +#define Dst_REF (ctx->D) +#define DASM_CHECKS 1 + +#include "../dynasm/dasm_proto.h" + +/* Glue macros for DynASM. */ +static int collect_reloc(BuildCtx *ctx, uint8_t *addr, int idx, int type); + +#define DASM_EXTERN(ctx, addr, idx, type) \ + collect_reloc(ctx, addr, idx, type) + +/* ------------------------------------------------------------------------ */ + +/* Avoid trouble if cross-compiling for an x86 target. Speed doesn't matter. */ +#define DASM_ALIGNED_WRITES 1 + +/* Embed architecture-specific DynASM encoder. */ +#if LJ_TARGET_X86ORX64 +#include "../dynasm/dasm_x86.h" +#elif LJ_TARGET_ARM +#include "../dynasm/dasm_arm.h" +#elif LJ_TARGET_ARM64 +#include "../dynasm/dasm_arm64.h" +#elif LJ_TARGET_PPC +#include "../dynasm/dasm_ppc.h" +#elif LJ_TARGET_MIPS +#include "../dynasm/dasm_mips.h" +#else +#error "No support for this architecture (yet)" +#endif + +/* Embed generated architecture-specific backend. */ +#include "buildvm_arch.h" + +/* ------------------------------------------------------------------------ */ + +void owrite(BuildCtx *ctx, const void *ptr, size_t sz) +{ + if (fwrite(ptr, 1, sz, ctx->fp) != sz) { + fprintf(stderr, "Error: cannot write to output file: %s\n", + strerror(errno)); + exit(1); + } +} + +/* ------------------------------------------------------------------------ */ + +/* Emit code as raw bytes. Only used for DynASM debugging. */ +static void emit_raw(BuildCtx *ctx) +{ + owrite(ctx, ctx->code, ctx->codesz); +} + +/* -- Build machine code -------------------------------------------------- */ + +static const char *sym_decorate(BuildCtx *ctx, + const char *prefix, const char *suffix) +{ + char name[256]; + char *p; +#if LJ_64 + const char *symprefix = ctx->mode == BUILD_machasm ? "_" : ""; +#elif LJ_TARGET_XBOX360 + const char *symprefix = ""; +#else + const char *symprefix = ctx->mode != BUILD_elfasm ? "_" : ""; +#endif + sprintf(name, "%s%s%s", symprefix, prefix, suffix); + p = strchr(name, '@'); + if (p) { +#if LJ_TARGET_X86ORX64 + if (!LJ_64 && (ctx->mode == BUILD_coffasm || ctx->mode == BUILD_peobj)) + name[0] = name[1] == 'R' ? '_' : '@'; /* Just for _RtlUnwind@16. */ + else + *p = '\0'; +#elif LJ_TARGET_PPC && !LJ_TARGET_CONSOLE + /* Keep @plt etc. */ +#else + *p = '\0'; +#endif + } + p = (char *)malloc(strlen(name)+1); /* MSVC doesn't like strdup. */ + strcpy(p, name); + return p; +} + +#define NRELOCSYM (sizeof(extnames)/sizeof(extnames[0])-1) + +static int relocmap[NRELOCSYM]; + +/* Collect external relocations. */ +static int collect_reloc(BuildCtx *ctx, uint8_t *addr, int idx, int type) +{ + if (ctx->nreloc >= BUILD_MAX_RELOC) { + fprintf(stderr, "Error: too many relocations, increase BUILD_MAX_RELOC.\n"); + exit(1); + } + if (relocmap[idx] < 0) { + relocmap[idx] = ctx->nrelocsym; + ctx->relocsym[ctx->nrelocsym] = sym_decorate(ctx, "", extnames[idx]); + ctx->nrelocsym++; + } + ctx->reloc[ctx->nreloc].ofs = (int32_t)(addr - ctx->code); + ctx->reloc[ctx->nreloc].sym = relocmap[idx]; + ctx->reloc[ctx->nreloc].type = type; + ctx->nreloc++; +#if LJ_TARGET_XBOX360 + return (int)(ctx->code - addr) + 4; /* Encode symbol offset of .text. */ +#else + return 0; /* Encode symbol offset of 0. */ +#endif +} + +/* Naive insertion sort. Performance doesn't matter here. */ +static void sym_insert(BuildCtx *ctx, int32_t ofs, + const char *prefix, const char *suffix) +{ + ptrdiff_t i = ctx->nsym++; + while (i > 0) { + if (ctx->sym[i-1].ofs <= ofs) + break; + ctx->sym[i] = ctx->sym[i-1]; + i--; + } + ctx->sym[i].ofs = ofs; + ctx->sym[i].name = sym_decorate(ctx, prefix, suffix); +} + +/* Build the machine code. */ +static int build_code(BuildCtx *ctx) +{ + int status; + int i; + + /* Initialize DynASM structures. */ + ctx->nglob = GLOB__MAX; + ctx->glob = (void **)malloc(ctx->nglob*sizeof(void *)); + memset(ctx->glob, 0, ctx->nglob*sizeof(void *)); + ctx->nreloc = 0; + + ctx->globnames = globnames; + ctx->extnames = extnames; + ctx->relocsym = (const char **)malloc(NRELOCSYM*sizeof(const char *)); + ctx->nrelocsym = 0; + for (i = 0; i < (int)NRELOCSYM; i++) relocmap[i] = -1; + + ctx->dasm_ident = DASM_IDENT; + ctx->dasm_arch = DASM_ARCH; + + dasm_init(Dst, DASM_MAXSECTION); + dasm_setupglobal(Dst, ctx->glob, ctx->nglob); + dasm_setup(Dst, build_actionlist); + + /* Call arch-specific backend to emit the code. */ + ctx->npc = build_backend(ctx); + + /* Finalize the code. */ + (void)dasm_checkstep(Dst, -1); + if ((status = dasm_link(Dst, &ctx->codesz))) return status; + ctx->code = (uint8_t *)malloc(ctx->codesz); + if ((status = dasm_encode(Dst, (void *)ctx->code))) return status; + + /* Allocate symbol table and bytecode offsets. */ + ctx->beginsym = sym_decorate(ctx, "", LABEL_PREFIX "vm_asm_begin"); + ctx->sym = (BuildSym *)malloc((ctx->npc+ctx->nglob+1)*sizeof(BuildSym)); + ctx->nsym = 0; + ctx->bc_ofs = (int32_t *)malloc(ctx->npc*sizeof(int32_t)); + + /* Collect the opcodes (PC labels). */ + for (i = 0; i < ctx->npc; i++) { + int32_t ofs = dasm_getpclabel(Dst, i); + if (ofs < 0) return 0x22000000|i; + ctx->bc_ofs[i] = ofs; + if ((LJ_HASJIT || + !(i == BC_JFORI || i == BC_JFORL || i == BC_JITERL || i == BC_JLOOP || + i == BC_IFORL || i == BC_IITERL || i == BC_ILOOP)) && + (LJ_HASFFI || i != BC_KCDATA)) + sym_insert(ctx, ofs, LABEL_PREFIX_BC, bc_names[i]); + } + + /* Collect the globals (named labels). */ + for (i = 0; i < ctx->nglob; i++) { + const char *gl = globnames[i]; + int len = (int)strlen(gl); + if (!ctx->glob[i]) { + fprintf(stderr, "Error: undefined global %s\n", gl); + exit(2); + } + /* Skip the _Z symbols. */ + if (!(len >= 2 && gl[len-2] == '_' && gl[len-1] == 'Z')) + sym_insert(ctx, (int32_t)((uint8_t *)(ctx->glob[i]) - ctx->code), + LABEL_PREFIX, globnames[i]); + } + + /* Close the address range. */ + sym_insert(ctx, (int32_t)ctx->codesz, "", ""); + ctx->nsym--; + + dasm_free(Dst); + + return 0; +} + +/* -- Generate VM enums --------------------------------------------------- */ + +const char *const bc_names[] = { +#define BCNAME(name, ma, mb, mc, mt) #name, +BCDEF(BCNAME) +#undef BCNAME + NULL +}; + +const char *const ir_names[] = { +#define IRNAME(name, m, m1, m2) #name, +IRDEF(IRNAME) +#undef IRNAME + NULL +}; + +const char *const irt_names[] = { +#define IRTNAME(name, size) #name, +IRTDEF(IRTNAME) +#undef IRTNAME + NULL +}; + +const char *const irfpm_names[] = { +#define FPMNAME(name) #name, +IRFPMDEF(FPMNAME) +#undef FPMNAME + NULL +}; + +const char *const irfield_names[] = { +#define FLNAME(name, ofs) #name, +IRFLDEF(FLNAME) +#undef FLNAME + NULL +}; + +const char *const ircall_names[] = { +#define IRCALLNAME(cond, name, nargs, kind, type, flags) #name, +IRCALLDEF(IRCALLNAME) +#undef IRCALLNAME + NULL +}; + +static const char *const trace_errors[] = { +#define TREDEF(name, msg) msg, +#include "lj_traceerr.h" + NULL +}; + +static const char *lower(char *buf, const char *s) +{ + char *p = buf; + while (*s) { + *p++ = (*s >= 'A' && *s <= 'Z') ? *s+0x20 : *s; + s++; + } + *p = '\0'; + return buf; +} + +/* Emit C source code for bytecode-related definitions. */ +static void emit_bcdef(BuildCtx *ctx) +{ + int i; + fprintf(ctx->fp, "/* This is a generated file. DO NOT EDIT! */\n\n"); + fprintf(ctx->fp, "LJ_DATADEF const uint16_t lj_bc_ofs[] = {\n"); + for (i = 0; i < ctx->npc; i++) { + if (i != 0) + fprintf(ctx->fp, ",\n"); + fprintf(ctx->fp, "%d", ctx->bc_ofs[i]); + } +} + +/* Emit VM definitions as Lua code for debug modules. */ +static void emit_vmdef(BuildCtx *ctx) +{ + char buf[80]; + int i; + fprintf(ctx->fp, "-- This is a generated file. DO NOT EDIT!\n\n"); + fprintf(ctx->fp, "return {\n\n"); + + fprintf(ctx->fp, "bcnames = \""); + for (i = 0; bc_names[i]; i++) fprintf(ctx->fp, "%-6s", bc_names[i]); + fprintf(ctx->fp, "\",\n\n"); + + fprintf(ctx->fp, "irnames = \""); + for (i = 0; ir_names[i]; i++) fprintf(ctx->fp, "%-6s", ir_names[i]); + fprintf(ctx->fp, "\",\n\n"); + + fprintf(ctx->fp, "irfpm = { [0]="); + for (i = 0; irfpm_names[i]; i++) + fprintf(ctx->fp, "\"%s\", ", lower(buf, irfpm_names[i])); + fprintf(ctx->fp, "},\n\n"); + + fprintf(ctx->fp, "irfield = { [0]="); + for (i = 0; irfield_names[i]; i++) { + char *p; + lower(buf, irfield_names[i]); + p = strchr(buf, '_'); + if (p) *p = '.'; + fprintf(ctx->fp, "\"%s\", ", buf); + } + fprintf(ctx->fp, "},\n\n"); + + fprintf(ctx->fp, "ircall = {\n[0]="); + for (i = 0; ircall_names[i]; i++) + fprintf(ctx->fp, "\"%s\",\n", ircall_names[i]); + fprintf(ctx->fp, "},\n\n"); + + fprintf(ctx->fp, "traceerr = {\n[0]="); + for (i = 0; trace_errors[i]; i++) + fprintf(ctx->fp, "\"%s\",\n", trace_errors[i]); + fprintf(ctx->fp, "},\n\n"); +} + +/* -- Argument parsing ---------------------------------------------------- */ + +/* Build mode names. */ +static const char *const modenames[] = { +#define BUILDNAME(name) #name, +BUILDDEF(BUILDNAME) +#undef BUILDNAME + NULL +}; + +/* Print usage information and exit. */ +static void usage(void) +{ + int i; + fprintf(stderr, LUAJIT_VERSION " VM builder.\n"); + fprintf(stderr, LUAJIT_COPYRIGHT ", " LUAJIT_URL "\n"); + fprintf(stderr, "Target architecture: " LJ_ARCH_NAME "\n\n"); + fprintf(stderr, "Usage: buildvm -m mode [-o outfile] [infiles...]\n\n"); + fprintf(stderr, "Available modes:\n"); + for (i = 0; i < BUILD__MAX; i++) + fprintf(stderr, " %s\n", modenames[i]); + exit(1); +} + +/* Parse the output mode name. */ +static BuildMode parsemode(const char *mode) +{ + int i; + for (i = 0; modenames[i]; i++) + if (!strcmp(mode, modenames[i])) + return (BuildMode)i; + usage(); + return (BuildMode)-1; +} + +/* Parse arguments. */ +static void parseargs(BuildCtx *ctx, char **argv) +{ + const char *a; + int i; + ctx->mode = (BuildMode)-1; + ctx->outname = "-"; + for (i = 1; (a = argv[i]) != NULL; i++) { + if (a[0] != '-') + break; + switch (a[1]) { + case '-': + if (a[2]) goto err; + i++; + goto ok; + case '\0': + goto ok; + case 'm': + i++; + if (a[2] || argv[i] == NULL) goto err; + ctx->mode = parsemode(argv[i]); + break; + case 'o': + i++; + if (a[2] || argv[i] == NULL) goto err; + ctx->outname = argv[i]; + break; + default: err: + usage(); + break; + } + } +ok: + ctx->args = argv+i; + if (ctx->mode == (BuildMode)-1) goto err; +} + +int main(int argc, char **argv) +{ + BuildCtx ctx_; + BuildCtx *ctx = &ctx_; + int status, binmode; + + if (sizeof(void *) != 4*LJ_32+8*LJ_64) { + fprintf(stderr,"Error: pointer size mismatch in cross-build.\n"); + fprintf(stderr,"Try: make HOST_CC=\"gcc -m32\" CROSS=...\n\n"); + return 1; + } + + UNUSED(argc); + parseargs(ctx, argv); + + if ((status = build_code(ctx))) { + fprintf(stderr,"Error: DASM error %08x\n", status); + return 1; + } + + switch (ctx->mode) { + case BUILD_peobj: + case BUILD_raw: + binmode = 1; + break; + default: + binmode = 0; + break; + } + + if (ctx->outname[0] == '-' && ctx->outname[1] == '\0') { + ctx->fp = stdout; +#if defined(_WIN32) + if (binmode) + _setmode(_fileno(stdout), _O_BINARY); /* Yuck. */ +#endif + } else if (!(ctx->fp = fopen(ctx->outname, binmode ? "wb" : "w"))) { + fprintf(stderr, "Error: cannot open output file '%s': %s\n", + ctx->outname, strerror(errno)); + exit(1); + } + + switch (ctx->mode) { + case BUILD_elfasm: + case BUILD_coffasm: + case BUILD_machasm: + emit_asm(ctx); + emit_asm_debug(ctx); + break; + case BUILD_peobj: + emit_peobj(ctx); + break; + case BUILD_raw: + emit_raw(ctx); + break; + case BUILD_bcdef: + emit_bcdef(ctx); + emit_lib(ctx); + break; + case BUILD_vmdef: + emit_vmdef(ctx); + emit_lib(ctx); + fprintf(ctx->fp, "}\n\n"); + break; + case BUILD_ffdef: + case BUILD_libdef: + case BUILD_recdef: + emit_lib(ctx); + break; + case BUILD_folddef: + emit_fold(ctx); + break; + default: + break; + } + + fflush(ctx->fp); + if (ferror(ctx->fp)) { + fprintf(stderr, "Error: cannot write to output file: %s\n", + strerror(errno)); + exit(1); + } + fclose(ctx->fp); + + return 0; +} + diff --git a/lib/LuaJIT/host/buildvm.h b/lib/LuaJIT/host/buildvm.h new file mode 100644 index 0000000..b90428d --- /dev/null +++ b/lib/LuaJIT/host/buildvm.h @@ -0,0 +1,105 @@ +/* +** LuaJIT VM builder. +** Copyright (C) 2005-2017 Mike Pall. See Copyright Notice in luajit.h +*/ + +#ifndef _BUILDVM_H +#define _BUILDVM_H + +#include +#include +#include +#include +#include + +#include "lj_def.h" +#include "lj_arch.h" + +/* Hardcoded limits. Increase as needed. */ +#define BUILD_MAX_RELOC 200 /* Max. number of relocations. */ +#define BUILD_MAX_FOLD 4096 /* Max. number of fold rules. */ + +/* Prefix for scanned library definitions. */ +#define LIBDEF_PREFIX "LJLIB_" + +/* Prefix for scanned fold definitions. */ +#define FOLDDEF_PREFIX "LJFOLD" + +/* Prefixes for generated labels. */ +#define LABEL_PREFIX "lj_" +#define LABEL_PREFIX_BC LABEL_PREFIX "BC_" +#define LABEL_PREFIX_FF LABEL_PREFIX "ff_" +#define LABEL_PREFIX_CF LABEL_PREFIX "cf_" +#define LABEL_PREFIX_FFH LABEL_PREFIX "ffh_" +#define LABEL_PREFIX_LIBCF LABEL_PREFIX "lib_cf_" +#define LABEL_PREFIX_LIBINIT LABEL_PREFIX "lib_init_" + +/* Forward declaration. */ +struct dasm_State; + +/* Build modes. */ +#define BUILDDEF(_) \ + _(elfasm) _(coffasm) _(machasm) _(peobj) _(raw) \ + _(bcdef) _(ffdef) _(libdef) _(recdef) _(vmdef) \ + _(folddef) + +typedef enum { +#define BUILDENUM(name) BUILD_##name, +BUILDDEF(BUILDENUM) +#undef BUILDENUM + BUILD__MAX +} BuildMode; + +/* Code relocation. */ +typedef struct BuildReloc { + int32_t ofs; + int sym; + int type; +} BuildReloc; + +typedef struct BuildSym { + const char *name; + int32_t ofs; +} BuildSym; + +/* Build context structure. */ +typedef struct BuildCtx { + /* DynASM state pointer. Should be first member. */ + struct dasm_State *D; + /* Parsed command line. */ + BuildMode mode; + FILE *fp; + const char *outname; + char **args; + /* Code and symbols generated by DynASM. */ + uint8_t *code; + size_t codesz; + int npc, nglob, nsym, nreloc, nrelocsym; + void **glob; + BuildSym *sym; + const char **relocsym; + int32_t *bc_ofs; + const char *beginsym; + /* Strings generated by DynASM. */ + const char *const *globnames; + const char *const *extnames; + const char *dasm_ident; + const char *dasm_arch; + /* Relocations. */ + BuildReloc reloc[BUILD_MAX_RELOC]; +} BuildCtx; + +extern void owrite(BuildCtx *ctx, const void *ptr, size_t sz); +extern void emit_asm(BuildCtx *ctx); +extern void emit_peobj(BuildCtx *ctx); +extern void emit_lib(BuildCtx *ctx); +extern void emit_fold(BuildCtx *ctx); + +extern const char *const bc_names[]; +extern const char *const ir_names[]; +extern const char *const irt_names[]; +extern const char *const irfpm_names[]; +extern const char *const irfield_names[]; +extern const char *const ircall_names[]; + +#endif diff --git a/lib/LuaJIT/host/buildvm.o b/lib/LuaJIT/host/buildvm.o new file mode 100644 index 0000000000000000000000000000000000000000..d7fe311d4d485d95c8a0635b4da60d26af1c864d GIT binary patch literal 88800 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 literal 0 HcmV?d00001 diff --git a/lib/LuaJIT/host/buildvm_asm.c b/lib/LuaJIT/host/buildvm_asm.c new file mode 100644 index 0000000..43595b3 --- /dev/null +++ b/lib/LuaJIT/host/buildvm_asm.c @@ -0,0 +1,359 @@ +/* +** LuaJIT VM builder: Assembler source code emitter. +** Copyright (C) 2005-2017 Mike Pall. See Copyright Notice in luajit.h +*/ + +#include "buildvm.h" +#include "lj_bc.h" + +/* ------------------------------------------------------------------------ */ + +#if LJ_TARGET_X86ORX64 +/* Emit bytes piecewise as assembler text. */ +static void emit_asm_bytes(BuildCtx *ctx, uint8_t *p, int n) +{ + int i; + for (i = 0; i < n; i++) { + if ((i & 15) == 0) + fprintf(ctx->fp, "\t.byte %d", p[i]); + else + fprintf(ctx->fp, ",%d", p[i]); + if ((i & 15) == 15) putc('\n', ctx->fp); + } + if ((n & 15) != 0) putc('\n', ctx->fp); +} + +/* Emit relocation */ +static void emit_asm_reloc(BuildCtx *ctx, int type, const char *sym) +{ + switch (ctx->mode) { + case BUILD_elfasm: + if (type) + fprintf(ctx->fp, "\t.long %s-.-4\n", sym); + else + fprintf(ctx->fp, "\t.long %s\n", sym); + break; + case BUILD_coffasm: + fprintf(ctx->fp, "\t.def %s; .scl 3; .type 32; .endef\n", sym); + if (type) + fprintf(ctx->fp, "\t.long %s-.-4\n", sym); + else + fprintf(ctx->fp, "\t.long %s\n", sym); + break; + default: /* BUILD_machasm for relative relocations handled below. */ + fprintf(ctx->fp, "\t.long %s\n", sym); + break; + } +} + +static const char *const jccnames[] = { + "jo", "jno", "jb", "jnb", "jz", "jnz", "jbe", "ja", + "js", "jns", "jpe", "jpo", "jl", "jge", "jle", "jg" +}; + +/* Emit x86/x64 text relocations. */ +static void emit_asm_reloc_text(BuildCtx *ctx, uint8_t *cp, int n, + const char *sym) +{ + const char *opname = NULL; + if (--n < 0) goto err; + if (cp[n] == 0xe8) { + opname = "call"; + } else if (cp[n] == 0xe9) { + opname = "jmp"; + } else if (cp[n] >= 0x80 && cp[n] <= 0x8f && n > 0 && cp[n-1] == 0x0f) { + opname = jccnames[cp[n]-0x80]; + n--; + } else { +err: + fprintf(stderr, "Error: unsupported opcode for %s symbol relocation.\n", + sym); + exit(1); + } + emit_asm_bytes(ctx, cp, n); + if (strncmp(sym+(*sym == '_'), LABEL_PREFIX, sizeof(LABEL_PREFIX)-1)) { + /* Various fixups for external symbols outside of our binary. */ + if (ctx->mode == BUILD_elfasm) { + if (LJ_32) + fprintf(ctx->fp, "#if __PIC__\n\t%s lj_wrap_%s\n#else\n", opname, sym); + fprintf(ctx->fp, "\t%s %s@PLT\n", opname, sym); + if (LJ_32) + fprintf(ctx->fp, "#endif\n"); + return; + } else if (LJ_32 && ctx->mode == BUILD_machasm) { + fprintf(ctx->fp, "\t%s L%s$stub\n", opname, sym); + return; + } + } + fprintf(ctx->fp, "\t%s %s\n", opname, sym); +} +#else +/* Emit words piecewise as assembler text. */ +static void emit_asm_words(BuildCtx *ctx, uint8_t *p, int n) +{ + int i; + for (i = 0; i < n; i += 4) { + uint32_t ins = *(uint32_t *)(p+i); +#if LJ_TARGET_ARM64 && LJ_BE + ins = lj_bswap(ins); /* ARM64 instructions are always little-endian. */ +#endif + if ((i & 15) == 0) + fprintf(ctx->fp, "\t.long 0x%08x", ins); + else + fprintf(ctx->fp, ",0x%08x", ins); + if ((i & 15) == 12) putc('\n', ctx->fp); + } + if ((n & 15) != 0) putc('\n', ctx->fp); +} + +/* Emit relocation as part of an instruction. */ +static void emit_asm_wordreloc(BuildCtx *ctx, uint8_t *p, int n, + const char *sym) +{ + uint32_t ins; + emit_asm_words(ctx, p, n-4); + ins = *(uint32_t *)(p+n-4); +#if LJ_TARGET_ARM + if ((ins & 0xff000000u) == 0xfa000000u) { + fprintf(ctx->fp, "\tblx %s\n", sym); + } else if ((ins & 0x0e000000u) == 0x0a000000u) { + fprintf(ctx->fp, "\t%s%.2s %s\n", (ins & 0x01000000u) ? "bl" : "b", + &"eqnecsccmiplvsvchilsgeltgtle"[2*(ins >> 28)], sym); + } else { + fprintf(stderr, + "Error: unsupported opcode %08x for %s symbol relocation.\n", + ins, sym); + exit(1); + } +#elif LJ_TARGET_ARM64 + if ((ins >> 26) == 0x25u) { + fprintf(ctx->fp, "\tbl %s\n", sym); + } else { + fprintf(stderr, + "Error: unsupported opcode %08x for %s symbol relocation.\n", + ins, sym); + exit(1); + } +#elif LJ_TARGET_PPC +#if LJ_TARGET_PS3 +#define TOCPREFIX "." +#else +#define TOCPREFIX "" +#endif + if ((ins >> 26) == 16) { + fprintf(ctx->fp, "\t%s %d, %d, " TOCPREFIX "%s\n", + (ins & 1) ? "bcl" : "bc", (ins >> 21) & 31, (ins >> 16) & 31, sym); + } else if ((ins >> 26) == 18) { +#if LJ_ARCH_PPC64 + const char *suffix = strchr(sym, '@'); + if (suffix && suffix[1] == 'h') { + fprintf(ctx->fp, "\taddis 11, 2, %s\n", sym); + } else if (suffix && suffix[1] == 'l') { + fprintf(ctx->fp, "\tld 12, %s\n", sym); + } else +#endif + fprintf(ctx->fp, "\t%s " TOCPREFIX "%s\n", (ins & 1) ? "bl" : "b", sym); + } else { + fprintf(stderr, + "Error: unsupported opcode %08x for %s symbol relocation.\n", + ins, sym); + exit(1); + } +#elif LJ_TARGET_MIPS + fprintf(stderr, + "Error: unsupported opcode %08x for %s symbol relocation.\n", + ins, sym); + exit(1); +#else +#error "missing relocation support for this architecture" +#endif +} +#endif + +#if LJ_TARGET_ARM +#define ELFASM_PX "%%" +#else +#define ELFASM_PX "@" +#endif + +/* Emit an assembler label. */ +static void emit_asm_label(BuildCtx *ctx, const char *name, int size, int isfunc) +{ + switch (ctx->mode) { + case BUILD_elfasm: +#if LJ_TARGET_PS3 + if (!strncmp(name, "lj_vm_", 6) && + strcmp(name, ctx->beginsym) && + !strstr(name, "hook")) { + fprintf(ctx->fp, + "\n\t.globl %s\n" + "\t.section \".opd\",\"aw\"\n" + "%s:\n" + "\t.long .%s,.TOC.@tocbase32\n" + "\t.size %s,8\n" + "\t.previous\n" + "\t.globl .%s\n" + "\t.hidden .%s\n" + "\t.type .%s, " ELFASM_PX "function\n" + "\t.size .%s, %d\n" + ".%s:\n", + name, name, name, name, name, name, name, name, size, name); + break; + } +#endif + fprintf(ctx->fp, + "\n\t.globl %s\n" + "\t.hidden %s\n" + "\t.type %s, " ELFASM_PX "%s\n" + "\t.size %s, %d\n" + "%s:\n", + name, name, name, isfunc ? "function" : "object", name, size, name); + break; + case BUILD_coffasm: + fprintf(ctx->fp, "\n\t.globl %s\n", name); + if (isfunc) + fprintf(ctx->fp, "\t.def %s; .scl 3; .type 32; .endef\n", name); + fprintf(ctx->fp, "%s:\n", name); + break; + case BUILD_machasm: + fprintf(ctx->fp, + "\n\t.private_extern %s\n" + "\t.no_dead_strip %s\n" + "%s:\n", name, name, name); + break; + default: + break; + } +} + +/* Emit alignment. */ +static void emit_asm_align(BuildCtx *ctx, int bits) +{ + switch (ctx->mode) { + case BUILD_elfasm: + case BUILD_coffasm: + fprintf(ctx->fp, "\t.p2align %d\n", bits); + break; + case BUILD_machasm: + fprintf(ctx->fp, "\t.align %d\n", bits); + break; + default: + break; + } +} + +/* ------------------------------------------------------------------------ */ + +/* Emit assembler source code. */ +void emit_asm(BuildCtx *ctx) +{ + int i, rel; + + fprintf(ctx->fp, "\t.file \"buildvm_%s.dasc\"\n", ctx->dasm_arch); +#if LJ_ARCH_PPC64 + fprintf(ctx->fp, "\t.abiversion 2\n"); +#endif + fprintf(ctx->fp, "\t.text\n"); + emit_asm_align(ctx, 4); + +#if LJ_TARGET_PS3 + emit_asm_label(ctx, ctx->beginsym, ctx->codesz, 0); +#else + emit_asm_label(ctx, ctx->beginsym, 0, 0); +#endif + if (ctx->mode != BUILD_machasm) + fprintf(ctx->fp, ".Lbegin:\n"); + +#if LJ_TARGET_ARM && defined(__GNUC__) && !LJ_NO_UNWIND + /* This should really be moved into buildvm_arm.dasc. */ +#if LJ_ARCH_HASFPU + fprintf(ctx->fp, + ".fnstart\n" + ".save {r5, r6, r7, r8, r9, r10, r11, lr}\n" + ".vsave {d8-d15}\n" + ".save {r4}\n" + ".pad #28\n"); +#else + fprintf(ctx->fp, + ".fnstart\n" + ".save {r4, r5, r6, r7, r8, r9, r10, r11, lr}\n" + ".pad #28\n"); +#endif +#endif +#if LJ_TARGET_MIPS + fprintf(ctx->fp, ".set nomips16\n.abicalls\n.set noreorder\n.set nomacro\n"); +#endif + + for (i = rel = 0; i < ctx->nsym; i++) { + int32_t ofs = ctx->sym[i].ofs; + int32_t next = ctx->sym[i+1].ofs; +#if LJ_TARGET_ARM && defined(__GNUC__) && !LJ_NO_UNWIND && LJ_HASFFI + if (!strcmp(ctx->sym[i].name, "lj_vm_ffi_call")) + fprintf(ctx->fp, + ".globl lj_err_unwind_arm\n" + ".personality lj_err_unwind_arm\n" + ".fnend\n" + ".fnstart\n" + ".save {r4, r5, r11, lr}\n" + ".setfp r11, sp\n"); +#endif + emit_asm_label(ctx, ctx->sym[i].name, next - ofs, 1); + while (rel < ctx->nreloc && ctx->reloc[rel].ofs <= next) { + BuildReloc *r = &ctx->reloc[rel]; + int n = r->ofs - ofs; +#if LJ_TARGET_X86ORX64 + if (r->type != 0 && + (ctx->mode == BUILD_elfasm || ctx->mode == BUILD_machasm)) { + emit_asm_reloc_text(ctx, ctx->code+ofs, n, ctx->relocsym[r->sym]); + } else { + emit_asm_bytes(ctx, ctx->code+ofs, n); + emit_asm_reloc(ctx, r->type, ctx->relocsym[r->sym]); + } + ofs += n+4; +#else + emit_asm_wordreloc(ctx, ctx->code+ofs, n, ctx->relocsym[r->sym]); + ofs += n; +#endif + rel++; + } +#if LJ_TARGET_X86ORX64 + emit_asm_bytes(ctx, ctx->code+ofs, next-ofs); +#else + emit_asm_words(ctx, ctx->code+ofs, next-ofs); +#endif + } + +#if LJ_TARGET_ARM && defined(__GNUC__) && !LJ_NO_UNWIND + fprintf(ctx->fp, +#if !LJ_HASFFI + ".globl lj_err_unwind_arm\n" + ".personality lj_err_unwind_arm\n" +#endif + ".fnend\n"); +#endif + + fprintf(ctx->fp, "\n"); + switch (ctx->mode) { + case BUILD_elfasm: +#if !(LJ_TARGET_PS3 || LJ_TARGET_PSVITA) + fprintf(ctx->fp, "\t.section .note.GNU-stack,\"\"," ELFASM_PX "progbits\n"); +#endif +#if LJ_TARGET_PPC && !LJ_TARGET_PS3 && !LJ_ABI_SOFTFP + /* Hard-float ABI. */ + fprintf(ctx->fp, "\t.gnu_attribute 4, 1\n"); +#endif + /* fallthrough */ + case BUILD_coffasm: + fprintf(ctx->fp, "\t.ident \"%s\"\n", ctx->dasm_ident); + break; + case BUILD_machasm: + fprintf(ctx->fp, + "\t.cstring\n" + "\t.ascii \"%s\\0\"\n", ctx->dasm_ident); + break; + default: + break; + } + fprintf(ctx->fp, "\n"); +} + diff --git a/lib/LuaJIT/host/buildvm_asm.o b/lib/LuaJIT/host/buildvm_asm.o new file mode 100644 index 0000000000000000000000000000000000000000..a31d788d47c4b8cb0b141bd20134d15b610f4309 GIT binary patch literal 6768 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 xjVeX>5) == 0)) + return 0; /* Avoid zero rotates. */ + memset(htab, 0xff, (sz+1)*sizeof(uint32_t)); + for (i = 0; i < nkeys; i++) { + uint32_t key = foldkeys[i]; + uint32_t k = key & 0xffffff; + uint32_t h = (dorol ? lj_rol(lj_rol(k, r>>5) - k, r&31) : + (((k << (r>>5)) - k) << (r&31))) % sz; + if (htab[h] != 0xffffffff) { /* Collision on primary slot. */ + if (htab[h+1] != 0xffffffff) { /* Collision on secondary slot. */ + /* Try to move the colliding key, if possible. */ + if (h < sz-1 && htab[h+2] == 0xffffffff) { + uint32_t k2 = htab[h+1] & 0xffffff; + uint32_t h2 = (dorol ? lj_rol(lj_rol(k2, r>>5) - k2, r&31) : + (((k2 << (r>>5)) - k2) << (r&31))) % sz; + if (h2 != h+1) return 0; /* Cannot resolve collision. */ + htab[h+2] = htab[h+1]; /* Move colliding key to secondary slot. */ + } else { + return 0; /* Collision. */ + } + } + htab[h+1] = key; + } else { + htab[h] = key; + } + } + return 1; /* Success, all keys could be stored. */ +} + +/* Print the generated hash table. */ +static void printhash(BuildCtx *ctx, uint32_t *htab, uint32_t sz) +{ + uint32_t i; + fprintf(ctx->fp, "static const uint32_t fold_hash[%d] = {\n0x%08x", + sz+1, htab[0]); + for (i = 1; i < sz+1; i++) + fprintf(ctx->fp, ",\n0x%08x", htab[i]); + fprintf(ctx->fp, "\n};\n\n"); +} + +/* Exhaustive search for the shortest semi-perfect hash table. */ +static void makehash(BuildCtx *ctx) +{ + uint32_t htab[BUILD_MAX_FOLD*2+1]; + uint32_t sz, r; + /* Search for the smallest hash table with an odd size. */ + for (sz = (nkeys|1); sz < BUILD_MAX_FOLD*2; sz += 2) { + /* First try all shift hash combinations. */ + for (r = 0; r < 32*32; r++) { + if (tryhash(htab, sz, r, 0)) { + printhash(ctx, htab, sz); + fprintf(ctx->fp, + "#define fold_hashkey(k)\t(((((k)<<%u)-(k))<<%u)%%%u)\n\n", + r>>5, r&31, sz); + return; + } + } + /* Then try all rotate hash combinations. */ + for (r = 0; r < 32*32; r++) { + if (tryhash(htab, sz, r, 1)) { + printhash(ctx, htab, sz); + fprintf(ctx->fp, + "#define fold_hashkey(k)\t(lj_rol(lj_rol((k),%u)-(k),%u)%%%u)\n\n", + r>>5, r&31, sz); + return; + } + } + } + fprintf(stderr, "Error: search for perfect hash failed\n"); + exit(1); +} + +/* Parse one token of a fold rule. */ +static uint32_t nexttoken(char **pp, int allowlit, int allowany) +{ + char *p = *pp; + if (p) { + uint32_t i; + char *q = strchr(p, ' '); + if (q) *q++ = '\0'; + *pp = q; + if (allowlit && !strncmp(p, "IRFPM_", 6)) { + for (i = 0; irfpm_names[i]; i++) + if (!strcmp(irfpm_names[i], p+6)) + return i; + } else if (allowlit && !strncmp(p, "IRFL_", 5)) { + for (i = 0; irfield_names[i]; i++) + if (!strcmp(irfield_names[i], p+5)) + return i; + } else if (allowlit && !strncmp(p, "IRCALL_", 7)) { + for (i = 0; ircall_names[i]; i++) + if (!strcmp(ircall_names[i], p+7)) + return i; + } else if (allowlit && !strncmp(p, "IRCONV_", 7)) { + for (i = 0; irt_names[i]; i++) { + const char *r = strchr(p+7, '_'); + if (r && !strncmp(irt_names[i], p+7, r-(p+7))) { + uint32_t j; + for (j = 0; irt_names[j]; j++) + if (!strcmp(irt_names[j], r+1)) + return (i << 5) + j; + } + } + } else if (allowlit && *p >= '0' && *p <= '9') { + for (i = 0; *p >= '0' && *p <= '9'; p++) + i = i*10 + (*p - '0'); + if (*p == '\0') + return i; + } else if (allowany && !strcmp("any", p)) { + return allowany; + } else { + for (i = 0; ir_names[i]; i++) + if (!strcmp(ir_names[i], p)) + return i; + } + fprintf(stderr, "Error: bad fold definition token \"%s\" at line %d\n", p, lineno); + exit(1); + } + return 0; +} + +/* Parse a fold rule. */ +static void foldrule(char *p) +{ + uint32_t op = nexttoken(&p, 0, 0); + uint32_t left = nexttoken(&p, 0, 0x7f); + uint32_t right = nexttoken(&p, 1, 0x3ff); + uint32_t key = (funcidx << 24) | (op << 17) | (left << 10) | right; + uint32_t i; + if (nkeys >= BUILD_MAX_FOLD) { + fprintf(stderr, "Error: too many fold rules, increase BUILD_MAX_FOLD.\n"); + exit(1); + } + /* Simple insertion sort to detect duplicates. */ + for (i = nkeys; i > 0; i--) { + if ((foldkeys[i-1]&0xffffff) < (key & 0xffffff)) + break; + if ((foldkeys[i-1]&0xffffff) == (key & 0xffffff)) { + fprintf(stderr, "Error: duplicate fold definition at line %d\n", lineno); + exit(1); + } + foldkeys[i] = foldkeys[i-1]; + } + foldkeys[i] = key; + nkeys++; +} + +/* Emit C source code for IR folding hash table. */ +void emit_fold(BuildCtx *ctx) +{ + char buf[256]; /* We don't care about analyzing lines longer than that. */ + const char *fname = ctx->args[0]; + FILE *fp; + + if (fname == NULL) { + fprintf(stderr, "Error: missing input filename\n"); + exit(1); + } + + if (fname[0] == '-' && fname[1] == '\0') { + fp = stdin; + } else { + fp = fopen(fname, "r"); + if (!fp) { + fprintf(stderr, "Error: cannot open input file '%s': %s\n", + fname, strerror(errno)); + exit(1); + } + } + + fprintf(ctx->fp, "/* This is a generated file. DO NOT EDIT! */\n\n"); + fprintf(ctx->fp, "static const FoldFunc fold_func[] = {\n"); + + lineno = 0; + funcidx = 0; + nkeys = 0; + while (fgets(buf, sizeof(buf), fp) != NULL) { + lineno++; + /* The prefix must be at the start of a line, otherwise it's ignored. */ + if (!strncmp(buf, FOLDDEF_PREFIX, sizeof(FOLDDEF_PREFIX)-1)) { + char *p = buf+sizeof(FOLDDEF_PREFIX)-1; + char *q = strchr(p, ')'); + if (p[0] == '(' && q) { + p++; + *q = '\0'; + foldrule(p); + } else if ((p[0] == 'F' || p[0] == 'X') && p[1] == '(' && q) { + p += 2; + *q = '\0'; + if (funcidx) + fprintf(ctx->fp, ",\n"); + if (p[-2] == 'X') + fprintf(ctx->fp, " %s", p); + else + fprintf(ctx->fp, " fold_%s", p); + funcidx++; + } else { + buf[strlen(buf)-1] = '\0'; + fprintf(stderr, "Error: unknown fold definition tag %s%s at line %d\n", + FOLDDEF_PREFIX, p, lineno); + exit(1); + } + } + } + fclose(fp); + fprintf(ctx->fp, "\n};\n\n"); + + makehash(ctx); +} + diff --git a/lib/LuaJIT/host/buildvm_fold.o b/lib/LuaJIT/host/buildvm_fold.o new file mode 100644 index 0000000000000000000000000000000000000000..cdb7a5020b29d2e163acca30bb11b344efb0c70d GIT binary patch literal 9568 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 literal 0 HcmV?d00001 diff --git a/lib/LuaJIT/host/buildvm_lib.c b/lib/LuaJIT/host/buildvm_lib.c new file mode 100644 index 0000000..2956fdb --- /dev/null +++ b/lib/LuaJIT/host/buildvm_lib.c @@ -0,0 +1,457 @@ +/* +** LuaJIT VM builder: library definition compiler. +** Copyright (C) 2005-2017 Mike Pall. See Copyright Notice in luajit.h +*/ + +#include "buildvm.h" +#include "lj_obj.h" +#include "lj_bc.h" +#include "lj_lib.h" +#include "buildvm_libbc.h" + +/* Context for library definitions. */ +static uint8_t obuf[8192]; +static uint8_t *optr; +static char modname[80]; +static size_t modnamelen; +static char funcname[80]; +static int modstate, regfunc; +static int ffid, recffid, ffasmfunc; + +enum { + REGFUNC_OK, + REGFUNC_NOREG, + REGFUNC_NOREGUV +}; + +static void libdef_name(const char *p, int kind) +{ + size_t n = strlen(p); + if (kind != LIBINIT_STRING) { + if (n > modnamelen && p[modnamelen] == '_' && + !strncmp(p, modname, modnamelen)) { + p += modnamelen+1; + n -= modnamelen+1; + } + } + if (n > LIBINIT_MAXSTR) { + fprintf(stderr, "Error: string too long: '%s'\n", p); + exit(1); + } + if (optr+1+n+2 > obuf+sizeof(obuf)) { /* +2 for caller. */ + fprintf(stderr, "Error: output buffer overflow\n"); + exit(1); + } + *optr++ = (uint8_t)(n | kind); + memcpy(optr, p, n); + optr += n; +} + +static void libdef_endmodule(BuildCtx *ctx) +{ + if (modstate != 0) { + char line[80]; + const uint8_t *p; + int n; + if (modstate == 1) + fprintf(ctx->fp, " (lua_CFunction)0"); + fprintf(ctx->fp, "\n};\n"); + fprintf(ctx->fp, "static const uint8_t %s%s[] = {\n", + LABEL_PREFIX_LIBINIT, modname); + line[0] = '\0'; + for (n = 0, p = obuf; p < optr; p++) { + n += sprintf(line+n, "%d,", *p); + if (n >= 75) { + fprintf(ctx->fp, "%s\n", line); + n = 0; + line[0] = '\0'; + } + } + fprintf(ctx->fp, "%s%d\n};\n#endif\n\n", line, LIBINIT_END); + } +} + +static void libdef_module(BuildCtx *ctx, char *p, int arg) +{ + UNUSED(arg); + if (ctx->mode == BUILD_libdef) { + libdef_endmodule(ctx); + optr = obuf; + *optr++ = (uint8_t)ffid; + *optr++ = (uint8_t)ffasmfunc; + *optr++ = 0; /* Hash table size. */ + modstate = 1; + fprintf(ctx->fp, "#ifdef %sMODULE_%s\n", LIBDEF_PREFIX, p); + fprintf(ctx->fp, "#undef %sMODULE_%s\n", LIBDEF_PREFIX, p); + fprintf(ctx->fp, "static const lua_CFunction %s%s[] = {\n", + LABEL_PREFIX_LIBCF, p); + } + modnamelen = strlen(p); + if (modnamelen > sizeof(modname)-1) { + fprintf(stderr, "Error: module name too long: '%s'\n", p); + exit(1); + } + strcpy(modname, p); +} + +static int find_ffofs(BuildCtx *ctx, const char *name) +{ + int i; + for (i = 0; i < ctx->nglob; i++) { + const char *gl = ctx->globnames[i]; + if (gl[0] == 'f' && gl[1] == 'f' && gl[2] == '_' && !strcmp(gl+3, name)) { + return (int)((uint8_t *)ctx->glob[i] - ctx->code); + } + } + fprintf(stderr, "Error: undefined fast function %s%s\n", + LABEL_PREFIX_FF, name); + exit(1); +} + +static void libdef_func(BuildCtx *ctx, char *p, int arg) +{ + if (arg != LIBINIT_CF) + ffasmfunc++; + if (ctx->mode == BUILD_libdef) { + if (modstate == 0) { + fprintf(stderr, "Error: no module for function definition %s\n", p); + exit(1); + } + if (regfunc == REGFUNC_NOREG) { + if (optr+1 > obuf+sizeof(obuf)) { + fprintf(stderr, "Error: output buffer overflow\n"); + exit(1); + } + *optr++ = LIBINIT_FFID; + } else { + if (arg != LIBINIT_ASM_) { + if (modstate != 1) fprintf(ctx->fp, ",\n"); + modstate = 2; + fprintf(ctx->fp, " %s%s", arg ? LABEL_PREFIX_FFH : LABEL_PREFIX_CF, p); + } + if (regfunc != REGFUNC_NOREGUV) obuf[2]++; /* Bump hash table size. */ + libdef_name(regfunc == REGFUNC_NOREGUV ? "" : p, arg); + } + } else if (ctx->mode == BUILD_ffdef) { + fprintf(ctx->fp, "FFDEF(%s)\n", p); + } else if (ctx->mode == BUILD_recdef) { + if (strlen(p) > sizeof(funcname)-1) { + fprintf(stderr, "Error: function name too long: '%s'\n", p); + exit(1); + } + strcpy(funcname, p); + } else if (ctx->mode == BUILD_vmdef) { + int i; + for (i = 1; p[i] && modname[i-1]; i++) + if (p[i] == '_') p[i] = '.'; + fprintf(ctx->fp, "\"%s\",\n", p); + } else if (ctx->mode == BUILD_bcdef) { + if (arg != LIBINIT_CF) + fprintf(ctx->fp, ",\n%d", find_ffofs(ctx, p)); + } + ffid++; + regfunc = REGFUNC_OK; +} + +static uint8_t *libdef_uleb128(uint8_t *p, uint32_t *vv) +{ + uint32_t v = *p++; + if (v >= 0x80) { + int sh = 0; v &= 0x7f; + do { v |= ((*p & 0x7f) << (sh += 7)); } while (*p++ >= 0x80); + } + *vv = v; + return p; +} + +static void libdef_fixupbc(uint8_t *p) +{ + uint32_t i, sizebc; + p += 4; + p = libdef_uleb128(p, &sizebc); + p = libdef_uleb128(p, &sizebc); + p = libdef_uleb128(p, &sizebc); + for (i = 0; i < sizebc; i++, p += 4) { + uint8_t op = p[libbc_endian ? 3 : 0]; + uint8_t ra = p[libbc_endian ? 2 : 1]; + uint8_t rc = p[libbc_endian ? 1 : 2]; + uint8_t rb = p[libbc_endian ? 0 : 3]; + if (!LJ_DUALNUM && op == BC_ISTYPE && rc == ~LJ_TNUMX+1) { + op = BC_ISNUM; rc++; + } + p[LJ_ENDIAN_SELECT(0, 3)] = op; + p[LJ_ENDIAN_SELECT(1, 2)] = ra; + p[LJ_ENDIAN_SELECT(2, 1)] = rc; + p[LJ_ENDIAN_SELECT(3, 0)] = rb; + } +} + +static void libdef_lua(BuildCtx *ctx, char *p, int arg) +{ + UNUSED(arg); + if (ctx->mode == BUILD_libdef) { + int i; + for (i = 0; libbc_map[i].name != NULL; i++) { + if (!strcmp(libbc_map[i].name, p)) { + int ofs = libbc_map[i].ofs; + int len = libbc_map[i+1].ofs - ofs; + obuf[2]++; /* Bump hash table size. */ + *optr++ = LIBINIT_LUA; + libdef_name(p, 0); + memcpy(optr, libbc_code + ofs, len); + libdef_fixupbc(optr); + optr += len; + return; + } + } + fprintf(stderr, "Error: missing libbc definition for %s\n", p); + exit(1); + } +} + +static uint32_t find_rec(char *name) +{ + char *p = (char *)obuf; + uint32_t n; + for (n = 2; *p; n++) { + if (strcmp(p, name) == 0) + return n; + p += strlen(p)+1; + } + if (p+strlen(name)+1 >= (char *)obuf+sizeof(obuf)) { + fprintf(stderr, "Error: output buffer overflow\n"); + exit(1); + } + strcpy(p, name); + return n; +} + +static void libdef_rec(BuildCtx *ctx, char *p, int arg) +{ + UNUSED(arg); + if (ctx->mode == BUILD_recdef) { + char *q; + uint32_t n; + for (; recffid+1 < ffid; recffid++) + fprintf(ctx->fp, ",\n0"); + recffid = ffid; + if (*p == '.') p = funcname; + q = strchr(p, ' '); + if (q) *q++ = '\0'; + n = find_rec(p); + if (q) + fprintf(ctx->fp, ",\n0x%02x00+(%s)", n, q); + else + fprintf(ctx->fp, ",\n0x%02x00", n); + } +} + +static void memcpy_endian(void *dst, void *src, size_t n) +{ + union { uint8_t b; uint32_t u; } host_endian; + host_endian.u = 1; + if (host_endian.b == LJ_ENDIAN_SELECT(1, 0)) { + memcpy(dst, src, n); + } else { + size_t i; + for (i = 0; i < n; i++) + ((uint8_t *)dst)[i] = ((uint8_t *)src)[n-i-1]; + } +} + +static void libdef_push(BuildCtx *ctx, char *p, int arg) +{ + UNUSED(arg); + if (ctx->mode == BUILD_libdef) { + int len = (int)strlen(p); + if (*p == '"') { + if (len > 1 && p[len-1] == '"') { + p[len-1] = '\0'; + libdef_name(p+1, LIBINIT_STRING); + return; + } + } else if (*p >= '0' && *p <= '9') { + char *ep; + double d = strtod(p, &ep); + if (*ep == '\0') { + if (optr+1+sizeof(double) > obuf+sizeof(obuf)) { + fprintf(stderr, "Error: output buffer overflow\n"); + exit(1); + } + *optr++ = LIBINIT_NUMBER; + memcpy_endian(optr, &d, sizeof(double)); + optr += sizeof(double); + return; + } + } else if (!strcmp(p, "lastcl")) { + if (optr+1 > obuf+sizeof(obuf)) { + fprintf(stderr, "Error: output buffer overflow\n"); + exit(1); + } + *optr++ = LIBINIT_LASTCL; + return; + } else if (len > 4 && !strncmp(p, "top-", 4)) { + if (optr+2 > obuf+sizeof(obuf)) { + fprintf(stderr, "Error: output buffer overflow\n"); + exit(1); + } + *optr++ = LIBINIT_COPY; + *optr++ = (uint8_t)atoi(p+4); + return; + } + fprintf(stderr, "Error: bad value for %sPUSH(%s)\n", LIBDEF_PREFIX, p); + exit(1); + } +} + +static void libdef_set(BuildCtx *ctx, char *p, int arg) +{ + UNUSED(arg); + if (ctx->mode == BUILD_libdef) { + if (p[0] == '!' && p[1] == '\0') p[0] = '\0'; /* Set env. */ + libdef_name(p, LIBINIT_STRING); + *optr++ = LIBINIT_SET; + obuf[2]++; /* Bump hash table size. */ + } +} + +static void libdef_regfunc(BuildCtx *ctx, char *p, int arg) +{ + UNUSED(ctx); UNUSED(p); + regfunc = arg; +} + +typedef void (*LibDefFunc)(BuildCtx *ctx, char *p, int arg); + +typedef struct LibDefHandler { + const char *suffix; + const char *stop; + const LibDefFunc func; + const int arg; +} LibDefHandler; + +static const LibDefHandler libdef_handlers[] = { + { "MODULE_", " \t\r\n", libdef_module, 0 }, + { "CF(", ")", libdef_func, LIBINIT_CF }, + { "ASM(", ")", libdef_func, LIBINIT_ASM }, + { "ASM_(", ")", libdef_func, LIBINIT_ASM_ }, + { "LUA(", ")", libdef_lua, 0 }, + { "REC(", ")", libdef_rec, 0 }, + { "PUSH(", ")", libdef_push, 0 }, + { "SET(", ")", libdef_set, 0 }, + { "NOREGUV", NULL, libdef_regfunc, REGFUNC_NOREGUV }, + { "NOREG", NULL, libdef_regfunc, REGFUNC_NOREG }, + { NULL, NULL, (LibDefFunc)0, 0 } +}; + +/* Emit C source code for library function definitions. */ +void emit_lib(BuildCtx *ctx) +{ + const char *fname; + + if (ctx->mode == BUILD_ffdef || ctx->mode == BUILD_libdef || + ctx->mode == BUILD_recdef) + fprintf(ctx->fp, "/* This is a generated file. DO NOT EDIT! */\n\n"); + else if (ctx->mode == BUILD_vmdef) + fprintf(ctx->fp, "ffnames = {\n[0]=\"Lua\",\n\"C\",\n"); + if (ctx->mode == BUILD_recdef) + fprintf(ctx->fp, "static const uint16_t recff_idmap[] = {\n0,\n0x0100"); + recffid = ffid = FF_C+1; + ffasmfunc = 0; + + while ((fname = *ctx->args++)) { + char buf[256]; /* We don't care about analyzing lines longer than that. */ + FILE *fp; + if (fname[0] == '-' && fname[1] == '\0') { + fp = stdin; + } else { + fp = fopen(fname, "r"); + if (!fp) { + fprintf(stderr, "Error: cannot open input file '%s': %s\n", + fname, strerror(errno)); + exit(1); + } + } + modstate = 0; + regfunc = REGFUNC_OK; + while (fgets(buf, sizeof(buf), fp) != NULL) { + char *p; + /* Simplistic pre-processor. Only handles top-level #if/#endif. */ + if (buf[0] == '#' && buf[1] == 'i' && buf[2] == 'f') { + int ok = 1; + if (!strcmp(buf, "#if LJ_52\n")) + ok = LJ_52; + else if (!strcmp(buf, "#if LJ_HASJIT\n")) + ok = LJ_HASJIT; + else if (!strcmp(buf, "#if LJ_HASFFI\n")) + ok = LJ_HASFFI; + if (!ok) { + int lvl = 1; + while (fgets(buf, sizeof(buf), fp) != NULL) { + if (buf[0] == '#' && buf[1] == 'e' && buf[2] == 'n') { + if (--lvl == 0) break; + } else if (buf[0] == '#' && buf[1] == 'i' && buf[2] == 'f') { + lvl++; + } + } + continue; + } + } + for (p = buf; (p = strstr(p, LIBDEF_PREFIX)) != NULL; ) { + const LibDefHandler *ldh; + p += sizeof(LIBDEF_PREFIX)-1; + for (ldh = libdef_handlers; ldh->suffix != NULL; ldh++) { + size_t n, len = strlen(ldh->suffix); + if (!strncmp(p, ldh->suffix, len)) { + p += len; + n = ldh->stop ? strcspn(p, ldh->stop) : 0; + if (!p[n]) break; + p[n] = '\0'; + ldh->func(ctx, p, ldh->arg); + p += n+1; + break; + } + } + if (ldh->suffix == NULL) { + buf[strlen(buf)-1] = '\0'; + fprintf(stderr, "Error: unknown library definition tag %s%s\n", + LIBDEF_PREFIX, p); + exit(1); + } + } + } + fclose(fp); + if (ctx->mode == BUILD_libdef) { + libdef_endmodule(ctx); + } + } + + if (ctx->mode == BUILD_ffdef) { + fprintf(ctx->fp, "\n#undef FFDEF\n\n"); + fprintf(ctx->fp, + "#ifndef FF_NUM_ASMFUNC\n#define FF_NUM_ASMFUNC %d\n#endif\n\n", + ffasmfunc); + } else if (ctx->mode == BUILD_vmdef) { + fprintf(ctx->fp, "},\n\n"); + } else if (ctx->mode == BUILD_bcdef) { + int i; + fprintf(ctx->fp, "\n};\n\n"); + fprintf(ctx->fp, "LJ_DATADEF const uint16_t lj_bc_mode[] = {\n"); + fprintf(ctx->fp, "BCDEF(BCMODE)\n"); + for (i = ffasmfunc-1; i > 0; i--) + fprintf(ctx->fp, "BCMODE_FF,\n"); + fprintf(ctx->fp, "BCMODE_FF\n};\n\n"); + } else if (ctx->mode == BUILD_recdef) { + char *p = (char *)obuf; + fprintf(ctx->fp, "\n};\n\n"); + fprintf(ctx->fp, "static const RecordFunc recff_func[] = {\n" + "recff_nyi,\n" + "recff_c"); + while (*p) { + fprintf(ctx->fp, ",\nrecff_%s", p); + p += strlen(p)+1; + } + fprintf(ctx->fp, "\n};\n\n"); + } +} + diff --git a/lib/LuaJIT/host/buildvm_lib.o b/lib/LuaJIT/host/buildvm_lib.o new file mode 100644 index 0000000000000000000000000000000000000000..7847c157cc50b330e35b577afb1f8b4a00f8b89f GIT binary patch literal 18776 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 Z8tgaLABV|g`I8==oqbDOa^J@M{|n))$e;iK literal 0 HcmV?d00001 diff --git a/lib/LuaJIT/host/buildvm_libbc.h b/lib/LuaJIT/host/buildvm_libbc.h new file mode 100644 index 0000000..b2600bd --- /dev/null +++ b/lib/LuaJIT/host/buildvm_libbc.h @@ -0,0 +1,56 @@ +/* This is a generated file. DO NOT EDIT! */ + +static const int libbc_endian = 0; + +static const uint8_t libbc_code[] = { +#if LJ_FR2 +0,1,2,0,0,1,2,24,1,0,0,76,1,2,0,241,135,158,166,3,220,203,178,130,4,0,1,2,0, +0,1,2,24,1,0,0,76,1,2,0,243,244,148,165,20,198,190,199,252,3,0,1,2,0,0,0,3, +16,0,5,0,21,1,0,0,76,1,2,0,0,2,10,0,0,0,15,16,0,12,0,16,1,9,0,41,2,1,0,21,3, +0,0,41,4,1,0,77,2,8,128,18,6,1,0,18,8,5,0,59,9,5,0,66,6,3,2,10,6,0,0,88,7,1, +128,76,6,2,0,79,2,248,127,75,0,1,0,0,2,11,0,0,0,16,16,0,12,0,16,1,9,0,43,2, +0,0,18,3,0,0,41,4,0,0,88,5,7,128,18,7,1,0,18,9,5,0,18,10,6,0,66,7,3,2,10,7, +0,0,88,8,1,128,76,7,2,0,70,5,3,3,82,5,247,127,75,0,1,0,0,1,2,0,0,0,3,16,0,12, +0,21,1,0,0,76,1,2,0,0,2,10,0,0,2,30,16,0,12,0,21,2,0,0,11,1,0,0,88,3,7,128, +8,2,0,0,88,3,23,128,59,3,2,0,43,4,0,0,64,4,2,0,76,3,2,0,88,3,18,128,16,1,14, +0,41,3,1,0,3,3,1,0,88,3,14,128,3,1,2,0,88,3,12,128,59,3,1,0,22,4,1,1,18,5,2, +0,41,6,1,0,77,4,4,128,23,8,1,7,59,9,7,0,64,9,8,0,79,4,252,127,43,4,0,0,64,4, +2,0,76,3,2,0,75,0,1,0,0,2,0,5,12,0,0,0,35,16,0,12,0,16,1,14,0,16,2,14,0,16, +3,14,0,11,4,0,0,88,5,1,128,18,4,0,0,16,4,12,0,3,1,2,0,88,5,24,128,33,5,1,3, +0,2,3,0,88,6,4,128,2,3,1,0,88,6,2,128,4,4,0,0,88,6,9,128,18,6,1,0,18,7,2,0, +41,8,1,0,77,6,4,128,32,10,5,9,59,11,9,0,64,11,10,4,79,6,252,127,88,6,8,128, +18,6,2,0,18,7,1,0,41,8,255,255,77,6,4,128,32,10,5,9,59,11,9,0,64,11,10,4,79, +6,252,127,76,4,2,0,0 +#else +0,1,2,0,0,1,2,24,1,0,0,76,1,2,0,241,135,158,166,3,220,203,178,130,4,0,1,2,0, +0,1,2,24,1,0,0,76,1,2,0,243,244,148,165,20,198,190,199,252,3,0,1,2,0,0,0,3, +16,0,5,0,21,1,0,0,76,1,2,0,0,2,9,0,0,0,15,16,0,12,0,16,1,9,0,41,2,1,0,21,3, +0,0,41,4,1,0,77,2,8,128,18,6,1,0,18,7,5,0,59,8,5,0,66,6,3,2,10,6,0,0,88,7,1, +128,76,6,2,0,79,2,248,127,75,0,1,0,0,2,10,0,0,0,16,16,0,12,0,16,1,9,0,43,2, +0,0,18,3,0,0,41,4,0,0,88,5,7,128,18,7,1,0,18,8,5,0,18,9,6,0,66,7,3,2,10,7,0, +0,88,8,1,128,76,7,2,0,70,5,3,3,82,5,247,127,75,0,1,0,0,1,2,0,0,0,3,16,0,12, +0,21,1,0,0,76,1,2,0,0,2,10,0,0,2,30,16,0,12,0,21,2,0,0,11,1,0,0,88,3,7,128, +8,2,0,0,88,3,23,128,59,3,2,0,43,4,0,0,64,4,2,0,76,3,2,0,88,3,18,128,16,1,14, +0,41,3,1,0,3,3,1,0,88,3,14,128,3,1,2,0,88,3,12,128,59,3,1,0,22,4,1,1,18,5,2, +0,41,6,1,0,77,4,4,128,23,8,1,7,59,9,7,0,64,9,8,0,79,4,252,127,43,4,0,0,64,4, +2,0,76,3,2,0,75,0,1,0,0,2,0,5,12,0,0,0,35,16,0,12,0,16,1,14,0,16,2,14,0,16, +3,14,0,11,4,0,0,88,5,1,128,18,4,0,0,16,4,12,0,3,1,2,0,88,5,24,128,33,5,1,3, +0,2,3,0,88,6,4,128,2,3,1,0,88,6,2,128,4,4,0,0,88,6,9,128,18,6,1,0,18,7,2,0, +41,8,1,0,77,6,4,128,32,10,5,9,59,11,9,0,64,11,10,4,79,6,252,127,88,6,8,128, +18,6,2,0,18,7,1,0,41,8,255,255,77,6,4,128,32,10,5,9,59,11,9,0,64,11,10,4,79, +6,252,127,76,4,2,0,0 +#endif +}; + +static const struct { const char *name; int ofs; } libbc_map[] = { +{"math_deg",0}, +{"math_rad",25}, +{"string_len",50}, +{"table_foreachi",69}, +{"table_foreach",136}, +{"table_getn",207}, +{"table_remove",226}, +{"table_move",355}, +{NULL,502} +}; + diff --git a/lib/LuaJIT/host/buildvm_peobj.c b/lib/LuaJIT/host/buildvm_peobj.c new file mode 100644 index 0000000..2eb2bb7 --- /dev/null +++ b/lib/LuaJIT/host/buildvm_peobj.c @@ -0,0 +1,392 @@ +/* +** LuaJIT VM builder: PE object emitter. +** Copyright (C) 2005-2017 Mike Pall. See Copyright Notice in luajit.h +** +** Only used for building on Windows, since we cannot assume the presence +** of a suitable assembler. The host and target byte order must match. +*/ + +#include "buildvm.h" +#include "lj_bc.h" + +#if LJ_TARGET_X86ORX64 || LJ_TARGET_PPC + +/* Context for PE object emitter. */ +static char *strtab; +static size_t strtabofs; + +/* -- PE object definitions ----------------------------------------------- */ + +/* PE header. */ +typedef struct PEheader { + uint16_t arch; + uint16_t nsects; + uint32_t time; + uint32_t symtabofs; + uint32_t nsyms; + uint16_t opthdrsz; + uint16_t flags; +} PEheader; + +/* PE section. */ +typedef struct PEsection { + char name[8]; + uint32_t vsize; + uint32_t vaddr; + uint32_t size; + uint32_t ofs; + uint32_t relocofs; + uint32_t lineofs; + uint16_t nreloc; + uint16_t nline; + uint32_t flags; +} PEsection; + +/* PE relocation. */ +typedef struct PEreloc { + uint32_t vaddr; + uint32_t symidx; + uint16_t type; +} PEreloc; + +/* Cannot use sizeof, because it pads up to the max. alignment. */ +#define PEOBJ_RELOC_SIZE (4+4+2) + +/* PE symbol table entry. */ +typedef struct PEsym { + union { + char name[8]; + uint32_t nameref[2]; + } n; + uint32_t value; + int16_t sect; + uint16_t type; + uint8_t scl; + uint8_t naux; +} PEsym; + +/* PE symbol table auxiliary entry for a section. */ +typedef struct PEsymaux { + uint32_t size; + uint16_t nreloc; + uint16_t nline; + uint32_t cksum; + uint16_t assoc; + uint8_t comdatsel; + uint8_t unused[3]; +} PEsymaux; + +/* Cannot use sizeof, because it pads up to the max. alignment. */ +#define PEOBJ_SYM_SIZE (8+4+2+2+1+1) + +/* PE object CPU specific defines. */ +#if LJ_TARGET_X86 +#define PEOBJ_ARCH_TARGET 0x014c +#define PEOBJ_RELOC_REL32 0x14 /* MS: REL32, GNU: DISP32. */ +#define PEOBJ_RELOC_DIR32 0x06 +#define PEOBJ_RELOC_OFS 0 +#define PEOBJ_TEXT_FLAGS 0x60500020 /* 60=r+x, 50=align16, 20=code. */ +#elif LJ_TARGET_X64 +#define PEOBJ_ARCH_TARGET 0x8664 +#define PEOBJ_RELOC_REL32 0x04 /* MS: REL32, GNU: DISP32. */ +#define PEOBJ_RELOC_DIR32 0x02 +#define PEOBJ_RELOC_ADDR32NB 0x03 +#define PEOBJ_RELOC_OFS 0 +#define PEOBJ_TEXT_FLAGS 0x60500020 /* 60=r+x, 50=align16, 20=code. */ +#elif LJ_TARGET_PPC +#define PEOBJ_ARCH_TARGET 0x01f2 +#define PEOBJ_RELOC_REL32 0x06 +#define PEOBJ_RELOC_DIR32 0x02 +#define PEOBJ_RELOC_OFS (-4) +#define PEOBJ_TEXT_FLAGS 0x60400020 /* 60=r+x, 40=align8, 20=code. */ +#endif + +/* Section numbers (0-based). */ +enum { + PEOBJ_SECT_ABS = -2, + PEOBJ_SECT_UNDEF = -1, + PEOBJ_SECT_TEXT, +#if LJ_TARGET_X64 + PEOBJ_SECT_PDATA, + PEOBJ_SECT_XDATA, +#elif LJ_TARGET_X86 + PEOBJ_SECT_SXDATA, +#endif + PEOBJ_SECT_RDATA_Z, + PEOBJ_NSECTIONS +}; + +/* Symbol types. */ +#define PEOBJ_TYPE_NULL 0 +#define PEOBJ_TYPE_FUNC 0x20 + +/* Symbol storage class. */ +#define PEOBJ_SCL_EXTERN 2 +#define PEOBJ_SCL_STATIC 3 + +/* -- PE object emitter --------------------------------------------------- */ + +/* Emit PE object symbol. */ +static void emit_peobj_sym(BuildCtx *ctx, const char *name, uint32_t value, + int sect, int type, int scl) +{ + PEsym sym; + size_t len = strlen(name); + if (!strtab) { /* Pass 1: only calculate string table length. */ + if (len > 8) strtabofs += len+1; + return; + } + if (len <= 8) { + memcpy(sym.n.name, name, len); + memset(sym.n.name+len, 0, 8-len); + } else { + sym.n.nameref[0] = 0; + sym.n.nameref[1] = (uint32_t)strtabofs; + memcpy(strtab + strtabofs, name, len); + strtab[strtabofs+len] = 0; + strtabofs += len+1; + } + sym.value = value; + sym.sect = (int16_t)(sect+1); /* 1-based section number. */ + sym.type = (uint16_t)type; + sym.scl = (uint8_t)scl; + sym.naux = 0; + owrite(ctx, &sym, PEOBJ_SYM_SIZE); +} + +/* Emit PE object section symbol. */ +static void emit_peobj_sym_sect(BuildCtx *ctx, PEsection *pesect, int sect) +{ + PEsym sym; + PEsymaux aux; + if (!strtab) return; /* Pass 1: no output. */ + memcpy(sym.n.name, pesect[sect].name, 8); + sym.value = 0; + sym.sect = (int16_t)(sect+1); /* 1-based section number. */ + sym.type = PEOBJ_TYPE_NULL; + sym.scl = PEOBJ_SCL_STATIC; + sym.naux = 1; + owrite(ctx, &sym, PEOBJ_SYM_SIZE); + memset(&aux, 0, sizeof(PEsymaux)); + aux.size = pesect[sect].size; + aux.nreloc = pesect[sect].nreloc; + owrite(ctx, &aux, PEOBJ_SYM_SIZE); +} + +/* Emit Windows PE object file. */ +void emit_peobj(BuildCtx *ctx) +{ + PEheader pehdr; + PEsection pesect[PEOBJ_NSECTIONS]; + uint32_t sofs; + int i, nrsym; + union { uint8_t b; uint32_t u; } host_endian; + + sofs = sizeof(PEheader) + PEOBJ_NSECTIONS*sizeof(PEsection); + + /* Fill in PE sections. */ + memset(&pesect, 0, PEOBJ_NSECTIONS*sizeof(PEsection)); + memcpy(pesect[PEOBJ_SECT_TEXT].name, ".text", sizeof(".text")-1); + pesect[PEOBJ_SECT_TEXT].ofs = sofs; + sofs += (pesect[PEOBJ_SECT_TEXT].size = (uint32_t)ctx->codesz); + pesect[PEOBJ_SECT_TEXT].relocofs = sofs; + sofs += (pesect[PEOBJ_SECT_TEXT].nreloc = (uint16_t)ctx->nreloc) * PEOBJ_RELOC_SIZE; + /* Flags: 60 = read+execute, 50 = align16, 20 = code. */ + pesect[PEOBJ_SECT_TEXT].flags = PEOBJ_TEXT_FLAGS; + +#if LJ_TARGET_X64 + memcpy(pesect[PEOBJ_SECT_PDATA].name, ".pdata", sizeof(".pdata")-1); + pesect[PEOBJ_SECT_PDATA].ofs = sofs; + sofs += (pesect[PEOBJ_SECT_PDATA].size = 6*4); + pesect[PEOBJ_SECT_PDATA].relocofs = sofs; + sofs += (pesect[PEOBJ_SECT_PDATA].nreloc = 6) * PEOBJ_RELOC_SIZE; + /* Flags: 40 = read, 30 = align4, 40 = initialized data. */ + pesect[PEOBJ_SECT_PDATA].flags = 0x40300040; + + memcpy(pesect[PEOBJ_SECT_XDATA].name, ".xdata", sizeof(".xdata")-1); + pesect[PEOBJ_SECT_XDATA].ofs = sofs; + sofs += (pesect[PEOBJ_SECT_XDATA].size = 8*2+4+6*2); /* See below. */ + pesect[PEOBJ_SECT_XDATA].relocofs = sofs; + sofs += (pesect[PEOBJ_SECT_XDATA].nreloc = 1) * PEOBJ_RELOC_SIZE; + /* Flags: 40 = read, 30 = align4, 40 = initialized data. */ + pesect[PEOBJ_SECT_XDATA].flags = 0x40300040; +#elif LJ_TARGET_X86 + memcpy(pesect[PEOBJ_SECT_SXDATA].name, ".sxdata", sizeof(".sxdata")-1); + pesect[PEOBJ_SECT_SXDATA].ofs = sofs; + sofs += (pesect[PEOBJ_SECT_SXDATA].size = 4); + pesect[PEOBJ_SECT_SXDATA].relocofs = sofs; + /* Flags: 40 = read, 30 = align4, 02 = lnk_info, 40 = initialized data. */ + pesect[PEOBJ_SECT_SXDATA].flags = 0x40300240; +#endif + + memcpy(pesect[PEOBJ_SECT_RDATA_Z].name, ".rdata$Z", sizeof(".rdata$Z")-1); + pesect[PEOBJ_SECT_RDATA_Z].ofs = sofs; + sofs += (pesect[PEOBJ_SECT_RDATA_Z].size = (uint32_t)strlen(ctx->dasm_ident)+1); + /* Flags: 40 = read, 30 = align4, 40 = initialized data. */ + pesect[PEOBJ_SECT_RDATA_Z].flags = 0x40300040; + + /* Fill in PE header. */ + pehdr.arch = PEOBJ_ARCH_TARGET; + pehdr.nsects = PEOBJ_NSECTIONS; + pehdr.time = 0; /* Timestamp is optional. */ + pehdr.symtabofs = sofs; + pehdr.opthdrsz = 0; + pehdr.flags = 0; + + /* Compute the size of the symbol table: + ** @feat.00 + nsections*2 + ** + asm_start + nsym + ** + nrsym + */ + nrsym = ctx->nrelocsym; + pehdr.nsyms = 1+PEOBJ_NSECTIONS*2 + 1+ctx->nsym + nrsym; +#if LJ_TARGET_X64 + pehdr.nsyms += 1; /* Symbol for lj_err_unwind_win. */ +#endif + + /* Write PE object header and all sections. */ + owrite(ctx, &pehdr, sizeof(PEheader)); + owrite(ctx, &pesect, sizeof(PEsection)*PEOBJ_NSECTIONS); + + /* Write .text section. */ + host_endian.u = 1; + if (host_endian.b != LJ_ENDIAN_SELECT(1, 0)) { +#if LJ_TARGET_PPC + uint32_t *p = (uint32_t *)ctx->code; + int n = (int)(ctx->codesz >> 2); + for (i = 0; i < n; i++, p++) + *p = lj_bswap(*p); /* Byteswap .text section. */ +#else + fprintf(stderr, "Error: different byte order for host and target\n"); + exit(1); +#endif + } + owrite(ctx, ctx->code, ctx->codesz); + for (i = 0; i < ctx->nreloc; i++) { + PEreloc reloc; + reloc.vaddr = (uint32_t)ctx->reloc[i].ofs + PEOBJ_RELOC_OFS; + reloc.symidx = 1+2+ctx->reloc[i].sym; /* Reloc syms are after .text sym. */ + reloc.type = ctx->reloc[i].type ? PEOBJ_RELOC_REL32 : PEOBJ_RELOC_DIR32; + owrite(ctx, &reloc, PEOBJ_RELOC_SIZE); + } + +#if LJ_TARGET_X64 + { /* Write .pdata section. */ + uint32_t fcofs = (uint32_t)ctx->sym[ctx->nsym-1].ofs; + uint32_t pdata[3]; /* Start of .text, end of .text and .xdata. */ + PEreloc reloc; + pdata[0] = 0; pdata[1] = fcofs; pdata[2] = 0; + owrite(ctx, &pdata, sizeof(pdata)); + pdata[0] = fcofs; pdata[1] = (uint32_t)ctx->codesz; pdata[2] = 20; + owrite(ctx, &pdata, sizeof(pdata)); + reloc.vaddr = 0; reloc.symidx = 1+2+nrsym+2+2+1; + reloc.type = PEOBJ_RELOC_ADDR32NB; + owrite(ctx, &reloc, PEOBJ_RELOC_SIZE); + reloc.vaddr = 4; reloc.symidx = 1+2+nrsym+2+2+1; + reloc.type = PEOBJ_RELOC_ADDR32NB; + owrite(ctx, &reloc, PEOBJ_RELOC_SIZE); + reloc.vaddr = 8; reloc.symidx = 1+2+nrsym+2; + reloc.type = PEOBJ_RELOC_ADDR32NB; + owrite(ctx, &reloc, PEOBJ_RELOC_SIZE); + reloc.vaddr = 12; reloc.symidx = 1+2+nrsym+2+2+1; + reloc.type = PEOBJ_RELOC_ADDR32NB; + owrite(ctx, &reloc, PEOBJ_RELOC_SIZE); + reloc.vaddr = 16; reloc.symidx = 1+2+nrsym+2+2+1; + reloc.type = PEOBJ_RELOC_ADDR32NB; + owrite(ctx, &reloc, PEOBJ_RELOC_SIZE); + reloc.vaddr = 20; reloc.symidx = 1+2+nrsym+2; + reloc.type = PEOBJ_RELOC_ADDR32NB; + owrite(ctx, &reloc, PEOBJ_RELOC_SIZE); + } + { /* Write .xdata section. */ + uint16_t xdata[8+2+6]; + PEreloc reloc; + xdata[0] = 0x01|0x08|0x10; /* Ver. 1, uhandler/ehandler, prolog size 0. */ + xdata[1] = 0x0005; /* Number of unwind codes, no frame pointer. */ + xdata[2] = 0x4200; /* Stack offset 4*8+8 = aword*5. */ + xdata[3] = 0x3000; /* Push rbx. */ + xdata[4] = 0x6000; /* Push rsi. */ + xdata[5] = 0x7000; /* Push rdi. */ + xdata[6] = 0x5000; /* Push rbp. */ + xdata[7] = 0; /* Alignment. */ + xdata[8] = xdata[9] = 0; /* Relocated address of exception handler. */ + xdata[10] = 0x01; /* Ver. 1, no handler, prolog size 0. */ + xdata[11] = 0x1504; /* Number of unwind codes, fp = rbp, fpofs = 16. */ + xdata[12] = 0x0300; /* set_fpreg. */ + xdata[13] = 0x0200; /* stack offset 0*8+8 = aword*1. */ + xdata[14] = 0x3000; /* Push rbx. */ + xdata[15] = 0x5000; /* Push rbp. */ + owrite(ctx, &xdata, sizeof(xdata)); + reloc.vaddr = 2*8; reloc.symidx = 1+2+nrsym+2+2; + reloc.type = PEOBJ_RELOC_ADDR32NB; + owrite(ctx, &reloc, PEOBJ_RELOC_SIZE); + } +#elif LJ_TARGET_X86 + /* Write .sxdata section. */ + for (i = 0; i < nrsym; i++) { + if (!strcmp(ctx->relocsym[i], "_lj_err_unwind_win")) { + uint32_t symidx = 1+2+i; + owrite(ctx, &symidx, 4); + break; + } + } + if (i == nrsym) { + fprintf(stderr, "Error: extern lj_err_unwind_win not used\n"); + exit(1); + } +#endif + + /* Write .rdata$Z section. */ + owrite(ctx, ctx->dasm_ident, strlen(ctx->dasm_ident)+1); + + /* Write symbol table. */ + strtab = NULL; /* 1st pass: collect string sizes. */ + for (;;) { + strtabofs = 4; + /* Mark as SafeSEH compliant. */ + emit_peobj_sym(ctx, "@feat.00", 1, + PEOBJ_SECT_ABS, PEOBJ_TYPE_NULL, PEOBJ_SCL_STATIC); + + emit_peobj_sym_sect(ctx, pesect, PEOBJ_SECT_TEXT); + for (i = 0; i < nrsym; i++) + emit_peobj_sym(ctx, ctx->relocsym[i], 0, + PEOBJ_SECT_UNDEF, PEOBJ_TYPE_FUNC, PEOBJ_SCL_EXTERN); + +#if LJ_TARGET_X64 + emit_peobj_sym_sect(ctx, pesect, PEOBJ_SECT_PDATA); + emit_peobj_sym_sect(ctx, pesect, PEOBJ_SECT_XDATA); + emit_peobj_sym(ctx, "lj_err_unwind_win", 0, + PEOBJ_SECT_UNDEF, PEOBJ_TYPE_FUNC, PEOBJ_SCL_EXTERN); +#elif LJ_TARGET_X86 + emit_peobj_sym_sect(ctx, pesect, PEOBJ_SECT_SXDATA); +#endif + + emit_peobj_sym(ctx, ctx->beginsym, 0, + PEOBJ_SECT_TEXT, PEOBJ_TYPE_NULL, PEOBJ_SCL_EXTERN); + for (i = 0; i < ctx->nsym; i++) + emit_peobj_sym(ctx, ctx->sym[i].name, (uint32_t)ctx->sym[i].ofs, + PEOBJ_SECT_TEXT, PEOBJ_TYPE_FUNC, PEOBJ_SCL_EXTERN); + + emit_peobj_sym_sect(ctx, pesect, PEOBJ_SECT_RDATA_Z); + + if (strtab) + break; + /* 2nd pass: alloc strtab, write syms and copy strings. */ + strtab = (char *)malloc(strtabofs); + *(uint32_t *)strtab = (uint32_t)strtabofs; + } + + /* Write string table. */ + owrite(ctx, strtab, strtabofs); +} + +#else + +void emit_peobj(BuildCtx *ctx) +{ + UNUSED(ctx); + fprintf(stderr, "Error: no PE object support for this target\n"); + exit(1); +} + +#endif diff --git a/lib/LuaJIT/host/buildvm_peobj.o b/lib/LuaJIT/host/buildvm_peobj.o new file mode 100644 index 0000000000000000000000000000000000000000..daa3e0fc0e5d34300387a7aa9d56fea40be5f44e GIT binary patch literal 5264 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 T{`5><&VF9T-{-QWrq}*|4sey{ literal 0 HcmV?d00001 diff --git a/lib/LuaJIT/host/genlibbc.lua b/lib/LuaJIT/host/genlibbc.lua new file mode 100644 index 0000000..6f5a05c --- /dev/null +++ b/lib/LuaJIT/host/genlibbc.lua @@ -0,0 +1,197 @@ +---------------------------------------------------------------------------- +-- Lua script to dump the bytecode of the library functions written in Lua. +-- The resulting 'buildvm_libbc.h' is used for the build process of LuaJIT. +---------------------------------------------------------------------------- +-- Copyright (C) 2005-2017 Mike Pall. All rights reserved. +-- Released under the MIT license. See Copyright Notice in luajit.h +---------------------------------------------------------------------------- + +local ffi = require("ffi") +local bit = require("bit") +local vmdef = require("jit.vmdef") +local bcnames = vmdef.bcnames + +local format = string.format + +local isbe = (string.byte(string.dump(function() end), 5) % 2 == 1) + +local function usage(arg) + io.stderr:write("Usage: ", arg and arg[0] or "genlibbc", + " [-o buildvm_libbc.h] lib_*.c\n") + os.exit(1) +end + +local function parse_arg(arg) + local outfile = "-" + if not (arg and arg[1]) then + usage(arg) + end + if arg[1] == "-o" then + outfile = arg[2] + if not outfile then usage(arg) end + table.remove(arg, 1) + table.remove(arg, 1) + end + return outfile +end + +local function read_files(names) + local src = "" + for _,name in ipairs(names) do + local fp = assert(io.open(name)) + src = src .. fp:read("*a") + fp:close() + end + return src +end + +local function transform_lua(code) + local fixup = {} + local n = -30000 + code = string.gsub(code, "CHECK_(%w*)%((.-)%)", function(tp, var) + n = n + 1 + fixup[n] = { "CHECK", tp } + return format("%s=%d", var, n) + end) + code = string.gsub(code, "PAIRS%((.-)%)", function(var) + fixup.PAIRS = true + return format("nil, %s, 0", var) + end) + return "return "..code, fixup +end + +local function read_uleb128(p) + local v = p[0]; p = p + 1 + if v >= 128 then + local sh = 7; v = v - 128 + repeat + local r = p[0] + v = v + bit.lshift(bit.band(r, 127), sh) + sh = sh + 7 + p = p + 1 + until r < 128 + end + return p, v +end + +-- ORDER LJ_T +local name2itype = { + str = 5, func = 9, tab = 12, int = 14, num = 15 +} + +local BC = {} +for i=0,#bcnames/6-1 do + BC[string.gsub(string.sub(bcnames, i*6+1, i*6+6), " ", "")] = i +end +local xop, xra = isbe and 3 or 0, isbe and 2 or 1 +local xrc, xrb = isbe and 1 or 2, isbe and 0 or 3 + +local function fixup_dump(dump, fixup) + local buf = ffi.new("uint8_t[?]", #dump+1, dump) + local p = buf+5 + local n, sizebc + p, n = read_uleb128(p) + local start = p + p = p + 4 + p = read_uleb128(p) + p = read_uleb128(p) + p, sizebc = read_uleb128(p) + local rawtab = {} + for i=0,sizebc-1 do + local op = p[xop] + if op == BC.KSHORT then + local rd = p[xrc] + 256*p[xrb] + rd = bit.arshift(bit.lshift(rd, 16), 16) + local f = fixup[rd] + if f then + if f[1] == "CHECK" then + local tp = f[2] + if tp == "tab" then rawtab[p[xra]] = true end + p[xop] = tp == "num" and BC.ISNUM or BC.ISTYPE + p[xrb] = 0 + p[xrc] = name2itype[tp] + else + error("unhandled fixup type: "..f[1]) + end + end + elseif op == BC.TGETV then + if rawtab[p[xrb]] then + p[xop] = BC.TGETR + end + elseif op == BC.TSETV then + if rawtab[p[xrb]] then + p[xop] = BC.TSETR + end + elseif op == BC.ITERC then + if fixup.PAIRS then + p[xop] = BC.ITERN + end + end + p = p + 4 + end + return ffi.string(start, n) +end + +local function find_defs(src) + local defs = {} + for name, code in string.gmatch(src, "LJLIB_LUA%(([^)]*)%)%s*/%*(.-)%*/") do + local env = {} + local tcode, fixup = transform_lua(code) + local func = assert(load(tcode, "", nil, env))() + defs[name] = fixup_dump(string.dump(func, true), fixup) + defs[#defs+1] = name + end + return defs +end + +local function gen_header(defs) + local t = {} + local function w(x) t[#t+1] = x end + w("/* This is a generated file. DO NOT EDIT! */\n\n") + w("static const int libbc_endian = ") w(isbe and 1 or 0) w(";\n\n") + local s = "" + for _,name in ipairs(defs) do + s = s .. defs[name] + end + w("static const uint8_t libbc_code[] = {\n") + local n = 0 + for i=1,#s do + local x = string.byte(s, i) + w(x); w(",") + n = n + (x < 10 and 2 or (x < 100 and 3 or 4)) + if n >= 75 then n = 0; w("\n") end + end + w("0\n};\n\n") + w("static const struct { const char *name; int ofs; } libbc_map[] = {\n") + local m = 0 + for _,name in ipairs(defs) do + w('{"'); w(name); w('",'); w(m) w('},\n') + m = m + #defs[name] + end + w("{NULL,"); w(m); w("}\n};\n\n") + return table.concat(t) +end + +local function write_file(name, data) + if name == "-" then + assert(io.write(data)) + assert(io.flush()) + else + local fp = io.open(name) + if fp then + local old = fp:read("*a") + fp:close() + if data == old then return end + end + fp = assert(io.open(name, "w")) + assert(fp:write(data)) + assert(fp:close()) + end +end + +local outfile = parse_arg(arg) +local src = read_files(arg) +local defs = find_defs(src) +local hdr = gen_header(defs) +write_file(outfile, hdr) + diff --git a/lib/LuaJIT/host/genminilua.lua b/lib/LuaJIT/host/genminilua.lua new file mode 100644 index 0000000..50feff0 --- /dev/null +++ b/lib/LuaJIT/host/genminilua.lua @@ -0,0 +1,429 @@ +---------------------------------------------------------------------------- +-- Lua script to generate a customized, minified version of Lua. +-- The resulting 'minilua' is used for the build process of LuaJIT. +---------------------------------------------------------------------------- +-- Copyright (C) 2005-2017 Mike Pall. All rights reserved. +-- Released under the MIT license. See Copyright Notice in luajit.h +---------------------------------------------------------------------------- + +local sub, match, gsub = string.sub, string.match, string.gsub + +local LUA_VERSION = "5.1.5" +local LUA_SOURCE + +local function usage() + io.stderr:write("Usage: ", arg and arg[0] or "genminilua", + " lua-", LUA_VERSION, "-source-dir\n") + os.exit(1) +end + +local function find_sources() + LUA_SOURCE = arg and arg[1] + if not LUA_SOURCE then usage() end + if sub(LUA_SOURCE, -1) ~= "/" then LUA_SOURCE = LUA_SOURCE.."/" end + local fp = io.open(LUA_SOURCE .. "lua.h") + if not fp then + LUA_SOURCE = LUA_SOURCE.."src/" + fp = io.open(LUA_SOURCE .. "lua.h") + if not fp then usage() end + end + local all = fp:read("*a") + fp:close() + if not match(all, 'LUA_RELEASE%s*"Lua '..LUA_VERSION..'"') then + io.stderr:write("Error: version mismatch\n") + usage() + end +end + +local LUA_FILES = { +"lmem.c", "lobject.c", "ltm.c", "lfunc.c", "ldo.c", "lstring.c", "ltable.c", +"lgc.c", "lstate.c", "ldebug.c", "lzio.c", "lopcodes.c", +"llex.c", "lcode.c", "lparser.c", "lvm.c", "lapi.c", "lauxlib.c", +"lbaselib.c", "ltablib.c", "liolib.c", "loslib.c", "lstrlib.c", "linit.c", +} + +local REMOVE_LIB = {} +gsub([[ +collectgarbage dofile gcinfo getfenv getmetatable load print rawequal rawset +select tostring xpcall +foreach foreachi getn maxn setn +popen tmpfile seek setvbuf __tostring +clock date difftime execute getenv rename setlocale time tmpname +dump gfind len reverse +LUA_LOADLIBNAME LUA_MATHLIBNAME LUA_DBLIBNAME +]], "%S+", function(name) + REMOVE_LIB[name] = true +end) + +local REMOVE_EXTINC = { [""] = true, [""] = true, } + +local CUSTOM_MAIN = [[ +typedef unsigned int UB; +static UB barg(lua_State *L,int idx){ +union{lua_Number n;U64 b;}bn; +bn.n=lua_tonumber(L,idx)+6755399441055744.0; +if (bn.n==0.0&&!lua_isnumber(L,idx))luaL_typerror(L,idx,"number"); +return(UB)bn.b; +} +#define BRET(b) lua_pushnumber(L,(lua_Number)(int)(b));return 1; +static int tobit(lua_State *L){ +BRET(barg(L,1))} +static int bnot(lua_State *L){ +BRET(~barg(L,1))} +static int band(lua_State *L){ +int i;UB b=barg(L,1);for(i=lua_gettop(L);i>1;i--)b&=barg(L,i);BRET(b)} +static int bor(lua_State *L){ +int i;UB b=barg(L,1);for(i=lua_gettop(L);i>1;i--)b|=barg(L,i);BRET(b)} +static int bxor(lua_State *L){ +int i;UB b=barg(L,1);for(i=lua_gettop(L);i>1;i--)b^=barg(L,i);BRET(b)} +static int lshift(lua_State *L){ +UB b=barg(L,1),n=barg(L,2)&31;BRET(b<>n)} +static int arshift(lua_State *L){ +UB b=barg(L,1),n=barg(L,2)&31;BRET((int)b>>n)} +static int rol(lua_State *L){ +UB b=barg(L,1),n=barg(L,2)&31;BRET((b<>(32-n)))} +static int ror(lua_State *L){ +UB b=barg(L,1),n=barg(L,2)&31;BRET((b>>n)|(b<<(32-n)))} +static int bswap(lua_State *L){ +UB b=barg(L,1);b=(b>>24)|((b>>8)&0xff00)|((b&0xff00)<<8)|(b<<24);BRET(b)} +static int tohex(lua_State *L){ +UB b=barg(L,1); +int n=lua_isnone(L,2)?8:(int)barg(L,2); +const char *hexdigits="0123456789abcdef"; +char buf[8]; +int i; +if(n<0){n=-n;hexdigits="0123456789ABCDEF";} +if(n>8)n=8; +for(i=(int)n;--i>=0;){buf[i]=hexdigits[b&15];b>>=4;} +lua_pushlstring(L,buf,(size_t)n); +return 1; +} +static const struct luaL_Reg bitlib[] = { +{"tobit",tobit}, +{"bnot",bnot}, +{"band",band}, +{"bor",bor}, +{"bxor",bxor}, +{"lshift",lshift}, +{"rshift",rshift}, +{"arshift",arshift}, +{"rol",rol}, +{"ror",ror}, +{"bswap",bswap}, +{"tohex",tohex}, +{NULL,NULL} +}; +int main(int argc, char **argv){ + lua_State *L = luaL_newstate(); + int i; + luaL_openlibs(L); + luaL_register(L, "bit", bitlib); + if (argc < 2) return sizeof(void *); + lua_createtable(L, 0, 1); + lua_pushstring(L, argv[1]); + lua_rawseti(L, -2, 0); + lua_setglobal(L, "arg"); + if (luaL_loadfile(L, argv[1])) + goto err; + for (i = 2; i < argc; i++) + lua_pushstring(L, argv[i]); + if (lua_pcall(L, argc - 2, 0, 0)) { + err: + fprintf(stderr, "Error: %s\n", lua_tostring(L, -1)); + return 1; + } + lua_close(L); + return 0; +} +]] + +local function read_sources() + local t = {} + for i, name in ipairs(LUA_FILES) do + local fp = assert(io.open(LUA_SOURCE..name, "r")) + t[i] = fp:read("*a") + assert(fp:close()) + end + t[#t+1] = CUSTOM_MAIN + return table.concat(t) +end + +local includes = {} + +local function merge_includes(src) + return gsub(src, '#include%s*"([^"]*)"%s*\n', function(name) + if includes[name] then return "" end + includes[name] = true + local fp = assert(io.open(LUA_SOURCE..name, "r")) + local inc = fp:read("*a") + assert(fp:close()) + inc = gsub(inc, "#ifndef%s+%w+_h\n#define%s+%w+_h\n", "") + inc = gsub(inc, "#endif%s*$", "") + return merge_includes(inc) + end) +end + +local function get_license(src) + return match(src, "/%*+\n%* Copyright %(.-%*/\n") +end + +local function fold_lines(src) + return gsub(src, "\\\n", " ") +end + +local strings = {} + +local function save_str(str) + local n = #strings+1 + strings[n] = str + return "\1"..n.."\2" +end + +local function save_strings(src) + src = gsub(src, '"[^"\n]*"', save_str) + return gsub(src, "'[^'\n]*'", save_str) +end + +local function restore_strings(src) + return gsub(src, "\1(%d+)\2", function(numstr) + return strings[tonumber(numstr)] + end) +end + +local function def_istrue(def) + return def == "INT_MAX > 2147483640L" or + def == "LUAI_BITSINT >= 32" or + def == "SIZE_Bx < LUAI_BITSINT-1" or + def == "cast" or + def == "defined(LUA_CORE)" or + def == "MINSTRTABSIZE" or + def == "LUA_MINBUFFER" or + def == "HARDSTACKTESTS" or + def == "UNUSED" +end + +local head, defs = {[[ +#ifdef _MSC_VER +typedef unsigned __int64 U64; +#else +typedef unsigned long long U64; +#endif +int _CRT_glob = 0; +]]}, {} + +local function preprocess(src) + local t = { match(src, "^(.-)#") } + local lvl, on, oldon = 0, true, {} + for pp, def, txt in string.gmatch(src, "#(%w+) *([^\n]*)\n([^#]*)") do + if pp == "if" or pp == "ifdef" or pp == "ifndef" then + lvl = lvl + 1 + oldon[lvl] = on + on = def_istrue(def) + elseif pp == "else" then + if oldon[lvl] then + if on == false then on = true else on = false end + end + elseif pp == "elif" then + if oldon[lvl] then + on = def_istrue(def) + end + elseif pp == "endif" then + on = oldon[lvl] + lvl = lvl - 1 + elseif on then + if pp == "include" then + if not head[def] and not REMOVE_EXTINC[def] then + head[def] = true + head[#head+1] = "#include "..def.."\n" + end + elseif pp == "define" then + local k, sp, v = match(def, "([%w_]+)(%s*)(.*)") + if k and not (sp == "" and sub(v, 1, 1) == "(") then + defs[k] = gsub(v, "%a[%w_]*", function(tok) + return defs[tok] or tok + end) + else + t[#t+1] = "#define "..def.."\n" + end + elseif pp ~= "undef" then + error("unexpected directive: "..pp.." "..def) + end + end + if on then t[#t+1] = txt end + end + return gsub(table.concat(t), "%a[%w_]*", function(tok) + return defs[tok] or tok + end) +end + +local function merge_header(src, license) + local hdr = string.format([[ +/* This is a heavily customized and minimized copy of Lua %s. */ +/* It's only used to build LuaJIT. It does NOT have all standard functions! */ +]], LUA_VERSION) + return hdr..license..table.concat(head)..src +end + +local function strip_unused1(src) + return gsub(src, '( {"?([%w_]+)"?,%s+%a[%w_]*},\n)', function(line, func) + return REMOVE_LIB[func] and "" or line + end) +end + +local function strip_unused2(src) + return gsub(src, "Symbolic Execution.-}=", "") +end + +local function strip_unused3(src) + src = gsub(src, "extern", "static") + src = gsub(src, "\nstatic([^\n]-)%(([^)]*)%)%(", "\nstatic%1 %2(") + src = gsub(src, "#define lua_assert[^\n]*\n", "") + src = gsub(src, "lua_assert%b();?", "") + src = gsub(src, "default:\n}", "default:;\n}") + src = gsub(src, "lua_lock%b();", "") + src = gsub(src, "lua_unlock%b();", "") + src = gsub(src, "luai_threadyield%b();", "") + src = gsub(src, "luai_userstateopen%b();", "{}") + src = gsub(src, "luai_userstate%w+%b();", "") + src = gsub(src, "%(%(c==.*luaY_parser%)", "luaY_parser") + src = gsub(src, "trydecpoint%(ls,seminfo%)", + "luaX_lexerror(ls,\"malformed number\",TK_NUMBER)") + src = gsub(src, "int c=luaZ_lookahead%b();", "") + src = gsub(src, "luaL_register%(L,[^,]*,co_funcs%);\nreturn 2;", + "return 1;") + src = gsub(src, "getfuncname%b():", "NULL:") + src = gsub(src, "getobjname%b():", "NULL:") + src = gsub(src, "if%([^\n]*hookmask[^\n]*%)\n[^\n]*\n", "") + src = gsub(src, "if%([^\n]*hookmask[^\n]*%)%b{}\n", "") + src = gsub(src, "if%([^\n]*hookmask[^\n]*&&\n[^\n]*%b{}\n", "") + src = gsub(src, "(twoto%b()%()", "%1(size_t)") + src = gsub(src, "i +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +typedef enum{ +TM_INDEX, +TM_NEWINDEX, +TM_GC, +TM_MODE, +TM_EQ, +TM_ADD, +TM_SUB, +TM_MUL, +TM_DIV, +TM_MOD, +TM_POW, +TM_UNM, +TM_LEN, +TM_LT, +TM_LE, +TM_CONCAT, +TM_CALL, +TM_N +}TMS; +enum OpMode{iABC,iABx,iAsBx}; +typedef enum{ +OP_MOVE, +OP_LOADK, +OP_LOADBOOL, +OP_LOADNIL, +OP_GETUPVAL, +OP_GETGLOBAL, +OP_GETTABLE, +OP_SETGLOBAL, +OP_SETUPVAL, +OP_SETTABLE, +OP_NEWTABLE, +OP_SELF, +OP_ADD, +OP_SUB, +OP_MUL, +OP_DIV, +OP_MOD, +OP_POW, +OP_UNM, +OP_NOT, +OP_LEN, +OP_CONCAT, +OP_JMP, +OP_EQ, +OP_LT, +OP_LE, +OP_TEST, +OP_TESTSET, +OP_CALL, +OP_TAILCALL, +OP_RETURN, +OP_FORLOOP, +OP_FORPREP, +OP_TFORLOOP, +OP_SETLIST, +OP_CLOSE, +OP_CLOSURE, +OP_VARARG +}OpCode; +enum OpArgMask{ +OpArgN, +OpArgU, +OpArgR, +OpArgK +}; +typedef enum{ +VVOID, +VNIL, +VTRUE, +VFALSE, +VK, +VKNUM, +VLOCAL, +VUPVAL, +VGLOBAL, +VINDEXED, +VJMP, +VRELOCABLE, +VNONRELOC, +VCALL, +VVARARG +}expkind; +enum RESERVED{ +TK_AND=257,TK_BREAK, +TK_DO,TK_ELSE,TK_ELSEIF,TK_END,TK_FALSE,TK_FOR,TK_FUNCTION, +TK_IF,TK_IN,TK_LOCAL,TK_NIL,TK_NOT,TK_OR,TK_REPEAT, +TK_RETURN,TK_THEN,TK_TRUE,TK_UNTIL,TK_WHILE, +TK_CONCAT,TK_DOTS,TK_EQ,TK_GE,TK_LE,TK_NE,TK_NUMBER, +TK_NAME,TK_STRING,TK_EOS +}; +typedef enum BinOpr{ +OPR_ADD,OPR_SUB,OPR_MUL,OPR_DIV,OPR_MOD,OPR_POW, +OPR_CONCAT, +OPR_NE,OPR_EQ, +OPR_LT,OPR_LE,OPR_GT,OPR_GE, +OPR_AND,OPR_OR, +OPR_NOBINOPR +}BinOpr; +typedef enum UnOpr{OPR_MINUS,OPR_NOT,OPR_LEN,OPR_NOUNOPR}UnOpr; +#define LUA_QL(x)"'"x"'" +#define luai_apicheck(L,o){(void)L;} +#define lua_number2str(s,n)sprintf((s),"%.14g",(n)) +#define lua_str2number(s,p)strtod((s),(p)) +#define luai_numadd(a,b)((a)+(b)) +#define luai_numsub(a,b)((a)-(b)) +#define luai_nummul(a,b)((a)*(b)) +#define luai_numdiv(a,b)((a)/(b)) +#define luai_nummod(a,b)((a)-floor((a)/(b))*(b)) +#define luai_numpow(a,b)(pow(a,b)) +#define luai_numunm(a)(-(a)) +#define luai_numeq(a,b)((a)==(b)) +#define luai_numlt(a,b)((a)<(b)) +#define luai_numle(a,b)((a)<=(b)) +#define luai_numisnan(a)(!luai_numeq((a),(a))) +#define lua_number2int(i,d)((i)=(int)(d)) +#define lua_number2integer(i,d)((i)=(lua_Integer)(d)) +#define LUAI_THROW(L,c)longjmp((c)->b,1) +#define LUAI_TRY(L,c,a)if(setjmp((c)->b)==0){a} +#define lua_pclose(L,file)((void)((void)L,file),0) +#define lua_upvalueindex(i)((-10002)-(i)) +typedef struct lua_State lua_State; +typedef int(*lua_CFunction)(lua_State*L); +typedef const char*(*lua_Reader)(lua_State*L,void*ud,size_t*sz); +typedef void*(*lua_Alloc)(void*ud,void*ptr,size_t osize,size_t nsize); +typedef double lua_Number; +typedef ptrdiff_t lua_Integer; +static void lua_settop(lua_State*L,int idx); +static int lua_type(lua_State*L,int idx); +static const char* lua_tolstring(lua_State*L,int idx,size_t*len); +static size_t lua_objlen(lua_State*L,int idx); +static void lua_pushlstring(lua_State*L,const char*s,size_t l); +static void lua_pushcclosure(lua_State*L,lua_CFunction fn,int n); +static void lua_createtable(lua_State*L,int narr,int nrec); +static void lua_setfield(lua_State*L,int idx,const char*k); +#define lua_pop(L,n)lua_settop(L,-(n)-1) +#define lua_newtable(L)lua_createtable(L,0,0) +#define lua_pushcfunction(L,f)lua_pushcclosure(L,(f),0) +#define lua_strlen(L,i)lua_objlen(L,(i)) +#define lua_isfunction(L,n)(lua_type(L,(n))==6) +#define lua_istable(L,n)(lua_type(L,(n))==5) +#define lua_isnil(L,n)(lua_type(L,(n))==0) +#define lua_isboolean(L,n)(lua_type(L,(n))==1) +#define lua_isnone(L,n)(lua_type(L,(n))==(-1)) +#define lua_isnoneornil(L,n)(lua_type(L,(n))<=0) +#define lua_pushliteral(L,s)lua_pushlstring(L,""s,(sizeof(s)/sizeof(char))-1) +#define lua_setglobal(L,s)lua_setfield(L,(-10002),(s)) +#define lua_tostring(L,i)lua_tolstring(L,(i),NULL) +typedef struct lua_Debug lua_Debug; +typedef void(*lua_Hook)(lua_State*L,lua_Debug*ar); +struct lua_Debug{ +int event; +const char*name; +const char*namewhat; +const char*what; +const char*source; +int currentline; +int nups; +int linedefined; +int lastlinedefined; +char short_src[60]; +int i_ci; +}; +typedef unsigned int lu_int32; +typedef size_t lu_mem; +typedef ptrdiff_t l_mem; +typedef unsigned char lu_byte; +#define IntPoint(p)((unsigned int)(lu_mem)(p)) +typedef union{double u;void*s;long l;}L_Umaxalign; +typedef double l_uacNumber; +#define check_exp(c,e)(e) +#define UNUSED(x)((void)(x)) +#define cast(t,exp)((t)(exp)) +#define cast_byte(i)cast(lu_byte,(i)) +#define cast_num(i)cast(lua_Number,(i)) +#define cast_int(i)cast(int,(i)) +typedef lu_int32 Instruction; +#define condhardstacktests(x)((void)0) +typedef union GCObject GCObject; +typedef struct GCheader{ +GCObject*next;lu_byte tt;lu_byte marked; +}GCheader; +typedef union{ +GCObject*gc; +void*p; +lua_Number n; +int b; +}Value; +typedef struct lua_TValue{ +Value value;int tt; +}TValue; +#define ttisnil(o)(ttype(o)==0) +#define ttisnumber(o)(ttype(o)==3) +#define ttisstring(o)(ttype(o)==4) +#define ttistable(o)(ttype(o)==5) +#define ttisfunction(o)(ttype(o)==6) +#define ttisboolean(o)(ttype(o)==1) +#define ttisuserdata(o)(ttype(o)==7) +#define ttisthread(o)(ttype(o)==8) +#define ttislightuserdata(o)(ttype(o)==2) +#define ttype(o)((o)->tt) +#define gcvalue(o)check_exp(iscollectable(o),(o)->value.gc) +#define pvalue(o)check_exp(ttislightuserdata(o),(o)->value.p) +#define nvalue(o)check_exp(ttisnumber(o),(o)->value.n) +#define rawtsvalue(o)check_exp(ttisstring(o),&(o)->value.gc->ts) +#define tsvalue(o)(&rawtsvalue(o)->tsv) +#define rawuvalue(o)check_exp(ttisuserdata(o),&(o)->value.gc->u) +#define uvalue(o)(&rawuvalue(o)->uv) +#define clvalue(o)check_exp(ttisfunction(o),&(o)->value.gc->cl) +#define hvalue(o)check_exp(ttistable(o),&(o)->value.gc->h) +#define bvalue(o)check_exp(ttisboolean(o),(o)->value.b) +#define thvalue(o)check_exp(ttisthread(o),&(o)->value.gc->th) +#define l_isfalse(o)(ttisnil(o)||(ttisboolean(o)&&bvalue(o)==0)) +#define checkconsistency(obj) +#define checkliveness(g,obj) +#define setnilvalue(obj)((obj)->tt=0) +#define setnvalue(obj,x){TValue*i_o=(obj);i_o->value.n=(x);i_o->tt=3;} +#define setbvalue(obj,x){TValue*i_o=(obj);i_o->value.b=(x);i_o->tt=1;} +#define setsvalue(L,obj,x){TValue*i_o=(obj);i_o->value.gc=cast(GCObject*,(x));i_o->tt=4;checkliveness(G(L),i_o);} +#define setuvalue(L,obj,x){TValue*i_o=(obj);i_o->value.gc=cast(GCObject*,(x));i_o->tt=7;checkliveness(G(L),i_o);} +#define setthvalue(L,obj,x){TValue*i_o=(obj);i_o->value.gc=cast(GCObject*,(x));i_o->tt=8;checkliveness(G(L),i_o);} +#define setclvalue(L,obj,x){TValue*i_o=(obj);i_o->value.gc=cast(GCObject*,(x));i_o->tt=6;checkliveness(G(L),i_o);} +#define sethvalue(L,obj,x){TValue*i_o=(obj);i_o->value.gc=cast(GCObject*,(x));i_o->tt=5;checkliveness(G(L),i_o);} +#define setptvalue(L,obj,x){TValue*i_o=(obj);i_o->value.gc=cast(GCObject*,(x));i_o->tt=(8+1);checkliveness(G(L),i_o);} +#define setobj(L,obj1,obj2){const TValue*o2=(obj2);TValue*o1=(obj1);o1->value=o2->value;o1->tt=o2->tt;checkliveness(G(L),o1);} +#define setttype(obj,tt)(ttype(obj)=(tt)) +#define iscollectable(o)(ttype(o)>=4) +typedef TValue*StkId; +typedef union TString{ +L_Umaxalign dummy; +struct{ +GCObject*next;lu_byte tt;lu_byte marked; +lu_byte reserved; +unsigned int hash; +size_t len; +}tsv; +}TString; +#define getstr(ts)cast(const char*,(ts)+1) +#define svalue(o)getstr(rawtsvalue(o)) +typedef union Udata{ +L_Umaxalign dummy; +struct{ +GCObject*next;lu_byte tt;lu_byte marked; +struct Table*metatable; +struct Table*env; +size_t len; +}uv; +}Udata; +typedef struct Proto{ +GCObject*next;lu_byte tt;lu_byte marked; +TValue*k; +Instruction*code; +struct Proto**p; +int*lineinfo; +struct LocVar*locvars; +TString**upvalues; +TString*source; +int sizeupvalues; +int sizek; +int sizecode; +int sizelineinfo; +int sizep; +int sizelocvars; +int linedefined; +int lastlinedefined; +GCObject*gclist; +lu_byte nups; +lu_byte numparams; +lu_byte is_vararg; +lu_byte maxstacksize; +}Proto; +typedef struct LocVar{ +TString*varname; +int startpc; +int endpc; +}LocVar; +typedef struct UpVal{ +GCObject*next;lu_byte tt;lu_byte marked; +TValue*v; +union{ +TValue value; +struct{ +struct UpVal*prev; +struct UpVal*next; +}l; +}u; +}UpVal; +typedef struct CClosure{ +GCObject*next;lu_byte tt;lu_byte marked;lu_byte isC;lu_byte nupvalues;GCObject*gclist;struct Table*env; +lua_CFunction f; +TValue upvalue[1]; +}CClosure; +typedef struct LClosure{ +GCObject*next;lu_byte tt;lu_byte marked;lu_byte isC;lu_byte nupvalues;GCObject*gclist;struct Table*env; +struct Proto*p; +UpVal*upvals[1]; +}LClosure; +typedef union Closure{ +CClosure c; +LClosure l; +}Closure; +#define iscfunction(o)(ttype(o)==6&&clvalue(o)->c.isC) +typedef union TKey{ +struct{ +Value value;int tt; +struct Node*next; +}nk; +TValue tvk; +}TKey; +typedef struct Node{ +TValue i_val; +TKey i_key; +}Node; +typedef struct Table{ +GCObject*next;lu_byte tt;lu_byte marked; +lu_byte flags; +lu_byte lsizenode; +struct Table*metatable; +TValue*array; +Node*node; +Node*lastfree; +GCObject*gclist; +int sizearray; +}Table; +#define lmod(s,size)(check_exp((size&(size-1))==0,(cast(int,(s)&((size)-1))))) +#define twoto(x)((size_t)1<<(x)) +#define sizenode(t)(twoto((t)->lsizenode)) +static const TValue luaO_nilobject_; +#define ceillog2(x)(luaO_log2((x)-1)+1) +static int luaO_log2(unsigned int x); +#define gfasttm(g,et,e)((et)==NULL?NULL:((et)->flags&(1u<<(e)))?NULL:luaT_gettm(et,e,(g)->tmname[e])) +#define fasttm(l,et,e)gfasttm(G(l),et,e) +static const TValue*luaT_gettm(Table*events,TMS event,TString*ename); +#define luaM_reallocv(L,b,on,n,e)((cast(size_t,(n)+1)<=((size_t)(~(size_t)0)-2)/(e))?luaM_realloc_(L,(b),(on)*(e),(n)*(e)):luaM_toobig(L)) +#define luaM_freemem(L,b,s)luaM_realloc_(L,(b),(s),0) +#define luaM_free(L,b)luaM_realloc_(L,(b),sizeof(*(b)),0) +#define luaM_freearray(L,b,n,t)luaM_reallocv(L,(b),n,0,sizeof(t)) +#define luaM_malloc(L,t)luaM_realloc_(L,NULL,0,(t)) +#define luaM_new(L,t)cast(t*,luaM_malloc(L,sizeof(t))) +#define luaM_newvector(L,n,t)cast(t*,luaM_reallocv(L,NULL,0,n,sizeof(t))) +#define luaM_growvector(L,v,nelems,size,t,limit,e)if((nelems)+1>(size))((v)=cast(t*,luaM_growaux_(L,v,&(size),sizeof(t),limit,e))) +#define luaM_reallocvector(L,v,oldn,n,t)((v)=cast(t*,luaM_reallocv(L,v,oldn,n,sizeof(t)))) +static void*luaM_realloc_(lua_State*L,void*block,size_t oldsize, +size_t size); +static void*luaM_toobig(lua_State*L); +static void*luaM_growaux_(lua_State*L,void*block,int*size, +size_t size_elem,int limit, +const char*errormsg); +typedef struct Zio ZIO; +#define char2int(c)cast(int,cast(unsigned char,(c))) +#define zgetc(z)(((z)->n--)>0?char2int(*(z)->p++):luaZ_fill(z)) +typedef struct Mbuffer{ +char*buffer; +size_t n; +size_t buffsize; +}Mbuffer; +#define luaZ_initbuffer(L,buff)((buff)->buffer=NULL,(buff)->buffsize=0) +#define luaZ_buffer(buff)((buff)->buffer) +#define luaZ_sizebuffer(buff)((buff)->buffsize) +#define luaZ_bufflen(buff)((buff)->n) +#define luaZ_resetbuffer(buff)((buff)->n=0) +#define luaZ_resizebuffer(L,buff,size)(luaM_reallocvector(L,(buff)->buffer,(buff)->buffsize,size,char),(buff)->buffsize=size) +#define luaZ_freebuffer(L,buff)luaZ_resizebuffer(L,buff,0) +struct Zio{ +size_t n; +const char*p; +lua_Reader reader; +void*data; +lua_State*L; +}; +static int luaZ_fill(ZIO*z); +struct lua_longjmp; +#define gt(L)(&L->l_gt) +#define registry(L)(&G(L)->l_registry) +typedef struct stringtable{ +GCObject**hash; +lu_int32 nuse; +int size; +}stringtable; +typedef struct CallInfo{ +StkId base; +StkId func; +StkId top; +const Instruction*savedpc; +int nresults; +int tailcalls; +}CallInfo; +#define curr_func(L)(clvalue(L->ci->func)) +#define ci_func(ci)(clvalue((ci)->func)) +#define f_isLua(ci)(!ci_func(ci)->c.isC) +#define isLua(ci)(ttisfunction((ci)->func)&&f_isLua(ci)) +typedef struct global_State{ +stringtable strt; +lua_Alloc frealloc; +void*ud; +lu_byte currentwhite; +lu_byte gcstate; +int sweepstrgc; +GCObject*rootgc; +GCObject**sweepgc; +GCObject*gray; +GCObject*grayagain; +GCObject*weak; +GCObject*tmudata; +Mbuffer buff; +lu_mem GCthreshold; +lu_mem totalbytes; +lu_mem estimate; +lu_mem gcdept; +int gcpause; +int gcstepmul; +lua_CFunction panic; +TValue l_registry; +struct lua_State*mainthread; +UpVal uvhead; +struct Table*mt[(8+1)]; +TString*tmname[TM_N]; +}global_State; +struct lua_State{ +GCObject*next;lu_byte tt;lu_byte marked; +lu_byte status; +StkId top; +StkId base; +global_State*l_G; +CallInfo*ci; +const Instruction*savedpc; +StkId stack_last; +StkId stack; +CallInfo*end_ci; +CallInfo*base_ci; +int stacksize; +int size_ci; +unsigned short nCcalls; +unsigned short baseCcalls; +lu_byte hookmask; +lu_byte allowhook; +int basehookcount; +int hookcount; +lua_Hook hook; +TValue l_gt; +TValue env; +GCObject*openupval; +GCObject*gclist; +struct lua_longjmp*errorJmp; +ptrdiff_t errfunc; +}; +#define G(L)(L->l_G) +union GCObject{ +GCheader gch; +union TString ts; +union Udata u; +union Closure cl; +struct Table h; +struct Proto p; +struct UpVal uv; +struct lua_State th; +}; +#define rawgco2ts(o)check_exp((o)->gch.tt==4,&((o)->ts)) +#define gco2ts(o)(&rawgco2ts(o)->tsv) +#define rawgco2u(o)check_exp((o)->gch.tt==7,&((o)->u)) +#define gco2u(o)(&rawgco2u(o)->uv) +#define gco2cl(o)check_exp((o)->gch.tt==6,&((o)->cl)) +#define gco2h(o)check_exp((o)->gch.tt==5,&((o)->h)) +#define gco2p(o)check_exp((o)->gch.tt==(8+1),&((o)->p)) +#define gco2uv(o)check_exp((o)->gch.tt==(8+2),&((o)->uv)) +#define ngcotouv(o)check_exp((o)==NULL||(o)->gch.tt==(8+2),&((o)->uv)) +#define gco2th(o)check_exp((o)->gch.tt==8,&((o)->th)) +#define obj2gco(v)(cast(GCObject*,(v))) +static void luaE_freethread(lua_State*L,lua_State*L1); +#define pcRel(pc,p)(cast(int,(pc)-(p)->code)-1) +#define getline_(f,pc)(((f)->lineinfo)?(f)->lineinfo[pc]:0) +#define resethookcount(L)(L->hookcount=L->basehookcount) +static void luaG_typeerror(lua_State*L,const TValue*o, +const char*opname); +static void luaG_runerror(lua_State*L,const char*fmt,...); +#define luaD_checkstack(L,n)if((char*)L->stack_last-(char*)L->top<=(n)*(int)sizeof(TValue))luaD_growstack(L,n);else condhardstacktests(luaD_reallocstack(L,L->stacksize-5-1)); +#define incr_top(L){luaD_checkstack(L,1);L->top++;} +#define savestack(L,p)((char*)(p)-(char*)L->stack) +#define restorestack(L,n)((TValue*)((char*)L->stack+(n))) +#define saveci(L,p)((char*)(p)-(char*)L->base_ci) +#define restoreci(L,n)((CallInfo*)((char*)L->base_ci+(n))) +typedef void(*Pfunc)(lua_State*L,void*ud); +static int luaD_poscall(lua_State*L,StkId firstResult); +static void luaD_reallocCI(lua_State*L,int newsize); +static void luaD_reallocstack(lua_State*L,int newsize); +static void luaD_growstack(lua_State*L,int n); +static void luaD_throw(lua_State*L,int errcode); +static void*luaM_growaux_(lua_State*L,void*block,int*size,size_t size_elems, +int limit,const char*errormsg){ +void*newblock; +int newsize; +if(*size>=limit/2){ +if(*size>=limit) +luaG_runerror(L,errormsg); +newsize=limit; +} +else{ +newsize=(*size)*2; +if(newsize<4) +newsize=4; +} +newblock=luaM_reallocv(L,block,*size,newsize,size_elems); +*size=newsize; +return newblock; +} +static void*luaM_toobig(lua_State*L){ +luaG_runerror(L,"memory allocation error: block too big"); +return NULL; +} +static void*luaM_realloc_(lua_State*L,void*block,size_t osize,size_t nsize){ +global_State*g=G(L); +block=(*g->frealloc)(g->ud,block,osize,nsize); +if(block==NULL&&nsize>0) +luaD_throw(L,4); +g->totalbytes=(g->totalbytes-osize)+nsize; +return block; +} +#define resetbits(x,m)((x)&=cast(lu_byte,~(m))) +#define setbits(x,m)((x)|=(m)) +#define testbits(x,m)((x)&(m)) +#define bitmask(b)(1<<(b)) +#define bit2mask(b1,b2)(bitmask(b1)|bitmask(b2)) +#define l_setbit(x,b)setbits(x,bitmask(b)) +#define resetbit(x,b)resetbits(x,bitmask(b)) +#define testbit(x,b)testbits(x,bitmask(b)) +#define set2bits(x,b1,b2)setbits(x,(bit2mask(b1,b2))) +#define reset2bits(x,b1,b2)resetbits(x,(bit2mask(b1,b2))) +#define test2bits(x,b1,b2)testbits(x,(bit2mask(b1,b2))) +#define iswhite(x)test2bits((x)->gch.marked,0,1) +#define isblack(x)testbit((x)->gch.marked,2) +#define isgray(x)(!isblack(x)&&!iswhite(x)) +#define otherwhite(g)(g->currentwhite^bit2mask(0,1)) +#define isdead(g,v)((v)->gch.marked&otherwhite(g)&bit2mask(0,1)) +#define changewhite(x)((x)->gch.marked^=bit2mask(0,1)) +#define gray2black(x)l_setbit((x)->gch.marked,2) +#define valiswhite(x)(iscollectable(x)&&iswhite(gcvalue(x))) +#define luaC_white(g)cast(lu_byte,(g)->currentwhite&bit2mask(0,1)) +#define luaC_checkGC(L){condhardstacktests(luaD_reallocstack(L,L->stacksize-5-1));if(G(L)->totalbytes>=G(L)->GCthreshold)luaC_step(L);} +#define luaC_barrier(L,p,v){if(valiswhite(v)&&isblack(obj2gco(p)))luaC_barrierf(L,obj2gco(p),gcvalue(v));} +#define luaC_barriert(L,t,v){if(valiswhite(v)&&isblack(obj2gco(t)))luaC_barrierback(L,t);} +#define luaC_objbarrier(L,p,o){if(iswhite(obj2gco(o))&&isblack(obj2gco(p)))luaC_barrierf(L,obj2gco(p),obj2gco(o));} +#define luaC_objbarriert(L,t,o){if(iswhite(obj2gco(o))&&isblack(obj2gco(t)))luaC_barrierback(L,t);} +static void luaC_step(lua_State*L); +static void luaC_link(lua_State*L,GCObject*o,lu_byte tt); +static void luaC_linkupval(lua_State*L,UpVal*uv); +static void luaC_barrierf(lua_State*L,GCObject*o,GCObject*v); +static void luaC_barrierback(lua_State*L,Table*t); +#define sizestring(s)(sizeof(union TString)+((s)->len+1)*sizeof(char)) +#define sizeudata(u)(sizeof(union Udata)+(u)->len) +#define luaS_new(L,s)(luaS_newlstr(L,s,strlen(s))) +#define luaS_newliteral(L,s)(luaS_newlstr(L,""s,(sizeof(s)/sizeof(char))-1)) +#define luaS_fix(s)l_setbit((s)->tsv.marked,5) +static TString*luaS_newlstr(lua_State*L,const char*str,size_t l); +#define tostring(L,o)((ttype(o)==4)||(luaV_tostring(L,o))) +#define tonumber(o,n)(ttype(o)==3||(((o)=luaV_tonumber(o,n))!=NULL)) +#define equalobj(L,o1,o2)(ttype(o1)==ttype(o2)&&luaV_equalval(L,o1,o2)) +static int luaV_equalval(lua_State*L,const TValue*t1,const TValue*t2); +static const TValue*luaV_tonumber(const TValue*obj,TValue*n); +static int luaV_tostring(lua_State*L,StkId obj); +static void luaV_execute(lua_State*L,int nexeccalls); +static void luaV_concat(lua_State*L,int total,int last); +static const TValue luaO_nilobject_={{NULL},0}; +static int luaO_int2fb(unsigned int x){ +int e=0; +while(x>=16){ +x=(x+1)>>1; +e++; +} +if(x<8)return x; +else return((e+1)<<3)|(cast_int(x)-8); +} +static int luaO_fb2int(int x){ +int e=(x>>3)&31; +if(e==0)return x; +else return((x&7)+8)<<(e-1); +} +static int luaO_log2(unsigned int x){ +static const lu_byte log_2[256]={ +0,1,2,2,3,3,3,3,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, +6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, +7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, +7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, +8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, +8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, +8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, +8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8 +}; +int l=-1; +while(x>=256){l+=8;x>>=8;} +return l+log_2[x]; +} +static int luaO_rawequalObj(const TValue*t1,const TValue*t2){ +if(ttype(t1)!=ttype(t2))return 0; +else switch(ttype(t1)){ +case 0: +return 1; +case 3: +return luai_numeq(nvalue(t1),nvalue(t2)); +case 1: +return bvalue(t1)==bvalue(t2); +case 2: +return pvalue(t1)==pvalue(t2); +default: +return gcvalue(t1)==gcvalue(t2); +} +} +static int luaO_str2d(const char*s,lua_Number*result){ +char*endptr; +*result=lua_str2number(s,&endptr); +if(endptr==s)return 0; +if(*endptr=='x'||*endptr=='X') +*result=cast_num(strtoul(s,&endptr,16)); +if(*endptr=='\0')return 1; +while(isspace(cast(unsigned char,*endptr)))endptr++; +if(*endptr!='\0')return 0; +return 1; +} +static void pushstr(lua_State*L,const char*str){ +setsvalue(L,L->top,luaS_new(L,str)); +incr_top(L); +} +static const char*luaO_pushvfstring(lua_State*L,const char*fmt,va_list argp){ +int n=1; +pushstr(L,""); +for(;;){ +const char*e=strchr(fmt,'%'); +if(e==NULL)break; +setsvalue(L,L->top,luaS_newlstr(L,fmt,e-fmt)); +incr_top(L); +switch(*(e+1)){ +case's':{ +const char*s=va_arg(argp,char*); +if(s==NULL)s="(null)"; +pushstr(L,s); +break; +} +case'c':{ +char buff[2]; +buff[0]=cast(char,va_arg(argp,int)); +buff[1]='\0'; +pushstr(L,buff); +break; +} +case'd':{ +setnvalue(L->top,cast_num(va_arg(argp,int))); +incr_top(L); +break; +} +case'f':{ +setnvalue(L->top,cast_num(va_arg(argp,l_uacNumber))); +incr_top(L); +break; +} +case'p':{ +char buff[4*sizeof(void*)+8]; +sprintf(buff,"%p",va_arg(argp,void*)); +pushstr(L,buff); +break; +} +case'%':{ +pushstr(L,"%"); +break; +} +default:{ +char buff[3]; +buff[0]='%'; +buff[1]=*(e+1); +buff[2]='\0'; +pushstr(L,buff); +break; +} +} +n+=2; +fmt=e+2; +} +pushstr(L,fmt); +luaV_concat(L,n+1,cast_int(L->top-L->base)-1); +L->top-=n; +return svalue(L->top-1); +} +static const char*luaO_pushfstring(lua_State*L,const char*fmt,...){ +const char*msg; +va_list argp; +va_start(argp,fmt); +msg=luaO_pushvfstring(L,fmt,argp); +va_end(argp); +return msg; +} +static void luaO_chunkid(char*out,const char*source,size_t bufflen){ +if(*source=='='){ +strncpy(out,source+1,bufflen); +out[bufflen-1]='\0'; +} +else{ +if(*source=='@'){ +size_t l; +source++; +bufflen-=sizeof(" '...' "); +l=strlen(source); +strcpy(out,""); +if(l>bufflen){ +source+=(l-bufflen); +strcat(out,"..."); +} +strcat(out,source); +} +else{ +size_t len=strcspn(source,"\n\r"); +bufflen-=sizeof(" [string \"...\"] "); +if(len>bufflen)len=bufflen; +strcpy(out,"[string \""); +if(source[len]!='\0'){ +strncat(out,source,len); +strcat(out,"..."); +} +else +strcat(out,source); +strcat(out,"\"]"); +} +} +} +#define gnode(t,i)(&(t)->node[i]) +#define gkey(n)(&(n)->i_key.nk) +#define gval(n)(&(n)->i_val) +#define gnext(n)((n)->i_key.nk.next) +#define key2tval(n)(&(n)->i_key.tvk) +static TValue*luaH_setnum(lua_State*L,Table*t,int key); +static const TValue*luaH_getstr(Table*t,TString*key); +static TValue*luaH_set(lua_State*L,Table*t,const TValue*key); +static const char*const luaT_typenames[]={ +"nil","boolean","userdata","number", +"string","table","function","userdata","thread", +"proto","upval" +}; +static void luaT_init(lua_State*L){ +static const char*const luaT_eventname[]={ +"__index","__newindex", +"__gc","__mode","__eq", +"__add","__sub","__mul","__div","__mod", +"__pow","__unm","__len","__lt","__le", +"__concat","__call" +}; +int i; +for(i=0;itmname[i]=luaS_new(L,luaT_eventname[i]); +luaS_fix(G(L)->tmname[i]); +} +} +static const TValue*luaT_gettm(Table*events,TMS event,TString*ename){ +const TValue*tm=luaH_getstr(events,ename); +if(ttisnil(tm)){ +events->flags|=cast_byte(1u<metatable; +break; +case 7: +mt=uvalue(o)->metatable; +break; +default: +mt=G(L)->mt[ttype(o)]; +} +return(mt?luaH_getstr(mt,G(L)->tmname[event]):(&luaO_nilobject_)); +} +#define sizeCclosure(n)(cast(int,sizeof(CClosure))+cast(int,sizeof(TValue)*((n)-1))) +#define sizeLclosure(n)(cast(int,sizeof(LClosure))+cast(int,sizeof(TValue*)*((n)-1))) +static Closure*luaF_newCclosure(lua_State*L,int nelems,Table*e){ +Closure*c=cast(Closure*,luaM_malloc(L,sizeCclosure(nelems))); +luaC_link(L,obj2gco(c),6); +c->c.isC=1; +c->c.env=e; +c->c.nupvalues=cast_byte(nelems); +return c; +} +static Closure*luaF_newLclosure(lua_State*L,int nelems,Table*e){ +Closure*c=cast(Closure*,luaM_malloc(L,sizeLclosure(nelems))); +luaC_link(L,obj2gco(c),6); +c->l.isC=0; +c->l.env=e; +c->l.nupvalues=cast_byte(nelems); +while(nelems--)c->l.upvals[nelems]=NULL; +return c; +} +static UpVal*luaF_newupval(lua_State*L){ +UpVal*uv=luaM_new(L,UpVal); +luaC_link(L,obj2gco(uv),(8+2)); +uv->v=&uv->u.value; +setnilvalue(uv->v); +return uv; +} +static UpVal*luaF_findupval(lua_State*L,StkId level){ +global_State*g=G(L); +GCObject**pp=&L->openupval; +UpVal*p; +UpVal*uv; +while(*pp!=NULL&&(p=ngcotouv(*pp))->v>=level){ +if(p->v==level){ +if(isdead(g,obj2gco(p))) +changewhite(obj2gco(p)); +return p; +} +pp=&p->next; +} +uv=luaM_new(L,UpVal); +uv->tt=(8+2); +uv->marked=luaC_white(g); +uv->v=level; +uv->next=*pp; +*pp=obj2gco(uv); +uv->u.l.prev=&g->uvhead; +uv->u.l.next=g->uvhead.u.l.next; +uv->u.l.next->u.l.prev=uv; +g->uvhead.u.l.next=uv; +return uv; +} +static void unlinkupval(UpVal*uv){ +uv->u.l.next->u.l.prev=uv->u.l.prev; +uv->u.l.prev->u.l.next=uv->u.l.next; +} +static void luaF_freeupval(lua_State*L,UpVal*uv){ +if(uv->v!=&uv->u.value) +unlinkupval(uv); +luaM_free(L,uv); +} +static void luaF_close(lua_State*L,StkId level){ +UpVal*uv; +global_State*g=G(L); +while(L->openupval!=NULL&&(uv=ngcotouv(L->openupval))->v>=level){ +GCObject*o=obj2gco(uv); +L->openupval=uv->next; +if(isdead(g,o)) +luaF_freeupval(L,uv); +else{ +unlinkupval(uv); +setobj(L,&uv->u.value,uv->v); +uv->v=&uv->u.value; +luaC_linkupval(L,uv); +} +} +} +static Proto*luaF_newproto(lua_State*L){ +Proto*f=luaM_new(L,Proto); +luaC_link(L,obj2gco(f),(8+1)); +f->k=NULL; +f->sizek=0; +f->p=NULL; +f->sizep=0; +f->code=NULL; +f->sizecode=0; +f->sizelineinfo=0; +f->sizeupvalues=0; +f->nups=0; +f->upvalues=NULL; +f->numparams=0; +f->is_vararg=0; +f->maxstacksize=0; +f->lineinfo=NULL; +f->sizelocvars=0; +f->locvars=NULL; +f->linedefined=0; +f->lastlinedefined=0; +f->source=NULL; +return f; +} +static void luaF_freeproto(lua_State*L,Proto*f){ +luaM_freearray(L,f->code,f->sizecode,Instruction); +luaM_freearray(L,f->p,f->sizep,Proto*); +luaM_freearray(L,f->k,f->sizek,TValue); +luaM_freearray(L,f->lineinfo,f->sizelineinfo,int); +luaM_freearray(L,f->locvars,f->sizelocvars,struct LocVar); +luaM_freearray(L,f->upvalues,f->sizeupvalues,TString*); +luaM_free(L,f); +} +static void luaF_freeclosure(lua_State*L,Closure*c){ +int size=(c->c.isC)?sizeCclosure(c->c.nupvalues): +sizeLclosure(c->l.nupvalues); +luaM_freemem(L,c,size); +} +#define MASK1(n,p)((~((~(Instruction)0)<>0)&MASK1(6,0))) +#define SET_OPCODE(i,o)((i)=(((i)&MASK0(6,0))|((cast(Instruction,o)<<0)&MASK1(6,0)))) +#define GETARG_A(i)(cast(int,((i)>>(0+6))&MASK1(8,0))) +#define SETARG_A(i,u)((i)=(((i)&MASK0(8,(0+6)))|((cast(Instruction,u)<<(0+6))&MASK1(8,(0+6))))) +#define GETARG_B(i)(cast(int,((i)>>(((0+6)+8)+9))&MASK1(9,0))) +#define SETARG_B(i,b)((i)=(((i)&MASK0(9,(((0+6)+8)+9)))|((cast(Instruction,b)<<(((0+6)+8)+9))&MASK1(9,(((0+6)+8)+9))))) +#define GETARG_C(i)(cast(int,((i)>>((0+6)+8))&MASK1(9,0))) +#define SETARG_C(i,b)((i)=(((i)&MASK0(9,((0+6)+8)))|((cast(Instruction,b)<<((0+6)+8))&MASK1(9,((0+6)+8))))) +#define GETARG_Bx(i)(cast(int,((i)>>((0+6)+8))&MASK1((9+9),0))) +#define SETARG_Bx(i,b)((i)=(((i)&MASK0((9+9),((0+6)+8)))|((cast(Instruction,b)<<((0+6)+8))&MASK1((9+9),((0+6)+8))))) +#define GETARG_sBx(i)(GETARG_Bx(i)-(((1<<(9+9))-1)>>1)) +#define SETARG_sBx(i,b)SETARG_Bx((i),cast(unsigned int,(b)+(((1<<(9+9))-1)>>1))) +#define CREATE_ABC(o,a,b,c)((cast(Instruction,o)<<0)|(cast(Instruction,a)<<(0+6))|(cast(Instruction,b)<<(((0+6)+8)+9))|(cast(Instruction,c)<<((0+6)+8))) +#define CREATE_ABx(o,a,bc)((cast(Instruction,o)<<0)|(cast(Instruction,a)<<(0+6))|(cast(Instruction,bc)<<((0+6)+8))) +#define ISK(x)((x)&(1<<(9-1))) +#define INDEXK(r)((int)(r)&~(1<<(9-1))) +#define RKASK(x)((x)|(1<<(9-1))) +static const lu_byte luaP_opmodes[(cast(int,OP_VARARG)+1)]; +#define getBMode(m)(cast(enum OpArgMask,(luaP_opmodes[m]>>4)&3)) +#define getCMode(m)(cast(enum OpArgMask,(luaP_opmodes[m]>>2)&3)) +#define testTMode(m)(luaP_opmodes[m]&(1<<7)) +typedef struct expdesc{ +expkind k; +union{ +struct{int info,aux;}s; +lua_Number nval; +}u; +int t; +int f; +}expdesc; +typedef struct upvaldesc{ +lu_byte k; +lu_byte info; +}upvaldesc; +struct BlockCnt; +typedef struct FuncState{ +Proto*f; +Table*h; +struct FuncState*prev; +struct LexState*ls; +struct lua_State*L; +struct BlockCnt*bl; +int pc; +int lasttarget; +int jpc; +int freereg; +int nk; +int np; +short nlocvars; +lu_byte nactvar; +upvaldesc upvalues[60]; +unsigned short actvar[200]; +}FuncState; +static Proto*luaY_parser(lua_State*L,ZIO*z,Mbuffer*buff, +const char*name); +struct lua_longjmp{ +struct lua_longjmp*previous; +jmp_buf b; +volatile int status; +}; +static void luaD_seterrorobj(lua_State*L,int errcode,StkId oldtop){ +switch(errcode){ +case 4:{ +setsvalue(L,oldtop,luaS_newliteral(L,"not enough memory")); +break; +} +case 5:{ +setsvalue(L,oldtop,luaS_newliteral(L,"error in error handling")); +break; +} +case 3: +case 2:{ +setobj(L,oldtop,L->top-1); +break; +} +} +L->top=oldtop+1; +} +static void restore_stack_limit(lua_State*L){ +if(L->size_ci>20000){ +int inuse=cast_int(L->ci-L->base_ci); +if(inuse+1<20000) +luaD_reallocCI(L,20000); +} +} +static void resetstack(lua_State*L,int status){ +L->ci=L->base_ci; +L->base=L->ci->base; +luaF_close(L,L->base); +luaD_seterrorobj(L,status,L->base); +L->nCcalls=L->baseCcalls; +L->allowhook=1; +restore_stack_limit(L); +L->errfunc=0; +L->errorJmp=NULL; +} +static void luaD_throw(lua_State*L,int errcode){ +if(L->errorJmp){ +L->errorJmp->status=errcode; +LUAI_THROW(L,L->errorJmp); +} +else{ +L->status=cast_byte(errcode); +if(G(L)->panic){ +resetstack(L,errcode); +G(L)->panic(L); +} +exit(EXIT_FAILURE); +} +} +static int luaD_rawrunprotected(lua_State*L,Pfunc f,void*ud){ +struct lua_longjmp lj; +lj.status=0; +lj.previous=L->errorJmp; +L->errorJmp=&lj; +LUAI_TRY(L,&lj, +(*f)(L,ud); +); +L->errorJmp=lj.previous; +return lj.status; +} +static void correctstack(lua_State*L,TValue*oldstack){ +CallInfo*ci; +GCObject*up; +L->top=(L->top-oldstack)+L->stack; +for(up=L->openupval;up!=NULL;up=up->gch.next) +gco2uv(up)->v=(gco2uv(up)->v-oldstack)+L->stack; +for(ci=L->base_ci;ci<=L->ci;ci++){ +ci->top=(ci->top-oldstack)+L->stack; +ci->base=(ci->base-oldstack)+L->stack; +ci->func=(ci->func-oldstack)+L->stack; +} +L->base=(L->base-oldstack)+L->stack; +} +static void luaD_reallocstack(lua_State*L,int newsize){ +TValue*oldstack=L->stack; +int realsize=newsize+1+5; +luaM_reallocvector(L,L->stack,L->stacksize,realsize,TValue); +L->stacksize=realsize; +L->stack_last=L->stack+newsize; +correctstack(L,oldstack); +} +static void luaD_reallocCI(lua_State*L,int newsize){ +CallInfo*oldci=L->base_ci; +luaM_reallocvector(L,L->base_ci,L->size_ci,newsize,CallInfo); +L->size_ci=newsize; +L->ci=(L->ci-oldci)+L->base_ci; +L->end_ci=L->base_ci+L->size_ci-1; +} +static void luaD_growstack(lua_State*L,int n){ +if(n<=L->stacksize) +luaD_reallocstack(L,2*L->stacksize); +else +luaD_reallocstack(L,L->stacksize+n); +} +static CallInfo*growCI(lua_State*L){ +if(L->size_ci>20000) +luaD_throw(L,5); +else{ +luaD_reallocCI(L,2*L->size_ci); +if(L->size_ci>20000) +luaG_runerror(L,"stack overflow"); +} +return++L->ci; +} +static StkId adjust_varargs(lua_State*L,Proto*p,int actual){ +int i; +int nfixargs=p->numparams; +Table*htab=NULL; +StkId base,fixed; +for(;actualtop++); +fixed=L->top-actual; +base=L->top; +for(i=0;itop++,fixed+i); +setnilvalue(fixed+i); +} +if(htab){ +sethvalue(L,L->top++,htab); +} +return base; +} +static StkId tryfuncTM(lua_State*L,StkId func){ +const TValue*tm=luaT_gettmbyobj(L,func,TM_CALL); +StkId p; +ptrdiff_t funcr=savestack(L,func); +if(!ttisfunction(tm)) +luaG_typeerror(L,func,"call"); +for(p=L->top;p>func;p--)setobj(L,p,p-1); +incr_top(L); +func=restorestack(L,funcr); +setobj(L,func,tm); +return func; +} +#define inc_ci(L)((L->ci==L->end_ci)?growCI(L):(condhardstacktests(luaD_reallocCI(L,L->size_ci)),++L->ci)) +static int luaD_precall(lua_State*L,StkId func,int nresults){ +LClosure*cl; +ptrdiff_t funcr; +if(!ttisfunction(func)) +func=tryfuncTM(L,func); +funcr=savestack(L,func); +cl=&clvalue(func)->l; +L->ci->savedpc=L->savedpc; +if(!cl->isC){ +CallInfo*ci; +StkId st,base; +Proto*p=cl->p; +luaD_checkstack(L,p->maxstacksize); +func=restorestack(L,funcr); +if(!p->is_vararg){ +base=func+1; +if(L->top>base+p->numparams) +L->top=base+p->numparams; +} +else{ +int nargs=cast_int(L->top-func)-1; +base=adjust_varargs(L,p,nargs); +func=restorestack(L,funcr); +} +ci=inc_ci(L); +ci->func=func; +L->base=ci->base=base; +ci->top=L->base+p->maxstacksize; +L->savedpc=p->code; +ci->tailcalls=0; +ci->nresults=nresults; +for(st=L->top;sttop;st++) +setnilvalue(st); +L->top=ci->top; +return 0; +} +else{ +CallInfo*ci; +int n; +luaD_checkstack(L,20); +ci=inc_ci(L); +ci->func=restorestack(L,funcr); +L->base=ci->base=ci->func+1; +ci->top=L->top+20; +ci->nresults=nresults; +n=(*curr_func(L)->c.f)(L); +if(n<0) +return 2; +else{ +luaD_poscall(L,L->top-n); +return 1; +} +} +} +static int luaD_poscall(lua_State*L,StkId firstResult){ +StkId res; +int wanted,i; +CallInfo*ci; +ci=L->ci--; +res=ci->func; +wanted=ci->nresults; +L->base=(ci-1)->base; +L->savedpc=(ci-1)->savedpc; +for(i=wanted;i!=0&&firstResulttop;i--) +setobj(L,res++,firstResult++); +while(i-->0) +setnilvalue(res++); +L->top=res; +return(wanted-(-1)); +} +static void luaD_call(lua_State*L,StkId func,int nResults){ +if(++L->nCcalls>=200){ +if(L->nCcalls==200) +luaG_runerror(L,"C stack overflow"); +else if(L->nCcalls>=(200+(200>>3))) +luaD_throw(L,5); +} +if(luaD_precall(L,func,nResults)==0) +luaV_execute(L,1); +L->nCcalls--; +luaC_checkGC(L); +} +static int luaD_pcall(lua_State*L,Pfunc func,void*u, +ptrdiff_t old_top,ptrdiff_t ef){ +int status; +unsigned short oldnCcalls=L->nCcalls; +ptrdiff_t old_ci=saveci(L,L->ci); +lu_byte old_allowhooks=L->allowhook; +ptrdiff_t old_errfunc=L->errfunc; +L->errfunc=ef; +status=luaD_rawrunprotected(L,func,u); +if(status!=0){ +StkId oldtop=restorestack(L,old_top); +luaF_close(L,oldtop); +luaD_seterrorobj(L,status,oldtop); +L->nCcalls=oldnCcalls; +L->ci=restoreci(L,old_ci); +L->base=L->ci->base; +L->savedpc=L->ci->savedpc; +L->allowhook=old_allowhooks; +restore_stack_limit(L); +} +L->errfunc=old_errfunc; +return status; +} +struct SParser{ +ZIO*z; +Mbuffer buff; +const char*name; +}; +static void f_parser(lua_State*L,void*ud){ +int i; +Proto*tf; +Closure*cl; +struct SParser*p=cast(struct SParser*,ud); +luaC_checkGC(L); +tf=luaY_parser(L,p->z, +&p->buff,p->name); +cl=luaF_newLclosure(L,tf->nups,hvalue(gt(L))); +cl->l.p=tf; +for(i=0;inups;i++) +cl->l.upvals[i]=luaF_newupval(L); +setclvalue(L,L->top,cl); +incr_top(L); +} +static int luaD_protectedparser(lua_State*L,ZIO*z,const char*name){ +struct SParser p; +int status; +p.z=z;p.name=name; +luaZ_initbuffer(L,&p.buff); +status=luaD_pcall(L,f_parser,&p,savestack(L,L->top),L->errfunc); +luaZ_freebuffer(L,&p.buff); +return status; +} +static void luaS_resize(lua_State*L,int newsize){ +GCObject**newhash; +stringtable*tb; +int i; +if(G(L)->gcstate==2) +return; +newhash=luaM_newvector(L,newsize,GCObject*); +tb=&G(L)->strt; +for(i=0;isize;i++){ +GCObject*p=tb->hash[i]; +while(p){ +GCObject*next=p->gch.next; +unsigned int h=gco2ts(p)->hash; +int h1=lmod(h,newsize); +p->gch.next=newhash[h1]; +newhash[h1]=p; +p=next; +} +} +luaM_freearray(L,tb->hash,tb->size,TString*); +tb->size=newsize; +tb->hash=newhash; +} +static TString*newlstr(lua_State*L,const char*str,size_t l, +unsigned int h){ +TString*ts; +stringtable*tb; +if(l+1>(((size_t)(~(size_t)0)-2)-sizeof(TString))/sizeof(char)) +luaM_toobig(L); +ts=cast(TString*,luaM_malloc(L,(l+1)*sizeof(char)+sizeof(TString))); +ts->tsv.len=l; +ts->tsv.hash=h; +ts->tsv.marked=luaC_white(G(L)); +ts->tsv.tt=4; +ts->tsv.reserved=0; +memcpy(ts+1,str,l*sizeof(char)); +((char*)(ts+1))[l]='\0'; +tb=&G(L)->strt; +h=lmod(h,tb->size); +ts->tsv.next=tb->hash[h]; +tb->hash[h]=obj2gco(ts); +tb->nuse++; +if(tb->nuse>cast(lu_int32,tb->size)&&tb->size<=(INT_MAX-2)/2) +luaS_resize(L,tb->size*2); +return ts; +} +static TString*luaS_newlstr(lua_State*L,const char*str,size_t l){ +GCObject*o; +unsigned int h=cast(unsigned int,l); +size_t step=(l>>5)+1; +size_t l1; +for(l1=l;l1>=step;l1-=step) +h=h^((h<<5)+(h>>2)+cast(unsigned char,str[l1-1])); +for(o=G(L)->strt.hash[lmod(h,G(L)->strt.size)]; +o!=NULL; +o=o->gch.next){ +TString*ts=rawgco2ts(o); +if(ts->tsv.len==l&&(memcmp(str,getstr(ts),l)==0)){ +if(isdead(G(L),o))changewhite(o); +return ts; +} +} +return newlstr(L,str,l,h); +} +static Udata*luaS_newudata(lua_State*L,size_t s,Table*e){ +Udata*u; +if(s>((size_t)(~(size_t)0)-2)-sizeof(Udata)) +luaM_toobig(L); +u=cast(Udata*,luaM_malloc(L,s+sizeof(Udata))); +u->uv.marked=luaC_white(G(L)); +u->uv.tt=7; +u->uv.len=s; +u->uv.metatable=NULL; +u->uv.env=e; +u->uv.next=G(L)->mainthread->next; +G(L)->mainthread->next=obj2gco(u); +return u; +} +#define hashpow2(t,n)(gnode(t,lmod((n),sizenode(t)))) +#define hashstr(t,str)hashpow2(t,(str)->tsv.hash) +#define hashboolean(t,p)hashpow2(t,p) +#define hashmod(t,n)(gnode(t,((n)%((sizenode(t)-1)|1)))) +#define hashpointer(t,p)hashmod(t,IntPoint(p)) +static const Node dummynode_={ +{{NULL},0}, +{{{NULL},0,NULL}} +}; +static Node*hashnum(const Table*t,lua_Number n){ +unsigned int a[cast_int(sizeof(lua_Number)/sizeof(int))]; +int i; +if(luai_numeq(n,0)) +return gnode(t,0); +memcpy(a,&n,sizeof(a)); +for(i=1;isizearray) +return i-1; +else{ +Node*n=mainposition(t,key); +do{ +if(luaO_rawequalObj(key2tval(n),key)|| +(ttype(gkey(n))==(8+3)&&iscollectable(key)&& +gcvalue(gkey(n))==gcvalue(key))){ +i=cast_int(n-gnode(t,0)); +return i+t->sizearray; +} +else n=gnext(n); +}while(n); +luaG_runerror(L,"invalid key to "LUA_QL("next")); +return 0; +} +} +static int luaH_next(lua_State*L,Table*t,StkId key){ +int i=findindex(L,t,key); +for(i++;isizearray;i++){ +if(!ttisnil(&t->array[i])){ +setnvalue(key,cast_num(i+1)); +setobj(L,key+1,&t->array[i]); +return 1; +} +} +for(i-=t->sizearray;i<(int)sizenode(t);i++){ +if(!ttisnil(gval(gnode(t,i)))){ +setobj(L,key,key2tval(gnode(t,i))); +setobj(L,key+1,gval(gnode(t,i))); +return 1; +} +} +return 0; +} +static int computesizes(int nums[],int*narray){ +int i; +int twotoi; +int a=0; +int na=0; +int n=0; +for(i=0,twotoi=1;twotoi/2<*narray;i++,twotoi*=2){ +if(nums[i]>0){ +a+=nums[i]; +if(a>twotoi/2){ +n=twotoi; +na=a; +} +} +if(a==*narray)break; +} +*narray=n; +return na; +} +static int countint(const TValue*key,int*nums){ +int k=arrayindex(key); +if(0t->sizearray){ +lim=t->sizearray; +if(i>lim) +break; +} +for(;i<=lim;i++){ +if(!ttisnil(&t->array[i-1])) +lc++; +} +nums[lg]+=lc; +ause+=lc; +} +return ause; +} +static int numusehash(const Table*t,int*nums,int*pnasize){ +int totaluse=0; +int ause=0; +int i=sizenode(t); +while(i--){ +Node*n=&t->node[i]; +if(!ttisnil(gval(n))){ +ause+=countint(key2tval(n),nums); +totaluse++; +} +} +*pnasize+=ause; +return totaluse; +} +static void setarrayvector(lua_State*L,Table*t,int size){ +int i; +luaM_reallocvector(L,t->array,t->sizearray,size,TValue); +for(i=t->sizearray;iarray[i]); +t->sizearray=size; +} +static void setnodevector(lua_State*L,Table*t,int size){ +int lsize; +if(size==0){ +t->node=cast(Node*,(&dummynode_)); +lsize=0; +} +else{ +int i; +lsize=ceillog2(size); +if(lsize>(32-2)) +luaG_runerror(L,"table overflow"); +size=twoto(lsize); +t->node=luaM_newvector(L,size,Node); +for(i=0;ilsizenode=cast_byte(lsize); +t->lastfree=gnode(t,size); +} +static void resize(lua_State*L,Table*t,int nasize,int nhsize){ +int i; +int oldasize=t->sizearray; +int oldhsize=t->lsizenode; +Node*nold=t->node; +if(nasize>oldasize) +setarrayvector(L,t,nasize); +setnodevector(L,t,nhsize); +if(nasizesizearray=nasize; +for(i=nasize;iarray[i])) +setobj(L,luaH_setnum(L,t,i+1),&t->array[i]); +} +luaM_reallocvector(L,t->array,oldasize,nasize,TValue); +} +for(i=twoto(oldhsize)-1;i>=0;i--){ +Node*old=nold+i; +if(!ttisnil(gval(old))) +setobj(L,luaH_set(L,t,key2tval(old)),gval(old)); +} +if(nold!=(&dummynode_)) +luaM_freearray(L,nold,twoto(oldhsize),Node); +} +static void luaH_resizearray(lua_State*L,Table*t,int nasize){ +int nsize=(t->node==(&dummynode_))?0:sizenode(t); +resize(L,t,nasize,nsize); +} +static void rehash(lua_State*L,Table*t,const TValue*ek){ +int nasize,na; +int nums[(32-2)+1]; +int i; +int totaluse; +for(i=0;i<=(32-2);i++)nums[i]=0; +nasize=numusearray(t,nums); +totaluse=nasize; +totaluse+=numusehash(t,nums,&nasize); +nasize+=countint(ek,nums); +totaluse++; +na=computesizes(nums,&nasize); +resize(L,t,nasize,totaluse-na); +} +static Table*luaH_new(lua_State*L,int narray,int nhash){ +Table*t=luaM_new(L,Table); +luaC_link(L,obj2gco(t),5); +t->metatable=NULL; +t->flags=cast_byte(~0); +t->array=NULL; +t->sizearray=0; +t->lsizenode=0; +t->node=cast(Node*,(&dummynode_)); +setarrayvector(L,t,narray); +setnodevector(L,t,nhash); +return t; +} +static void luaH_free(lua_State*L,Table*t){ +if(t->node!=(&dummynode_)) +luaM_freearray(L,t->node,sizenode(t),Node); +luaM_freearray(L,t->array,t->sizearray,TValue); +luaM_free(L,t); +} +static Node*getfreepos(Table*t){ +while(t->lastfree-->t->node){ +if(ttisnil(gkey(t->lastfree))) +return t->lastfree; +} +return NULL; +} +static TValue*newkey(lua_State*L,Table*t,const TValue*key){ +Node*mp=mainposition(t,key); +if(!ttisnil(gval(mp))||mp==(&dummynode_)){ +Node*othern; +Node*n=getfreepos(t); +if(n==NULL){ +rehash(L,t,key); +return luaH_set(L,t,key); +} +othern=mainposition(t,key2tval(mp)); +if(othern!=mp){ +while(gnext(othern)!=mp)othern=gnext(othern); +gnext(othern)=n; +*n=*mp; +gnext(mp)=NULL; +setnilvalue(gval(mp)); +} +else{ +gnext(n)=gnext(mp); +gnext(mp)=n; +mp=n; +} +} +gkey(mp)->value=key->value;gkey(mp)->tt=key->tt; +luaC_barriert(L,t,key); +return gval(mp); +} +static const TValue*luaH_getnum(Table*t,int key){ +if(cast(unsigned int,key)-1sizearray)) +return&t->array[key-1]; +else{ +lua_Number nk=cast_num(key); +Node*n=hashnum(t,nk); +do{ +if(ttisnumber(gkey(n))&&luai_numeq(nvalue(gkey(n)),nk)) +return gval(n); +else n=gnext(n); +}while(n); +return(&luaO_nilobject_); +} +} +static const TValue*luaH_getstr(Table*t,TString*key){ +Node*n=hashstr(t,key); +do{ +if(ttisstring(gkey(n))&&rawtsvalue(gkey(n))==key) +return gval(n); +else n=gnext(n); +}while(n); +return(&luaO_nilobject_); +} +static const TValue*luaH_get(Table*t,const TValue*key){ +switch(ttype(key)){ +case 0:return(&luaO_nilobject_); +case 4:return luaH_getstr(t,rawtsvalue(key)); +case 3:{ +int k; +lua_Number n=nvalue(key); +lua_number2int(k,n); +if(luai_numeq(cast_num(k),nvalue(key))) +return luaH_getnum(t,k); +} +default:{ +Node*n=mainposition(t,key); +do{ +if(luaO_rawequalObj(key2tval(n),key)) +return gval(n); +else n=gnext(n); +}while(n); +return(&luaO_nilobject_); +} +} +} +static TValue*luaH_set(lua_State*L,Table*t,const TValue*key){ +const TValue*p=luaH_get(t,key); +t->flags=0; +if(p!=(&luaO_nilobject_)) +return cast(TValue*,p); +else{ +if(ttisnil(key))luaG_runerror(L,"table index is nil"); +else if(ttisnumber(key)&&luai_numisnan(nvalue(key))) +luaG_runerror(L,"table index is NaN"); +return newkey(L,t,key); +} +} +static TValue*luaH_setnum(lua_State*L,Table*t,int key){ +const TValue*p=luaH_getnum(t,key); +if(p!=(&luaO_nilobject_)) +return cast(TValue*,p); +else{ +TValue k; +setnvalue(&k,cast_num(key)); +return newkey(L,t,&k); +} +} +static TValue*luaH_setstr(lua_State*L,Table*t,TString*key){ +const TValue*p=luaH_getstr(t,key); +if(p!=(&luaO_nilobject_)) +return cast(TValue*,p); +else{ +TValue k; +setsvalue(L,&k,key); +return newkey(L,t,&k); +} +} +static int unbound_search(Table*t,unsigned int j){ +unsigned int i=j; +j++; +while(!ttisnil(luaH_getnum(t,j))){ +i=j; +j*=2; +if(j>cast(unsigned int,(INT_MAX-2))){ +i=1; +while(!ttisnil(luaH_getnum(t,i)))i++; +return i-1; +} +} +while(j-i>1){ +unsigned int m=(i+j)/2; +if(ttisnil(luaH_getnum(t,m)))j=m; +else i=m; +} +return i; +} +static int luaH_getn(Table*t){ +unsigned int j=t->sizearray; +if(j>0&&ttisnil(&t->array[j-1])){ +unsigned int i=0; +while(j-i>1){ +unsigned int m=(i+j)/2; +if(ttisnil(&t->array[m-1]))j=m; +else i=m; +} +return i; +} +else if(t->node==(&dummynode_)) +return j; +else return unbound_search(t,j); +} +#define makewhite(g,x)((x)->gch.marked=cast_byte(((x)->gch.marked&cast_byte(~(bitmask(2)|bit2mask(0,1))))|luaC_white(g))) +#define white2gray(x)reset2bits((x)->gch.marked,0,1) +#define black2gray(x)resetbit((x)->gch.marked,2) +#define stringmark(s)reset2bits((s)->tsv.marked,0,1) +#define isfinalized(u)testbit((u)->marked,3) +#define markfinalized(u)l_setbit((u)->marked,3) +#define markvalue(g,o){checkconsistency(o);if(iscollectable(o)&&iswhite(gcvalue(o)))reallymarkobject(g,gcvalue(o));} +#define markobject(g,t){if(iswhite(obj2gco(t)))reallymarkobject(g,obj2gco(t));} +#define setthreshold(g)(g->GCthreshold=(g->estimate/100)*g->gcpause) +static void removeentry(Node*n){ +if(iscollectable(gkey(n))) +setttype(gkey(n),(8+3)); +} +static void reallymarkobject(global_State*g,GCObject*o){ +white2gray(o); +switch(o->gch.tt){ +case 4:{ +return; +} +case 7:{ +Table*mt=gco2u(o)->metatable; +gray2black(o); +if(mt)markobject(g,mt); +markobject(g,gco2u(o)->env); +return; +} +case(8+2):{ +UpVal*uv=gco2uv(o); +markvalue(g,uv->v); +if(uv->v==&uv->u.value) +gray2black(o); +return; +} +case 6:{ +gco2cl(o)->c.gclist=g->gray; +g->gray=o; +break; +} +case 5:{ +gco2h(o)->gclist=g->gray; +g->gray=o; +break; +} +case 8:{ +gco2th(o)->gclist=g->gray; +g->gray=o; +break; +} +case(8+1):{ +gco2p(o)->gclist=g->gray; +g->gray=o; +break; +} +default:; +} +} +static void marktmu(global_State*g){ +GCObject*u=g->tmudata; +if(u){ +do{ +u=u->gch.next; +makewhite(g,u); +reallymarkobject(g,u); +}while(u!=g->tmudata); +} +} +static size_t luaC_separateudata(lua_State*L,int all){ +global_State*g=G(L); +size_t deadmem=0; +GCObject**p=&g->mainthread->next; +GCObject*curr; +while((curr=*p)!=NULL){ +if(!(iswhite(curr)||all)||isfinalized(gco2u(curr))) +p=&curr->gch.next; +else if(fasttm(L,gco2u(curr)->metatable,TM_GC)==NULL){ +markfinalized(gco2u(curr)); +p=&curr->gch.next; +} +else{ +deadmem+=sizeudata(gco2u(curr)); +markfinalized(gco2u(curr)); +*p=curr->gch.next; +if(g->tmudata==NULL) +g->tmudata=curr->gch.next=curr; +else{ +curr->gch.next=g->tmudata->gch.next; +g->tmudata->gch.next=curr; +g->tmudata=curr; +} +} +} +return deadmem; +} +static int traversetable(global_State*g,Table*h){ +int i; +int weakkey=0; +int weakvalue=0; +const TValue*mode; +if(h->metatable) +markobject(g,h->metatable); +mode=gfasttm(g,h->metatable,TM_MODE); +if(mode&&ttisstring(mode)){ +weakkey=(strchr(svalue(mode),'k')!=NULL); +weakvalue=(strchr(svalue(mode),'v')!=NULL); +if(weakkey||weakvalue){ +h->marked&=~(bitmask(3)|bitmask(4)); +h->marked|=cast_byte((weakkey<<3)| +(weakvalue<<4)); +h->gclist=g->weak; +g->weak=obj2gco(h); +} +} +if(weakkey&&weakvalue)return 1; +if(!weakvalue){ +i=h->sizearray; +while(i--) +markvalue(g,&h->array[i]); +} +i=sizenode(h); +while(i--){ +Node*n=gnode(h,i); +if(ttisnil(gval(n))) +removeentry(n); +else{ +if(!weakkey)markvalue(g,gkey(n)); +if(!weakvalue)markvalue(g,gval(n)); +} +} +return weakkey||weakvalue; +} +static void traverseproto(global_State*g,Proto*f){ +int i; +if(f->source)stringmark(f->source); +for(i=0;isizek;i++) +markvalue(g,&f->k[i]); +for(i=0;isizeupvalues;i++){ +if(f->upvalues[i]) +stringmark(f->upvalues[i]); +} +for(i=0;isizep;i++){ +if(f->p[i]) +markobject(g,f->p[i]); +} +for(i=0;isizelocvars;i++){ +if(f->locvars[i].varname) +stringmark(f->locvars[i].varname); +} +} +static void traverseclosure(global_State*g,Closure*cl){ +markobject(g,cl->c.env); +if(cl->c.isC){ +int i; +for(i=0;ic.nupvalues;i++) +markvalue(g,&cl->c.upvalue[i]); +} +else{ +int i; +markobject(g,cl->l.p); +for(i=0;il.nupvalues;i++) +markobject(g,cl->l.upvals[i]); +} +} +static void checkstacksizes(lua_State*L,StkId max){ +int ci_used=cast_int(L->ci-L->base_ci); +int s_used=cast_int(max-L->stack); +if(L->size_ci>20000) +return; +if(4*ci_usedsize_ci&&2*8size_ci) +luaD_reallocCI(L,L->size_ci/2); +condhardstacktests(luaD_reallocCI(L,ci_used+1)); +if(4*s_usedstacksize&& +2*((2*20)+5)stacksize) +luaD_reallocstack(L,L->stacksize/2); +condhardstacktests(luaD_reallocstack(L,s_used)); +} +static void traversestack(global_State*g,lua_State*l){ +StkId o,lim; +CallInfo*ci; +markvalue(g,gt(l)); +lim=l->top; +for(ci=l->base_ci;ci<=l->ci;ci++){ +if(limtop)lim=ci->top; +} +for(o=l->stack;otop;o++) +markvalue(g,o); +for(;o<=lim;o++) +setnilvalue(o); +checkstacksizes(l,lim); +} +static l_mem propagatemark(global_State*g){ +GCObject*o=g->gray; +gray2black(o); +switch(o->gch.tt){ +case 5:{ +Table*h=gco2h(o); +g->gray=h->gclist; +if(traversetable(g,h)) +black2gray(o); +return sizeof(Table)+sizeof(TValue)*h->sizearray+ +sizeof(Node)*sizenode(h); +} +case 6:{ +Closure*cl=gco2cl(o); +g->gray=cl->c.gclist; +traverseclosure(g,cl); +return(cl->c.isC)?sizeCclosure(cl->c.nupvalues): +sizeLclosure(cl->l.nupvalues); +} +case 8:{ +lua_State*th=gco2th(o); +g->gray=th->gclist; +th->gclist=g->grayagain; +g->grayagain=o; +black2gray(o); +traversestack(g,th); +return sizeof(lua_State)+sizeof(TValue)*th->stacksize+ +sizeof(CallInfo)*th->size_ci; +} +case(8+1):{ +Proto*p=gco2p(o); +g->gray=p->gclist; +traverseproto(g,p); +return sizeof(Proto)+sizeof(Instruction)*p->sizecode+ +sizeof(Proto*)*p->sizep+ +sizeof(TValue)*p->sizek+ +sizeof(int)*p->sizelineinfo+ +sizeof(LocVar)*p->sizelocvars+ +sizeof(TString*)*p->sizeupvalues; +} +default:return 0; +} +} +static size_t propagateall(global_State*g){ +size_t m=0; +while(g->gray)m+=propagatemark(g); +return m; +} +static int iscleared(const TValue*o,int iskey){ +if(!iscollectable(o))return 0; +if(ttisstring(o)){ +stringmark(rawtsvalue(o)); +return 0; +} +return iswhite(gcvalue(o))|| +(ttisuserdata(o)&&(!iskey&&isfinalized(uvalue(o)))); +} +static void cleartable(GCObject*l){ +while(l){ +Table*h=gco2h(l); +int i=h->sizearray; +if(testbit(h->marked,4)){ +while(i--){ +TValue*o=&h->array[i]; +if(iscleared(o,0)) +setnilvalue(o); +} +} +i=sizenode(h); +while(i--){ +Node*n=gnode(h,i); +if(!ttisnil(gval(n))&& +(iscleared(key2tval(n),1)||iscleared(gval(n),0))){ +setnilvalue(gval(n)); +removeentry(n); +} +} +l=h->gclist; +} +} +static void freeobj(lua_State*L,GCObject*o){ +switch(o->gch.tt){ +case(8+1):luaF_freeproto(L,gco2p(o));break; +case 6:luaF_freeclosure(L,gco2cl(o));break; +case(8+2):luaF_freeupval(L,gco2uv(o));break; +case 5:luaH_free(L,gco2h(o));break; +case 8:{ +luaE_freethread(L,gco2th(o)); +break; +} +case 4:{ +G(L)->strt.nuse--; +luaM_freemem(L,o,sizestring(gco2ts(o))); +break; +} +case 7:{ +luaM_freemem(L,o,sizeudata(gco2u(o))); +break; +} +default:; +} +} +#define sweepwholelist(L,p)sweeplist(L,p,((lu_mem)(~(lu_mem)0)-2)) +static GCObject**sweeplist(lua_State*L,GCObject**p,lu_mem count){ +GCObject*curr; +global_State*g=G(L); +int deadmask=otherwhite(g); +while((curr=*p)!=NULL&&count-->0){ +if(curr->gch.tt==8) +sweepwholelist(L,&gco2th(curr)->openupval); +if((curr->gch.marked^bit2mask(0,1))&deadmask){ +makewhite(g,curr); +p=&curr->gch.next; +} +else{ +*p=curr->gch.next; +if(curr==g->rootgc) +g->rootgc=curr->gch.next; +freeobj(L,curr); +} +} +return p; +} +static void checkSizes(lua_State*L){ +global_State*g=G(L); +if(g->strt.nusestrt.size/4)&& +g->strt.size>32*2) +luaS_resize(L,g->strt.size/2); +if(luaZ_sizebuffer(&g->buff)>32*2){ +size_t newsize=luaZ_sizebuffer(&g->buff)/2; +luaZ_resizebuffer(L,&g->buff,newsize); +} +} +static void GCTM(lua_State*L){ +global_State*g=G(L); +GCObject*o=g->tmudata->gch.next; +Udata*udata=rawgco2u(o); +const TValue*tm; +if(o==g->tmudata) +g->tmudata=NULL; +else +g->tmudata->gch.next=udata->uv.next; +udata->uv.next=g->mainthread->next; +g->mainthread->next=o; +makewhite(g,o); +tm=fasttm(L,udata->uv.metatable,TM_GC); +if(tm!=NULL){ +lu_byte oldah=L->allowhook; +lu_mem oldt=g->GCthreshold; +L->allowhook=0; +g->GCthreshold=2*g->totalbytes; +setobj(L,L->top,tm); +setuvalue(L,L->top+1,udata); +L->top+=2; +luaD_call(L,L->top-2,0); +L->allowhook=oldah; +g->GCthreshold=oldt; +} +} +static void luaC_callGCTM(lua_State*L){ +while(G(L)->tmudata) +GCTM(L); +} +static void luaC_freeall(lua_State*L){ +global_State*g=G(L); +int i; +g->currentwhite=bit2mask(0,1)|bitmask(6); +sweepwholelist(L,&g->rootgc); +for(i=0;istrt.size;i++) +sweepwholelist(L,&g->strt.hash[i]); +} +static void markmt(global_State*g){ +int i; +for(i=0;i<(8+1);i++) +if(g->mt[i])markobject(g,g->mt[i]); +} +static void markroot(lua_State*L){ +global_State*g=G(L); +g->gray=NULL; +g->grayagain=NULL; +g->weak=NULL; +markobject(g,g->mainthread); +markvalue(g,gt(g->mainthread)); +markvalue(g,registry(L)); +markmt(g); +g->gcstate=1; +} +static void remarkupvals(global_State*g){ +UpVal*uv; +for(uv=g->uvhead.u.l.next;uv!=&g->uvhead;uv=uv->u.l.next){ +if(isgray(obj2gco(uv))) +markvalue(g,uv->v); +} +} +static void atomic(lua_State*L){ +global_State*g=G(L); +size_t udsize; +remarkupvals(g); +propagateall(g); +g->gray=g->weak; +g->weak=NULL; +markobject(g,L); +markmt(g); +propagateall(g); +g->gray=g->grayagain; +g->grayagain=NULL; +propagateall(g); +udsize=luaC_separateudata(L,0); +marktmu(g); +udsize+=propagateall(g); +cleartable(g->weak); +g->currentwhite=cast_byte(otherwhite(g)); +g->sweepstrgc=0; +g->sweepgc=&g->rootgc; +g->gcstate=2; +g->estimate=g->totalbytes-udsize; +} +static l_mem singlestep(lua_State*L){ +global_State*g=G(L); +switch(g->gcstate){ +case 0:{ +markroot(L); +return 0; +} +case 1:{ +if(g->gray) +return propagatemark(g); +else{ +atomic(L); +return 0; +} +} +case 2:{ +lu_mem old=g->totalbytes; +sweepwholelist(L,&g->strt.hash[g->sweepstrgc++]); +if(g->sweepstrgc>=g->strt.size) +g->gcstate=3; +g->estimate-=old-g->totalbytes; +return 10; +} +case 3:{ +lu_mem old=g->totalbytes; +g->sweepgc=sweeplist(L,g->sweepgc,40); +if(*g->sweepgc==NULL){ +checkSizes(L); +g->gcstate=4; +} +g->estimate-=old-g->totalbytes; +return 40*10; +} +case 4:{ +if(g->tmudata){ +GCTM(L); +if(g->estimate>100) +g->estimate-=100; +return 100; +} +else{ +g->gcstate=0; +g->gcdept=0; +return 0; +} +} +default:return 0; +} +} +static void luaC_step(lua_State*L){ +global_State*g=G(L); +l_mem lim=(1024u/100)*g->gcstepmul; +if(lim==0) +lim=(((lu_mem)(~(lu_mem)0)-2)-1)/2; +g->gcdept+=g->totalbytes-g->GCthreshold; +do{ +lim-=singlestep(L); +if(g->gcstate==0) +break; +}while(lim>0); +if(g->gcstate!=0){ +if(g->gcdept<1024u) +g->GCthreshold=g->totalbytes+1024u; +else{ +g->gcdept-=1024u; +g->GCthreshold=g->totalbytes; +} +} +else{ +setthreshold(g); +} +} +static void luaC_barrierf(lua_State*L,GCObject*o,GCObject*v){ +global_State*g=G(L); +if(g->gcstate==1) +reallymarkobject(g,v); +else +makewhite(g,o); +} +static void luaC_barrierback(lua_State*L,Table*t){ +global_State*g=G(L); +GCObject*o=obj2gco(t); +black2gray(o); +t->gclist=g->grayagain; +g->grayagain=o; +} +static void luaC_link(lua_State*L,GCObject*o,lu_byte tt){ +global_State*g=G(L); +o->gch.next=g->rootgc; +g->rootgc=o; +o->gch.marked=luaC_white(g); +o->gch.tt=tt; +} +static void luaC_linkupval(lua_State*L,UpVal*uv){ +global_State*g=G(L); +GCObject*o=obj2gco(uv); +o->gch.next=g->rootgc; +g->rootgc=o; +if(isgray(o)){ +if(g->gcstate==1){ +gray2black(o); +luaC_barrier(L,uv,uv->v); +} +else{ +makewhite(g,o); +} +} +} +typedef union{ +lua_Number r; +TString*ts; +}SemInfo; +typedef struct Token{ +int token; +SemInfo seminfo; +}Token; +typedef struct LexState{ +int current; +int linenumber; +int lastline; +Token t; +Token lookahead; +struct FuncState*fs; +struct lua_State*L; +ZIO*z; +Mbuffer*buff; +TString*source; +char decpoint; +}LexState; +static void luaX_init(lua_State*L); +static void luaX_lexerror(LexState*ls,const char*msg,int token); +#define state_size(x)(sizeof(x)+0) +#define fromstate(l)(cast(lu_byte*,(l))-0) +#define tostate(l)(cast(lua_State*,cast(lu_byte*,l)+0)) +typedef struct LG{ +lua_State l; +global_State g; +}LG; +static void stack_init(lua_State*L1,lua_State*L){ +L1->base_ci=luaM_newvector(L,8,CallInfo); +L1->ci=L1->base_ci; +L1->size_ci=8; +L1->end_ci=L1->base_ci+L1->size_ci-1; +L1->stack=luaM_newvector(L,(2*20)+5,TValue); +L1->stacksize=(2*20)+5; +L1->top=L1->stack; +L1->stack_last=L1->stack+(L1->stacksize-5)-1; +L1->ci->func=L1->top; +setnilvalue(L1->top++); +L1->base=L1->ci->base=L1->top; +L1->ci->top=L1->top+20; +} +static void freestack(lua_State*L,lua_State*L1){ +luaM_freearray(L,L1->base_ci,L1->size_ci,CallInfo); +luaM_freearray(L,L1->stack,L1->stacksize,TValue); +} +static void f_luaopen(lua_State*L,void*ud){ +global_State*g=G(L); +UNUSED(ud); +stack_init(L,L); +sethvalue(L,gt(L),luaH_new(L,0,2)); +sethvalue(L,registry(L),luaH_new(L,0,2)); +luaS_resize(L,32); +luaT_init(L); +luaX_init(L); +luaS_fix(luaS_newliteral(L,"not enough memory")); +g->GCthreshold=4*g->totalbytes; +} +static void preinit_state(lua_State*L,global_State*g){ +G(L)=g; +L->stack=NULL; +L->stacksize=0; +L->errorJmp=NULL; +L->hook=NULL; +L->hookmask=0; +L->basehookcount=0; +L->allowhook=1; +resethookcount(L); +L->openupval=NULL; +L->size_ci=0; +L->nCcalls=L->baseCcalls=0; +L->status=0; +L->base_ci=L->ci=NULL; +L->savedpc=NULL; +L->errfunc=0; +setnilvalue(gt(L)); +} +static void close_state(lua_State*L){ +global_State*g=G(L); +luaF_close(L,L->stack); +luaC_freeall(L); +luaM_freearray(L,G(L)->strt.hash,G(L)->strt.size,TString*); +luaZ_freebuffer(L,&g->buff); +freestack(L,L); +(*g->frealloc)(g->ud,fromstate(L),state_size(LG),0); +} +static void luaE_freethread(lua_State*L,lua_State*L1){ +luaF_close(L1,L1->stack); +freestack(L,L1); +luaM_freemem(L,fromstate(L1),state_size(lua_State)); +} +static lua_State*lua_newstate(lua_Alloc f,void*ud){ +int i; +lua_State*L; +global_State*g; +void*l=(*f)(ud,NULL,0,state_size(LG)); +if(l==NULL)return NULL; +L=tostate(l); +g=&((LG*)L)->g; +L->next=NULL; +L->tt=8; +g->currentwhite=bit2mask(0,5); +L->marked=luaC_white(g); +set2bits(L->marked,5,6); +preinit_state(L,g); +g->frealloc=f; +g->ud=ud; +g->mainthread=L; +g->uvhead.u.l.prev=&g->uvhead; +g->uvhead.u.l.next=&g->uvhead; +g->GCthreshold=0; +g->strt.size=0; +g->strt.nuse=0; +g->strt.hash=NULL; +setnilvalue(registry(L)); +luaZ_initbuffer(L,&g->buff); +g->panic=NULL; +g->gcstate=0; +g->rootgc=obj2gco(L); +g->sweepstrgc=0; +g->sweepgc=&g->rootgc; +g->gray=NULL; +g->grayagain=NULL; +g->weak=NULL; +g->tmudata=NULL; +g->totalbytes=sizeof(LG); +g->gcpause=200; +g->gcstepmul=200; +g->gcdept=0; +for(i=0;i<(8+1);i++)g->mt[i]=NULL; +if(luaD_rawrunprotected(L,f_luaopen,NULL)!=0){ +close_state(L); +L=NULL; +} +else +{} +return L; +} +static void callallgcTM(lua_State*L,void*ud){ +UNUSED(ud); +luaC_callGCTM(L); +} +static void lua_close(lua_State*L){ +L=G(L)->mainthread; +luaF_close(L,L->stack); +luaC_separateudata(L,1); +L->errfunc=0; +do{ +L->ci=L->base_ci; +L->base=L->top=L->ci->base; +L->nCcalls=L->baseCcalls=0; +}while(luaD_rawrunprotected(L,callallgcTM,NULL)!=0); +close_state(L); +} +#define getcode(fs,e)((fs)->f->code[(e)->u.s.info]) +#define luaK_codeAsBx(fs,o,A,sBx)luaK_codeABx(fs,o,A,(sBx)+(((1<<(9+9))-1)>>1)) +#define luaK_setmultret(fs,e)luaK_setreturns(fs,e,(-1)) +static int luaK_codeABx(FuncState*fs,OpCode o,int A,unsigned int Bx); +static int luaK_codeABC(FuncState*fs,OpCode o,int A,int B,int C); +static void luaK_setreturns(FuncState*fs,expdesc*e,int nresults); +static void luaK_patchtohere(FuncState*fs,int list); +static void luaK_concat(FuncState*fs,int*l1,int l2); +static int currentpc(lua_State*L,CallInfo*ci){ +if(!isLua(ci))return-1; +if(ci==L->ci) +ci->savedpc=L->savedpc; +return pcRel(ci->savedpc,ci_func(ci)->l.p); +} +static int currentline(lua_State*L,CallInfo*ci){ +int pc=currentpc(L,ci); +if(pc<0) +return-1; +else +return getline_(ci_func(ci)->l.p,pc); +} +static int lua_getstack(lua_State*L,int level,lua_Debug*ar){ +int status; +CallInfo*ci; +for(ci=L->ci;level>0&&ci>L->base_ci;ci--){ +level--; +if(f_isLua(ci)) +level-=ci->tailcalls; +} +if(level==0&&ci>L->base_ci){ +status=1; +ar->i_ci=cast_int(ci-L->base_ci); +} +else if(level<0){ +status=1; +ar->i_ci=0; +} +else status=0; +return status; +} +static Proto*getluaproto(CallInfo*ci){ +return(isLua(ci)?ci_func(ci)->l.p:NULL); +} +static void funcinfo(lua_Debug*ar,Closure*cl){ +if(cl->c.isC){ +ar->source="=[C]"; +ar->linedefined=-1; +ar->lastlinedefined=-1; +ar->what="C"; +} +else{ +ar->source=getstr(cl->l.p->source); +ar->linedefined=cl->l.p->linedefined; +ar->lastlinedefined=cl->l.p->lastlinedefined; +ar->what=(ar->linedefined==0)?"main":"Lua"; +} +luaO_chunkid(ar->short_src,ar->source,60); +} +static void info_tailcall(lua_Debug*ar){ +ar->name=ar->namewhat=""; +ar->what="tail"; +ar->lastlinedefined=ar->linedefined=ar->currentline=-1; +ar->source="=(tail call)"; +luaO_chunkid(ar->short_src,ar->source,60); +ar->nups=0; +} +static void collectvalidlines(lua_State*L,Closure*f){ +if(f==NULL||f->c.isC){ +setnilvalue(L->top); +} +else{ +Table*t=luaH_new(L,0,0); +int*lineinfo=f->l.p->lineinfo; +int i; +for(i=0;il.p->sizelineinfo;i++) +setbvalue(luaH_setnum(L,t,lineinfo[i]),1); +sethvalue(L,L->top,t); +} +incr_top(L); +} +static int auxgetinfo(lua_State*L,const char*what,lua_Debug*ar, +Closure*f,CallInfo*ci){ +int status=1; +if(f==NULL){ +info_tailcall(ar); +return status; +} +for(;*what;what++){ +switch(*what){ +case'S':{ +funcinfo(ar,f); +break; +} +case'l':{ +ar->currentline=(ci)?currentline(L,ci):-1; +break; +} +case'u':{ +ar->nups=f->c.nupvalues; +break; +} +case'n':{ +ar->namewhat=(ci)?NULL:NULL; +if(ar->namewhat==NULL){ +ar->namewhat=""; +ar->name=NULL; +} +break; +} +case'L': +case'f': +break; +default:status=0; +} +} +return status; +} +static int lua_getinfo(lua_State*L,const char*what,lua_Debug*ar){ +int status; +Closure*f=NULL; +CallInfo*ci=NULL; +if(*what=='>'){ +StkId func=L->top-1; +luai_apicheck(L,ttisfunction(func)); +what++; +f=clvalue(func); +L->top--; +} +else if(ar->i_ci!=0){ +ci=L->base_ci+ar->i_ci; +f=clvalue(ci->func); +} +status=auxgetinfo(L,what,ar,f,ci); +if(strchr(what,'f')){ +if(f==NULL)setnilvalue(L->top); +else setclvalue(L,L->top,f); +incr_top(L); +} +if(strchr(what,'L')) +collectvalidlines(L,f); +return status; +} +static int isinstack(CallInfo*ci,const TValue*o){ +StkId p; +for(p=ci->base;ptop;p++) +if(o==p)return 1; +return 0; +} +static void luaG_typeerror(lua_State*L,const TValue*o,const char*op){ +const char*name=NULL; +const char*t=luaT_typenames[ttype(o)]; +const char*kind=(isinstack(L->ci,o))? +NULL: +NULL; +if(kind) +luaG_runerror(L,"attempt to %s %s "LUA_QL("%s")" (a %s value)", +op,kind,name,t); +else +luaG_runerror(L,"attempt to %s a %s value",op,t); +} +static void luaG_concaterror(lua_State*L,StkId p1,StkId p2){ +if(ttisstring(p1)||ttisnumber(p1))p1=p2; +luaG_typeerror(L,p1,"concatenate"); +} +static void luaG_aritherror(lua_State*L,const TValue*p1,const TValue*p2){ +TValue temp; +if(luaV_tonumber(p1,&temp)==NULL) +p2=p1; +luaG_typeerror(L,p2,"perform arithmetic on"); +} +static int luaG_ordererror(lua_State*L,const TValue*p1,const TValue*p2){ +const char*t1=luaT_typenames[ttype(p1)]; +const char*t2=luaT_typenames[ttype(p2)]; +if(t1[2]==t2[2]) +luaG_runerror(L,"attempt to compare two %s values",t1); +else +luaG_runerror(L,"attempt to compare %s with %s",t1,t2); +return 0; +} +static void addinfo(lua_State*L,const char*msg){ +CallInfo*ci=L->ci; +if(isLua(ci)){ +char buff[60]; +int line=currentline(L,ci); +luaO_chunkid(buff,getstr(getluaproto(ci)->source),60); +luaO_pushfstring(L,"%s:%d: %s",buff,line,msg); +} +} +static void luaG_errormsg(lua_State*L){ +if(L->errfunc!=0){ +StkId errfunc=restorestack(L,L->errfunc); +if(!ttisfunction(errfunc))luaD_throw(L,5); +setobj(L,L->top,L->top-1); +setobj(L,L->top-1,errfunc); +incr_top(L); +luaD_call(L,L->top-2,1); +} +luaD_throw(L,2); +} +static void luaG_runerror(lua_State*L,const char*fmt,...){ +va_list argp; +va_start(argp,fmt); +addinfo(L,luaO_pushvfstring(L,fmt,argp)); +va_end(argp); +luaG_errormsg(L); +} +static int luaZ_fill(ZIO*z){ +size_t size; +lua_State*L=z->L; +const char*buff; +buff=z->reader(L,z->data,&size); +if(buff==NULL||size==0)return(-1); +z->n=size-1; +z->p=buff; +return char2int(*(z->p++)); +} +static void luaZ_init(lua_State*L,ZIO*z,lua_Reader reader,void*data){ +z->L=L; +z->reader=reader; +z->data=data; +z->n=0; +z->p=NULL; +} +static char*luaZ_openspace(lua_State*L,Mbuffer*buff,size_t n){ +if(n>buff->buffsize){ +if(n<32)n=32; +luaZ_resizebuffer(L,buff,n); +} +return buff->buffer; +} +#define opmode(t,a,b,c,m)(((t)<<7)|((a)<<6)|((b)<<4)|((c)<<2)|(m)) +static const lu_byte luaP_opmodes[(cast(int,OP_VARARG)+1)]={ +opmode(0,1,OpArgR,OpArgN,iABC) +,opmode(0,1,OpArgK,OpArgN,iABx) +,opmode(0,1,OpArgU,OpArgU,iABC) +,opmode(0,1,OpArgR,OpArgN,iABC) +,opmode(0,1,OpArgU,OpArgN,iABC) +,opmode(0,1,OpArgK,OpArgN,iABx) +,opmode(0,1,OpArgR,OpArgK,iABC) +,opmode(0,0,OpArgK,OpArgN,iABx) +,opmode(0,0,OpArgU,OpArgN,iABC) +,opmode(0,0,OpArgK,OpArgK,iABC) +,opmode(0,1,OpArgU,OpArgU,iABC) +,opmode(0,1,OpArgR,OpArgK,iABC) +,opmode(0,1,OpArgK,OpArgK,iABC) +,opmode(0,1,OpArgK,OpArgK,iABC) +,opmode(0,1,OpArgK,OpArgK,iABC) +,opmode(0,1,OpArgK,OpArgK,iABC) +,opmode(0,1,OpArgK,OpArgK,iABC) +,opmode(0,1,OpArgK,OpArgK,iABC) +,opmode(0,1,OpArgR,OpArgN,iABC) +,opmode(0,1,OpArgR,OpArgN,iABC) +,opmode(0,1,OpArgR,OpArgN,iABC) +,opmode(0,1,OpArgR,OpArgR,iABC) +,opmode(0,0,OpArgR,OpArgN,iAsBx) +,opmode(1,0,OpArgK,OpArgK,iABC) +,opmode(1,0,OpArgK,OpArgK,iABC) +,opmode(1,0,OpArgK,OpArgK,iABC) +,opmode(1,1,OpArgR,OpArgU,iABC) +,opmode(1,1,OpArgR,OpArgU,iABC) +,opmode(0,1,OpArgU,OpArgU,iABC) +,opmode(0,1,OpArgU,OpArgU,iABC) +,opmode(0,0,OpArgU,OpArgN,iABC) +,opmode(0,1,OpArgR,OpArgN,iAsBx) +,opmode(0,1,OpArgR,OpArgN,iAsBx) +,opmode(1,0,OpArgN,OpArgU,iABC) +,opmode(0,0,OpArgU,OpArgU,iABC) +,opmode(0,0,OpArgN,OpArgN,iABC) +,opmode(0,1,OpArgU,OpArgN,iABx) +,opmode(0,1,OpArgU,OpArgN,iABC) +}; +#define next(ls)(ls->current=zgetc(ls->z)) +#define currIsNewline(ls)(ls->current=='\n'||ls->current=='\r') +static const char*const luaX_tokens[]={ +"and","break","do","else","elseif", +"end","false","for","function","if", +"in","local","nil","not","or","repeat", +"return","then","true","until","while", +"..","...","==",">=","<=","~=", +"","","","", +NULL +}; +#define save_and_next(ls)(save(ls,ls->current),next(ls)) +static void save(LexState*ls,int c){ +Mbuffer*b=ls->buff; +if(b->n+1>b->buffsize){ +size_t newsize; +if(b->buffsize>=((size_t)(~(size_t)0)-2)/2) +luaX_lexerror(ls,"lexical element too long",0); +newsize=b->buffsize*2; +luaZ_resizebuffer(ls->L,b,newsize); +} +b->buffer[b->n++]=cast(char,c); +} +static void luaX_init(lua_State*L){ +int i; +for(i=0;i<(cast(int,TK_WHILE-257+1));i++){ +TString*ts=luaS_new(L,luaX_tokens[i]); +luaS_fix(ts); +ts->tsv.reserved=cast_byte(i+1); +} +} +static const char*luaX_token2str(LexState*ls,int token){ +if(token<257){ +return(iscntrl(token))?luaO_pushfstring(ls->L,"char(%d)",token): +luaO_pushfstring(ls->L,"%c",token); +} +else +return luaX_tokens[token-257]; +} +static const char*txtToken(LexState*ls,int token){ +switch(token){ +case TK_NAME: +case TK_STRING: +case TK_NUMBER: +save(ls,'\0'); +return luaZ_buffer(ls->buff); +default: +return luaX_token2str(ls,token); +} +} +static void luaX_lexerror(LexState*ls,const char*msg,int token){ +char buff[80]; +luaO_chunkid(buff,getstr(ls->source),80); +msg=luaO_pushfstring(ls->L,"%s:%d: %s",buff,ls->linenumber,msg); +if(token) +luaO_pushfstring(ls->L,"%s near "LUA_QL("%s"),msg,txtToken(ls,token)); +luaD_throw(ls->L,3); +} +static void luaX_syntaxerror(LexState*ls,const char*msg){ +luaX_lexerror(ls,msg,ls->t.token); +} +static TString*luaX_newstring(LexState*ls,const char*str,size_t l){ +lua_State*L=ls->L; +TString*ts=luaS_newlstr(L,str,l); +TValue*o=luaH_setstr(L,ls->fs->h,ts); +if(ttisnil(o)){ +setbvalue(o,1); +luaC_checkGC(L); +} +return ts; +} +static void inclinenumber(LexState*ls){ +int old=ls->current; +next(ls); +if(currIsNewline(ls)&&ls->current!=old) +next(ls); +if(++ls->linenumber>=(INT_MAX-2)) +luaX_syntaxerror(ls,"chunk has too many lines"); +} +static void luaX_setinput(lua_State*L,LexState*ls,ZIO*z,TString*source){ +ls->decpoint='.'; +ls->L=L; +ls->lookahead.token=TK_EOS; +ls->z=z; +ls->fs=NULL; +ls->linenumber=1; +ls->lastline=1; +ls->source=source; +luaZ_resizebuffer(ls->L,ls->buff,32); +next(ls); +} +static int check_next(LexState*ls,const char*set){ +if(!strchr(set,ls->current)) +return 0; +save_and_next(ls); +return 1; +} +static void buffreplace(LexState*ls,char from,char to){ +size_t n=luaZ_bufflen(ls->buff); +char*p=luaZ_buffer(ls->buff); +while(n--) +if(p[n]==from)p[n]=to; +} +static void read_numeral(LexState*ls,SemInfo*seminfo){ +do{ +save_and_next(ls); +}while(isdigit(ls->current)||ls->current=='.'); +if(check_next(ls,"Ee")) +check_next(ls,"+-"); +while(isalnum(ls->current)||ls->current=='_') +save_and_next(ls); +save(ls,'\0'); +buffreplace(ls,'.',ls->decpoint); +if(!luaO_str2d(luaZ_buffer(ls->buff),&seminfo->r)) +luaX_lexerror(ls,"malformed number",TK_NUMBER); +} +static int skip_sep(LexState*ls){ +int count=0; +int s=ls->current; +save_and_next(ls); +while(ls->current=='='){ +save_and_next(ls); +count++; +} +return(ls->current==s)?count:(-count)-1; +} +static void read_long_string(LexState*ls,SemInfo*seminfo,int sep){ +int cont=0; +(void)(cont); +save_and_next(ls); +if(currIsNewline(ls)) +inclinenumber(ls); +for(;;){ +switch(ls->current){ +case(-1): +luaX_lexerror(ls,(seminfo)?"unfinished long string": +"unfinished long comment",TK_EOS); +break; +case']':{ +if(skip_sep(ls)==sep){ +save_and_next(ls); +goto endloop; +} +break; +} +case'\n': +case'\r':{ +save(ls,'\n'); +inclinenumber(ls); +if(!seminfo)luaZ_resetbuffer(ls->buff); +break; +} +default:{ +if(seminfo)save_and_next(ls); +else next(ls); +} +} +}endloop: +if(seminfo) +seminfo->ts=luaX_newstring(ls,luaZ_buffer(ls->buff)+(2+sep), +luaZ_bufflen(ls->buff)-2*(2+sep)); +} +static void read_string(LexState*ls,int del,SemInfo*seminfo){ +save_and_next(ls); +while(ls->current!=del){ +switch(ls->current){ +case(-1): +luaX_lexerror(ls,"unfinished string",TK_EOS); +continue; +case'\n': +case'\r': +luaX_lexerror(ls,"unfinished string",TK_STRING); +continue; +case'\\':{ +int c; +next(ls); +switch(ls->current){ +case'a':c='\a';break; +case'b':c='\b';break; +case'f':c='\f';break; +case'n':c='\n';break; +case'r':c='\r';break; +case't':c='\t';break; +case'v':c='\v';break; +case'\n': +case'\r':save(ls,'\n');inclinenumber(ls);continue; +case(-1):continue; +default:{ +if(!isdigit(ls->current)) +save_and_next(ls); +else{ +int i=0; +c=0; +do{ +c=10*c+(ls->current-'0'); +next(ls); +}while(++i<3&&isdigit(ls->current)); +if(c>UCHAR_MAX) +luaX_lexerror(ls,"escape sequence too large",TK_STRING); +save(ls,c); +} +continue; +} +} +save(ls,c); +next(ls); +continue; +} +default: +save_and_next(ls); +} +} +save_and_next(ls); +seminfo->ts=luaX_newstring(ls,luaZ_buffer(ls->buff)+1, +luaZ_bufflen(ls->buff)-2); +} +static int llex(LexState*ls,SemInfo*seminfo){ +luaZ_resetbuffer(ls->buff); +for(;;){ +switch(ls->current){ +case'\n': +case'\r':{ +inclinenumber(ls); +continue; +} +case'-':{ +next(ls); +if(ls->current!='-')return'-'; +next(ls); +if(ls->current=='['){ +int sep=skip_sep(ls); +luaZ_resetbuffer(ls->buff); +if(sep>=0){ +read_long_string(ls,NULL,sep); +luaZ_resetbuffer(ls->buff); +continue; +} +} +while(!currIsNewline(ls)&&ls->current!=(-1)) +next(ls); +continue; +} +case'[':{ +int sep=skip_sep(ls); +if(sep>=0){ +read_long_string(ls,seminfo,sep); +return TK_STRING; +} +else if(sep==-1)return'['; +else luaX_lexerror(ls,"invalid long string delimiter",TK_STRING); +} +case'=':{ +next(ls); +if(ls->current!='=')return'='; +else{next(ls);return TK_EQ;} +} +case'<':{ +next(ls); +if(ls->current!='=')return'<'; +else{next(ls);return TK_LE;} +} +case'>':{ +next(ls); +if(ls->current!='=')return'>'; +else{next(ls);return TK_GE;} +} +case'~':{ +next(ls); +if(ls->current!='=')return'~'; +else{next(ls);return TK_NE;} +} +case'"': +case'\'':{ +read_string(ls,ls->current,seminfo); +return TK_STRING; +} +case'.':{ +save_and_next(ls); +if(check_next(ls,".")){ +if(check_next(ls,".")) +return TK_DOTS; +else return TK_CONCAT; +} +else if(!isdigit(ls->current))return'.'; +else{ +read_numeral(ls,seminfo); +return TK_NUMBER; +} +} +case(-1):{ +return TK_EOS; +} +default:{ +if(isspace(ls->current)){ +next(ls); +continue; +} +else if(isdigit(ls->current)){ +read_numeral(ls,seminfo); +return TK_NUMBER; +} +else if(isalpha(ls->current)||ls->current=='_'){ +TString*ts; +do{ +save_and_next(ls); +}while(isalnum(ls->current)||ls->current=='_'); +ts=luaX_newstring(ls,luaZ_buffer(ls->buff), +luaZ_bufflen(ls->buff)); +if(ts->tsv.reserved>0) +return ts->tsv.reserved-1+257; +else{ +seminfo->ts=ts; +return TK_NAME; +} +} +else{ +int c=ls->current; +next(ls); +return c; +} +} +} +} +} +static void luaX_next(LexState*ls){ +ls->lastline=ls->linenumber; +if(ls->lookahead.token!=TK_EOS){ +ls->t=ls->lookahead; +ls->lookahead.token=TK_EOS; +} +else +ls->t.token=llex(ls,&ls->t.seminfo); +} +static void luaX_lookahead(LexState*ls){ +ls->lookahead.token=llex(ls,&ls->lookahead.seminfo); +} +#define hasjumps(e)((e)->t!=(e)->f) +static int isnumeral(expdesc*e){ +return(e->k==VKNUM&&e->t==(-1)&&e->f==(-1)); +} +static void luaK_nil(FuncState*fs,int from,int n){ +Instruction*previous; +if(fs->pc>fs->lasttarget){ +if(fs->pc==0){ +if(from>=fs->nactvar) +return; +} +else{ +previous=&fs->f->code[fs->pc-1]; +if(GET_OPCODE(*previous)==OP_LOADNIL){ +int pfrom=GETARG_A(*previous); +int pto=GETARG_B(*previous); +if(pfrom<=from&&from<=pto+1){ +if(from+n-1>pto) +SETARG_B(*previous,from+n-1); +return; +} +} +} +} +luaK_codeABC(fs,OP_LOADNIL,from,from+n-1,0); +} +static int luaK_jump(FuncState*fs){ +int jpc=fs->jpc; +int j; +fs->jpc=(-1); +j=luaK_codeAsBx(fs,OP_JMP,0,(-1)); +luaK_concat(fs,&j,jpc); +return j; +} +static void luaK_ret(FuncState*fs,int first,int nret){ +luaK_codeABC(fs,OP_RETURN,first,nret+1,0); +} +static int condjump(FuncState*fs,OpCode op,int A,int B,int C){ +luaK_codeABC(fs,op,A,B,C); +return luaK_jump(fs); +} +static void fixjump(FuncState*fs,int pc,int dest){ +Instruction*jmp=&fs->f->code[pc]; +int offset=dest-(pc+1); +if(abs(offset)>(((1<<(9+9))-1)>>1)) +luaX_syntaxerror(fs->ls,"control structure too long"); +SETARG_sBx(*jmp,offset); +} +static int luaK_getlabel(FuncState*fs){ +fs->lasttarget=fs->pc; +return fs->pc; +} +static int getjump(FuncState*fs,int pc){ +int offset=GETARG_sBx(fs->f->code[pc]); +if(offset==(-1)) +return(-1); +else +return(pc+1)+offset; +} +static Instruction*getjumpcontrol(FuncState*fs,int pc){ +Instruction*pi=&fs->f->code[pc]; +if(pc>=1&&testTMode(GET_OPCODE(*(pi-1)))) +return pi-1; +else +return pi; +} +static int need_value(FuncState*fs,int list){ +for(;list!=(-1);list=getjump(fs,list)){ +Instruction i=*getjumpcontrol(fs,list); +if(GET_OPCODE(i)!=OP_TESTSET)return 1; +} +return 0; +} +static int patchtestreg(FuncState*fs,int node,int reg){ +Instruction*i=getjumpcontrol(fs,node); +if(GET_OPCODE(*i)!=OP_TESTSET) +return 0; +if(reg!=((1<<8)-1)&®!=GETARG_B(*i)) +SETARG_A(*i,reg); +else +*i=CREATE_ABC(OP_TEST,GETARG_B(*i),0,GETARG_C(*i)); +return 1; +} +static void removevalues(FuncState*fs,int list){ +for(;list!=(-1);list=getjump(fs,list)) +patchtestreg(fs,list,((1<<8)-1)); +} +static void patchlistaux(FuncState*fs,int list,int vtarget,int reg, +int dtarget){ +while(list!=(-1)){ +int next=getjump(fs,list); +if(patchtestreg(fs,list,reg)) +fixjump(fs,list,vtarget); +else +fixjump(fs,list,dtarget); +list=next; +} +} +static void dischargejpc(FuncState*fs){ +patchlistaux(fs,fs->jpc,fs->pc,((1<<8)-1),fs->pc); +fs->jpc=(-1); +} +static void luaK_patchlist(FuncState*fs,int list,int target){ +if(target==fs->pc) +luaK_patchtohere(fs,list); +else{ +patchlistaux(fs,list,target,((1<<8)-1),target); +} +} +static void luaK_patchtohere(FuncState*fs,int list){ +luaK_getlabel(fs); +luaK_concat(fs,&fs->jpc,list); +} +static void luaK_concat(FuncState*fs,int*l1,int l2){ +if(l2==(-1))return; +else if(*l1==(-1)) +*l1=l2; +else{ +int list=*l1; +int next; +while((next=getjump(fs,list))!=(-1)) +list=next; +fixjump(fs,list,l2); +} +} +static void luaK_checkstack(FuncState*fs,int n){ +int newstack=fs->freereg+n; +if(newstack>fs->f->maxstacksize){ +if(newstack>=250) +luaX_syntaxerror(fs->ls,"function or expression too complex"); +fs->f->maxstacksize=cast_byte(newstack); +} +} +static void luaK_reserveregs(FuncState*fs,int n){ +luaK_checkstack(fs,n); +fs->freereg+=n; +} +static void freereg(FuncState*fs,int reg){ +if(!ISK(reg)&®>=fs->nactvar){ +fs->freereg--; +} +} +static void freeexp(FuncState*fs,expdesc*e){ +if(e->k==VNONRELOC) +freereg(fs,e->u.s.info); +} +static int addk(FuncState*fs,TValue*k,TValue*v){ +lua_State*L=fs->L; +TValue*idx=luaH_set(L,fs->h,k); +Proto*f=fs->f; +int oldsize=f->sizek; +if(ttisnumber(idx)){ +return cast_int(nvalue(idx)); +} +else{ +setnvalue(idx,cast_num(fs->nk)); +luaM_growvector(L,f->k,fs->nk,f->sizek,TValue, +((1<<(9+9))-1),"constant table overflow"); +while(oldsizesizek)setnilvalue(&f->k[oldsize++]); +setobj(L,&f->k[fs->nk],v); +luaC_barrier(L,f,v); +return fs->nk++; +} +} +static int luaK_stringK(FuncState*fs,TString*s){ +TValue o; +setsvalue(fs->L,&o,s); +return addk(fs,&o,&o); +} +static int luaK_numberK(FuncState*fs,lua_Number r){ +TValue o; +setnvalue(&o,r); +return addk(fs,&o,&o); +} +static int boolK(FuncState*fs,int b){ +TValue o; +setbvalue(&o,b); +return addk(fs,&o,&o); +} +static int nilK(FuncState*fs){ +TValue k,v; +setnilvalue(&v); +sethvalue(fs->L,&k,fs->h); +return addk(fs,&k,&v); +} +static void luaK_setreturns(FuncState*fs,expdesc*e,int nresults){ +if(e->k==VCALL){ +SETARG_C(getcode(fs,e),nresults+1); +} +else if(e->k==VVARARG){ +SETARG_B(getcode(fs,e),nresults+1); +SETARG_A(getcode(fs,e),fs->freereg); +luaK_reserveregs(fs,1); +} +} +static void luaK_setoneret(FuncState*fs,expdesc*e){ +if(e->k==VCALL){ +e->k=VNONRELOC; +e->u.s.info=GETARG_A(getcode(fs,e)); +} +else if(e->k==VVARARG){ +SETARG_B(getcode(fs,e),2); +e->k=VRELOCABLE; +} +} +static void luaK_dischargevars(FuncState*fs,expdesc*e){ +switch(e->k){ +case VLOCAL:{ +e->k=VNONRELOC; +break; +} +case VUPVAL:{ +e->u.s.info=luaK_codeABC(fs,OP_GETUPVAL,0,e->u.s.info,0); +e->k=VRELOCABLE; +break; +} +case VGLOBAL:{ +e->u.s.info=luaK_codeABx(fs,OP_GETGLOBAL,0,e->u.s.info); +e->k=VRELOCABLE; +break; +} +case VINDEXED:{ +freereg(fs,e->u.s.aux); +freereg(fs,e->u.s.info); +e->u.s.info=luaK_codeABC(fs,OP_GETTABLE,0,e->u.s.info,e->u.s.aux); +e->k=VRELOCABLE; +break; +} +case VVARARG: +case VCALL:{ +luaK_setoneret(fs,e); +break; +} +default:break; +} +} +static int code_label(FuncState*fs,int A,int b,int jump){ +luaK_getlabel(fs); +return luaK_codeABC(fs,OP_LOADBOOL,A,b,jump); +} +static void discharge2reg(FuncState*fs,expdesc*e,int reg){ +luaK_dischargevars(fs,e); +switch(e->k){ +case VNIL:{ +luaK_nil(fs,reg,1); +break; +} +case VFALSE:case VTRUE:{ +luaK_codeABC(fs,OP_LOADBOOL,reg,e->k==VTRUE,0); +break; +} +case VK:{ +luaK_codeABx(fs,OP_LOADK,reg,e->u.s.info); +break; +} +case VKNUM:{ +luaK_codeABx(fs,OP_LOADK,reg,luaK_numberK(fs,e->u.nval)); +break; +} +case VRELOCABLE:{ +Instruction*pc=&getcode(fs,e); +SETARG_A(*pc,reg); +break; +} +case VNONRELOC:{ +if(reg!=e->u.s.info) +luaK_codeABC(fs,OP_MOVE,reg,e->u.s.info,0); +break; +} +default:{ +return; +} +} +e->u.s.info=reg; +e->k=VNONRELOC; +} +static void discharge2anyreg(FuncState*fs,expdesc*e){ +if(e->k!=VNONRELOC){ +luaK_reserveregs(fs,1); +discharge2reg(fs,e,fs->freereg-1); +} +} +static void exp2reg(FuncState*fs,expdesc*e,int reg){ +discharge2reg(fs,e,reg); +if(e->k==VJMP) +luaK_concat(fs,&e->t,e->u.s.info); +if(hasjumps(e)){ +int final; +int p_f=(-1); +int p_t=(-1); +if(need_value(fs,e->t)||need_value(fs,e->f)){ +int fj=(e->k==VJMP)?(-1):luaK_jump(fs); +p_f=code_label(fs,reg,0,1); +p_t=code_label(fs,reg,1,0); +luaK_patchtohere(fs,fj); +} +final=luaK_getlabel(fs); +patchlistaux(fs,e->f,final,reg,p_f); +patchlistaux(fs,e->t,final,reg,p_t); +} +e->f=e->t=(-1); +e->u.s.info=reg; +e->k=VNONRELOC; +} +static void luaK_exp2nextreg(FuncState*fs,expdesc*e){ +luaK_dischargevars(fs,e); +freeexp(fs,e); +luaK_reserveregs(fs,1); +exp2reg(fs,e,fs->freereg-1); +} +static int luaK_exp2anyreg(FuncState*fs,expdesc*e){ +luaK_dischargevars(fs,e); +if(e->k==VNONRELOC){ +if(!hasjumps(e))return e->u.s.info; +if(e->u.s.info>=fs->nactvar){ +exp2reg(fs,e,e->u.s.info); +return e->u.s.info; +} +} +luaK_exp2nextreg(fs,e); +return e->u.s.info; +} +static void luaK_exp2val(FuncState*fs,expdesc*e){ +if(hasjumps(e)) +luaK_exp2anyreg(fs,e); +else +luaK_dischargevars(fs,e); +} +static int luaK_exp2RK(FuncState*fs,expdesc*e){ +luaK_exp2val(fs,e); +switch(e->k){ +case VKNUM: +case VTRUE: +case VFALSE: +case VNIL:{ +if(fs->nk<=((1<<(9-1))-1)){ +e->u.s.info=(e->k==VNIL)?nilK(fs): +(e->k==VKNUM)?luaK_numberK(fs,e->u.nval): +boolK(fs,(e->k==VTRUE)); +e->k=VK; +return RKASK(e->u.s.info); +} +else break; +} +case VK:{ +if(e->u.s.info<=((1<<(9-1))-1)) +return RKASK(e->u.s.info); +else break; +} +default:break; +} +return luaK_exp2anyreg(fs,e); +} +static void luaK_storevar(FuncState*fs,expdesc*var,expdesc*ex){ +switch(var->k){ +case VLOCAL:{ +freeexp(fs,ex); +exp2reg(fs,ex,var->u.s.info); +return; +} +case VUPVAL:{ +int e=luaK_exp2anyreg(fs,ex); +luaK_codeABC(fs,OP_SETUPVAL,e,var->u.s.info,0); +break; +} +case VGLOBAL:{ +int e=luaK_exp2anyreg(fs,ex); +luaK_codeABx(fs,OP_SETGLOBAL,e,var->u.s.info); +break; +} +case VINDEXED:{ +int e=luaK_exp2RK(fs,ex); +luaK_codeABC(fs,OP_SETTABLE,var->u.s.info,var->u.s.aux,e); +break; +} +default:{ +break; +} +} +freeexp(fs,ex); +} +static void luaK_self(FuncState*fs,expdesc*e,expdesc*key){ +int func; +luaK_exp2anyreg(fs,e); +freeexp(fs,e); +func=fs->freereg; +luaK_reserveregs(fs,2); +luaK_codeABC(fs,OP_SELF,func,e->u.s.info,luaK_exp2RK(fs,key)); +freeexp(fs,key); +e->u.s.info=func; +e->k=VNONRELOC; +} +static void invertjump(FuncState*fs,expdesc*e){ +Instruction*pc=getjumpcontrol(fs,e->u.s.info); +SETARG_A(*pc,!(GETARG_A(*pc))); +} +static int jumponcond(FuncState*fs,expdesc*e,int cond){ +if(e->k==VRELOCABLE){ +Instruction ie=getcode(fs,e); +if(GET_OPCODE(ie)==OP_NOT){ +fs->pc--; +return condjump(fs,OP_TEST,GETARG_B(ie),0,!cond); +} +} +discharge2anyreg(fs,e); +freeexp(fs,e); +return condjump(fs,OP_TESTSET,((1<<8)-1),e->u.s.info,cond); +} +static void luaK_goiftrue(FuncState*fs,expdesc*e){ +int pc; +luaK_dischargevars(fs,e); +switch(e->k){ +case VK:case VKNUM:case VTRUE:{ +pc=(-1); +break; +} +case VJMP:{ +invertjump(fs,e); +pc=e->u.s.info; +break; +} +default:{ +pc=jumponcond(fs,e,0); +break; +} +} +luaK_concat(fs,&e->f,pc); +luaK_patchtohere(fs,e->t); +e->t=(-1); +} +static void luaK_goiffalse(FuncState*fs,expdesc*e){ +int pc; +luaK_dischargevars(fs,e); +switch(e->k){ +case VNIL:case VFALSE:{ +pc=(-1); +break; +} +case VJMP:{ +pc=e->u.s.info; +break; +} +default:{ +pc=jumponcond(fs,e,1); +break; +} +} +luaK_concat(fs,&e->t,pc); +luaK_patchtohere(fs,e->f); +e->f=(-1); +} +static void codenot(FuncState*fs,expdesc*e){ +luaK_dischargevars(fs,e); +switch(e->k){ +case VNIL:case VFALSE:{ +e->k=VTRUE; +break; +} +case VK:case VKNUM:case VTRUE:{ +e->k=VFALSE; +break; +} +case VJMP:{ +invertjump(fs,e); +break; +} +case VRELOCABLE: +case VNONRELOC:{ +discharge2anyreg(fs,e); +freeexp(fs,e); +e->u.s.info=luaK_codeABC(fs,OP_NOT,0,e->u.s.info,0); +e->k=VRELOCABLE; +break; +} +default:{ +break; +} +} +{int temp=e->f;e->f=e->t;e->t=temp;} +removevalues(fs,e->f); +removevalues(fs,e->t); +} +static void luaK_indexed(FuncState*fs,expdesc*t,expdesc*k){ +t->u.s.aux=luaK_exp2RK(fs,k); +t->k=VINDEXED; +} +static int constfolding(OpCode op,expdesc*e1,expdesc*e2){ +lua_Number v1,v2,r; +if(!isnumeral(e1)||!isnumeral(e2))return 0; +v1=e1->u.nval; +v2=e2->u.nval; +switch(op){ +case OP_ADD:r=luai_numadd(v1,v2);break; +case OP_SUB:r=luai_numsub(v1,v2);break; +case OP_MUL:r=luai_nummul(v1,v2);break; +case OP_DIV: +if(v2==0)return 0; +r=luai_numdiv(v1,v2);break; +case OP_MOD: +if(v2==0)return 0; +r=luai_nummod(v1,v2);break; +case OP_POW:r=luai_numpow(v1,v2);break; +case OP_UNM:r=luai_numunm(v1);break; +case OP_LEN:return 0; +default:r=0;break; +} +if(luai_numisnan(r))return 0; +e1->u.nval=r; +return 1; +} +static void codearith(FuncState*fs,OpCode op,expdesc*e1,expdesc*e2){ +if(constfolding(op,e1,e2)) +return; +else{ +int o2=(op!=OP_UNM&&op!=OP_LEN)?luaK_exp2RK(fs,e2):0; +int o1=luaK_exp2RK(fs,e1); +if(o1>o2){ +freeexp(fs,e1); +freeexp(fs,e2); +} +else{ +freeexp(fs,e2); +freeexp(fs,e1); +} +e1->u.s.info=luaK_codeABC(fs,op,0,o1,o2); +e1->k=VRELOCABLE; +} +} +static void codecomp(FuncState*fs,OpCode op,int cond,expdesc*e1, +expdesc*e2){ +int o1=luaK_exp2RK(fs,e1); +int o2=luaK_exp2RK(fs,e2); +freeexp(fs,e2); +freeexp(fs,e1); +if(cond==0&&op!=OP_EQ){ +int temp; +temp=o1;o1=o2;o2=temp; +cond=1; +} +e1->u.s.info=condjump(fs,op,cond,o1,o2); +e1->k=VJMP; +} +static void luaK_prefix(FuncState*fs,UnOpr op,expdesc*e){ +expdesc e2; +e2.t=e2.f=(-1);e2.k=VKNUM;e2.u.nval=0; +switch(op){ +case OPR_MINUS:{ +if(!isnumeral(e)) +luaK_exp2anyreg(fs,e); +codearith(fs,OP_UNM,e,&e2); +break; +} +case OPR_NOT:codenot(fs,e);break; +case OPR_LEN:{ +luaK_exp2anyreg(fs,e); +codearith(fs,OP_LEN,e,&e2); +break; +} +default:; +} +} +static void luaK_infix(FuncState*fs,BinOpr op,expdesc*v){ +switch(op){ +case OPR_AND:{ +luaK_goiftrue(fs,v); +break; +} +case OPR_OR:{ +luaK_goiffalse(fs,v); +break; +} +case OPR_CONCAT:{ +luaK_exp2nextreg(fs,v); +break; +} +case OPR_ADD:case OPR_SUB:case OPR_MUL:case OPR_DIV: +case OPR_MOD:case OPR_POW:{ +if(!isnumeral(v))luaK_exp2RK(fs,v); +break; +} +default:{ +luaK_exp2RK(fs,v); +break; +} +} +} +static void luaK_posfix(FuncState*fs,BinOpr op,expdesc*e1,expdesc*e2){ +switch(op){ +case OPR_AND:{ +luaK_dischargevars(fs,e2); +luaK_concat(fs,&e2->f,e1->f); +*e1=*e2; +break; +} +case OPR_OR:{ +luaK_dischargevars(fs,e2); +luaK_concat(fs,&e2->t,e1->t); +*e1=*e2; +break; +} +case OPR_CONCAT:{ +luaK_exp2val(fs,e2); +if(e2->k==VRELOCABLE&&GET_OPCODE(getcode(fs,e2))==OP_CONCAT){ +freeexp(fs,e1); +SETARG_B(getcode(fs,e2),e1->u.s.info); +e1->k=VRELOCABLE;e1->u.s.info=e2->u.s.info; +} +else{ +luaK_exp2nextreg(fs,e2); +codearith(fs,OP_CONCAT,e1,e2); +} +break; +} +case OPR_ADD:codearith(fs,OP_ADD,e1,e2);break; +case OPR_SUB:codearith(fs,OP_SUB,e1,e2);break; +case OPR_MUL:codearith(fs,OP_MUL,e1,e2);break; +case OPR_DIV:codearith(fs,OP_DIV,e1,e2);break; +case OPR_MOD:codearith(fs,OP_MOD,e1,e2);break; +case OPR_POW:codearith(fs,OP_POW,e1,e2);break; +case OPR_EQ:codecomp(fs,OP_EQ,1,e1,e2);break; +case OPR_NE:codecomp(fs,OP_EQ,0,e1,e2);break; +case OPR_LT:codecomp(fs,OP_LT,1,e1,e2);break; +case OPR_LE:codecomp(fs,OP_LE,1,e1,e2);break; +case OPR_GT:codecomp(fs,OP_LT,0,e1,e2);break; +case OPR_GE:codecomp(fs,OP_LE,0,e1,e2);break; +default:; +} +} +static void luaK_fixline(FuncState*fs,int line){ +fs->f->lineinfo[fs->pc-1]=line; +} +static int luaK_code(FuncState*fs,Instruction i,int line){ +Proto*f=fs->f; +dischargejpc(fs); +luaM_growvector(fs->L,f->code,fs->pc,f->sizecode,Instruction, +(INT_MAX-2),"code size overflow"); +f->code[fs->pc]=i; +luaM_growvector(fs->L,f->lineinfo,fs->pc,f->sizelineinfo,int, +(INT_MAX-2),"code size overflow"); +f->lineinfo[fs->pc]=line; +return fs->pc++; +} +static int luaK_codeABC(FuncState*fs,OpCode o,int a,int b,int c){ +return luaK_code(fs,CREATE_ABC(o,a,b,c),fs->ls->lastline); +} +static int luaK_codeABx(FuncState*fs,OpCode o,int a,unsigned int bc){ +return luaK_code(fs,CREATE_ABx(o,a,bc),fs->ls->lastline); +} +static void luaK_setlist(FuncState*fs,int base,int nelems,int tostore){ +int c=(nelems-1)/50+1; +int b=(tostore==(-1))?0:tostore; +if(c<=((1<<9)-1)) +luaK_codeABC(fs,OP_SETLIST,base,b,c); +else{ +luaK_codeABC(fs,OP_SETLIST,base,b,0); +luaK_code(fs,cast(Instruction,c),fs->ls->lastline); +} +fs->freereg=base+1; +} +#define hasmultret(k)((k)==VCALL||(k)==VVARARG) +#define getlocvar(fs,i)((fs)->f->locvars[(fs)->actvar[i]]) +#define luaY_checklimit(fs,v,l,m)if((v)>(l))errorlimit(fs,l,m) +typedef struct BlockCnt{ +struct BlockCnt*previous; +int breaklist; +lu_byte nactvar; +lu_byte upval; +lu_byte isbreakable; +}BlockCnt; +static void chunk(LexState*ls); +static void expr(LexState*ls,expdesc*v); +static void anchor_token(LexState*ls){ +if(ls->t.token==TK_NAME||ls->t.token==TK_STRING){ +TString*ts=ls->t.seminfo.ts; +luaX_newstring(ls,getstr(ts),ts->tsv.len); +} +} +static void error_expected(LexState*ls,int token){ +luaX_syntaxerror(ls, +luaO_pushfstring(ls->L,LUA_QL("%s")" expected",luaX_token2str(ls,token))); +} +static void errorlimit(FuncState*fs,int limit,const char*what){ +const char*msg=(fs->f->linedefined==0)? +luaO_pushfstring(fs->L,"main function has more than %d %s",limit,what): +luaO_pushfstring(fs->L,"function at line %d has more than %d %s", +fs->f->linedefined,limit,what); +luaX_lexerror(fs->ls,msg,0); +} +static int testnext(LexState*ls,int c){ +if(ls->t.token==c){ +luaX_next(ls); +return 1; +} +else return 0; +} +static void check(LexState*ls,int c){ +if(ls->t.token!=c) +error_expected(ls,c); +} +static void checknext(LexState*ls,int c){ +check(ls,c); +luaX_next(ls); +} +#define check_condition(ls,c,msg){if(!(c))luaX_syntaxerror(ls,msg);} +static void check_match(LexState*ls,int what,int who,int where){ +if(!testnext(ls,what)){ +if(where==ls->linenumber) +error_expected(ls,what); +else{ +luaX_syntaxerror(ls,luaO_pushfstring(ls->L, +LUA_QL("%s")" expected (to close "LUA_QL("%s")" at line %d)", +luaX_token2str(ls,what),luaX_token2str(ls,who),where)); +} +} +} +static TString*str_checkname(LexState*ls){ +TString*ts; +check(ls,TK_NAME); +ts=ls->t.seminfo.ts; +luaX_next(ls); +return ts; +} +static void init_exp(expdesc*e,expkind k,int i){ +e->f=e->t=(-1); +e->k=k; +e->u.s.info=i; +} +static void codestring(LexState*ls,expdesc*e,TString*s){ +init_exp(e,VK,luaK_stringK(ls->fs,s)); +} +static void checkname(LexState*ls,expdesc*e){ +codestring(ls,e,str_checkname(ls)); +} +static int registerlocalvar(LexState*ls,TString*varname){ +FuncState*fs=ls->fs; +Proto*f=fs->f; +int oldsize=f->sizelocvars; +luaM_growvector(ls->L,f->locvars,fs->nlocvars,f->sizelocvars, +LocVar,SHRT_MAX,"too many local variables"); +while(oldsizesizelocvars)f->locvars[oldsize++].varname=NULL; +f->locvars[fs->nlocvars].varname=varname; +luaC_objbarrier(ls->L,f,varname); +return fs->nlocvars++; +} +#define new_localvarliteral(ls,v,n)new_localvar(ls,luaX_newstring(ls,""v,(sizeof(v)/sizeof(char))-1),n) +static void new_localvar(LexState*ls,TString*name,int n){ +FuncState*fs=ls->fs; +luaY_checklimit(fs,fs->nactvar+n+1,200,"local variables"); +fs->actvar[fs->nactvar+n]=cast(unsigned short,registerlocalvar(ls,name)); +} +static void adjustlocalvars(LexState*ls,int nvars){ +FuncState*fs=ls->fs; +fs->nactvar=cast_byte(fs->nactvar+nvars); +for(;nvars;nvars--){ +getlocvar(fs,fs->nactvar-nvars).startpc=fs->pc; +} +} +static void removevars(LexState*ls,int tolevel){ +FuncState*fs=ls->fs; +while(fs->nactvar>tolevel) +getlocvar(fs,--fs->nactvar).endpc=fs->pc; +} +static int indexupvalue(FuncState*fs,TString*name,expdesc*v){ +int i; +Proto*f=fs->f; +int oldsize=f->sizeupvalues; +for(i=0;inups;i++){ +if(fs->upvalues[i].k==v->k&&fs->upvalues[i].info==v->u.s.info){ +return i; +} +} +luaY_checklimit(fs,f->nups+1,60,"upvalues"); +luaM_growvector(fs->L,f->upvalues,f->nups,f->sizeupvalues, +TString*,(INT_MAX-2),""); +while(oldsizesizeupvalues)f->upvalues[oldsize++]=NULL; +f->upvalues[f->nups]=name; +luaC_objbarrier(fs->L,f,name); +fs->upvalues[f->nups].k=cast_byte(v->k); +fs->upvalues[f->nups].info=cast_byte(v->u.s.info); +return f->nups++; +} +static int searchvar(FuncState*fs,TString*n){ +int i; +for(i=fs->nactvar-1;i>=0;i--){ +if(n==getlocvar(fs,i).varname) +return i; +} +return-1; +} +static void markupval(FuncState*fs,int level){ +BlockCnt*bl=fs->bl; +while(bl&&bl->nactvar>level)bl=bl->previous; +if(bl)bl->upval=1; +} +static int singlevaraux(FuncState*fs,TString*n,expdesc*var,int base){ +if(fs==NULL){ +init_exp(var,VGLOBAL,((1<<8)-1)); +return VGLOBAL; +} +else{ +int v=searchvar(fs,n); +if(v>=0){ +init_exp(var,VLOCAL,v); +if(!base) +markupval(fs,v); +return VLOCAL; +} +else{ +if(singlevaraux(fs->prev,n,var,0)==VGLOBAL) +return VGLOBAL; +var->u.s.info=indexupvalue(fs,n,var); +var->k=VUPVAL; +return VUPVAL; +} +} +} +static void singlevar(LexState*ls,expdesc*var){ +TString*varname=str_checkname(ls); +FuncState*fs=ls->fs; +if(singlevaraux(fs,varname,var,1)==VGLOBAL) +var->u.s.info=luaK_stringK(fs,varname); +} +static void adjust_assign(LexState*ls,int nvars,int nexps,expdesc*e){ +FuncState*fs=ls->fs; +int extra=nvars-nexps; +if(hasmultret(e->k)){ +extra++; +if(extra<0)extra=0; +luaK_setreturns(fs,e,extra); +if(extra>1)luaK_reserveregs(fs,extra-1); +} +else{ +if(e->k!=VVOID)luaK_exp2nextreg(fs,e); +if(extra>0){ +int reg=fs->freereg; +luaK_reserveregs(fs,extra); +luaK_nil(fs,reg,extra); +} +} +} +static void enterlevel(LexState*ls){ +if(++ls->L->nCcalls>200) +luaX_lexerror(ls,"chunk has too many syntax levels",0); +} +#define leavelevel(ls)((ls)->L->nCcalls--) +static void enterblock(FuncState*fs,BlockCnt*bl,lu_byte isbreakable){ +bl->breaklist=(-1); +bl->isbreakable=isbreakable; +bl->nactvar=fs->nactvar; +bl->upval=0; +bl->previous=fs->bl; +fs->bl=bl; +} +static void leaveblock(FuncState*fs){ +BlockCnt*bl=fs->bl; +fs->bl=bl->previous; +removevars(fs->ls,bl->nactvar); +if(bl->upval) +luaK_codeABC(fs,OP_CLOSE,bl->nactvar,0,0); +fs->freereg=fs->nactvar; +luaK_patchtohere(fs,bl->breaklist); +} +static void pushclosure(LexState*ls,FuncState*func,expdesc*v){ +FuncState*fs=ls->fs; +Proto*f=fs->f; +int oldsize=f->sizep; +int i; +luaM_growvector(ls->L,f->p,fs->np,f->sizep,Proto*, +((1<<(9+9))-1),"constant table overflow"); +while(oldsizesizep)f->p[oldsize++]=NULL; +f->p[fs->np++]=func->f; +luaC_objbarrier(ls->L,f,func->f); +init_exp(v,VRELOCABLE,luaK_codeABx(fs,OP_CLOSURE,0,fs->np-1)); +for(i=0;if->nups;i++){ +OpCode o=(func->upvalues[i].k==VLOCAL)?OP_MOVE:OP_GETUPVAL; +luaK_codeABC(fs,o,0,func->upvalues[i].info,0); +} +} +static void open_func(LexState*ls,FuncState*fs){ +lua_State*L=ls->L; +Proto*f=luaF_newproto(L); +fs->f=f; +fs->prev=ls->fs; +fs->ls=ls; +fs->L=L; +ls->fs=fs; +fs->pc=0; +fs->lasttarget=-1; +fs->jpc=(-1); +fs->freereg=0; +fs->nk=0; +fs->np=0; +fs->nlocvars=0; +fs->nactvar=0; +fs->bl=NULL; +f->source=ls->source; +f->maxstacksize=2; +fs->h=luaH_new(L,0,0); +sethvalue(L,L->top,fs->h); +incr_top(L); +setptvalue(L,L->top,f); +incr_top(L); +} +static void close_func(LexState*ls){ +lua_State*L=ls->L; +FuncState*fs=ls->fs; +Proto*f=fs->f; +removevars(ls,0); +luaK_ret(fs,0,0); +luaM_reallocvector(L,f->code,f->sizecode,fs->pc,Instruction); +f->sizecode=fs->pc; +luaM_reallocvector(L,f->lineinfo,f->sizelineinfo,fs->pc,int); +f->sizelineinfo=fs->pc; +luaM_reallocvector(L,f->k,f->sizek,fs->nk,TValue); +f->sizek=fs->nk; +luaM_reallocvector(L,f->p,f->sizep,fs->np,Proto*); +f->sizep=fs->np; +luaM_reallocvector(L,f->locvars,f->sizelocvars,fs->nlocvars,LocVar); +f->sizelocvars=fs->nlocvars; +luaM_reallocvector(L,f->upvalues,f->sizeupvalues,f->nups,TString*); +f->sizeupvalues=f->nups; +ls->fs=fs->prev; +if(fs)anchor_token(ls); +L->top-=2; +} +static Proto*luaY_parser(lua_State*L,ZIO*z,Mbuffer*buff,const char*name){ +struct LexState lexstate; +struct FuncState funcstate; +lexstate.buff=buff; +luaX_setinput(L,&lexstate,z,luaS_new(L,name)); +open_func(&lexstate,&funcstate); +funcstate.f->is_vararg=2; +luaX_next(&lexstate); +chunk(&lexstate); +check(&lexstate,TK_EOS); +close_func(&lexstate); +return funcstate.f; +} +static void field(LexState*ls,expdesc*v){ +FuncState*fs=ls->fs; +expdesc key; +luaK_exp2anyreg(fs,v); +luaX_next(ls); +checkname(ls,&key); +luaK_indexed(fs,v,&key); +} +static void yindex(LexState*ls,expdesc*v){ +luaX_next(ls); +expr(ls,v); +luaK_exp2val(ls->fs,v); +checknext(ls,']'); +} +struct ConsControl{ +expdesc v; +expdesc*t; +int nh; +int na; +int tostore; +}; +static void recfield(LexState*ls,struct ConsControl*cc){ +FuncState*fs=ls->fs; +int reg=ls->fs->freereg; +expdesc key,val; +int rkkey; +if(ls->t.token==TK_NAME){ +luaY_checklimit(fs,cc->nh,(INT_MAX-2),"items in a constructor"); +checkname(ls,&key); +} +else +yindex(ls,&key); +cc->nh++; +checknext(ls,'='); +rkkey=luaK_exp2RK(fs,&key); +expr(ls,&val); +luaK_codeABC(fs,OP_SETTABLE,cc->t->u.s.info,rkkey,luaK_exp2RK(fs,&val)); +fs->freereg=reg; +} +static void closelistfield(FuncState*fs,struct ConsControl*cc){ +if(cc->v.k==VVOID)return; +luaK_exp2nextreg(fs,&cc->v); +cc->v.k=VVOID; +if(cc->tostore==50){ +luaK_setlist(fs,cc->t->u.s.info,cc->na,cc->tostore); +cc->tostore=0; +} +} +static void lastlistfield(FuncState*fs,struct ConsControl*cc){ +if(cc->tostore==0)return; +if(hasmultret(cc->v.k)){ +luaK_setmultret(fs,&cc->v); +luaK_setlist(fs,cc->t->u.s.info,cc->na,(-1)); +cc->na--; +} +else{ +if(cc->v.k!=VVOID) +luaK_exp2nextreg(fs,&cc->v); +luaK_setlist(fs,cc->t->u.s.info,cc->na,cc->tostore); +} +} +static void listfield(LexState*ls,struct ConsControl*cc){ +expr(ls,&cc->v); +luaY_checklimit(ls->fs,cc->na,(INT_MAX-2),"items in a constructor"); +cc->na++; +cc->tostore++; +} +static void constructor(LexState*ls,expdesc*t){ +FuncState*fs=ls->fs; +int line=ls->linenumber; +int pc=luaK_codeABC(fs,OP_NEWTABLE,0,0,0); +struct ConsControl cc; +cc.na=cc.nh=cc.tostore=0; +cc.t=t; +init_exp(t,VRELOCABLE,pc); +init_exp(&cc.v,VVOID,0); +luaK_exp2nextreg(ls->fs,t); +checknext(ls,'{'); +do{ +if(ls->t.token=='}')break; +closelistfield(fs,&cc); +switch(ls->t.token){ +case TK_NAME:{ +luaX_lookahead(ls); +if(ls->lookahead.token!='=') +listfield(ls,&cc); +else +recfield(ls,&cc); +break; +} +case'[':{ +recfield(ls,&cc); +break; +} +default:{ +listfield(ls,&cc); +break; +} +} +}while(testnext(ls,',')||testnext(ls,';')); +check_match(ls,'}','{',line); +lastlistfield(fs,&cc); +SETARG_B(fs->f->code[pc],luaO_int2fb(cc.na)); +SETARG_C(fs->f->code[pc],luaO_int2fb(cc.nh)); +} +static void parlist(LexState*ls){ +FuncState*fs=ls->fs; +Proto*f=fs->f; +int nparams=0; +f->is_vararg=0; +if(ls->t.token!=')'){ +do{ +switch(ls->t.token){ +case TK_NAME:{ +new_localvar(ls,str_checkname(ls),nparams++); +break; +} +case TK_DOTS:{ +luaX_next(ls); +f->is_vararg|=2; +break; +} +default:luaX_syntaxerror(ls," or "LUA_QL("...")" expected"); +} +}while(!f->is_vararg&&testnext(ls,',')); +} +adjustlocalvars(ls,nparams); +f->numparams=cast_byte(fs->nactvar-(f->is_vararg&1)); +luaK_reserveregs(fs,fs->nactvar); +} +static void body(LexState*ls,expdesc*e,int needself,int line){ +FuncState new_fs; +open_func(ls,&new_fs); +new_fs.f->linedefined=line; +checknext(ls,'('); +if(needself){ +new_localvarliteral(ls,"self",0); +adjustlocalvars(ls,1); +} +parlist(ls); +checknext(ls,')'); +chunk(ls); +new_fs.f->lastlinedefined=ls->linenumber; +check_match(ls,TK_END,TK_FUNCTION,line); +close_func(ls); +pushclosure(ls,&new_fs,e); +} +static int explist1(LexState*ls,expdesc*v){ +int n=1; +expr(ls,v); +while(testnext(ls,',')){ +luaK_exp2nextreg(ls->fs,v); +expr(ls,v); +n++; +} +return n; +} +static void funcargs(LexState*ls,expdesc*f){ +FuncState*fs=ls->fs; +expdesc args; +int base,nparams; +int line=ls->linenumber; +switch(ls->t.token){ +case'(':{ +if(line!=ls->lastline) +luaX_syntaxerror(ls,"ambiguous syntax (function call x new statement)"); +luaX_next(ls); +if(ls->t.token==')') +args.k=VVOID; +else{ +explist1(ls,&args); +luaK_setmultret(fs,&args); +} +check_match(ls,')','(',line); +break; +} +case'{':{ +constructor(ls,&args); +break; +} +case TK_STRING:{ +codestring(ls,&args,ls->t.seminfo.ts); +luaX_next(ls); +break; +} +default:{ +luaX_syntaxerror(ls,"function arguments expected"); +return; +} +} +base=f->u.s.info; +if(hasmultret(args.k)) +nparams=(-1); +else{ +if(args.k!=VVOID) +luaK_exp2nextreg(fs,&args); +nparams=fs->freereg-(base+1); +} +init_exp(f,VCALL,luaK_codeABC(fs,OP_CALL,base,nparams+1,2)); +luaK_fixline(fs,line); +fs->freereg=base+1; +} +static void prefixexp(LexState*ls,expdesc*v){ +switch(ls->t.token){ +case'(':{ +int line=ls->linenumber; +luaX_next(ls); +expr(ls,v); +check_match(ls,')','(',line); +luaK_dischargevars(ls->fs,v); +return; +} +case TK_NAME:{ +singlevar(ls,v); +return; +} +default:{ +luaX_syntaxerror(ls,"unexpected symbol"); +return; +} +} +} +static void primaryexp(LexState*ls,expdesc*v){ +FuncState*fs=ls->fs; +prefixexp(ls,v); +for(;;){ +switch(ls->t.token){ +case'.':{ +field(ls,v); +break; +} +case'[':{ +expdesc key; +luaK_exp2anyreg(fs,v); +yindex(ls,&key); +luaK_indexed(fs,v,&key); +break; +} +case':':{ +expdesc key; +luaX_next(ls); +checkname(ls,&key); +luaK_self(fs,v,&key); +funcargs(ls,v); +break; +} +case'(':case TK_STRING:case'{':{ +luaK_exp2nextreg(fs,v); +funcargs(ls,v); +break; +} +default:return; +} +} +} +static void simpleexp(LexState*ls,expdesc*v){ +switch(ls->t.token){ +case TK_NUMBER:{ +init_exp(v,VKNUM,0); +v->u.nval=ls->t.seminfo.r; +break; +} +case TK_STRING:{ +codestring(ls,v,ls->t.seminfo.ts); +break; +} +case TK_NIL:{ +init_exp(v,VNIL,0); +break; +} +case TK_TRUE:{ +init_exp(v,VTRUE,0); +break; +} +case TK_FALSE:{ +init_exp(v,VFALSE,0); +break; +} +case TK_DOTS:{ +FuncState*fs=ls->fs; +check_condition(ls,fs->f->is_vararg, +"cannot use "LUA_QL("...")" outside a vararg function"); +fs->f->is_vararg&=~4; +init_exp(v,VVARARG,luaK_codeABC(fs,OP_VARARG,0,1,0)); +break; +} +case'{':{ +constructor(ls,v); +return; +} +case TK_FUNCTION:{ +luaX_next(ls); +body(ls,v,0,ls->linenumber); +return; +} +default:{ +primaryexp(ls,v); +return; +} +} +luaX_next(ls); +} +static UnOpr getunopr(int op){ +switch(op){ +case TK_NOT:return OPR_NOT; +case'-':return OPR_MINUS; +case'#':return OPR_LEN; +default:return OPR_NOUNOPR; +} +} +static BinOpr getbinopr(int op){ +switch(op){ +case'+':return OPR_ADD; +case'-':return OPR_SUB; +case'*':return OPR_MUL; +case'/':return OPR_DIV; +case'%':return OPR_MOD; +case'^':return OPR_POW; +case TK_CONCAT:return OPR_CONCAT; +case TK_NE:return OPR_NE; +case TK_EQ:return OPR_EQ; +case'<':return OPR_LT; +case TK_LE:return OPR_LE; +case'>':return OPR_GT; +case TK_GE:return OPR_GE; +case TK_AND:return OPR_AND; +case TK_OR:return OPR_OR; +default:return OPR_NOBINOPR; +} +} +static const struct{ +lu_byte left; +lu_byte right; +}priority[]={ +{6,6},{6,6},{7,7},{7,7},{7,7}, +{10,9},{5,4}, +{3,3},{3,3}, +{3,3},{3,3},{3,3},{3,3}, +{2,2},{1,1} +}; +static BinOpr subexpr(LexState*ls,expdesc*v,unsigned int limit){ +BinOpr op; +UnOpr uop; +enterlevel(ls); +uop=getunopr(ls->t.token); +if(uop!=OPR_NOUNOPR){ +luaX_next(ls); +subexpr(ls,v,8); +luaK_prefix(ls->fs,uop,v); +} +else simpleexp(ls,v); +op=getbinopr(ls->t.token); +while(op!=OPR_NOBINOPR&&priority[op].left>limit){ +expdesc v2; +BinOpr nextop; +luaX_next(ls); +luaK_infix(ls->fs,op,v); +nextop=subexpr(ls,&v2,priority[op].right); +luaK_posfix(ls->fs,op,v,&v2); +op=nextop; +} +leavelevel(ls); +return op; +} +static void expr(LexState*ls,expdesc*v){ +subexpr(ls,v,0); +} +static int block_follow(int token){ +switch(token){ +case TK_ELSE:case TK_ELSEIF:case TK_END: +case TK_UNTIL:case TK_EOS: +return 1; +default:return 0; +} +} +static void block(LexState*ls){ +FuncState*fs=ls->fs; +BlockCnt bl; +enterblock(fs,&bl,0); +chunk(ls); +leaveblock(fs); +} +struct LHS_assign{ +struct LHS_assign*prev; +expdesc v; +}; +static void check_conflict(LexState*ls,struct LHS_assign*lh,expdesc*v){ +FuncState*fs=ls->fs; +int extra=fs->freereg; +int conflict=0; +for(;lh;lh=lh->prev){ +if(lh->v.k==VINDEXED){ +if(lh->v.u.s.info==v->u.s.info){ +conflict=1; +lh->v.u.s.info=extra; +} +if(lh->v.u.s.aux==v->u.s.info){ +conflict=1; +lh->v.u.s.aux=extra; +} +} +} +if(conflict){ +luaK_codeABC(fs,OP_MOVE,fs->freereg,v->u.s.info,0); +luaK_reserveregs(fs,1); +} +} +static void assignment(LexState*ls,struct LHS_assign*lh,int nvars){ +expdesc e; +check_condition(ls,VLOCAL<=lh->v.k&&lh->v.k<=VINDEXED, +"syntax error"); +if(testnext(ls,',')){ +struct LHS_assign nv; +nv.prev=lh; +primaryexp(ls,&nv.v); +if(nv.v.k==VLOCAL) +check_conflict(ls,lh,&nv.v); +luaY_checklimit(ls->fs,nvars,200-ls->L->nCcalls, +"variables in assignment"); +assignment(ls,&nv,nvars+1); +} +else{ +int nexps; +checknext(ls,'='); +nexps=explist1(ls,&e); +if(nexps!=nvars){ +adjust_assign(ls,nvars,nexps,&e); +if(nexps>nvars) +ls->fs->freereg-=nexps-nvars; +} +else{ +luaK_setoneret(ls->fs,&e); +luaK_storevar(ls->fs,&lh->v,&e); +return; +} +} +init_exp(&e,VNONRELOC,ls->fs->freereg-1); +luaK_storevar(ls->fs,&lh->v,&e); +} +static int cond(LexState*ls){ +expdesc v; +expr(ls,&v); +if(v.k==VNIL)v.k=VFALSE; +luaK_goiftrue(ls->fs,&v); +return v.f; +} +static void breakstat(LexState*ls){ +FuncState*fs=ls->fs; +BlockCnt*bl=fs->bl; +int upval=0; +while(bl&&!bl->isbreakable){ +upval|=bl->upval; +bl=bl->previous; +} +if(!bl) +luaX_syntaxerror(ls,"no loop to break"); +if(upval) +luaK_codeABC(fs,OP_CLOSE,bl->nactvar,0,0); +luaK_concat(fs,&bl->breaklist,luaK_jump(fs)); +} +static void whilestat(LexState*ls,int line){ +FuncState*fs=ls->fs; +int whileinit; +int condexit; +BlockCnt bl; +luaX_next(ls); +whileinit=luaK_getlabel(fs); +condexit=cond(ls); +enterblock(fs,&bl,1); +checknext(ls,TK_DO); +block(ls); +luaK_patchlist(fs,luaK_jump(fs),whileinit); +check_match(ls,TK_END,TK_WHILE,line); +leaveblock(fs); +luaK_patchtohere(fs,condexit); +} +static void repeatstat(LexState*ls,int line){ +int condexit; +FuncState*fs=ls->fs; +int repeat_init=luaK_getlabel(fs); +BlockCnt bl1,bl2; +enterblock(fs,&bl1,1); +enterblock(fs,&bl2,0); +luaX_next(ls); +chunk(ls); +check_match(ls,TK_UNTIL,TK_REPEAT,line); +condexit=cond(ls); +if(!bl2.upval){ +leaveblock(fs); +luaK_patchlist(ls->fs,condexit,repeat_init); +} +else{ +breakstat(ls); +luaK_patchtohere(ls->fs,condexit); +leaveblock(fs); +luaK_patchlist(ls->fs,luaK_jump(fs),repeat_init); +} +leaveblock(fs); +} +static int exp1(LexState*ls){ +expdesc e; +int k; +expr(ls,&e); +k=e.k; +luaK_exp2nextreg(ls->fs,&e); +return k; +} +static void forbody(LexState*ls,int base,int line,int nvars,int isnum){ +BlockCnt bl; +FuncState*fs=ls->fs; +int prep,endfor; +adjustlocalvars(ls,3); +checknext(ls,TK_DO); +prep=isnum?luaK_codeAsBx(fs,OP_FORPREP,base,(-1)):luaK_jump(fs); +enterblock(fs,&bl,0); +adjustlocalvars(ls,nvars); +luaK_reserveregs(fs,nvars); +block(ls); +leaveblock(fs); +luaK_patchtohere(fs,prep); +endfor=(isnum)?luaK_codeAsBx(fs,OP_FORLOOP,base,(-1)): +luaK_codeABC(fs,OP_TFORLOOP,base,0,nvars); +luaK_fixline(fs,line); +luaK_patchlist(fs,(isnum?endfor:luaK_jump(fs)),prep+1); +} +static void fornum(LexState*ls,TString*varname,int line){ +FuncState*fs=ls->fs; +int base=fs->freereg; +new_localvarliteral(ls,"(for index)",0); +new_localvarliteral(ls,"(for limit)",1); +new_localvarliteral(ls,"(for step)",2); +new_localvar(ls,varname,3); +checknext(ls,'='); +exp1(ls); +checknext(ls,','); +exp1(ls); +if(testnext(ls,',')) +exp1(ls); +else{ +luaK_codeABx(fs,OP_LOADK,fs->freereg,luaK_numberK(fs,1)); +luaK_reserveregs(fs,1); +} +forbody(ls,base,line,1,1); +} +static void forlist(LexState*ls,TString*indexname){ +FuncState*fs=ls->fs; +expdesc e; +int nvars=0; +int line; +int base=fs->freereg; +new_localvarliteral(ls,"(for generator)",nvars++); +new_localvarliteral(ls,"(for state)",nvars++); +new_localvarliteral(ls,"(for control)",nvars++); +new_localvar(ls,indexname,nvars++); +while(testnext(ls,',')) +new_localvar(ls,str_checkname(ls),nvars++); +checknext(ls,TK_IN); +line=ls->linenumber; +adjust_assign(ls,3,explist1(ls,&e),&e); +luaK_checkstack(fs,3); +forbody(ls,base,line,nvars-3,0); +} +static void forstat(LexState*ls,int line){ +FuncState*fs=ls->fs; +TString*varname; +BlockCnt bl; +enterblock(fs,&bl,1); +luaX_next(ls); +varname=str_checkname(ls); +switch(ls->t.token){ +case'=':fornum(ls,varname,line);break; +case',':case TK_IN:forlist(ls,varname);break; +default:luaX_syntaxerror(ls,LUA_QL("=")" or "LUA_QL("in")" expected"); +} +check_match(ls,TK_END,TK_FOR,line); +leaveblock(fs); +} +static int test_then_block(LexState*ls){ +int condexit; +luaX_next(ls); +condexit=cond(ls); +checknext(ls,TK_THEN); +block(ls); +return condexit; +} +static void ifstat(LexState*ls,int line){ +FuncState*fs=ls->fs; +int flist; +int escapelist=(-1); +flist=test_then_block(ls); +while(ls->t.token==TK_ELSEIF){ +luaK_concat(fs,&escapelist,luaK_jump(fs)); +luaK_patchtohere(fs,flist); +flist=test_then_block(ls); +} +if(ls->t.token==TK_ELSE){ +luaK_concat(fs,&escapelist,luaK_jump(fs)); +luaK_patchtohere(fs,flist); +luaX_next(ls); +block(ls); +} +else +luaK_concat(fs,&escapelist,flist); +luaK_patchtohere(fs,escapelist); +check_match(ls,TK_END,TK_IF,line); +} +static void localfunc(LexState*ls){ +expdesc v,b; +FuncState*fs=ls->fs; +new_localvar(ls,str_checkname(ls),0); +init_exp(&v,VLOCAL,fs->freereg); +luaK_reserveregs(fs,1); +adjustlocalvars(ls,1); +body(ls,&b,0,ls->linenumber); +luaK_storevar(fs,&v,&b); +getlocvar(fs,fs->nactvar-1).startpc=fs->pc; +} +static void localstat(LexState*ls){ +int nvars=0; +int nexps; +expdesc e; +do{ +new_localvar(ls,str_checkname(ls),nvars++); +}while(testnext(ls,',')); +if(testnext(ls,'=')) +nexps=explist1(ls,&e); +else{ +e.k=VVOID; +nexps=0; +} +adjust_assign(ls,nvars,nexps,&e); +adjustlocalvars(ls,nvars); +} +static int funcname(LexState*ls,expdesc*v){ +int needself=0; +singlevar(ls,v); +while(ls->t.token=='.') +field(ls,v); +if(ls->t.token==':'){ +needself=1; +field(ls,v); +} +return needself; +} +static void funcstat(LexState*ls,int line){ +int needself; +expdesc v,b; +luaX_next(ls); +needself=funcname(ls,&v); +body(ls,&b,needself,line); +luaK_storevar(ls->fs,&v,&b); +luaK_fixline(ls->fs,line); +} +static void exprstat(LexState*ls){ +FuncState*fs=ls->fs; +struct LHS_assign v; +primaryexp(ls,&v.v); +if(v.v.k==VCALL) +SETARG_C(getcode(fs,&v.v),1); +else{ +v.prev=NULL; +assignment(ls,&v,1); +} +} +static void retstat(LexState*ls){ +FuncState*fs=ls->fs; +expdesc e; +int first,nret; +luaX_next(ls); +if(block_follow(ls->t.token)||ls->t.token==';') +first=nret=0; +else{ +nret=explist1(ls,&e); +if(hasmultret(e.k)){ +luaK_setmultret(fs,&e); +if(e.k==VCALL&&nret==1){ +SET_OPCODE(getcode(fs,&e),OP_TAILCALL); +} +first=fs->nactvar; +nret=(-1); +} +else{ +if(nret==1) +first=luaK_exp2anyreg(fs,&e); +else{ +luaK_exp2nextreg(fs,&e); +first=fs->nactvar; +} +} +} +luaK_ret(fs,first,nret); +} +static int statement(LexState*ls){ +int line=ls->linenumber; +switch(ls->t.token){ +case TK_IF:{ +ifstat(ls,line); +return 0; +} +case TK_WHILE:{ +whilestat(ls,line); +return 0; +} +case TK_DO:{ +luaX_next(ls); +block(ls); +check_match(ls,TK_END,TK_DO,line); +return 0; +} +case TK_FOR:{ +forstat(ls,line); +return 0; +} +case TK_REPEAT:{ +repeatstat(ls,line); +return 0; +} +case TK_FUNCTION:{ +funcstat(ls,line); +return 0; +} +case TK_LOCAL:{ +luaX_next(ls); +if(testnext(ls,TK_FUNCTION)) +localfunc(ls); +else +localstat(ls); +return 0; +} +case TK_RETURN:{ +retstat(ls); +return 1; +} +case TK_BREAK:{ +luaX_next(ls); +breakstat(ls); +return 1; +} +default:{ +exprstat(ls); +return 0; +} +} +} +static void chunk(LexState*ls){ +int islast=0; +enterlevel(ls); +while(!islast&&!block_follow(ls->t.token)){ +islast=statement(ls); +testnext(ls,';'); +ls->fs->freereg=ls->fs->nactvar; +} +leavelevel(ls); +} +static const TValue*luaV_tonumber(const TValue*obj,TValue*n){ +lua_Number num; +if(ttisnumber(obj))return obj; +if(ttisstring(obj)&&luaO_str2d(svalue(obj),&num)){ +setnvalue(n,num); +return n; +} +else +return NULL; +} +static int luaV_tostring(lua_State*L,StkId obj){ +if(!ttisnumber(obj)) +return 0; +else{ +char s[32]; +lua_Number n=nvalue(obj); +lua_number2str(s,n); +setsvalue(L,obj,luaS_new(L,s)); +return 1; +} +} +static void callTMres(lua_State*L,StkId res,const TValue*f, +const TValue*p1,const TValue*p2){ +ptrdiff_t result=savestack(L,res); +setobj(L,L->top,f); +setobj(L,L->top+1,p1); +setobj(L,L->top+2,p2); +luaD_checkstack(L,3); +L->top+=3; +luaD_call(L,L->top-3,1); +res=restorestack(L,result); +L->top--; +setobj(L,res,L->top); +} +static void callTM(lua_State*L,const TValue*f,const TValue*p1, +const TValue*p2,const TValue*p3){ +setobj(L,L->top,f); +setobj(L,L->top+1,p1); +setobj(L,L->top+2,p2); +setobj(L,L->top+3,p3); +luaD_checkstack(L,4); +L->top+=4; +luaD_call(L,L->top-4,0); +} +static void luaV_gettable(lua_State*L,const TValue*t,TValue*key,StkId val){ +int loop; +for(loop=0;loop<100;loop++){ +const TValue*tm; +if(ttistable(t)){ +Table*h=hvalue(t); +const TValue*res=luaH_get(h,key); +if(!ttisnil(res)|| +(tm=fasttm(L,h->metatable,TM_INDEX))==NULL){ +setobj(L,val,res); +return; +} +} +else if(ttisnil(tm=luaT_gettmbyobj(L,t,TM_INDEX))) +luaG_typeerror(L,t,"index"); +if(ttisfunction(tm)){ +callTMres(L,val,tm,t,key); +return; +} +t=tm; +} +luaG_runerror(L,"loop in gettable"); +} +static void luaV_settable(lua_State*L,const TValue*t,TValue*key,StkId val){ +int loop; +TValue temp; +for(loop=0;loop<100;loop++){ +const TValue*tm; +if(ttistable(t)){ +Table*h=hvalue(t); +TValue*oldval=luaH_set(L,h,key); +if(!ttisnil(oldval)|| +(tm=fasttm(L,h->metatable,TM_NEWINDEX))==NULL){ +setobj(L,oldval,val); +h->flags=0; +luaC_barriert(L,h,val); +return; +} +} +else if(ttisnil(tm=luaT_gettmbyobj(L,t,TM_NEWINDEX))) +luaG_typeerror(L,t,"index"); +if(ttisfunction(tm)){ +callTM(L,tm,t,key,val); +return; +} +setobj(L,&temp,tm); +t=&temp; +} +luaG_runerror(L,"loop in settable"); +} +static int call_binTM(lua_State*L,const TValue*p1,const TValue*p2, +StkId res,TMS event){ +const TValue*tm=luaT_gettmbyobj(L,p1,event); +if(ttisnil(tm)) +tm=luaT_gettmbyobj(L,p2,event); +if(ttisnil(tm))return 0; +callTMres(L,res,tm,p1,p2); +return 1; +} +static const TValue*get_compTM(lua_State*L,Table*mt1,Table*mt2, +TMS event){ +const TValue*tm1=fasttm(L,mt1,event); +const TValue*tm2; +if(tm1==NULL)return NULL; +if(mt1==mt2)return tm1; +tm2=fasttm(L,mt2,event); +if(tm2==NULL)return NULL; +if(luaO_rawequalObj(tm1,tm2)) +return tm1; +return NULL; +} +static int call_orderTM(lua_State*L,const TValue*p1,const TValue*p2, +TMS event){ +const TValue*tm1=luaT_gettmbyobj(L,p1,event); +const TValue*tm2; +if(ttisnil(tm1))return-1; +tm2=luaT_gettmbyobj(L,p2,event); +if(!luaO_rawequalObj(tm1,tm2)) +return-1; +callTMres(L,L->top,tm1,p1,p2); +return!l_isfalse(L->top); +} +static int l_strcmp(const TString*ls,const TString*rs){ +const char*l=getstr(ls); +size_t ll=ls->tsv.len; +const char*r=getstr(rs); +size_t lr=rs->tsv.len; +for(;;){ +int temp=strcoll(l,r); +if(temp!=0)return temp; +else{ +size_t len=strlen(l); +if(len==lr) +return(len==ll)?0:1; +else if(len==ll) +return-1; +len++; +l+=len;ll-=len;r+=len;lr-=len; +} +} +} +static int luaV_lessthan(lua_State*L,const TValue*l,const TValue*r){ +int res; +if(ttype(l)!=ttype(r)) +return luaG_ordererror(L,l,r); +else if(ttisnumber(l)) +return luai_numlt(nvalue(l),nvalue(r)); +else if(ttisstring(l)) +return l_strcmp(rawtsvalue(l),rawtsvalue(r))<0; +else if((res=call_orderTM(L,l,r,TM_LT))!=-1) +return res; +return luaG_ordererror(L,l,r); +} +static int lessequal(lua_State*L,const TValue*l,const TValue*r){ +int res; +if(ttype(l)!=ttype(r)) +return luaG_ordererror(L,l,r); +else if(ttisnumber(l)) +return luai_numle(nvalue(l),nvalue(r)); +else if(ttisstring(l)) +return l_strcmp(rawtsvalue(l),rawtsvalue(r))<=0; +else if((res=call_orderTM(L,l,r,TM_LE))!=-1) +return res; +else if((res=call_orderTM(L,r,l,TM_LT))!=-1) +return!res; +return luaG_ordererror(L,l,r); +} +static int luaV_equalval(lua_State*L,const TValue*t1,const TValue*t2){ +const TValue*tm; +switch(ttype(t1)){ +case 0:return 1; +case 3:return luai_numeq(nvalue(t1),nvalue(t2)); +case 1:return bvalue(t1)==bvalue(t2); +case 2:return pvalue(t1)==pvalue(t2); +case 7:{ +if(uvalue(t1)==uvalue(t2))return 1; +tm=get_compTM(L,uvalue(t1)->metatable,uvalue(t2)->metatable, +TM_EQ); +break; +} +case 5:{ +if(hvalue(t1)==hvalue(t2))return 1; +tm=get_compTM(L,hvalue(t1)->metatable,hvalue(t2)->metatable,TM_EQ); +break; +} +default:return gcvalue(t1)==gcvalue(t2); +} +if(tm==NULL)return 0; +callTMres(L,L->top,tm,t1,t2); +return!l_isfalse(L->top); +} +static void luaV_concat(lua_State*L,int total,int last){ +do{ +StkId top=L->base+last+1; +int n=2; +if(!(ttisstring(top-2)||ttisnumber(top-2))||!tostring(L,top-1)){ +if(!call_binTM(L,top-2,top-1,top-2,TM_CONCAT)) +luaG_concaterror(L,top-2,top-1); +}else if(tsvalue(top-1)->len==0) +(void)tostring(L,top-2); +else{ +size_t tl=tsvalue(top-1)->len; +char*buffer; +int i; +for(n=1;nlen; +if(l>=((size_t)(~(size_t)0)-2)-tl)luaG_runerror(L,"string length overflow"); +tl+=l; +} +buffer=luaZ_openspace(L,&G(L)->buff,tl); +tl=0; +for(i=n;i>0;i--){ +size_t l=tsvalue(top-i)->len; +memcpy(buffer+tl,svalue(top-i),l); +tl+=l; +} +setsvalue(L,top-n,luaS_newlstr(L,buffer,tl)); +} +total-=n-1; +last-=n-1; +}while(total>1); +} +static void Arith(lua_State*L,StkId ra,const TValue*rb, +const TValue*rc,TMS op){ +TValue tempb,tempc; +const TValue*b,*c; +if((b=luaV_tonumber(rb,&tempb))!=NULL&& +(c=luaV_tonumber(rc,&tempc))!=NULL){ +lua_Number nb=nvalue(b),nc=nvalue(c); +switch(op){ +case TM_ADD:setnvalue(ra,luai_numadd(nb,nc));break; +case TM_SUB:setnvalue(ra,luai_numsub(nb,nc));break; +case TM_MUL:setnvalue(ra,luai_nummul(nb,nc));break; +case TM_DIV:setnvalue(ra,luai_numdiv(nb,nc));break; +case TM_MOD:setnvalue(ra,luai_nummod(nb,nc));break; +case TM_POW:setnvalue(ra,luai_numpow(nb,nc));break; +case TM_UNM:setnvalue(ra,luai_numunm(nb));break; +default:break; +} +} +else if(!call_binTM(L,rb,rc,ra,op)) +luaG_aritherror(L,rb,rc); +} +#define runtime_check(L,c){if(!(c))break;} +#define RA(i)(base+GETARG_A(i)) +#define RB(i)check_exp(getBMode(GET_OPCODE(i))==OpArgR,base+GETARG_B(i)) +#define RKB(i)check_exp(getBMode(GET_OPCODE(i))==OpArgK,ISK(GETARG_B(i))?k+INDEXK(GETARG_B(i)):base+GETARG_B(i)) +#define RKC(i)check_exp(getCMode(GET_OPCODE(i))==OpArgK,ISK(GETARG_C(i))?k+INDEXK(GETARG_C(i)):base+GETARG_C(i)) +#define KBx(i)check_exp(getBMode(GET_OPCODE(i))==OpArgK,k+GETARG_Bx(i)) +#define dojump(L,pc,i){(pc)+=(i);} +#define Protect(x){L->savedpc=pc;{x;};base=L->base;} +#define arith_op(op,tm){TValue*rb=RKB(i);TValue*rc=RKC(i);if(ttisnumber(rb)&&ttisnumber(rc)){lua_Number nb=nvalue(rb),nc=nvalue(rc);setnvalue(ra,op(nb,nc));}else Protect(Arith(L,ra,rb,rc,tm));} +static void luaV_execute(lua_State*L,int nexeccalls){ +LClosure*cl; +StkId base; +TValue*k; +const Instruction*pc; +reentry: +pc=L->savedpc; +cl=&clvalue(L->ci->func)->l; +base=L->base; +k=cl->p->k; +for(;;){ +const Instruction i=*pc++; +StkId ra; +ra=RA(i); +switch(GET_OPCODE(i)){ +case OP_MOVE:{ +setobj(L,ra,RB(i)); +continue; +} +case OP_LOADK:{ +setobj(L,ra,KBx(i)); +continue; +} +case OP_LOADBOOL:{ +setbvalue(ra,GETARG_B(i)); +if(GETARG_C(i))pc++; +continue; +} +case OP_LOADNIL:{ +TValue*rb=RB(i); +do{ +setnilvalue(rb--); +}while(rb>=ra); +continue; +} +case OP_GETUPVAL:{ +int b=GETARG_B(i); +setobj(L,ra,cl->upvals[b]->v); +continue; +} +case OP_GETGLOBAL:{ +TValue g; +TValue*rb=KBx(i); +sethvalue(L,&g,cl->env); +Protect(luaV_gettable(L,&g,rb,ra)); +continue; +} +case OP_GETTABLE:{ +Protect(luaV_gettable(L,RB(i),RKC(i),ra)); +continue; +} +case OP_SETGLOBAL:{ +TValue g; +sethvalue(L,&g,cl->env); +Protect(luaV_settable(L,&g,KBx(i),ra)); +continue; +} +case OP_SETUPVAL:{ +UpVal*uv=cl->upvals[GETARG_B(i)]; +setobj(L,uv->v,ra); +luaC_barrier(L,uv,ra); +continue; +} +case OP_SETTABLE:{ +Protect(luaV_settable(L,ra,RKB(i),RKC(i))); +continue; +} +case OP_NEWTABLE:{ +int b=GETARG_B(i); +int c=GETARG_C(i); +sethvalue(L,ra,luaH_new(L,luaO_fb2int(b),luaO_fb2int(c))); +Protect(luaC_checkGC(L)); +continue; +} +case OP_SELF:{ +StkId rb=RB(i); +setobj(L,ra+1,rb); +Protect(luaV_gettable(L,rb,RKC(i),ra)); +continue; +} +case OP_ADD:{ +arith_op(luai_numadd,TM_ADD); +continue; +} +case OP_SUB:{ +arith_op(luai_numsub,TM_SUB); +continue; +} +case OP_MUL:{ +arith_op(luai_nummul,TM_MUL); +continue; +} +case OP_DIV:{ +arith_op(luai_numdiv,TM_DIV); +continue; +} +case OP_MOD:{ +arith_op(luai_nummod,TM_MOD); +continue; +} +case OP_POW:{ +arith_op(luai_numpow,TM_POW); +continue; +} +case OP_UNM:{ +TValue*rb=RB(i); +if(ttisnumber(rb)){ +lua_Number nb=nvalue(rb); +setnvalue(ra,luai_numunm(nb)); +} +else{ +Protect(Arith(L,ra,rb,rb,TM_UNM)); +} +continue; +} +case OP_NOT:{ +int res=l_isfalse(RB(i)); +setbvalue(ra,res); +continue; +} +case OP_LEN:{ +const TValue*rb=RB(i); +switch(ttype(rb)){ +case 5:{ +setnvalue(ra,cast_num(luaH_getn(hvalue(rb)))); +break; +} +case 4:{ +setnvalue(ra,cast_num(tsvalue(rb)->len)); +break; +} +default:{ +Protect( +if(!call_binTM(L,rb,(&luaO_nilobject_),ra,TM_LEN)) +luaG_typeerror(L,rb,"get length of"); +) +} +} +continue; +} +case OP_CONCAT:{ +int b=GETARG_B(i); +int c=GETARG_C(i); +Protect(luaV_concat(L,c-b+1,c);luaC_checkGC(L)); +setobj(L,RA(i),base+b); +continue; +} +case OP_JMP:{ +dojump(L,pc,GETARG_sBx(i)); +continue; +} +case OP_EQ:{ +TValue*rb=RKB(i); +TValue*rc=RKC(i); +Protect( +if(equalobj(L,rb,rc)==GETARG_A(i)) +dojump(L,pc,GETARG_sBx(*pc)); +) +pc++; +continue; +} +case OP_LT:{ +Protect( +if(luaV_lessthan(L,RKB(i),RKC(i))==GETARG_A(i)) +dojump(L,pc,GETARG_sBx(*pc)); +) +pc++; +continue; +} +case OP_LE:{ +Protect( +if(lessequal(L,RKB(i),RKC(i))==GETARG_A(i)) +dojump(L,pc,GETARG_sBx(*pc)); +) +pc++; +continue; +} +case OP_TEST:{ +if(l_isfalse(ra)!=GETARG_C(i)) +dojump(L,pc,GETARG_sBx(*pc)); +pc++; +continue; +} +case OP_TESTSET:{ +TValue*rb=RB(i); +if(l_isfalse(rb)!=GETARG_C(i)){ +setobj(L,ra,rb); +dojump(L,pc,GETARG_sBx(*pc)); +} +pc++; +continue; +} +case OP_CALL:{ +int b=GETARG_B(i); +int nresults=GETARG_C(i)-1; +if(b!=0)L->top=ra+b; +L->savedpc=pc; +switch(luaD_precall(L,ra,nresults)){ +case 0:{ +nexeccalls++; +goto reentry; +} +case 1:{ +if(nresults>=0)L->top=L->ci->top; +base=L->base; +continue; +} +default:{ +return; +} +} +} +case OP_TAILCALL:{ +int b=GETARG_B(i); +if(b!=0)L->top=ra+b; +L->savedpc=pc; +switch(luaD_precall(L,ra,(-1))){ +case 0:{ +CallInfo*ci=L->ci-1; +int aux; +StkId func=ci->func; +StkId pfunc=(ci+1)->func; +if(L->openupval)luaF_close(L,ci->base); +L->base=ci->base=ci->func+((ci+1)->base-pfunc); +for(aux=0;pfunc+auxtop;aux++) +setobj(L,func+aux,pfunc+aux); +ci->top=L->top=func+aux; +ci->savedpc=L->savedpc; +ci->tailcalls++; +L->ci--; +goto reentry; +} +case 1:{ +base=L->base; +continue; +} +default:{ +return; +} +} +} +case OP_RETURN:{ +int b=GETARG_B(i); +if(b!=0)L->top=ra+b-1; +if(L->openupval)luaF_close(L,base); +L->savedpc=pc; +b=luaD_poscall(L,ra); +if(--nexeccalls==0) +return; +else{ +if(b)L->top=L->ci->top; +goto reentry; +} +} +case OP_FORLOOP:{ +lua_Number step=nvalue(ra+2); +lua_Number idx=luai_numadd(nvalue(ra),step); +lua_Number limit=nvalue(ra+1); +if(luai_numlt(0,step)?luai_numle(idx,limit) +:luai_numle(limit,idx)){ +dojump(L,pc,GETARG_sBx(i)); +setnvalue(ra,idx); +setnvalue(ra+3,idx); +} +continue; +} +case OP_FORPREP:{ +const TValue*init=ra; +const TValue*plimit=ra+1; +const TValue*pstep=ra+2; +L->savedpc=pc; +if(!tonumber(init,ra)) +luaG_runerror(L,LUA_QL("for")" initial value must be a number"); +else if(!tonumber(plimit,ra+1)) +luaG_runerror(L,LUA_QL("for")" limit must be a number"); +else if(!tonumber(pstep,ra+2)) +luaG_runerror(L,LUA_QL("for")" step must be a number"); +setnvalue(ra,luai_numsub(nvalue(ra),nvalue(pstep))); +dojump(L,pc,GETARG_sBx(i)); +continue; +} +case OP_TFORLOOP:{ +StkId cb=ra+3; +setobj(L,cb+2,ra+2); +setobj(L,cb+1,ra+1); +setobj(L,cb,ra); +L->top=cb+3; +Protect(luaD_call(L,cb,GETARG_C(i))); +L->top=L->ci->top; +cb=RA(i)+3; +if(!ttisnil(cb)){ +setobj(L,cb-1,cb); +dojump(L,pc,GETARG_sBx(*pc)); +} +pc++; +continue; +} +case OP_SETLIST:{ +int n=GETARG_B(i); +int c=GETARG_C(i); +int last; +Table*h; +if(n==0){ +n=cast_int(L->top-ra)-1; +L->top=L->ci->top; +} +if(c==0)c=cast_int(*pc++); +runtime_check(L,ttistable(ra)); +h=hvalue(ra); +last=((c-1)*50)+n; +if(last>h->sizearray) +luaH_resizearray(L,h,last); +for(;n>0;n--){ +TValue*val=ra+n; +setobj(L,luaH_setnum(L,h,last--),val); +luaC_barriert(L,h,val); +} +continue; +} +case OP_CLOSE:{ +luaF_close(L,ra); +continue; +} +case OP_CLOSURE:{ +Proto*p; +Closure*ncl; +int nup,j; +p=cl->p->p[GETARG_Bx(i)]; +nup=p->nups; +ncl=luaF_newLclosure(L,nup,cl->env); +ncl->l.p=p; +for(j=0;jl.upvals[j]=cl->upvals[GETARG_B(*pc)]; +else{ +ncl->l.upvals[j]=luaF_findupval(L,base+GETARG_B(*pc)); +} +} +setclvalue(L,ra,ncl); +Protect(luaC_checkGC(L)); +continue; +} +case OP_VARARG:{ +int b=GETARG_B(i)-1; +int j; +CallInfo*ci=L->ci; +int n=cast_int(ci->base-ci->func)-cl->p->numparams-1; +if(b==(-1)){ +Protect(luaD_checkstack(L,n)); +ra=RA(i); +b=n; +L->top=ra+n; +} +for(j=0;jbase-n+j); +} +else{ +setnilvalue(ra+j); +} +} +continue; +} +} +} +} +#define api_checknelems(L,n)luai_apicheck(L,(n)<=(L->top-L->base)) +#define api_checkvalidindex(L,i)luai_apicheck(L,(i)!=(&luaO_nilobject_)) +#define api_incr_top(L){luai_apicheck(L,L->topci->top);L->top++;} +static TValue*index2adr(lua_State*L,int idx){ +if(idx>0){ +TValue*o=L->base+(idx-1); +luai_apicheck(L,idx<=L->ci->top-L->base); +if(o>=L->top)return cast(TValue*,(&luaO_nilobject_)); +else return o; +} +else if(idx>(-10000)){ +luai_apicheck(L,idx!=0&&-idx<=L->top-L->base); +return L->top+idx; +} +else switch(idx){ +case(-10000):return registry(L); +case(-10001):{ +Closure*func=curr_func(L); +sethvalue(L,&L->env,func->c.env); +return&L->env; +} +case(-10002):return gt(L); +default:{ +Closure*func=curr_func(L); +idx=(-10002)-idx; +return(idx<=func->c.nupvalues) +?&func->c.upvalue[idx-1] +:cast(TValue*,(&luaO_nilobject_)); +} +} +} +static Table*getcurrenv(lua_State*L){ +if(L->ci==L->base_ci) +return hvalue(gt(L)); +else{ +Closure*func=curr_func(L); +return func->c.env; +} +} +static int lua_checkstack(lua_State*L,int size){ +int res=1; +if(size>8000||(L->top-L->base+size)>8000) +res=0; +else if(size>0){ +luaD_checkstack(L,size); +if(L->ci->toptop+size) +L->ci->top=L->top+size; +} +return res; +} +static lua_CFunction lua_atpanic(lua_State*L,lua_CFunction panicf){ +lua_CFunction old; +old=G(L)->panic; +G(L)->panic=panicf; +return old; +} +static int lua_gettop(lua_State*L){ +return cast_int(L->top-L->base); +} +static void lua_settop(lua_State*L,int idx){ +if(idx>=0){ +luai_apicheck(L,idx<=L->stack_last-L->base); +while(L->topbase+idx) +setnilvalue(L->top++); +L->top=L->base+idx; +} +else{ +luai_apicheck(L,-(idx+1)<=(L->top-L->base)); +L->top+=idx+1; +} +} +static void lua_remove(lua_State*L,int idx){ +StkId p; +p=index2adr(L,idx); +api_checkvalidindex(L,p); +while(++ptop)setobj(L,p-1,p); +L->top--; +} +static void lua_insert(lua_State*L,int idx){ +StkId p; +StkId q; +p=index2adr(L,idx); +api_checkvalidindex(L,p); +for(q=L->top;q>p;q--)setobj(L,q,q-1); +setobj(L,p,L->top); +} +static void lua_replace(lua_State*L,int idx){ +StkId o; +if(idx==(-10001)&&L->ci==L->base_ci) +luaG_runerror(L,"no calling environment"); +api_checknelems(L,1); +o=index2adr(L,idx); +api_checkvalidindex(L,o); +if(idx==(-10001)){ +Closure*func=curr_func(L); +luai_apicheck(L,ttistable(L->top-1)); +func->c.env=hvalue(L->top-1); +luaC_barrier(L,func,L->top-1); +} +else{ +setobj(L,o,L->top-1); +if(idx<(-10002)) +luaC_barrier(L,curr_func(L),L->top-1); +} +L->top--; +} +static void lua_pushvalue(lua_State*L,int idx){ +setobj(L,L->top,index2adr(L,idx)); +api_incr_top(L); +} +static int lua_type(lua_State*L,int idx){ +StkId o=index2adr(L,idx); +return(o==(&luaO_nilobject_))?(-1):ttype(o); +} +static const char*lua_typename(lua_State*L,int t){ +UNUSED(L); +return(t==(-1))?"no value":luaT_typenames[t]; +} +static int lua_iscfunction(lua_State*L,int idx){ +StkId o=index2adr(L,idx); +return iscfunction(o); +} +static int lua_isnumber(lua_State*L,int idx){ +TValue n; +const TValue*o=index2adr(L,idx); +return tonumber(o,&n); +} +static int lua_isstring(lua_State*L,int idx){ +int t=lua_type(L,idx); +return(t==4||t==3); +} +static int lua_rawequal(lua_State*L,int index1,int index2){ +StkId o1=index2adr(L,index1); +StkId o2=index2adr(L,index2); +return(o1==(&luaO_nilobject_)||o2==(&luaO_nilobject_))?0 +:luaO_rawequalObj(o1,o2); +} +static int lua_lessthan(lua_State*L,int index1,int index2){ +StkId o1,o2; +int i; +o1=index2adr(L,index1); +o2=index2adr(L,index2); +i=(o1==(&luaO_nilobject_)||o2==(&luaO_nilobject_))?0 +:luaV_lessthan(L,o1,o2); +return i; +} +static lua_Number lua_tonumber(lua_State*L,int idx){ +TValue n; +const TValue*o=index2adr(L,idx); +if(tonumber(o,&n)) +return nvalue(o); +else +return 0; +} +static lua_Integer lua_tointeger(lua_State*L,int idx){ +TValue n; +const TValue*o=index2adr(L,idx); +if(tonumber(o,&n)){ +lua_Integer res; +lua_Number num=nvalue(o); +lua_number2integer(res,num); +return res; +} +else +return 0; +} +static int lua_toboolean(lua_State*L,int idx){ +const TValue*o=index2adr(L,idx); +return!l_isfalse(o); +} +static const char*lua_tolstring(lua_State*L,int idx,size_t*len){ +StkId o=index2adr(L,idx); +if(!ttisstring(o)){ +if(!luaV_tostring(L,o)){ +if(len!=NULL)*len=0; +return NULL; +} +luaC_checkGC(L); +o=index2adr(L,idx); +} +if(len!=NULL)*len=tsvalue(o)->len; +return svalue(o); +} +static size_t lua_objlen(lua_State*L,int idx){ +StkId o=index2adr(L,idx); +switch(ttype(o)){ +case 4:return tsvalue(o)->len; +case 7:return uvalue(o)->len; +case 5:return luaH_getn(hvalue(o)); +case 3:{ +size_t l; +l=(luaV_tostring(L,o)?tsvalue(o)->len:0); +return l; +} +default:return 0; +} +} +static lua_CFunction lua_tocfunction(lua_State*L,int idx){ +StkId o=index2adr(L,idx); +return(!iscfunction(o))?NULL:clvalue(o)->c.f; +} +static void*lua_touserdata(lua_State*L,int idx){ +StkId o=index2adr(L,idx); +switch(ttype(o)){ +case 7:return(rawuvalue(o)+1); +case 2:return pvalue(o); +default:return NULL; +} +} +static void lua_pushnil(lua_State*L){ +setnilvalue(L->top); +api_incr_top(L); +} +static void lua_pushnumber(lua_State*L,lua_Number n){ +setnvalue(L->top,n); +api_incr_top(L); +} +static void lua_pushinteger(lua_State*L,lua_Integer n){ +setnvalue(L->top,cast_num(n)); +api_incr_top(L); +} +static void lua_pushlstring(lua_State*L,const char*s,size_t len){ +luaC_checkGC(L); +setsvalue(L,L->top,luaS_newlstr(L,s,len)); +api_incr_top(L); +} +static void lua_pushstring(lua_State*L,const char*s){ +if(s==NULL) +lua_pushnil(L); +else +lua_pushlstring(L,s,strlen(s)); +} +static const char*lua_pushvfstring(lua_State*L,const char*fmt, +va_list argp){ +const char*ret; +luaC_checkGC(L); +ret=luaO_pushvfstring(L,fmt,argp); +return ret; +} +static const char*lua_pushfstring(lua_State*L,const char*fmt,...){ +const char*ret; +va_list argp; +luaC_checkGC(L); +va_start(argp,fmt); +ret=luaO_pushvfstring(L,fmt,argp); +va_end(argp); +return ret; +} +static void lua_pushcclosure(lua_State*L,lua_CFunction fn,int n){ +Closure*cl; +luaC_checkGC(L); +api_checknelems(L,n); +cl=luaF_newCclosure(L,n,getcurrenv(L)); +cl->c.f=fn; +L->top-=n; +while(n--) +setobj(L,&cl->c.upvalue[n],L->top+n); +setclvalue(L,L->top,cl); +api_incr_top(L); +} +static void lua_pushboolean(lua_State*L,int b){ +setbvalue(L->top,(b!=0)); +api_incr_top(L); +} +static int lua_pushthread(lua_State*L){ +setthvalue(L,L->top,L); +api_incr_top(L); +return(G(L)->mainthread==L); +} +static void lua_gettable(lua_State*L,int idx){ +StkId t; +t=index2adr(L,idx); +api_checkvalidindex(L,t); +luaV_gettable(L,t,L->top-1,L->top-1); +} +static void lua_getfield(lua_State*L,int idx,const char*k){ +StkId t; +TValue key; +t=index2adr(L,idx); +api_checkvalidindex(L,t); +setsvalue(L,&key,luaS_new(L,k)); +luaV_gettable(L,t,&key,L->top); +api_incr_top(L); +} +static void lua_rawget(lua_State*L,int idx){ +StkId t; +t=index2adr(L,idx); +luai_apicheck(L,ttistable(t)); +setobj(L,L->top-1,luaH_get(hvalue(t),L->top-1)); +} +static void lua_rawgeti(lua_State*L,int idx,int n){ +StkId o; +o=index2adr(L,idx); +luai_apicheck(L,ttistable(o)); +setobj(L,L->top,luaH_getnum(hvalue(o),n)); +api_incr_top(L); +} +static void lua_createtable(lua_State*L,int narray,int nrec){ +luaC_checkGC(L); +sethvalue(L,L->top,luaH_new(L,narray,nrec)); +api_incr_top(L); +} +static int lua_getmetatable(lua_State*L,int objindex){ +const TValue*obj; +Table*mt=NULL; +int res; +obj=index2adr(L,objindex); +switch(ttype(obj)){ +case 5: +mt=hvalue(obj)->metatable; +break; +case 7: +mt=uvalue(obj)->metatable; +break; +default: +mt=G(L)->mt[ttype(obj)]; +break; +} +if(mt==NULL) +res=0; +else{ +sethvalue(L,L->top,mt); +api_incr_top(L); +res=1; +} +return res; +} +static void lua_getfenv(lua_State*L,int idx){ +StkId o; +o=index2adr(L,idx); +api_checkvalidindex(L,o); +switch(ttype(o)){ +case 6: +sethvalue(L,L->top,clvalue(o)->c.env); +break; +case 7: +sethvalue(L,L->top,uvalue(o)->env); +break; +case 8: +setobj(L,L->top,gt(thvalue(o))); +break; +default: +setnilvalue(L->top); +break; +} +api_incr_top(L); +} +static void lua_settable(lua_State*L,int idx){ +StkId t; +api_checknelems(L,2); +t=index2adr(L,idx); +api_checkvalidindex(L,t); +luaV_settable(L,t,L->top-2,L->top-1); +L->top-=2; +} +static void lua_setfield(lua_State*L,int idx,const char*k){ +StkId t; +TValue key; +api_checknelems(L,1); +t=index2adr(L,idx); +api_checkvalidindex(L,t); +setsvalue(L,&key,luaS_new(L,k)); +luaV_settable(L,t,&key,L->top-1); +L->top--; +} +static void lua_rawset(lua_State*L,int idx){ +StkId t; +api_checknelems(L,2); +t=index2adr(L,idx); +luai_apicheck(L,ttistable(t)); +setobj(L,luaH_set(L,hvalue(t),L->top-2),L->top-1); +luaC_barriert(L,hvalue(t),L->top-1); +L->top-=2; +} +static void lua_rawseti(lua_State*L,int idx,int n){ +StkId o; +api_checknelems(L,1); +o=index2adr(L,idx); +luai_apicheck(L,ttistable(o)); +setobj(L,luaH_setnum(L,hvalue(o),n),L->top-1); +luaC_barriert(L,hvalue(o),L->top-1); +L->top--; +} +static int lua_setmetatable(lua_State*L,int objindex){ +TValue*obj; +Table*mt; +api_checknelems(L,1); +obj=index2adr(L,objindex); +api_checkvalidindex(L,obj); +if(ttisnil(L->top-1)) +mt=NULL; +else{ +luai_apicheck(L,ttistable(L->top-1)); +mt=hvalue(L->top-1); +} +switch(ttype(obj)){ +case 5:{ +hvalue(obj)->metatable=mt; +if(mt) +luaC_objbarriert(L,hvalue(obj),mt); +break; +} +case 7:{ +uvalue(obj)->metatable=mt; +if(mt) +luaC_objbarrier(L,rawuvalue(obj),mt); +break; +} +default:{ +G(L)->mt[ttype(obj)]=mt; +break; +} +} +L->top--; +return 1; +} +static int lua_setfenv(lua_State*L,int idx){ +StkId o; +int res=1; +api_checknelems(L,1); +o=index2adr(L,idx); +api_checkvalidindex(L,o); +luai_apicheck(L,ttistable(L->top-1)); +switch(ttype(o)){ +case 6: +clvalue(o)->c.env=hvalue(L->top-1); +break; +case 7: +uvalue(o)->env=hvalue(L->top-1); +break; +case 8: +sethvalue(L,gt(thvalue(o)),hvalue(L->top-1)); +break; +default: +res=0; +break; +} +if(res)luaC_objbarrier(L,gcvalue(o),hvalue(L->top-1)); +L->top--; +return res; +} +#define adjustresults(L,nres){if(nres==(-1)&&L->top>=L->ci->top)L->ci->top=L->top;} +#define checkresults(L,na,nr)luai_apicheck(L,(nr)==(-1)||(L->ci->top-L->top>=(nr)-(na))) +static void lua_call(lua_State*L,int nargs,int nresults){ +StkId func; +api_checknelems(L,nargs+1); +checkresults(L,nargs,nresults); +func=L->top-(nargs+1); +luaD_call(L,func,nresults); +adjustresults(L,nresults); +} +struct CallS{ +StkId func; +int nresults; +}; +static void f_call(lua_State*L,void*ud){ +struct CallS*c=cast(struct CallS*,ud); +luaD_call(L,c->func,c->nresults); +} +static int lua_pcall(lua_State*L,int nargs,int nresults,int errfunc){ +struct CallS c; +int status; +ptrdiff_t func; +api_checknelems(L,nargs+1); +checkresults(L,nargs,nresults); +if(errfunc==0) +func=0; +else{ +StkId o=index2adr(L,errfunc); +api_checkvalidindex(L,o); +func=savestack(L,o); +} +c.func=L->top-(nargs+1); +c.nresults=nresults; +status=luaD_pcall(L,f_call,&c,savestack(L,c.func),func); +adjustresults(L,nresults); +return status; +} +static int lua_load(lua_State*L,lua_Reader reader,void*data, +const char*chunkname){ +ZIO z; +int status; +if(!chunkname)chunkname="?"; +luaZ_init(L,&z,reader,data); +status=luaD_protectedparser(L,&z,chunkname); +return status; +} +static int lua_error(lua_State*L){ +api_checknelems(L,1); +luaG_errormsg(L); +return 0; +} +static int lua_next(lua_State*L,int idx){ +StkId t; +int more; +t=index2adr(L,idx); +luai_apicheck(L,ttistable(t)); +more=luaH_next(L,hvalue(t),L->top-1); +if(more){ +api_incr_top(L); +} +else +L->top-=1; +return more; +} +static void lua_concat(lua_State*L,int n){ +api_checknelems(L,n); +if(n>=2){ +luaC_checkGC(L); +luaV_concat(L,n,cast_int(L->top-L->base)-1); +L->top-=(n-1); +} +else if(n==0){ +setsvalue(L,L->top,luaS_newlstr(L,"",0)); +api_incr_top(L); +} +} +static void*lua_newuserdata(lua_State*L,size_t size){ +Udata*u; +luaC_checkGC(L); +u=luaS_newudata(L,size,getcurrenv(L)); +setuvalue(L,L->top,u); +api_incr_top(L); +return u+1; +} +#define luaL_getn(L,i)((int)lua_objlen(L,i)) +#define luaL_setn(L,i,j)((void)0) +typedef struct luaL_Reg{ +const char*name; +lua_CFunction func; +}luaL_Reg; +static void luaI_openlib(lua_State*L,const char*libname, +const luaL_Reg*l,int nup); +static int luaL_argerror(lua_State*L,int numarg,const char*extramsg); +static const char* luaL_checklstring(lua_State*L,int numArg, +size_t*l); +static const char* luaL_optlstring(lua_State*L,int numArg, +const char*def,size_t*l); +static lua_Integer luaL_checkinteger(lua_State*L,int numArg); +static lua_Integer luaL_optinteger(lua_State*L,int nArg, +lua_Integer def); +static int luaL_error(lua_State*L,const char*fmt,...); +static const char* luaL_findtable(lua_State*L,int idx, +const char*fname,int szhint); +#define luaL_argcheck(L,cond,numarg,extramsg)((void)((cond)||luaL_argerror(L,(numarg),(extramsg)))) +#define luaL_checkstring(L,n)(luaL_checklstring(L,(n),NULL)) +#define luaL_optstring(L,n,d)(luaL_optlstring(L,(n),(d),NULL)) +#define luaL_checkint(L,n)((int)luaL_checkinteger(L,(n))) +#define luaL_optint(L,n,d)((int)luaL_optinteger(L,(n),(d))) +#define luaL_typename(L,i)lua_typename(L,lua_type(L,(i))) +#define luaL_getmetatable(L,n)(lua_getfield(L,(-10000),(n))) +#define luaL_opt(L,f,n,d)(lua_isnoneornil(L,(n))?(d):f(L,(n))) +typedef struct luaL_Buffer{ +char*p; +int lvl; +lua_State*L; +char buffer[BUFSIZ]; +}luaL_Buffer; +#define luaL_addchar(B,c)((void)((B)->p<((B)->buffer+BUFSIZ)||luaL_prepbuffer(B)),(*(B)->p++=(char)(c))) +#define luaL_addsize(B,n)((B)->p+=(n)) +static char* luaL_prepbuffer(luaL_Buffer*B); +static int luaL_argerror(lua_State*L,int narg,const char*extramsg){ +lua_Debug ar; +if(!lua_getstack(L,0,&ar)) +return luaL_error(L,"bad argument #%d (%s)",narg,extramsg); +lua_getinfo(L,"n",&ar); +if(strcmp(ar.namewhat,"method")==0){ +narg--; +if(narg==0) +return luaL_error(L,"calling "LUA_QL("%s")" on bad self (%s)", +ar.name,extramsg); +} +if(ar.name==NULL) +ar.name="?"; +return luaL_error(L,"bad argument #%d to "LUA_QL("%s")" (%s)", +narg,ar.name,extramsg); +} +static int luaL_typerror(lua_State*L,int narg,const char*tname){ +const char*msg=lua_pushfstring(L,"%s expected, got %s", +tname,luaL_typename(L,narg)); +return luaL_argerror(L,narg,msg); +} +static void tag_error(lua_State*L,int narg,int tag){ +luaL_typerror(L,narg,lua_typename(L,tag)); +} +static void luaL_where(lua_State*L,int level){ +lua_Debug ar; +if(lua_getstack(L,level,&ar)){ +lua_getinfo(L,"Sl",&ar); +if(ar.currentline>0){ +lua_pushfstring(L,"%s:%d: ",ar.short_src,ar.currentline); +return; +} +} +lua_pushliteral(L,""); +} +static int luaL_error(lua_State*L,const char*fmt,...){ +va_list argp; +va_start(argp,fmt); +luaL_where(L,1); +lua_pushvfstring(L,fmt,argp); +va_end(argp); +lua_concat(L,2); +return lua_error(L); +} +static int luaL_newmetatable(lua_State*L,const char*tname){ +lua_getfield(L,(-10000),tname); +if(!lua_isnil(L,-1)) +return 0; +lua_pop(L,1); +lua_newtable(L); +lua_pushvalue(L,-1); +lua_setfield(L,(-10000),tname); +return 1; +} +static void*luaL_checkudata(lua_State*L,int ud,const char*tname){ +void*p=lua_touserdata(L,ud); +if(p!=NULL){ +if(lua_getmetatable(L,ud)){ +lua_getfield(L,(-10000),tname); +if(lua_rawequal(L,-1,-2)){ +lua_pop(L,2); +return p; +} +} +} +luaL_typerror(L,ud,tname); +return NULL; +} +static void luaL_checkstack(lua_State*L,int space,const char*mes){ +if(!lua_checkstack(L,space)) +luaL_error(L,"stack overflow (%s)",mes); +} +static void luaL_checktype(lua_State*L,int narg,int t){ +if(lua_type(L,narg)!=t) +tag_error(L,narg,t); +} +static void luaL_checkany(lua_State*L,int narg){ +if(lua_type(L,narg)==(-1)) +luaL_argerror(L,narg,"value expected"); +} +static const char*luaL_checklstring(lua_State*L,int narg,size_t*len){ +const char*s=lua_tolstring(L,narg,len); +if(!s)tag_error(L,narg,4); +return s; +} +static const char*luaL_optlstring(lua_State*L,int narg, +const char*def,size_t*len){ +if(lua_isnoneornil(L,narg)){ +if(len) +*len=(def?strlen(def):0); +return def; +} +else return luaL_checklstring(L,narg,len); +} +static lua_Number luaL_checknumber(lua_State*L,int narg){ +lua_Number d=lua_tonumber(L,narg); +if(d==0&&!lua_isnumber(L,narg)) +tag_error(L,narg,3); +return d; +} +static lua_Integer luaL_checkinteger(lua_State*L,int narg){ +lua_Integer d=lua_tointeger(L,narg); +if(d==0&&!lua_isnumber(L,narg)) +tag_error(L,narg,3); +return d; +} +static lua_Integer luaL_optinteger(lua_State*L,int narg, +lua_Integer def){ +return luaL_opt(L,luaL_checkinteger,narg,def); +} +static int luaL_getmetafield(lua_State*L,int obj,const char*event){ +if(!lua_getmetatable(L,obj)) +return 0; +lua_pushstring(L,event); +lua_rawget(L,-2); +if(lua_isnil(L,-1)){ +lua_pop(L,2); +return 0; +} +else{ +lua_remove(L,-2); +return 1; +} +} +static void luaL_register(lua_State*L,const char*libname, +const luaL_Reg*l){ +luaI_openlib(L,libname,l,0); +} +static int libsize(const luaL_Reg*l){ +int size=0; +for(;l->name;l++)size++; +return size; +} +static void luaI_openlib(lua_State*L,const char*libname, +const luaL_Reg*l,int nup){ +if(libname){ +int size=libsize(l); +luaL_findtable(L,(-10000),"_LOADED",1); +lua_getfield(L,-1,libname); +if(!lua_istable(L,-1)){ +lua_pop(L,1); +if(luaL_findtable(L,(-10002),libname,size)!=NULL) +luaL_error(L,"name conflict for module "LUA_QL("%s"),libname); +lua_pushvalue(L,-1); +lua_setfield(L,-3,libname); +} +lua_remove(L,-2); +lua_insert(L,-(nup+1)); +} +for(;l->name;l++){ +int i; +for(i=0;ifunc,nup); +lua_setfield(L,-(nup+2),l->name); +} +lua_pop(L,nup); +} +static const char*luaL_findtable(lua_State*L,int idx, +const char*fname,int szhint){ +const char*e; +lua_pushvalue(L,idx); +do{ +e=strchr(fname,'.'); +if(e==NULL)e=fname+strlen(fname); +lua_pushlstring(L,fname,e-fname); +lua_rawget(L,-2); +if(lua_isnil(L,-1)){ +lua_pop(L,1); +lua_createtable(L,0,(*e=='.'?1:szhint)); +lua_pushlstring(L,fname,e-fname); +lua_pushvalue(L,-2); +lua_settable(L,-4); +} +else if(!lua_istable(L,-1)){ +lua_pop(L,2); +return fname; +} +lua_remove(L,-2); +fname=e+1; +}while(*e=='.'); +return NULL; +} +#define bufflen(B)((B)->p-(B)->buffer) +#define bufffree(B)((size_t)(BUFSIZ-bufflen(B))) +static int emptybuffer(luaL_Buffer*B){ +size_t l=bufflen(B); +if(l==0)return 0; +else{ +lua_pushlstring(B->L,B->buffer,l); +B->p=B->buffer; +B->lvl++; +return 1; +} +} +static void adjuststack(luaL_Buffer*B){ +if(B->lvl>1){ +lua_State*L=B->L; +int toget=1; +size_t toplen=lua_strlen(L,-1); +do{ +size_t l=lua_strlen(L,-(toget+1)); +if(B->lvl-toget+1>=(20/2)||toplen>l){ +toplen+=l; +toget++; +} +else break; +}while(togetlvl); +lua_concat(L,toget); +B->lvl=B->lvl-toget+1; +} +} +static char*luaL_prepbuffer(luaL_Buffer*B){ +if(emptybuffer(B)) +adjuststack(B); +return B->buffer; +} +static void luaL_addlstring(luaL_Buffer*B,const char*s,size_t l){ +while(l--) +luaL_addchar(B,*s++); +} +static void luaL_pushresult(luaL_Buffer*B){ +emptybuffer(B); +lua_concat(B->L,B->lvl); +B->lvl=1; +} +static void luaL_addvalue(luaL_Buffer*B){ +lua_State*L=B->L; +size_t vl; +const char*s=lua_tolstring(L,-1,&vl); +if(vl<=bufffree(B)){ +memcpy(B->p,s,vl); +B->p+=vl; +lua_pop(L,1); +} +else{ +if(emptybuffer(B)) +lua_insert(L,-2); +B->lvl++; +adjuststack(B); +} +} +static void luaL_buffinit(lua_State*L,luaL_Buffer*B){ +B->L=L; +B->p=B->buffer; +B->lvl=0; +} +typedef struct LoadF{ +int extraline; +FILE*f; +char buff[BUFSIZ]; +}LoadF; +static const char*getF(lua_State*L,void*ud,size_t*size){ +LoadF*lf=(LoadF*)ud; +(void)L; +if(lf->extraline){ +lf->extraline=0; +*size=1; +return"\n"; +} +if(feof(lf->f))return NULL; +*size=fread(lf->buff,1,sizeof(lf->buff),lf->f); +return(*size>0)?lf->buff:NULL; +} +static int errfile(lua_State*L,const char*what,int fnameindex){ +const char*serr=strerror(errno); +const char*filename=lua_tostring(L,fnameindex)+1; +lua_pushfstring(L,"cannot %s %s: %s",what,filename,serr); +lua_remove(L,fnameindex); +return(5+1); +} +static int luaL_loadfile(lua_State*L,const char*filename){ +LoadF lf; +int status,readstatus; +int c; +int fnameindex=lua_gettop(L)+1; +lf.extraline=0; +if(filename==NULL){ +lua_pushliteral(L,"=stdin"); +lf.f=stdin; +} +else{ +lua_pushfstring(L,"@%s",filename); +lf.f=fopen(filename,"r"); +if(lf.f==NULL)return errfile(L,"open",fnameindex); +} +c=getc(lf.f); +if(c=='#'){ +lf.extraline=1; +while((c=getc(lf.f))!=EOF&&c!='\n'); +if(c=='\n')c=getc(lf.f); +} +if(c=="\033Lua"[0]&&filename){ +lf.f=freopen(filename,"rb",lf.f); +if(lf.f==NULL)return errfile(L,"reopen",fnameindex); +while((c=getc(lf.f))!=EOF&&c!="\033Lua"[0]); +lf.extraline=0; +} +ungetc(c,lf.f); +status=lua_load(L,getF,&lf,lua_tostring(L,-1)); +readstatus=ferror(lf.f); +if(filename)fclose(lf.f); +if(readstatus){ +lua_settop(L,fnameindex); +return errfile(L,"read",fnameindex); +} +lua_remove(L,fnameindex); +return status; +} +typedef struct LoadS{ +const char*s; +size_t size; +}LoadS; +static const char*getS(lua_State*L,void*ud,size_t*size){ +LoadS*ls=(LoadS*)ud; +(void)L; +if(ls->size==0)return NULL; +*size=ls->size; +ls->size=0; +return ls->s; +} +static int luaL_loadbuffer(lua_State*L,const char*buff,size_t size, +const char*name){ +LoadS ls; +ls.s=buff; +ls.size=size; +return lua_load(L,getS,&ls,name); +} +static void*l_alloc(void*ud,void*ptr,size_t osize,size_t nsize){ +(void)ud; +(void)osize; +if(nsize==0){ +free(ptr); +return NULL; +} +else +return realloc(ptr,nsize); +} +static int panic(lua_State*L){ +(void)L; +fprintf(stderr,"PANIC: unprotected error in call to Lua API (%s)\n", +lua_tostring(L,-1)); +return 0; +} +static lua_State*luaL_newstate(void){ +lua_State*L=lua_newstate(l_alloc,NULL); +if(L)lua_atpanic(L,&panic); +return L; +} +static int luaB_tonumber(lua_State*L){ +int base=luaL_optint(L,2,10); +if(base==10){ +luaL_checkany(L,1); +if(lua_isnumber(L,1)){ +lua_pushnumber(L,lua_tonumber(L,1)); +return 1; +} +} +else{ +const char*s1=luaL_checkstring(L,1); +char*s2; +unsigned long n; +luaL_argcheck(L,2<=base&&base<=36,2,"base out of range"); +n=strtoul(s1,&s2,base); +if(s1!=s2){ +while(isspace((unsigned char)(*s2)))s2++; +if(*s2=='\0'){ +lua_pushnumber(L,(lua_Number)n); +return 1; +} +} +} +lua_pushnil(L); +return 1; +} +static int luaB_error(lua_State*L){ +int level=luaL_optint(L,2,1); +lua_settop(L,1); +if(lua_isstring(L,1)&&level>0){ +luaL_where(L,level); +lua_pushvalue(L,1); +lua_concat(L,2); +} +return lua_error(L); +} +static int luaB_setmetatable(lua_State*L){ +int t=lua_type(L,2); +luaL_checktype(L,1,5); +luaL_argcheck(L,t==0||t==5,2, +"nil or table expected"); +if(luaL_getmetafield(L,1,"__metatable")) +luaL_error(L,"cannot change a protected metatable"); +lua_settop(L,2); +lua_setmetatable(L,1); +return 1; +} +static void getfunc(lua_State*L,int opt){ +if(lua_isfunction(L,1))lua_pushvalue(L,1); +else{ +lua_Debug ar; +int level=opt?luaL_optint(L,1,1):luaL_checkint(L,1); +luaL_argcheck(L,level>=0,1,"level must be non-negative"); +if(lua_getstack(L,level,&ar)==0) +luaL_argerror(L,1,"invalid level"); +lua_getinfo(L,"f",&ar); +if(lua_isnil(L,-1)) +luaL_error(L,"no function environment for tail call at level %d", +level); +} +} +static int luaB_setfenv(lua_State*L){ +luaL_checktype(L,2,5); +getfunc(L,0); +lua_pushvalue(L,2); +if(lua_isnumber(L,1)&&lua_tonumber(L,1)==0){ +lua_pushthread(L); +lua_insert(L,-2); +lua_setfenv(L,-2); +return 0; +} +else if(lua_iscfunction(L,-2)||lua_setfenv(L,-2)==0) +luaL_error(L, +LUA_QL("setfenv")" cannot change environment of given object"); +return 1; +} +static int luaB_rawget(lua_State*L){ +luaL_checktype(L,1,5); +luaL_checkany(L,2); +lua_settop(L,2); +lua_rawget(L,1); +return 1; +} +static int luaB_type(lua_State*L){ +luaL_checkany(L,1); +lua_pushstring(L,luaL_typename(L,1)); +return 1; +} +static int luaB_next(lua_State*L){ +luaL_checktype(L,1,5); +lua_settop(L,2); +if(lua_next(L,1)) +return 2; +else{ +lua_pushnil(L); +return 1; +} +} +static int luaB_pairs(lua_State*L){ +luaL_checktype(L,1,5); +lua_pushvalue(L,lua_upvalueindex(1)); +lua_pushvalue(L,1); +lua_pushnil(L); +return 3; +} +static int ipairsaux(lua_State*L){ +int i=luaL_checkint(L,2); +luaL_checktype(L,1,5); +i++; +lua_pushinteger(L,i); +lua_rawgeti(L,1,i); +return(lua_isnil(L,-1))?0:2; +} +static int luaB_ipairs(lua_State*L){ +luaL_checktype(L,1,5); +lua_pushvalue(L,lua_upvalueindex(1)); +lua_pushvalue(L,1); +lua_pushinteger(L,0); +return 3; +} +static int load_aux(lua_State*L,int status){ +if(status==0) +return 1; +else{ +lua_pushnil(L); +lua_insert(L,-2); +return 2; +} +} +static int luaB_loadstring(lua_State*L){ +size_t l; +const char*s=luaL_checklstring(L,1,&l); +const char*chunkname=luaL_optstring(L,2,s); +return load_aux(L,luaL_loadbuffer(L,s,l,chunkname)); +} +static int luaB_loadfile(lua_State*L){ +const char*fname=luaL_optstring(L,1,NULL); +return load_aux(L,luaL_loadfile(L,fname)); +} +static int luaB_assert(lua_State*L){ +luaL_checkany(L,1); +if(!lua_toboolean(L,1)) +return luaL_error(L,"%s",luaL_optstring(L,2,"assertion failed!")); +return lua_gettop(L); +} +static int luaB_unpack(lua_State*L){ +int i,e,n; +luaL_checktype(L,1,5); +i=luaL_optint(L,2,1); +e=luaL_opt(L,luaL_checkint,3,luaL_getn(L,1)); +if(i>e)return 0; +n=e-i+1; +if(n<=0||!lua_checkstack(L,n)) +return luaL_error(L,"too many results to unpack"); +lua_rawgeti(L,1,i); +while(i++e)e=pos; +for(i=e;i>pos;i--){ +lua_rawgeti(L,1,i-1); +lua_rawseti(L,1,i); +} +break; +} +default:{ +return luaL_error(L,"wrong number of arguments to "LUA_QL("insert")); +} +} +luaL_setn(L,1,e); +lua_rawseti(L,1,pos); +return 0; +} +static int tremove(lua_State*L){ +int e=aux_getn(L,1); +int pos=luaL_optint(L,2,e); +if(!(1<=pos&&pos<=e)) +return 0; +luaL_setn(L,1,e-1); +lua_rawgeti(L,1,pos); +for(;posu)luaL_error(L,"invalid order function for sorting"); +lua_pop(L,1); +} +while(lua_rawgeti(L,1,--j),sort_comp(L,-3,-1)){ +if(j0); +} +l=strlen(p); +if(l==0||p[l-1]!='\n') +luaL_addsize(&b,l); +else{ +luaL_addsize(&b,l-1); +luaL_pushresult(&b); +return 1; +} +} +} +static int read_chars(lua_State*L,FILE*f,size_t n){ +size_t rlen; +size_t nr; +luaL_Buffer b; +luaL_buffinit(L,&b); +rlen=BUFSIZ; +do{ +char*p=luaL_prepbuffer(&b); +if(rlen>n)rlen=n; +nr=fread(p,sizeof(char),rlen,f); +luaL_addsize(&b,nr); +n-=nr; +}while(n>0&&nr==rlen); +luaL_pushresult(&b); +return(n==0||lua_objlen(L,-1)>0); +} +static int g_read(lua_State*L,FILE*f,int first){ +int nargs=lua_gettop(L)-1; +int success; +int n; +clearerr(f); +if(nargs==0){ +success=read_line(L,f); +n=first+1; +} +else{ +luaL_checkstack(L,nargs+20,"too many arguments"); +success=1; +for(n=first;nargs--&&success;n++){ +if(lua_type(L,n)==3){ +size_t l=(size_t)lua_tointeger(L,n); +success=(l==0)?test_eof(L,f):read_chars(L,f,l); +} +else{ +const char*p=lua_tostring(L,n); +luaL_argcheck(L,p&&p[0]=='*',n,"invalid option"); +switch(p[1]){ +case'n': +success=read_number(L,f); +break; +case'l': +success=read_line(L,f); +break; +case'a': +read_chars(L,f,~((size_t)0)); +success=1; +break; +default: +return luaL_argerror(L,n,"invalid format"); +} +} +} +} +if(ferror(f)) +return pushresult(L,0,NULL); +if(!success){ +lua_pop(L,1); +lua_pushnil(L); +} +return n-first; +} +static int io_read(lua_State*L){ +return g_read(L,getiofile(L,1),1); +} +static int f_read(lua_State*L){ +return g_read(L,tofile(L),2); +} +static int io_readline(lua_State*L){ +FILE*f=*(FILE**)lua_touserdata(L,lua_upvalueindex(1)); +int sucess; +if(f==NULL) +luaL_error(L,"file is already closed"); +sucess=read_line(L,f); +if(ferror(f)) +return luaL_error(L,"%s",strerror(errno)); +if(sucess)return 1; +else{ +if(lua_toboolean(L,lua_upvalueindex(2))){ +lua_settop(L,0); +lua_pushvalue(L,lua_upvalueindex(1)); +aux_close(L); +} +return 0; +} +} +static int g_write(lua_State*L,FILE*f,int arg){ +int nargs=lua_gettop(L)-1; +int status=1; +for(;nargs--;arg++){ +if(lua_type(L,arg)==3){ +status=status&& +fprintf(f,"%.14g",lua_tonumber(L,arg))>0; +} +else{ +size_t l; +const char*s=luaL_checklstring(L,arg,&l); +status=status&&(fwrite(s,sizeof(char),l,f)==l); +} +} +return pushresult(L,status,NULL); +} +static int io_write(lua_State*L){ +return g_write(L,getiofile(L,2),1); +} +static int f_write(lua_State*L){ +return g_write(L,tofile(L),2); +} +static int io_flush(lua_State*L){ +return pushresult(L,fflush(getiofile(L,2))==0,NULL); +} +static int f_flush(lua_State*L){ +return pushresult(L,fflush(tofile(L))==0,NULL); +} +static const luaL_Reg iolib[]={ +{"close",io_close}, +{"flush",io_flush}, +{"input",io_input}, +{"lines",io_lines}, +{"open",io_open}, +{"output",io_output}, +{"read",io_read}, +{"type",io_type}, +{"write",io_write}, +{NULL,NULL} +}; +static const luaL_Reg flib[]={ +{"close",io_close}, +{"flush",f_flush}, +{"lines",f_lines}, +{"read",f_read}, +{"write",f_write}, +{"__gc",io_gc}, +{NULL,NULL} +}; +static void createmeta(lua_State*L){ +luaL_newmetatable(L,"FILE*"); +lua_pushvalue(L,-1); +lua_setfield(L,-2,"__index"); +luaL_register(L,NULL,flib); +} +static void createstdfile(lua_State*L,FILE*f,int k,const char*fname){ +*newfile(L)=f; +if(k>0){ +lua_pushvalue(L,-1); +lua_rawseti(L,(-10001),k); +} +lua_pushvalue(L,-2); +lua_setfenv(L,-2); +lua_setfield(L,-3,fname); +} +static void newfenv(lua_State*L,lua_CFunction cls){ +lua_createtable(L,0,1); +lua_pushcfunction(L,cls); +lua_setfield(L,-2,"__close"); +} +static int luaopen_io(lua_State*L){ +createmeta(L); +newfenv(L,io_fclose); +lua_replace(L,(-10001)); +luaL_register(L,"io",iolib); +newfenv(L,io_noclose); +createstdfile(L,stdin,1,"stdin"); +createstdfile(L,stdout,2,"stdout"); +createstdfile(L,stderr,0,"stderr"); +lua_pop(L,1); +lua_getfield(L,-1,"popen"); +newfenv(L,io_pclose); +lua_setfenv(L,-2); +lua_pop(L,1); +return 1; +} +static int os_pushresult(lua_State*L,int i,const char*filename){ +int en=errno; +if(i){ +lua_pushboolean(L,1); +return 1; +} +else{ +lua_pushnil(L); +lua_pushfstring(L,"%s: %s",filename,strerror(en)); +lua_pushinteger(L,en); +return 3; +} +} +static int os_remove(lua_State*L){ +const char*filename=luaL_checkstring(L,1); +return os_pushresult(L,remove(filename)==0,filename); +} +static int os_exit(lua_State*L){ +exit(luaL_optint(L,1,EXIT_SUCCESS)); +} +static const luaL_Reg syslib[]={ +{"exit",os_exit}, +{"remove",os_remove}, +{NULL,NULL} +}; +static int luaopen_os(lua_State*L){ +luaL_register(L,"os",syslib); +return 1; +} +#define uchar(c)((unsigned char)(c)) +static ptrdiff_t posrelat(ptrdiff_t pos,size_t len){ +if(pos<0)pos+=(ptrdiff_t)len+1; +return(pos>=0)?pos:0; +} +static int str_sub(lua_State*L){ +size_t l; +const char*s=luaL_checklstring(L,1,&l); +ptrdiff_t start=posrelat(luaL_checkinteger(L,2),l); +ptrdiff_t end=posrelat(luaL_optinteger(L,3,-1),l); +if(start<1)start=1; +if(end>(ptrdiff_t)l)end=(ptrdiff_t)l; +if(start<=end) +lua_pushlstring(L,s+start-1,end-start+1); +else lua_pushliteral(L,""); +return 1; +} +static int str_lower(lua_State*L){ +size_t l; +size_t i; +luaL_Buffer b; +const char*s=luaL_checklstring(L,1,&l); +luaL_buffinit(L,&b); +for(i=0;i0) +luaL_addlstring(&b,s,l); +luaL_pushresult(&b); +return 1; +} +static int str_byte(lua_State*L){ +size_t l; +const char*s=luaL_checklstring(L,1,&l); +ptrdiff_t posi=posrelat(luaL_optinteger(L,2,1),l); +ptrdiff_t pose=posrelat(luaL_optinteger(L,3,posi),l); +int n,i; +if(posi<=0)posi=1; +if((size_t)pose>l)pose=l; +if(posi>pose)return 0; +n=(int)(pose-posi+1); +if(posi+n<=pose) +luaL_error(L,"string slice too long"); +luaL_checkstack(L,n,"string slice too long"); +for(i=0;i=ms->level||ms->capture[l].len==(-1)) +return luaL_error(ms->L,"invalid capture index"); +return l; +} +static int capture_to_close(MatchState*ms){ +int level=ms->level; +for(level--;level>=0;level--) +if(ms->capture[level].len==(-1))return level; +return luaL_error(ms->L,"invalid pattern capture"); +} +static const char*classend(MatchState*ms,const char*p){ +switch(*p++){ +case'%':{ +if(*p=='\0') +luaL_error(ms->L,"malformed pattern (ends with "LUA_QL("%%")")"); +return p+1; +} +case'[':{ +if(*p=='^')p++; +do{ +if(*p=='\0') +luaL_error(ms->L,"malformed pattern (missing "LUA_QL("]")")"); +if(*(p++)=='%'&&*p!='\0') +p++; +}while(*p!=']'); +return p+1; +} +default:{ +return p; +} +} +} +static int match_class(int c,int cl){ +int res; +switch(tolower(cl)){ +case'a':res=isalpha(c);break; +case'c':res=iscntrl(c);break; +case'd':res=isdigit(c);break; +case'l':res=islower(c);break; +case'p':res=ispunct(c);break; +case's':res=isspace(c);break; +case'u':res=isupper(c);break; +case'w':res=isalnum(c);break; +case'x':res=isxdigit(c);break; +case'z':res=(c==0);break; +default:return(cl==c); +} +return(islower(cl)?res:!res); +} +static int matchbracketclass(int c,const char*p,const char*ec){ +int sig=1; +if(*(p+1)=='^'){ +sig=0; +p++; +} +while(++pL,"unbalanced pattern"); +if(*s!=*p)return NULL; +else{ +int b=*p; +int e=*(p+1); +int cont=1; +while(++ssrc_end){ +if(*s==e){ +if(--cont==0)return s+1; +} +else if(*s==b)cont++; +} +} +return NULL; +} +static const char*max_expand(MatchState*ms,const char*s, +const char*p,const char*ep){ +ptrdiff_t i=0; +while((s+i)src_end&&singlematch(uchar(*(s+i)),p,ep)) +i++; +while(i>=0){ +const char*res=match(ms,(s+i),ep+1); +if(res)return res; +i--; +} +return NULL; +} +static const char*min_expand(MatchState*ms,const char*s, +const char*p,const char*ep){ +for(;;){ +const char*res=match(ms,s,ep+1); +if(res!=NULL) +return res; +else if(ssrc_end&&singlematch(uchar(*s),p,ep)) +s++; +else return NULL; +} +} +static const char*start_capture(MatchState*ms,const char*s, +const char*p,int what){ +const char*res; +int level=ms->level; +if(level>=32)luaL_error(ms->L,"too many captures"); +ms->capture[level].init=s; +ms->capture[level].len=what; +ms->level=level+1; +if((res=match(ms,s,p))==NULL) +ms->level--; +return res; +} +static const char*end_capture(MatchState*ms,const char*s, +const char*p){ +int l=capture_to_close(ms); +const char*res; +ms->capture[l].len=s-ms->capture[l].init; +if((res=match(ms,s,p))==NULL) +ms->capture[l].len=(-1); +return res; +} +static const char*match_capture(MatchState*ms,const char*s,int l){ +size_t len; +l=check_capture(ms,l); +len=ms->capture[l].len; +if((size_t)(ms->src_end-s)>=len&& +memcmp(ms->capture[l].init,s,len)==0) +return s+len; +else return NULL; +} +static const char*match(MatchState*ms,const char*s,const char*p){ +init: +switch(*p){ +case'(':{ +if(*(p+1)==')') +return start_capture(ms,s,p+2,(-2)); +else +return start_capture(ms,s,p+1,(-1)); +} +case')':{ +return end_capture(ms,s,p+1); +} +case'%':{ +switch(*(p+1)){ +case'b':{ +s=matchbalance(ms,s,p+2); +if(s==NULL)return NULL; +p+=4;goto init; +} +case'f':{ +const char*ep;char previous; +p+=2; +if(*p!='[') +luaL_error(ms->L,"missing "LUA_QL("[")" after " +LUA_QL("%%f")" in pattern"); +ep=classend(ms,p); +previous=(s==ms->src_init)?'\0':*(s-1); +if(matchbracketclass(uchar(previous),p,ep-1)|| +!matchbracketclass(uchar(*s),p,ep-1))return NULL; +p=ep;goto init; +} +default:{ +if(isdigit(uchar(*(p+1)))){ +s=match_capture(ms,s,uchar(*(p+1))); +if(s==NULL)return NULL; +p+=2;goto init; +} +goto dflt; +} +} +} +case'\0':{ +return s; +} +case'$':{ +if(*(p+1)=='\0') +return(s==ms->src_end)?s:NULL; +else goto dflt; +} +default:dflt:{ +const char*ep=classend(ms,p); +int m=ssrc_end&&singlematch(uchar(*s),p,ep); +switch(*ep){ +case'?':{ +const char*res; +if(m&&((res=match(ms,s+1,ep+1))!=NULL)) +return res; +p=ep+1;goto init; +} +case'*':{ +return max_expand(ms,s,p,ep); +} +case'+':{ +return(m?max_expand(ms,s+1,p,ep):NULL); +} +case'-':{ +return min_expand(ms,s,p,ep); +} +default:{ +if(!m)return NULL; +s++;p=ep;goto init; +} +} +} +} +} +static const char*lmemfind(const char*s1,size_t l1, +const char*s2,size_t l2){ +if(l2==0)return s1; +else if(l2>l1)return NULL; +else{ +const char*init; +l2--; +l1=l1-l2; +while(l1>0&&(init=(const char*)memchr(s1,*s2,l1))!=NULL){ +init++; +if(memcmp(init,s2+1,l2)==0) +return init-1; +else{ +l1-=init-s1; +s1=init; +} +} +return NULL; +} +} +static void push_onecapture(MatchState*ms,int i,const char*s, +const char*e){ +if(i>=ms->level){ +if(i==0) +lua_pushlstring(ms->L,s,e-s); +else +luaL_error(ms->L,"invalid capture index"); +} +else{ +ptrdiff_t l=ms->capture[i].len; +if(l==(-1))luaL_error(ms->L,"unfinished capture"); +if(l==(-2)) +lua_pushinteger(ms->L,ms->capture[i].init-ms->src_init+1); +else +lua_pushlstring(ms->L,ms->capture[i].init,l); +} +} +static int push_captures(MatchState*ms,const char*s,const char*e){ +int i; +int nlevels=(ms->level==0&&s)?1:ms->level; +luaL_checkstack(ms->L,nlevels,"too many captures"); +for(i=0;il1)init=(ptrdiff_t)l1; +if(find&&(lua_toboolean(L,4)|| +strpbrk(p,"^$*+?.([%-")==NULL)){ +const char*s2=lmemfind(s+init,l1-init,p,l2); +if(s2){ +lua_pushinteger(L,s2-s+1); +lua_pushinteger(L,s2-s+l2); +return 2; +} +} +else{ +MatchState ms; +int anchor=(*p=='^')?(p++,1):0; +const char*s1=s+init; +ms.L=L; +ms.src_init=s; +ms.src_end=s+l1; +do{ +const char*res; +ms.level=0; +if((res=match(&ms,s1,p))!=NULL){ +if(find){ +lua_pushinteger(L,s1-s+1); +lua_pushinteger(L,res-s); +return push_captures(&ms,NULL,0)+2; +} +else +return push_captures(&ms,s1,res); +} +}while(s1++L,3,&l); +for(i=0;iL; +switch(lua_type(L,3)){ +case 3: +case 4:{ +add_s(ms,b,s,e); +return; +} +case 6:{ +int n; +lua_pushvalue(L,3); +n=push_captures(ms,s,e); +lua_call(L,n,1); +break; +} +case 5:{ +push_onecapture(ms,0,s,e); +lua_gettable(L,3); +break; +} +} +if(!lua_toboolean(L,-1)){ +lua_pop(L,1); +lua_pushlstring(L,s,e-s); +} +else if(!lua_isstring(L,-1)) +luaL_error(L,"invalid replacement value (a %s)",luaL_typename(L,-1)); +luaL_addvalue(b); +} +static int str_gsub(lua_State*L){ +size_t srcl; +const char*src=luaL_checklstring(L,1,&srcl); +const char*p=luaL_checkstring(L,2); +int tr=lua_type(L,3); +int max_s=luaL_optint(L,4,srcl+1); +int anchor=(*p=='^')?(p++,1):0; +int n=0; +MatchState ms; +luaL_Buffer b; +luaL_argcheck(L,tr==3||tr==4|| +tr==6||tr==5,3, +"string/function/table expected"); +luaL_buffinit(L,&b); +ms.L=L; +ms.src_init=src; +ms.src_end=src+srcl; +while(nsrc) +src=e; +else if(src=sizeof("-+ #0")) +luaL_error(L,"invalid format (repeated flags)"); +if(isdigit(uchar(*p)))p++; +if(isdigit(uchar(*p)))p++; +if(*p=='.'){ +p++; +if(isdigit(uchar(*p)))p++; +if(isdigit(uchar(*p)))p++; +} +if(isdigit(uchar(*p))) +luaL_error(L,"invalid format (width or precision too long)"); +*(form++)='%'; +strncpy(form,strfrmt,p-strfrmt+1); +form+=p-strfrmt+1; +*form='\0'; +return p; +} +static void addintlen(char*form){ +size_t l=strlen(form); +char spec=form[l-1]; +strcpy(form+l-1,"l"); +form[l+sizeof("l")-2]=spec; +form[l+sizeof("l")-1]='\0'; +} +static int str_format(lua_State*L){ +int top=lua_gettop(L); +int arg=1; +size_t sfl; +const char*strfrmt=luaL_checklstring(L,arg,&sfl); +const char*strfrmt_end=strfrmt+sfl; +luaL_Buffer b; +luaL_buffinit(L,&b); +while(strfrmttop) +luaL_argerror(L,arg,"no value"); +strfrmt=scanformat(L,strfrmt,form); +switch(*strfrmt++){ +case'c':{ +sprintf(buff,form,(int)luaL_checknumber(L,arg)); +break; +} +case'd':case'i':{ +addintlen(form); +sprintf(buff,form,(long)luaL_checknumber(L,arg)); +break; +} +case'o':case'u':case'x':case'X':{ +addintlen(form); +sprintf(buff,form,(unsigned long)luaL_checknumber(L,arg)); +break; +} +case'e':case'E':case'f': +case'g':case'G':{ +sprintf(buff,form,(double)luaL_checknumber(L,arg)); +break; +} +case'q':{ +addquoted(L,&b,arg); +continue; +} +case's':{ +size_t l; +const char*s=luaL_checklstring(L,arg,&l); +if(!strchr(form,'.')&&l>=100){ +lua_pushvalue(L,arg); +luaL_addvalue(&b); +continue; +} +else{ +sprintf(buff,form,s); +break; +} +} +default:{ +return luaL_error(L,"invalid option "LUA_QL("%%%c")" to " +LUA_QL("format"),*(strfrmt-1)); +} +} +luaL_addlstring(&b,buff,strlen(buff)); +} +} +luaL_pushresult(&b); +return 1; +} +static const luaL_Reg strlib[]={ +{"byte",str_byte}, +{"char",str_char}, +{"find",str_find}, +{"format",str_format}, +{"gmatch",gmatch}, +{"gsub",str_gsub}, +{"lower",str_lower}, +{"match",str_match}, +{"rep",str_rep}, +{"sub",str_sub}, +{"upper",str_upper}, +{NULL,NULL} +}; +static void createmetatable(lua_State*L){ +lua_createtable(L,0,1); +lua_pushliteral(L,""); +lua_pushvalue(L,-2); +lua_setmetatable(L,-2); +lua_pop(L,1); +lua_pushvalue(L,-2); +lua_setfield(L,-2,"__index"); +lua_pop(L,1); +} +static int luaopen_string(lua_State*L){ +luaL_register(L,"string",strlib); +createmetatable(L); +return 1; +} +static const luaL_Reg lualibs[]={ +{"",luaopen_base}, +{"table",luaopen_table}, +{"io",luaopen_io}, +{"os",luaopen_os}, +{"string",luaopen_string}, +{NULL,NULL} +}; +static void luaL_openlibs(lua_State*L){ +const luaL_Reg*lib=lualibs; +for(;lib->func;lib++){ +lua_pushcfunction(L,lib->func); +lua_pushstring(L,lib->name); +lua_call(L,1,0); +} +} +typedef unsigned int UB; +static UB barg(lua_State*L,int idx){ +union{lua_Number n;U64 b;}bn; +bn.n=lua_tonumber(L,idx)+6755399441055744.0; +if(bn.n==0.0&&!lua_isnumber(L,idx))luaL_typerror(L,idx,"number"); +return(UB)bn.b; +} +#define BRET(b)lua_pushnumber(L,(lua_Number)(int)(b));return 1; +static int tobit(lua_State*L){ +BRET(barg(L,1))} +static int bnot(lua_State*L){ +BRET(~barg(L,1))} +static int band(lua_State*L){ +int i;UB b=barg(L,1);for(i=lua_gettop(L);i>1;i--)b&=barg(L,i);BRET(b)} +static int bor(lua_State*L){ +int i;UB b=barg(L,1);for(i=lua_gettop(L);i>1;i--)b|=barg(L,i);BRET(b)} +static int bxor(lua_State*L){ +int i;UB b=barg(L,1);for(i=lua_gettop(L);i>1;i--)b^=barg(L,i);BRET(b)} +static int lshift(lua_State*L){ +UB b=barg(L,1),n=barg(L,2)&31;BRET(b<>n)} +static int arshift(lua_State*L){ +UB b=barg(L,1),n=barg(L,2)&31;BRET((int)b>>n)} +static int rol(lua_State*L){ +UB b=barg(L,1),n=barg(L,2)&31;BRET((b<>(32-n)))} +static int ror(lua_State*L){ +UB b=barg(L,1),n=barg(L,2)&31;BRET((b>>n)|(b<<(32-n)))} +static int bswap(lua_State*L){ +UB b=barg(L,1);b=(b>>24)|((b>>8)&0xff00)|((b&0xff00)<<8)|(b<<24);BRET(b)} +static int tohex(lua_State*L){ +UB b=barg(L,1); +int n=lua_isnone(L,2)?8:(int)barg(L,2); +const char*hexdigits="0123456789abcdef"; +char buf[8]; +int i; +if(n<0){n=-n;hexdigits="0123456789ABCDEF";} +if(n>8)n=8; +for(i=(int)n;--i>=0;){buf[i]=hexdigits[b&15];b>>=4;} +lua_pushlstring(L,buf,(size_t)n); +return 1; +} +static const struct luaL_Reg bitlib[]={ +{"tobit",tobit}, +{"bnot",bnot}, +{"band",band}, +{"bor",bor}, +{"bxor",bxor}, +{"lshift",lshift}, +{"rshift",rshift}, +{"arshift",arshift}, +{"rol",rol}, +{"ror",ror}, +{"bswap",bswap}, +{"tohex",tohex}, +{NULL,NULL} +}; +int main(int argc,char**argv){ +lua_State*L=luaL_newstate(); +int i; +luaL_openlibs(L); +luaL_register(L,"bit",bitlib); +if(argc<2)return sizeof(void*); +lua_createtable(L,0,1); +lua_pushstring(L,argv[1]); +lua_rawseti(L,-2,0); +lua_setglobal(L,"arg"); +if(luaL_loadfile(L,argv[1])) +goto err; +for(i=2;i!En8;=>O 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 z67 $l1P;_d`KP3#>ksuEeKR*oL=j0?mH^rg>xO7x$g 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 QBly86ws~xCBMBp~ZB|XRo&$e{Opg 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 _!EHS5y2q!q9cksIbK!g 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 cNc -- BYTECODE -- [...] +-- print(bc.line(foo, 2)) --> 0002 KSTR 1 1 ; "hello" +-- +-- local out = { +-- -- Do something with each line: +-- write = function(t, ...) io.write(...) end, +-- close = function(t) end, +-- flush = function(t) end, +-- } +-- bc.dump(foo, out) +-- +------------------------------------------------------------------------------ + +-- Cache some library functions and objects. +local jit = require("jit") +assert(jit.version_num == 20100, "LuaJIT core/library version mismatch") +local jutil = require("jit.util") +local vmdef = require("jit.vmdef") +local bit = require("bit") +local sub, gsub, format = string.sub, string.gsub, string.format +local byte, band, shr = string.byte, bit.band, bit.rshift +local funcinfo, funcbc, funck = jutil.funcinfo, jutil.funcbc, jutil.funck +local funcuvname = jutil.funcuvname +local bcnames = vmdef.bcnames +local stdout, stderr = io.stdout, io.stderr + +------------------------------------------------------------------------------ + +local function ctlsub(c) + if c == "\n" then return "\\n" + elseif c == "\r" then return "\\r" + elseif c == "\t" then return "\\t" + else return format("\\%03d", byte(c)) + end +end + +-- Return one bytecode line. +local function bcline(func, pc, prefix) + local ins, m = funcbc(func, pc) + if not ins then return end + local ma, mb, mc = band(m, 7), band(m, 15*8), band(m, 15*128) + local a = band(shr(ins, 8), 0xff) + local oidx = 6*band(ins, 0xff) + local op = sub(bcnames, oidx+1, oidx+6) + local s = format("%04d %s %-6s %3s ", + pc, prefix or " ", op, ma == 0 and "" or a) + local d = shr(ins, 16) + if mc == 13*128 then -- BCMjump + return format("%s=> %04d\n", s, pc+d-0x7fff) + end + if mb ~= 0 then + d = band(d, 0xff) + elseif mc == 0 then + return s.."\n" + end + local kc + if mc == 10*128 then -- BCMstr + kc = funck(func, -d-1) + kc = format(#kc > 40 and '"%.40s"~' or '"%s"', gsub(kc, "%c", ctlsub)) + elseif mc == 9*128 then -- BCMnum + kc = funck(func, d) + if op == "TSETM " then kc = kc - 2^52 end + elseif mc == 12*128 then -- BCMfunc + local fi = funcinfo(funck(func, -d-1)) + if fi.ffid then + kc = vmdef.ffnames[fi.ffid] + else + kc = fi.loc + end + elseif mc == 5*128 then -- BCMuv + kc = funcuvname(func, d) + end + if ma == 5 then -- BCMuv + local ka = funcuvname(func, a) + if kc then kc = ka.." ; "..kc else kc = ka end + end + if mb ~= 0 then + local b = shr(ins, 24) + if kc then return format("%s%3d %3d ; %s\n", s, b, d, kc) end + return format("%s%3d %3d\n", s, b, d) + end + if kc then return format("%s%3d ; %s\n", s, d, kc) end + if mc == 7*128 and d > 32767 then d = d - 65536 end -- BCMlits + return format("%s%3d\n", s, d) +end + +-- Collect branch targets of a function. +local function bctargets(func) + local target = {} + for pc=1,1000000000 do + local ins, m = funcbc(func, pc) + if not ins then break end + if band(m, 15*128) == 13*128 then target[pc+shr(ins, 16)-0x7fff] = true end + end + return target +end + +-- Dump bytecode instructions of a function. +local function bcdump(func, out, all) + if not out then out = stdout end + local fi = funcinfo(func) + if all and fi.children then + for n=-1,-1000000000,-1 do + local k = funck(func, n) + if not k then break end + if type(k) == "proto" then bcdump(k, out, true) end + end + end + out:write(format("-- BYTECODE -- %s-%d\n", fi.loc, fi.lastlinedefined)) + local target = bctargets(func) + for pc=1,1000000000 do + local s = bcline(func, pc, target[pc] and "=>") + if not s then break end + out:write(s) + end + out:write("\n") + out:flush() +end + +------------------------------------------------------------------------------ + +-- Active flag and output file handle. +local active, out + +-- List handler. +local function h_list(func) + return bcdump(func, out) +end + +-- Detach list handler. +local function bclistoff() + if active then + active = false + jit.attach(h_list) + if out and out ~= stdout and out ~= stderr then out:close() end + out = nil + end +end + +-- Open the output file and attach list handler. +local function bcliston(outfile) + if active then bclistoff() end + if not outfile then outfile = os.getenv("LUAJIT_LISTFILE") end + if outfile then + out = outfile == "-" and stdout or assert(io.open(outfile, "w")) + else + out = stderr + end + jit.attach(h_list, "bc") + active = true +end + +-- Public module functions. +return { + line = bcline, + dump = bcdump, + targets = bctargets, + on = bcliston, + off = bclistoff, + start = bcliston -- For -j command line option. +} + diff --git a/lib/LuaJIT/jit/bcsave.lua b/lib/LuaJIT/jit/bcsave.lua new file mode 100644 index 0000000..2553d97 --- /dev/null +++ b/lib/LuaJIT/jit/bcsave.lua @@ -0,0 +1,661 @@ +---------------------------------------------------------------------------- +-- LuaJIT module to save/list bytecode. +-- +-- Copyright (C) 2005-2017 Mike Pall. All rights reserved. +-- Released under the MIT license. See Copyright Notice in luajit.h +---------------------------------------------------------------------------- +-- +-- This module saves or lists the bytecode for an input file. +-- It's run by the -b command line option. +-- +------------------------------------------------------------------------------ + +local jit = require("jit") +assert(jit.version_num == 20100, "LuaJIT core/library version mismatch") +local bit = require("bit") + +-- Symbol name prefix for LuaJIT bytecode. +local LJBC_PREFIX = "luaJIT_BC_" + +------------------------------------------------------------------------------ + +local function usage() + io.stderr:write[[ +Save LuaJIT bytecode: luajit -b[options] input output + -l Only list bytecode. + -s Strip debug info (default). + -g Keep debug info. + -n name Set module name (default: auto-detect from input name). + -t type Set output file type (default: auto-detect from output name). + -a arch Override architecture for object files (default: native). + -o os Override OS for object files (default: native). + -e chunk Use chunk string as input. + -- Stop handling options. + - Use stdin as input and/or stdout as output. + +File types: c h obj o raw (default) +]] + os.exit(1) +end + +local function check(ok, ...) + if ok then return ok, ... end + io.stderr:write("luajit: ", ...) + io.stderr:write("\n") + os.exit(1) +end + +local function readfile(input) + if type(input) == "function" then return input end + if input == "-" then input = nil end + return check(loadfile(input)) +end + +local function savefile(name, mode) + if name == "-" then return io.stdout end + return check(io.open(name, mode)) +end + +------------------------------------------------------------------------------ + +local map_type = { + raw = "raw", c = "c", h = "h", o = "obj", obj = "obj", +} + +local map_arch = { + x86 = true, x64 = true, arm = true, arm64 = true, arm64be = true, + ppc = true, mips = true, mipsel = true, +} + +local map_os = { + linux = true, windows = true, osx = true, freebsd = true, netbsd = true, + openbsd = true, dragonfly = true, solaris = true, +} + +local function checkarg(str, map, err) + str = string.lower(str) + local s = check(map[str], "unknown ", err) + return s == true and str or s +end + +local function detecttype(str) + local ext = string.match(string.lower(str), "%.(%a+)$") + return map_type[ext] or "raw" +end + +local function checkmodname(str) + check(string.match(str, "^[%w_.%-]+$"), "bad module name") + return string.gsub(str, "[%.%-]", "_") +end + +local function detectmodname(str) + if type(str) == "string" then + local tail = string.match(str, "[^/\\]+$") + if tail then str = tail end + local head = string.match(str, "^(.*)%.[^.]*$") + if head then str = head end + str = string.match(str, "^[%w_.%-]+") + else + str = nil + end + check(str, "cannot derive module name, use -n name") + return string.gsub(str, "[%.%-]", "_") +end + +------------------------------------------------------------------------------ + +local function bcsave_tail(fp, output, s) + local ok, err = fp:write(s) + if ok and output ~= "-" then ok, err = fp:close() end + check(ok, "cannot write ", output, ": ", err) +end + +local function bcsave_raw(output, s) + local fp = savefile(output, "wb") + bcsave_tail(fp, output, s) +end + +local function bcsave_c(ctx, output, s) + local fp = savefile(output, "w") + if ctx.type == "c" then + fp:write(string.format([[ +#ifdef _cplusplus +extern "C" +#endif +#ifdef _WIN32 +__declspec(dllexport) +#endif +const unsigned char %s%s[] = { +]], LJBC_PREFIX, ctx.modname)) + else + fp:write(string.format([[ +#define %s%s_SIZE %d +static const unsigned char %s%s[] = { +]], LJBC_PREFIX, ctx.modname, #s, LJBC_PREFIX, ctx.modname)) + end + local t, n, m = {}, 0, 0 + for i=1,#s do + local b = tostring(string.byte(s, i)) + m = m + #b + 1 + if m > 78 then + fp:write(table.concat(t, ",", 1, n), ",\n") + n, m = 0, #b + 1 + end + n = n + 1 + t[n] = b + end + bcsave_tail(fp, output, table.concat(t, ",", 1, n).."\n};\n") +end + +local function bcsave_elfobj(ctx, output, s, ffi) + ffi.cdef[[ +typedef struct { + uint8_t emagic[4], eclass, eendian, eversion, eosabi, eabiversion, epad[7]; + uint16_t type, machine; + uint32_t version; + uint32_t entry, phofs, shofs; + uint32_t flags; + uint16_t ehsize, phentsize, phnum, shentsize, shnum, shstridx; +} ELF32header; +typedef struct { + uint8_t emagic[4], eclass, eendian, eversion, eosabi, eabiversion, epad[7]; + uint16_t type, machine; + uint32_t version; + uint64_t entry, phofs, shofs; + uint32_t flags; + uint16_t ehsize, phentsize, phnum, shentsize, shnum, shstridx; +} ELF64header; +typedef struct { + uint32_t name, type, flags, addr, ofs, size, link, info, align, entsize; +} ELF32sectheader; +typedef struct { + uint32_t name, type; + uint64_t flags, addr, ofs, size; + uint32_t link, info; + uint64_t align, entsize; +} ELF64sectheader; +typedef struct { + uint32_t name, value, size; + uint8_t info, other; + uint16_t sectidx; +} ELF32symbol; +typedef struct { + uint32_t name; + uint8_t info, other; + uint16_t sectidx; + uint64_t value, size; +} ELF64symbol; +typedef struct { + ELF32header hdr; + ELF32sectheader sect[6]; + ELF32symbol sym[2]; + uint8_t space[4096]; +} ELF32obj; +typedef struct { + ELF64header hdr; + ELF64sectheader sect[6]; + ELF64symbol sym[2]; + uint8_t space[4096]; +} ELF64obj; +]] + local symname = LJBC_PREFIX..ctx.modname + local is64, isbe = false, false + if ctx.arch == "x64" or ctx.arch == "arm64" or ctx.arch == "arm64be" then + is64 = true + elseif ctx.arch == "ppc" or ctx.arch == "mips" then + isbe = true + end + + -- Handle different host/target endianess. + local function f32(x) return x end + local f16, fofs = f32, f32 + if ffi.abi("be") ~= isbe then + f32 = bit.bswap + function f16(x) return bit.rshift(bit.bswap(x), 16) end + if is64 then + local two32 = ffi.cast("int64_t", 2^32) + function fofs(x) return bit.bswap(x)*two32 end + else + fofs = f32 + end + end + + -- Create ELF object and fill in header. + local o = ffi.new(is64 and "ELF64obj" or "ELF32obj") + local hdr = o.hdr + if ctx.os == "bsd" or ctx.os == "other" then -- Determine native hdr.eosabi. + local bf = assert(io.open("/bin/ls", "rb")) + local bs = bf:read(9) + bf:close() + ffi.copy(o, bs, 9) + check(hdr.emagic[0] == 127, "no support for writing native object files") + else + hdr.emagic = "\127ELF" + hdr.eosabi = ({ freebsd=9, netbsd=2, openbsd=12, solaris=6 })[ctx.os] or 0 + end + hdr.eclass = is64 and 2 or 1 + hdr.eendian = isbe and 2 or 1 + hdr.eversion = 1 + hdr.type = f16(1) + hdr.machine = f16(({ x86=3, x64=62, arm=40, arm64=183, arm64be=183, ppc=20, mips=8, mipsel=8 })[ctx.arch]) + if ctx.arch == "mips" or ctx.arch == "mipsel" then + hdr.flags = f32(0x50001006) + end + hdr.version = f32(1) + hdr.shofs = fofs(ffi.offsetof(o, "sect")) + hdr.ehsize = f16(ffi.sizeof(hdr)) + hdr.shentsize = f16(ffi.sizeof(o.sect[0])) + hdr.shnum = f16(6) + hdr.shstridx = f16(2) + + -- Fill in sections and symbols. + local sofs, ofs = ffi.offsetof(o, "space"), 1 + for i,name in ipairs{ + ".symtab", ".shstrtab", ".strtab", ".rodata", ".note.GNU-stack", + } do + local sect = o.sect[i] + sect.align = fofs(1) + sect.name = f32(ofs) + ffi.copy(o.space+ofs, name) + ofs = ofs + #name+1 + end + o.sect[1].type = f32(2) -- .symtab + o.sect[1].link = f32(3) + o.sect[1].info = f32(1) + o.sect[1].align = fofs(8) + o.sect[1].ofs = fofs(ffi.offsetof(o, "sym")) + o.sect[1].entsize = fofs(ffi.sizeof(o.sym[0])) + o.sect[1].size = fofs(ffi.sizeof(o.sym)) + o.sym[1].name = f32(1) + o.sym[1].sectidx = f16(4) + o.sym[1].size = fofs(#s) + o.sym[1].info = 17 + o.sect[2].type = f32(3) -- .shstrtab + o.sect[2].ofs = fofs(sofs) + o.sect[2].size = fofs(ofs) + o.sect[3].type = f32(3) -- .strtab + o.sect[3].ofs = fofs(sofs + ofs) + o.sect[3].size = fofs(#symname+2) + ffi.copy(o.space+ofs+1, symname) + ofs = ofs + #symname + 2 + o.sect[4].type = f32(1) -- .rodata + o.sect[4].flags = fofs(2) + o.sect[4].ofs = fofs(sofs + ofs) + o.sect[4].size = fofs(#s) + o.sect[5].type = f32(1) -- .note.GNU-stack + o.sect[5].ofs = fofs(sofs + ofs + #s) + + -- Write ELF object file. + local fp = savefile(output, "wb") + fp:write(ffi.string(o, ffi.sizeof(o)-4096+ofs)) + bcsave_tail(fp, output, s) +end + +local function bcsave_peobj(ctx, output, s, ffi) + ffi.cdef[[ +typedef struct { + uint16_t arch, nsects; + uint32_t time, symtabofs, nsyms; + uint16_t opthdrsz, flags; +} PEheader; +typedef struct { + char name[8]; + uint32_t vsize, vaddr, size, ofs, relocofs, lineofs; + uint16_t nreloc, nline; + uint32_t flags; +} PEsection; +typedef struct __attribute((packed)) { + union { + char name[8]; + uint32_t nameref[2]; + }; + uint32_t value; + int16_t sect; + uint16_t type; + uint8_t scl, naux; +} PEsym; +typedef struct __attribute((packed)) { + uint32_t size; + uint16_t nreloc, nline; + uint32_t cksum; + uint16_t assoc; + uint8_t comdatsel, unused[3]; +} PEsymaux; +typedef struct { + PEheader hdr; + PEsection sect[2]; + // Must be an even number of symbol structs. + PEsym sym0; + PEsymaux sym0aux; + PEsym sym1; + PEsymaux sym1aux; + PEsym sym2; + PEsym sym3; + uint32_t strtabsize; + uint8_t space[4096]; +} PEobj; +]] + local symname = LJBC_PREFIX..ctx.modname + local is64 = false + if ctx.arch == "x86" then + symname = "_"..symname + elseif ctx.arch == "x64" then + is64 = true + end + local symexport = " /EXPORT:"..symname..",DATA " + + -- The file format is always little-endian. Swap if the host is big-endian. + local function f32(x) return x end + local f16 = f32 + if ffi.abi("be") then + f32 = bit.bswap + function f16(x) return bit.rshift(bit.bswap(x), 16) end + end + + -- Create PE object and fill in header. + local o = ffi.new("PEobj") + local hdr = o.hdr + hdr.arch = f16(({ x86=0x14c, x64=0x8664, arm=0x1c0, ppc=0x1f2, mips=0x366, mipsel=0x366 })[ctx.arch]) + hdr.nsects = f16(2) + hdr.symtabofs = f32(ffi.offsetof(o, "sym0")) + hdr.nsyms = f32(6) + + -- Fill in sections and symbols. + o.sect[0].name = ".drectve" + o.sect[0].size = f32(#symexport) + o.sect[0].flags = f32(0x00100a00) + o.sym0.sect = f16(1) + o.sym0.scl = 3 + o.sym0.name = ".drectve" + o.sym0.naux = 1 + o.sym0aux.size = f32(#symexport) + o.sect[1].name = ".rdata" + o.sect[1].size = f32(#s) + o.sect[1].flags = f32(0x40300040) + o.sym1.sect = f16(2) + o.sym1.scl = 3 + o.sym1.name = ".rdata" + o.sym1.naux = 1 + o.sym1aux.size = f32(#s) + o.sym2.sect = f16(2) + o.sym2.scl = 2 + o.sym2.nameref[1] = f32(4) + o.sym3.sect = f16(-1) + o.sym3.scl = 2 + o.sym3.value = f32(1) + o.sym3.name = "@feat.00" -- Mark as SafeSEH compliant. + ffi.copy(o.space, symname) + local ofs = #symname + 1 + o.strtabsize = f32(ofs + 4) + o.sect[0].ofs = f32(ffi.offsetof(o, "space") + ofs) + ffi.copy(o.space + ofs, symexport) + ofs = ofs + #symexport + o.sect[1].ofs = f32(ffi.offsetof(o, "space") + ofs) + + -- Write PE object file. + local fp = savefile(output, "wb") + fp:write(ffi.string(o, ffi.sizeof(o)-4096+ofs)) + bcsave_tail(fp, output, s) +end + +local function bcsave_machobj(ctx, output, s, ffi) + ffi.cdef[[ +typedef struct +{ + uint32_t magic, cputype, cpusubtype, filetype, ncmds, sizeofcmds, flags; +} mach_header; +typedef struct +{ + mach_header; uint32_t reserved; +} mach_header_64; +typedef struct { + uint32_t cmd, cmdsize; + char segname[16]; + uint32_t vmaddr, vmsize, fileoff, filesize; + uint32_t maxprot, initprot, nsects, flags; +} mach_segment_command; +typedef struct { + uint32_t cmd, cmdsize; + char segname[16]; + uint64_t vmaddr, vmsize, fileoff, filesize; + uint32_t maxprot, initprot, nsects, flags; +} mach_segment_command_64; +typedef struct { + char sectname[16], segname[16]; + uint32_t addr, size; + uint32_t offset, align, reloff, nreloc, flags; + uint32_t reserved1, reserved2; +} mach_section; +typedef struct { + char sectname[16], segname[16]; + uint64_t addr, size; + uint32_t offset, align, reloff, nreloc, flags; + uint32_t reserved1, reserved2, reserved3; +} mach_section_64; +typedef struct { + uint32_t cmd, cmdsize, symoff, nsyms, stroff, strsize; +} mach_symtab_command; +typedef struct { + int32_t strx; + uint8_t type, sect; + int16_t desc; + uint32_t value; +} mach_nlist; +typedef struct { + uint32_t strx; + uint8_t type, sect; + uint16_t desc; + uint64_t value; +} mach_nlist_64; +typedef struct +{ + uint32_t magic, nfat_arch; +} mach_fat_header; +typedef struct +{ + uint32_t cputype, cpusubtype, offset, size, align; +} mach_fat_arch; +typedef struct { + struct { + mach_header hdr; + mach_segment_command seg; + mach_section sec; + mach_symtab_command sym; + } arch[1]; + mach_nlist sym_entry; + uint8_t space[4096]; +} mach_obj; +typedef struct { + struct { + mach_header_64 hdr; + mach_segment_command_64 seg; + mach_section_64 sec; + mach_symtab_command sym; + } arch[1]; + mach_nlist_64 sym_entry; + uint8_t space[4096]; +} mach_obj_64; +typedef struct { + mach_fat_header fat; + mach_fat_arch fat_arch[2]; + struct { + mach_header hdr; + mach_segment_command seg; + mach_section sec; + mach_symtab_command sym; + } arch[2]; + mach_nlist sym_entry; + uint8_t space[4096]; +} mach_fat_obj; +]] + local symname = '_'..LJBC_PREFIX..ctx.modname + local isfat, is64, align, mobj = false, false, 4, "mach_obj" + if ctx.arch == "x64" then + is64, align, mobj = true, 8, "mach_obj_64" + elseif ctx.arch == "arm" then + isfat, mobj = true, "mach_fat_obj" + elseif ctx.arch == "arm64" then + is64, align, isfat, mobj = true, 8, true, "mach_fat_obj" + else + check(ctx.arch == "x86", "unsupported architecture for OSX") + end + local function aligned(v, a) return bit.band(v+a-1, -a) end + local be32 = bit.bswap -- Mach-O FAT is BE, supported archs are LE. + + -- Create Mach-O object and fill in header. + local o = ffi.new(mobj) + local mach_size = aligned(ffi.offsetof(o, "space")+#symname+2, align) + local cputype = ({ x86={7}, x64={0x01000007}, arm={7,12}, arm64={0x01000007,0x0100000c} })[ctx.arch] + local cpusubtype = ({ x86={3}, x64={3}, arm={3,9}, arm64={3,0} })[ctx.arch] + if isfat then + o.fat.magic = be32(0xcafebabe) + o.fat.nfat_arch = be32(#cpusubtype) + end + + -- Fill in sections and symbols. + for i=0,#cpusubtype-1 do + local ofs = 0 + if isfat then + local a = o.fat_arch[i] + a.cputype = be32(cputype[i+1]) + a.cpusubtype = be32(cpusubtype[i+1]) + -- Subsequent slices overlap each other to share data. + ofs = ffi.offsetof(o, "arch") + i*ffi.sizeof(o.arch[0]) + a.offset = be32(ofs) + a.size = be32(mach_size-ofs+#s) + end + local a = o.arch[i] + a.hdr.magic = is64 and 0xfeedfacf or 0xfeedface + a.hdr.cputype = cputype[i+1] + a.hdr.cpusubtype = cpusubtype[i+1] + a.hdr.filetype = 1 + a.hdr.ncmds = 2 + a.hdr.sizeofcmds = ffi.sizeof(a.seg)+ffi.sizeof(a.sec)+ffi.sizeof(a.sym) + a.seg.cmd = is64 and 0x19 or 0x1 + a.seg.cmdsize = ffi.sizeof(a.seg)+ffi.sizeof(a.sec) + a.seg.vmsize = #s + a.seg.fileoff = mach_size-ofs + a.seg.filesize = #s + a.seg.maxprot = 1 + a.seg.initprot = 1 + a.seg.nsects = 1 + ffi.copy(a.sec.sectname, "__data") + ffi.copy(a.sec.segname, "__DATA") + a.sec.size = #s + a.sec.offset = mach_size-ofs + a.sym.cmd = 2 + a.sym.cmdsize = ffi.sizeof(a.sym) + a.sym.symoff = ffi.offsetof(o, "sym_entry")-ofs + a.sym.nsyms = 1 + a.sym.stroff = ffi.offsetof(o, "sym_entry")+ffi.sizeof(o.sym_entry)-ofs + a.sym.strsize = aligned(#symname+2, align) + end + o.sym_entry.type = 0xf + o.sym_entry.sect = 1 + o.sym_entry.strx = 1 + ffi.copy(o.space+1, symname) + + -- Write Macho-O object file. + local fp = savefile(output, "wb") + fp:write(ffi.string(o, mach_size)) + bcsave_tail(fp, output, s) +end + +local function bcsave_obj(ctx, output, s) + local ok, ffi = pcall(require, "ffi") + check(ok, "FFI library required to write this file type") + if ctx.os == "windows" then + return bcsave_peobj(ctx, output, s, ffi) + elseif ctx.os == "osx" then + return bcsave_machobj(ctx, output, s, ffi) + else + return bcsave_elfobj(ctx, output, s, ffi) + end +end + +------------------------------------------------------------------------------ + +local function bclist(input, output) + local f = readfile(input) + require("jit.bc").dump(f, savefile(output, "w"), true) +end + +local function bcsave(ctx, input, output) + local f = readfile(input) + local s = string.dump(f, ctx.strip) + local t = ctx.type + if not t then + t = detecttype(output) + ctx.type = t + end + if t == "raw" then + bcsave_raw(output, s) + else + if not ctx.modname then ctx.modname = detectmodname(input) end + if t == "obj" then + bcsave_obj(ctx, output, s) + else + bcsave_c(ctx, output, s) + end + end +end + +local function docmd(...) + local arg = {...} + local n = 1 + local list = false + local ctx = { + strip = true, arch = jit.arch, os = string.lower(jit.os), + type = false, modname = false, + } + while n <= #arg do + local a = arg[n] + if type(a) == "string" and string.sub(a, 1, 1) == "-" and a ~= "-" then + table.remove(arg, n) + if a == "--" then break end + for m=2,#a do + local opt = string.sub(a, m, m) + if opt == "l" then + list = true + elseif opt == "s" then + ctx.strip = true + elseif opt == "g" then + ctx.strip = false + else + if arg[n] == nil or m ~= #a then usage() end + if opt == "e" then + if n ~= 1 then usage() end + arg[1] = check(loadstring(arg[1])) + elseif opt == "n" then + ctx.modname = checkmodname(table.remove(arg, n)) + elseif opt == "t" then + ctx.type = checkarg(table.remove(arg, n), map_type, "file type") + elseif opt == "a" then + ctx.arch = checkarg(table.remove(arg, n), map_arch, "architecture") + elseif opt == "o" then + ctx.os = checkarg(table.remove(arg, n), map_os, "OS name") + else + usage() + end + end + end + else + n = n + 1 + end + end + if list then + if #arg == 0 or #arg > 2 then usage() end + bclist(arg[1], arg[2] or "-") + else + if #arg ~= 2 then usage() end + bcsave(ctx, arg[1], arg[2]) + end +end + +------------------------------------------------------------------------------ + +-- Public module functions. +return { + start = docmd -- Process -b command line option. +} + diff --git a/lib/LuaJIT/jit/dis_arm.lua b/lib/LuaJIT/jit/dis_arm.lua new file mode 100644 index 0000000..c2dd776 --- /dev/null +++ b/lib/LuaJIT/jit/dis_arm.lua @@ -0,0 +1,689 @@ +---------------------------------------------------------------------------- +-- LuaJIT ARM disassembler module. +-- +-- Copyright (C) 2005-2017 Mike Pall. All rights reserved. +-- Released under the MIT license. See Copyright Notice in luajit.h +---------------------------------------------------------------------------- +-- This is a helper module used by the LuaJIT machine code dumper module. +-- +-- It disassembles most user-mode ARMv7 instructions +-- NYI: Advanced SIMD and VFP instructions. +------------------------------------------------------------------------------ + +local type = type +local sub, byte, format = string.sub, string.byte, string.format +local match, gmatch = string.match, string.gmatch +local concat = table.concat +local bit = require("bit") +local band, bor, ror, tohex = bit.band, bit.bor, bit.ror, bit.tohex +local lshift, rshift, arshift = bit.lshift, bit.rshift, bit.arshift + +------------------------------------------------------------------------------ +-- Opcode maps +------------------------------------------------------------------------------ + +local map_loadc = { + shift = 8, mask = 15, + [10] = { + shift = 20, mask = 1, + [0] = { + shift = 23, mask = 3, + [0] = "vmovFmDN", "vstmFNdr", + _ = { + shift = 21, mask = 1, + [0] = "vstrFdl", + { shift = 16, mask = 15, [13] = "vpushFdr", _ = "vstmdbFNdr", } + }, + }, + { + shift = 23, mask = 3, + [0] = "vmovFDNm", + { shift = 16, mask = 15, [13] = "vpopFdr", _ = "vldmFNdr", }, + _ = { + shift = 21, mask = 1, + [0] = "vldrFdl", "vldmdbFNdr", + }, + }, + }, + [11] = { + shift = 20, mask = 1, + [0] = { + shift = 23, mask = 3, + [0] = "vmovGmDN", "vstmGNdr", + _ = { + shift = 21, mask = 1, + [0] = "vstrGdl", + { shift = 16, mask = 15, [13] = "vpushGdr", _ = "vstmdbGNdr", } + }, + }, + { + shift = 23, mask = 3, + [0] = "vmovGDNm", + { shift = 16, mask = 15, [13] = "vpopGdr", _ = "vldmGNdr", }, + _ = { + shift = 21, mask = 1, + [0] = "vldrGdl", "vldmdbGNdr", + }, + }, + }, + _ = { + shift = 0, mask = 0 -- NYI ldc, mcrr, mrrc. + }, +} + +local map_vfps = { + shift = 6, mask = 0x2c001, + [0] = "vmlaF.dnm", "vmlsF.dnm", + [0x04000] = "vnmlsF.dnm", [0x04001] = "vnmlaF.dnm", + [0x08000] = "vmulF.dnm", [0x08001] = "vnmulF.dnm", + [0x0c000] = "vaddF.dnm", [0x0c001] = "vsubF.dnm", + [0x20000] = "vdivF.dnm", + [0x24000] = "vfnmsF.dnm", [0x24001] = "vfnmaF.dnm", + [0x28000] = "vfmaF.dnm", [0x28001] = "vfmsF.dnm", + [0x2c000] = "vmovF.dY", + [0x2c001] = { + shift = 7, mask = 0x1e01, + [0] = "vmovF.dm", "vabsF.dm", + [0x0200] = "vnegF.dm", [0x0201] = "vsqrtF.dm", + [0x0800] = "vcmpF.dm", [0x0801] = "vcmpeF.dm", + [0x0a00] = "vcmpzF.d", [0x0a01] = "vcmpzeF.d", + [0x0e01] = "vcvtG.dF.m", + [0x1000] = "vcvt.f32.u32Fdm", [0x1001] = "vcvt.f32.s32Fdm", + [0x1800] = "vcvtr.u32F.dm", [0x1801] = "vcvt.u32F.dm", + [0x1a00] = "vcvtr.s32F.dm", [0x1a01] = "vcvt.s32F.dm", + }, +} + +local map_vfpd = { + shift = 6, mask = 0x2c001, + [0] = "vmlaG.dnm", "vmlsG.dnm", + [0x04000] = "vnmlsG.dnm", [0x04001] = "vnmlaG.dnm", + [0x08000] = "vmulG.dnm", [0x08001] = "vnmulG.dnm", + [0x0c000] = "vaddG.dnm", [0x0c001] = "vsubG.dnm", + [0x20000] = "vdivG.dnm", + [0x24000] = "vfnmsG.dnm", [0x24001] = "vfnmaG.dnm", + [0x28000] = "vfmaG.dnm", [0x28001] = "vfmsG.dnm", + [0x2c000] = "vmovG.dY", + [0x2c001] = { + shift = 7, mask = 0x1e01, + [0] = "vmovG.dm", "vabsG.dm", + [0x0200] = "vnegG.dm", [0x0201] = "vsqrtG.dm", + [0x0800] = "vcmpG.dm", [0x0801] = "vcmpeG.dm", + [0x0a00] = "vcmpzG.d", [0x0a01] = "vcmpzeG.d", + [0x0e01] = "vcvtF.dG.m", + [0x1000] = "vcvt.f64.u32GdFm", [0x1001] = "vcvt.f64.s32GdFm", + [0x1800] = "vcvtr.u32FdG.m", [0x1801] = "vcvt.u32FdG.m", + [0x1a00] = "vcvtr.s32FdG.m", [0x1a01] = "vcvt.s32FdG.m", + }, +} + +local map_datac = { + shift = 24, mask = 1, + [0] = { + shift = 4, mask = 1, + [0] = { + shift = 8, mask = 15, + [10] = map_vfps, + [11] = map_vfpd, + -- NYI cdp, mcr, mrc. + }, + { + shift = 8, mask = 15, + [10] = { + shift = 20, mask = 15, + [0] = "vmovFnD", "vmovFDn", + [14] = "vmsrD", + [15] = { shift = 12, mask = 15, [15] = "vmrs", _ = "vmrsD", }, + }, + }, + }, + "svcT", +} + +local map_loadcu = { + shift = 0, mask = 0, -- NYI unconditional CP load/store. +} + +local map_datacu = { + shift = 0, mask = 0, -- NYI unconditional CP data. +} + +local map_simddata = { + shift = 0, mask = 0, -- NYI SIMD data. +} + +local map_simdload = { + shift = 0, mask = 0, -- NYI SIMD load/store, preload. +} + +local map_preload = { + shift = 0, mask = 0, -- NYI preload. +} + +local map_media = { + shift = 20, mask = 31, + [0] = false, + { --01 + shift = 5, mask = 7, + [0] = "sadd16DNM", "sasxDNM", "ssaxDNM", "ssub16DNM", + "sadd8DNM", false, false, "ssub8DNM", + }, + { --02 + shift = 5, mask = 7, + [0] = "qadd16DNM", "qasxDNM", "qsaxDNM", "qsub16DNM", + "qadd8DNM", false, false, "qsub8DNM", + }, + { --03 + shift = 5, mask = 7, + [0] = "shadd16DNM", "shasxDNM", "shsaxDNM", "shsub16DNM", + "shadd8DNM", false, false, "shsub8DNM", + }, + false, + { --05 + shift = 5, mask = 7, + [0] = "uadd16DNM", "uasxDNM", "usaxDNM", "usub16DNM", + "uadd8DNM", false, false, "usub8DNM", + }, + { --06 + shift = 5, mask = 7, + [0] = "uqadd16DNM", "uqasxDNM", "uqsaxDNM", "uqsub16DNM", + "uqadd8DNM", false, false, "uqsub8DNM", + }, + { --07 + shift = 5, mask = 7, + [0] = "uhadd16DNM", "uhasxDNM", "uhsaxDNM", "uhsub16DNM", + "uhadd8DNM", false, false, "uhsub8DNM", + }, + { --08 + shift = 5, mask = 7, + [0] = "pkhbtDNMU", false, "pkhtbDNMU", + { shift = 16, mask = 15, [15] = "sxtb16DMU", _ = "sxtab16DNMU", }, + "pkhbtDNMU", "selDNM", "pkhtbDNMU", + }, + false, + { --0a + shift = 5, mask = 7, + [0] = "ssatDxMu", "ssat16DxM", "ssatDxMu", + { shift = 16, mask = 15, [15] = "sxtbDMU", _ = "sxtabDNMU", }, + "ssatDxMu", false, "ssatDxMu", + }, + { --0b + shift = 5, mask = 7, + [0] = "ssatDxMu", "revDM", "ssatDxMu", + { shift = 16, mask = 15, [15] = "sxthDMU", _ = "sxtahDNMU", }, + "ssatDxMu", "rev16DM", "ssatDxMu", + }, + { --0c + shift = 5, mask = 7, + [3] = { shift = 16, mask = 15, [15] = "uxtb16DMU", _ = "uxtab16DNMU", }, + }, + false, + { --0e + shift = 5, mask = 7, + [0] = "usatDwMu", "usat16DwM", "usatDwMu", + { shift = 16, mask = 15, [15] = "uxtbDMU", _ = "uxtabDNMU", }, + "usatDwMu", false, "usatDwMu", + }, + { --0f + shift = 5, mask = 7, + [0] = "usatDwMu", "rbitDM", "usatDwMu", + { shift = 16, mask = 15, [15] = "uxthDMU", _ = "uxtahDNMU", }, + "usatDwMu", "revshDM", "usatDwMu", + }, + { --10 + shift = 12, mask = 15, + [15] = { + shift = 5, mask = 7, + "smuadNMS", "smuadxNMS", "smusdNMS", "smusdxNMS", + }, + _ = { + shift = 5, mask = 7, + [0] = "smladNMSD", "smladxNMSD", "smlsdNMSD", "smlsdxNMSD", + }, + }, + false, false, false, + { --14 + shift = 5, mask = 7, + [0] = "smlaldDNMS", "smlaldxDNMS", "smlsldDNMS", "smlsldxDNMS", + }, + { --15 + shift = 5, mask = 7, + [0] = { shift = 12, mask = 15, [15] = "smmulNMS", _ = "smmlaNMSD", }, + { shift = 12, mask = 15, [15] = "smmulrNMS", _ = "smmlarNMSD", }, + false, false, false, false, + "smmlsNMSD", "smmlsrNMSD", + }, + false, false, + { --18 + shift = 5, mask = 7, + [0] = { shift = 12, mask = 15, [15] = "usad8NMS", _ = "usada8NMSD", }, + }, + false, + { --1a + shift = 5, mask = 3, [2] = "sbfxDMvw", + }, + { --1b + shift = 5, mask = 3, [2] = "sbfxDMvw", + }, + { --1c + shift = 5, mask = 3, + [0] = { shift = 0, mask = 15, [15] = "bfcDvX", _ = "bfiDMvX", }, + }, + { --1d + shift = 5, mask = 3, + [0] = { shift = 0, mask = 15, [15] = "bfcDvX", _ = "bfiDMvX", }, + }, + { --1e + shift = 5, mask = 3, [2] = "ubfxDMvw", + }, + { --1f + shift = 5, mask = 3, [2] = "ubfxDMvw", + }, +} + +local map_load = { + shift = 21, mask = 9, + { + shift = 20, mask = 5, + [0] = "strtDL", "ldrtDL", [4] = "strbtDL", [5] = "ldrbtDL", + }, + _ = { + shift = 20, mask = 5, + [0] = "strDL", "ldrDL", [4] = "strbDL", [5] = "ldrbDL", + } +} + +local map_load1 = { + shift = 4, mask = 1, + [0] = map_load, map_media, +} + +local map_loadm = { + shift = 20, mask = 1, + [0] = { + shift = 23, mask = 3, + [0] = "stmdaNR", "stmNR", + { shift = 16, mask = 63, [45] = "pushR", _ = "stmdbNR", }, "stmibNR", + }, + { + shift = 23, mask = 3, + [0] = "ldmdaNR", { shift = 16, mask = 63, [61] = "popR", _ = "ldmNR", }, + "ldmdbNR", "ldmibNR", + }, +} + +local map_data = { + shift = 21, mask = 15, + [0] = "andDNPs", "eorDNPs", "subDNPs", "rsbDNPs", + "addDNPs", "adcDNPs", "sbcDNPs", "rscDNPs", + "tstNP", "teqNP", "cmpNP", "cmnNP", + "orrDNPs", "movDPs", "bicDNPs", "mvnDPs", +} + +local map_mul = { + shift = 21, mask = 7, + [0] = "mulNMSs", "mlaNMSDs", "umaalDNMS", "mlsDNMS", + "umullDNMSs", "umlalDNMSs", "smullDNMSs", "smlalDNMSs", +} + +local map_sync = { + shift = 20, mask = 15, -- NYI: brackets around N. R(D+1) for ldrexd/strexd. + [0] = "swpDMN", false, false, false, + "swpbDMN", false, false, false, + "strexDMN", "ldrexDN", "strexdDN", "ldrexdDN", + "strexbDMN", "ldrexbDN", "strexhDN", "ldrexhDN", +} + +local map_mulh = { + shift = 21, mask = 3, + [0] = { shift = 5, mask = 3, + [0] = "smlabbNMSD", "smlatbNMSD", "smlabtNMSD", "smlattNMSD", }, + { shift = 5, mask = 3, + [0] = "smlawbNMSD", "smulwbNMS", "smlawtNMSD", "smulwtNMS", }, + { shift = 5, mask = 3, + [0] = "smlalbbDNMS", "smlaltbDNMS", "smlalbtDNMS", "smlalttDNMS", }, + { shift = 5, mask = 3, + [0] = "smulbbNMS", "smultbNMS", "smulbtNMS", "smulttNMS", }, +} + +local map_misc = { + shift = 4, mask = 7, + -- NYI: decode PSR bits of msr. + [0] = { shift = 21, mask = 1, [0] = "mrsD", "msrM", }, + { shift = 21, mask = 3, "bxM", false, "clzDM", }, + { shift = 21, mask = 3, "bxjM", }, + { shift = 21, mask = 3, "blxM", }, + false, + { shift = 21, mask = 3, [0] = "qaddDMN", "qsubDMN", "qdaddDMN", "qdsubDMN", }, + false, + { shift = 21, mask = 3, "bkptK", }, +} + +local map_datar = { + shift = 4, mask = 9, + [9] = { + shift = 5, mask = 3, + [0] = { shift = 24, mask = 1, [0] = map_mul, map_sync, }, + { shift = 20, mask = 1, [0] = "strhDL", "ldrhDL", }, + { shift = 20, mask = 1, [0] = "ldrdDL", "ldrsbDL", }, + { shift = 20, mask = 1, [0] = "strdDL", "ldrshDL", }, + }, + _ = { + shift = 20, mask = 25, + [16] = { shift = 7, mask = 1, [0] = map_misc, map_mulh, }, + _ = { + shift = 0, mask = 0xffffffff, + [bor(0xe1a00000)] = "nop", + _ = map_data, + } + }, +} + +local map_datai = { + shift = 20, mask = 31, -- NYI: decode PSR bits of msr. Decode imm12. + [16] = "movwDW", [20] = "movtDW", + [18] = { shift = 0, mask = 0xf00ff, [0] = "nopv6", _ = "msrNW", }, + [22] = "msrNW", + _ = map_data, +} + +local map_branch = { + shift = 24, mask = 1, + [0] = "bB", "blB" +} + +local map_condins = { + [0] = map_datar, map_datai, map_load, map_load1, + map_loadm, map_branch, map_loadc, map_datac +} + +-- NYI: setend. +local map_uncondins = { + [0] = false, map_simddata, map_simdload, map_preload, + false, "blxB", map_loadcu, map_datacu, +} + +------------------------------------------------------------------------------ + +local map_gpr = { + [0] = "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", + "r8", "r9", "r10", "r11", "r12", "sp", "lr", "pc", +} + +local map_cond = { + [0] = "eq", "ne", "hs", "lo", "mi", "pl", "vs", "vc", + "hi", "ls", "ge", "lt", "gt", "le", "al", +} + +local map_shift = { [0] = "lsl", "lsr", "asr", "ror", } + +------------------------------------------------------------------------------ + +-- Output a nicely formatted line with an opcode and operands. +local function putop(ctx, text, operands) + local pos = ctx.pos + local extra = "" + if ctx.rel then + local sym = ctx.symtab[ctx.rel] + if sym then + extra = "\t->"..sym + elseif band(ctx.op, 0x0e000000) ~= 0x0a000000 then + extra = "\t; 0x"..tohex(ctx.rel) + end + end + if ctx.hexdump > 0 then + ctx.out(format("%08x %s %-5s %s%s\n", + ctx.addr+pos, tohex(ctx.op), text, concat(operands, ", "), extra)) + else + ctx.out(format("%08x %-5s %s%s\n", + ctx.addr+pos, text, concat(operands, ", "), extra)) + end + ctx.pos = pos + 4 +end + +-- Fallback for unknown opcodes. +local function unknown(ctx) + return putop(ctx, ".long", { "0x"..tohex(ctx.op) }) +end + +-- Format operand 2 of load/store opcodes. +local function fmtload(ctx, op, pos) + local base = map_gpr[band(rshift(op, 16), 15)] + local x, ofs + local ext = (band(op, 0x04000000) == 0) + if not ext and band(op, 0x02000000) == 0 then + ofs = band(op, 4095) + if band(op, 0x00800000) == 0 then ofs = -ofs end + if base == "pc" then ctx.rel = ctx.addr + pos + 8 + ofs end + ofs = "#"..ofs + elseif ext and band(op, 0x00400000) ~= 0 then + ofs = band(op, 15) + band(rshift(op, 4), 0xf0) + if band(op, 0x00800000) == 0 then ofs = -ofs end + if base == "pc" then ctx.rel = ctx.addr + pos + 8 + ofs end + ofs = "#"..ofs + else + ofs = map_gpr[band(op, 15)] + if ext or band(op, 0xfe0) == 0 then + elseif band(op, 0xfe0) == 0x60 then + ofs = format("%s, rrx", ofs) + else + local sh = band(rshift(op, 7), 31) + if sh == 0 then sh = 32 end + ofs = format("%s, %s #%d", ofs, map_shift[band(rshift(op, 5), 3)], sh) + end + if band(op, 0x00800000) == 0 then ofs = "-"..ofs end + end + if ofs == "#0" then + x = format("[%s]", base) + elseif band(op, 0x01000000) == 0 then + x = format("[%s], %s", base, ofs) + else + x = format("[%s, %s]", base, ofs) + end + if band(op, 0x01200000) == 0x01200000 then x = x.."!" end + return x +end + +-- Format operand 2 of vector load/store opcodes. +local function fmtvload(ctx, op, pos) + local base = map_gpr[band(rshift(op, 16), 15)] + local ofs = band(op, 255)*4 + if band(op, 0x00800000) == 0 then ofs = -ofs end + if base == "pc" then ctx.rel = ctx.addr + pos + 8 + ofs end + if ofs == 0 then + return format("[%s]", base) + else + return format("[%s, #%d]", base, ofs) + end +end + +local function fmtvr(op, vr, sh0, sh1) + if vr == "s" then + return format("s%d", 2*band(rshift(op, sh0), 15)+band(rshift(op, sh1), 1)) + else + return format("d%d", band(rshift(op, sh0), 15)+band(rshift(op, sh1-4), 16)) + end +end + +-- Disassemble a single instruction. +local function disass_ins(ctx) + local pos = ctx.pos + local b0, b1, b2, b3 = byte(ctx.code, pos+1, pos+4) + local op = bor(lshift(b3, 24), lshift(b2, 16), lshift(b1, 8), b0) + local operands = {} + local suffix = "" + local last, name, pat + local vr + ctx.op = op + ctx.rel = nil + + local cond = rshift(op, 28) + local opat + if cond == 15 then + opat = map_uncondins[band(rshift(op, 25), 7)] + else + if cond ~= 14 then suffix = map_cond[cond] end + opat = map_condins[band(rshift(op, 25), 7)] + end + while type(opat) ~= "string" do + if not opat then return unknown(ctx) end + opat = opat[band(rshift(op, opat.shift), opat.mask)] or opat._ + end + name, pat = match(opat, "^([a-z0-9]*)(.*)") + if sub(pat, 1, 1) == "." then + local s2, p2 = match(pat, "^([a-z0-9.]*)(.*)") + suffix = suffix..s2 + pat = p2 + end + + for p in gmatch(pat, ".") do + local x = nil + if p == "D" then + x = map_gpr[band(rshift(op, 12), 15)] + elseif p == "N" then + x = map_gpr[band(rshift(op, 16), 15)] + elseif p == "S" then + x = map_gpr[band(rshift(op, 8), 15)] + elseif p == "M" then + x = map_gpr[band(op, 15)] + elseif p == "d" then + x = fmtvr(op, vr, 12, 22) + elseif p == "n" then + x = fmtvr(op, vr, 16, 7) + elseif p == "m" then + x = fmtvr(op, vr, 0, 5) + elseif p == "P" then + if band(op, 0x02000000) ~= 0 then + x = ror(band(op, 255), 2*band(rshift(op, 8), 15)) + else + x = map_gpr[band(op, 15)] + if band(op, 0xff0) ~= 0 then + operands[#operands+1] = x + local s = map_shift[band(rshift(op, 5), 3)] + local r = nil + if band(op, 0xf90) == 0 then + if s == "ror" then s = "rrx" else r = "#32" end + elseif band(op, 0x10) == 0 then + r = "#"..band(rshift(op, 7), 31) + else + r = map_gpr[band(rshift(op, 8), 15)] + end + if name == "mov" then name = s; x = r + elseif r then x = format("%s %s", s, r) + else x = s end + end + end + elseif p == "L" then + x = fmtload(ctx, op, pos) + elseif p == "l" then + x = fmtvload(ctx, op, pos) + elseif p == "B" then + local addr = ctx.addr + pos + 8 + arshift(lshift(op, 8), 6) + if cond == 15 then addr = addr + band(rshift(op, 23), 2) end + ctx.rel = addr + x = "0x"..tohex(addr) + elseif p == "F" then + vr = "s" + elseif p == "G" then + vr = "d" + elseif p == "." then + suffix = suffix..(vr == "s" and ".f32" or ".f64") + elseif p == "R" then + if band(op, 0x00200000) ~= 0 and #operands == 1 then + operands[1] = operands[1].."!" + end + local t = {} + for i=0,15 do + if band(rshift(op, i), 1) == 1 then t[#t+1] = map_gpr[i] end + end + x = "{"..concat(t, ", ").."}" + elseif p == "r" then + if band(op, 0x00200000) ~= 0 and #operands == 2 then + operands[1] = operands[1].."!" + end + local s = tonumber(sub(last, 2)) + local n = band(op, 255) + if vr == "d" then n = rshift(n, 1) end + operands[#operands] = format("{%s-%s%d}", last, vr, s+n-1) + elseif p == "W" then + x = band(op, 0x0fff) + band(rshift(op, 4), 0xf000) + elseif p == "T" then + x = "#0x"..tohex(band(op, 0x00ffffff), 6) + elseif p == "U" then + x = band(rshift(op, 7), 31) + if x == 0 then x = nil end + elseif p == "u" then + x = band(rshift(op, 7), 31) + if band(op, 0x40) == 0 then + if x == 0 then x = nil else x = "lsl #"..x end + else + if x == 0 then x = "asr #32" else x = "asr #"..x end + end + elseif p == "v" then + x = band(rshift(op, 7), 31) + elseif p == "w" then + x = band(rshift(op, 16), 31) + elseif p == "x" then + x = band(rshift(op, 16), 31) + 1 + elseif p == "X" then + x = band(rshift(op, 16), 31) - last + 1 + elseif p == "Y" then + x = band(rshift(op, 12), 0xf0) + band(op, 0x0f) + elseif p == "K" then + x = "#0x"..tohex(band(rshift(op, 4), 0x0000fff0) + band(op, 15), 4) + elseif p == "s" then + if band(op, 0x00100000) ~= 0 then suffix = "s"..suffix end + else + assert(false) + end + if x then + last = x + if type(x) == "number" then x = "#"..x end + operands[#operands+1] = x + end + end + + return putop(ctx, name..suffix, operands) +end + +------------------------------------------------------------------------------ + +-- Disassemble a block of code. +local function disass_block(ctx, ofs, len) + if not ofs then ofs = 0 end + local stop = len and ofs+len or #ctx.code + ctx.pos = ofs + ctx.rel = nil + while ctx.pos < stop do disass_ins(ctx) end +end + +-- Extended API: create a disassembler context. Then call ctx:disass(ofs, len). +local function create(code, addr, out) + local ctx = {} + ctx.code = code + ctx.addr = addr or 0 + ctx.out = out or io.write + ctx.symtab = {} + ctx.disass = disass_block + ctx.hexdump = 8 + return ctx +end + +-- Simple API: disassemble code (a string) at address and output via out. +local function disass(code, addr, out) + create(code, addr, out):disass() +end + +-- Return register name for RID. +local function regname(r) + if r < 16 then return map_gpr[r] end + return "d"..(r-16) +end + +-- Public module functions. +return { + create = create, + disass = disass, + regname = regname +} + diff --git a/lib/LuaJIT/jit/dis_arm64.lua b/lib/LuaJIT/jit/dis_arm64.lua new file mode 100644 index 0000000..a717332 --- /dev/null +++ b/lib/LuaJIT/jit/dis_arm64.lua @@ -0,0 +1,1216 @@ +---------------------------------------------------------------------------- +-- LuaJIT ARM64 disassembler module. +-- +-- Copyright (C) 2005-2017 Mike Pall. All rights reserved. +-- Released under the MIT license. See Copyright Notice in luajit.h +-- +-- Contributed by Djordje Kovacevic and Stefan Pejic from RT-RK.com. +-- Sponsored by Cisco Systems, Inc. +---------------------------------------------------------------------------- +-- This is a helper module used by the LuaJIT machine code dumper module. +-- +-- It disassembles most user-mode AArch64 instructions. +-- NYI: Advanced SIMD and VFP instructions. +------------------------------------------------------------------------------ + +local type = type +local sub, byte, format = string.sub, string.byte, string.format +local match, gmatch, gsub = string.match, string.gmatch, string.gsub +local concat = table.concat +local bit = require("bit") +local band, bor, bxor, tohex = bit.band, bit.bor, bit.bxor, bit.tohex +local lshift, rshift, arshift = bit.lshift, bit.rshift, bit.arshift +local ror = bit.ror + +------------------------------------------------------------------------------ +-- Opcode maps +------------------------------------------------------------------------------ + +local map_adr = { -- PC-relative addressing. + shift = 31, mask = 1, + [0] = "adrDBx", "adrpDBx" +} + +local map_addsubi = { -- Add/subtract immediate. + shift = 29, mask = 3, + [0] = "add|movDNIg", "adds|cmnD0NIg", "subDNIg", "subs|cmpD0NIg", +} + +local map_logi = { -- Logical immediate. + shift = 31, mask = 1, + [0] = { + shift = 22, mask = 1, + [0] = { + shift = 29, mask = 3, + [0] = "andDNig", "orr|movDN0ig", "eorDNig", "ands|tstD0Nig" + }, + false -- unallocated + }, + { + shift = 29, mask = 3, + [0] = "andDNig", "orr|movDN0ig", "eorDNig", "ands|tstD0Nig" + } +} + +local map_movwi = { -- Move wide immediate. + shift = 31, mask = 1, + [0] = { + shift = 22, mask = 1, + [0] = { + shift = 29, mask = 3, + [0] = "movnDWRg", false, "movz|movDYRg", "movkDWRg" + }, false -- unallocated + }, + { + shift = 29, mask = 3, + [0] = "movnDWRg", false, "movz|movDYRg", "movkDWRg" + }, +} + +local map_bitf = { -- Bitfield. + shift = 31, mask = 1, + [0] = { + shift = 22, mask = 1, + [0] = { + shift = 29, mask = 3, + [0] = "sbfm|sbfiz|sbfx|asr|sxtw|sxth|sxtbDN12w", + "bfm|bfi|bfxilDN13w", + "ubfm|ubfiz|ubfx|lsr|lsl|uxth|uxtbDN12w" + } + }, + { + shift = 22, mask = 1, + { + shift = 29, mask = 3, + [0] = "sbfm|sbfiz|sbfx|asr|sxtw|sxth|sxtbDN12x", + "bfm|bfi|bfxilDN13x", + "ubfm|ubfiz|ubfx|lsr|lsl|uxth|uxtbDN12x" + } + } +} + +local map_datai = { -- Data processing - immediate. + shift = 23, mask = 7, + [0] = map_adr, map_adr, map_addsubi, false, + map_logi, map_movwi, map_bitf, + { + shift = 15, mask = 0x1c0c1, + [0] = "extr|rorDNM4w", [0x10080] = "extr|rorDNM4x", + [0x10081] = "extr|rorDNM4x" + } +} + +local map_logsr = { -- Logical, shifted register. + shift = 31, mask = 1, + [0] = { + shift = 15, mask = 1, + [0] = { + shift = 29, mask = 3, + [0] = { + shift = 21, mask = 7, + [0] = "andDNMSg", "bicDNMSg", "andDNMSg", "bicDNMSg", + "andDNMSg", "bicDNMSg", "andDNMg", "bicDNMg" + }, + { + shift = 21, mask = 7, + [0] ="orr|movDN0MSg", "orn|mvnDN0MSg", "orr|movDN0MSg", "orn|mvnDN0MSg", + "orr|movDN0MSg", "orn|mvnDN0MSg", "orr|movDN0Mg", "orn|mvnDN0Mg" + }, + { + shift = 21, mask = 7, + [0] = "eorDNMSg", "eonDNMSg", "eorDNMSg", "eonDNMSg", + "eorDNMSg", "eonDNMSg", "eorDNMg", "eonDNMg" + }, + { + shift = 21, mask = 7, + [0] = "ands|tstD0NMSg", "bicsDNMSg", "ands|tstD0NMSg", "bicsDNMSg", + "ands|tstD0NMSg", "bicsDNMSg", "ands|tstD0NMg", "bicsDNMg" + } + }, + false -- unallocated + }, + { + shift = 29, mask = 3, + [0] = { + shift = 21, mask = 7, + [0] = "andDNMSg", "bicDNMSg", "andDNMSg", "bicDNMSg", + "andDNMSg", "bicDNMSg", "andDNMg", "bicDNMg" + }, + { + shift = 21, mask = 7, + [0] = "orr|movDN0MSg", "orn|mvnDN0MSg", "orr|movDN0MSg", "orn|mvnDN0MSg", + "orr|movDN0MSg", "orn|mvnDN0MSg", "orr|movDN0Mg", "orn|mvnDN0Mg" + }, + { + shift = 21, mask = 7, + [0] = "eorDNMSg", "eonDNMSg", "eorDNMSg", "eonDNMSg", + "eorDNMSg", "eonDNMSg", "eorDNMg", "eonDNMg" + }, + { + shift = 21, mask = 7, + [0] = "ands|tstD0NMSg", "bicsDNMSg", "ands|tstD0NMSg", "bicsDNMSg", + "ands|tstD0NMSg", "bicsDNMSg", "ands|tstD0NMg", "bicsDNMg" + } + } +} + +local map_assh = { + shift = 31, mask = 1, + [0] = { + shift = 15, mask = 1, + [0] = { + shift = 29, mask = 3, + [0] = { + shift = 22, mask = 3, + [0] = "addDNMSg", "addDNMSg", "addDNMSg", "addDNMg" + }, + { + shift = 22, mask = 3, + [0] = "adds|cmnD0NMSg", "adds|cmnD0NMSg", + "adds|cmnD0NMSg", "adds|cmnD0NMg" + }, + { + shift = 22, mask = 3, + [0] = "sub|negDN0MSg", "sub|negDN0MSg", "sub|negDN0MSg", "sub|negDN0Mg" + }, + { + shift = 22, mask = 3, + [0] = "subs|cmp|negsD0N0MzSg", "subs|cmp|negsD0N0MzSg", + "subs|cmp|negsD0N0MzSg", "subs|cmp|negsD0N0Mzg" + }, + }, + false -- unallocated + }, + { + shift = 29, mask = 3, + [0] = { + shift = 22, mask = 3, + [0] = "addDNMSg", "addDNMSg", "addDNMSg", "addDNMg" + }, + { + shift = 22, mask = 3, + [0] = "adds|cmnD0NMSg", "adds|cmnD0NMSg", "adds|cmnD0NMSg", + "adds|cmnD0NMg" + }, + { + shift = 22, mask = 3, + [0] = "sub|negDN0MSg", "sub|negDN0MSg", "sub|negDN0MSg", "sub|negDN0Mg" + }, + { + shift = 22, mask = 3, + [0] = "subs|cmp|negsD0N0MzSg", "subs|cmp|negsD0N0MzSg", + "subs|cmp|negsD0N0MzSg", "subs|cmp|negsD0N0Mzg" + } + } +} + +local map_addsubsh = { -- Add/subtract, shifted register. + shift = 22, mask = 3, + [0] = map_assh, map_assh, map_assh +} + +local map_addsubex = { -- Add/subtract, extended register. + shift = 22, mask = 3, + [0] = { + shift = 29, mask = 3, + [0] = "addDNMXg", "adds|cmnD0NMXg", "subDNMXg", "subs|cmpD0NMzXg", + } +} + +local map_addsubc = { -- Add/subtract, with carry. + shift = 10, mask = 63, + [0] = { + shift = 29, mask = 3, + [0] = "adcDNMg", "adcsDNMg", "sbc|ngcDN0Mg", "sbcs|ngcsDN0Mg", + } +} + +local map_ccomp = { + shift = 4, mask = 1, + [0] = { + shift = 10, mask = 3, + [0] = { -- Conditional compare register. + shift = 29, mask = 3, + "ccmnNMVCg", false, "ccmpNMVCg", + }, + [2] = { -- Conditional compare immediate. + shift = 29, mask = 3, + "ccmnN5VCg", false, "ccmpN5VCg", + } + } +} + +local map_csel = { -- Conditional select. + shift = 11, mask = 1, + [0] = { + shift = 10, mask = 1, + [0] = { + shift = 29, mask = 3, + [0] = "cselDNMzCg", false, "csinv|cinv|csetmDNMcg", false, + }, + { + shift = 29, mask = 3, + [0] = "csinc|cinc|csetDNMcg", false, "csneg|cnegDNMcg", false, + } + } +} + +local map_data1s = { -- Data processing, 1 source. + shift = 29, mask = 1, + [0] = { + shift = 31, mask = 1, + [0] = { + shift = 10, mask = 0x7ff, + [0] = "rbitDNg", "rev16DNg", "revDNw", false, "clzDNg", "clsDNg" + }, + { + shift = 10, mask = 0x7ff, + [0] = "rbitDNg", "rev16DNg", "rev32DNx", "revDNx", "clzDNg", "clsDNg" + } + } +} + +local map_data2s = { -- Data processing, 2 sources. + shift = 29, mask = 1, + [0] = { + shift = 10, mask = 63, + false, "udivDNMg", "sdivDNMg", false, false, false, false, "lslDNMg", + "lsrDNMg", "asrDNMg", "rorDNMg" + } +} + +local map_data3s = { -- Data processing, 3 sources. + shift = 29, mask = 7, + [0] = { + shift = 21, mask = 7, + [0] = { + shift = 15, mask = 1, + [0] = "madd|mulDNMA0g", "msub|mnegDNMA0g" + } + }, false, false, false, + { + shift = 15, mask = 1, + [0] = { + shift = 21, mask = 7, + [0] = "madd|mulDNMA0g", "smaddl|smullDxNMwA0x", "smulhDNMx", false, + false, "umaddl|umullDxNMwA0x", "umulhDNMx" + }, + { + shift = 21, mask = 7, + [0] = "msub|mnegDNMA0g", "smsubl|smneglDxNMwA0x", false, false, + false, "umsubl|umneglDxNMwA0x" + } + } +} + +local map_datar = { -- Data processing, register. + shift = 28, mask = 1, + [0] = { + shift = 24, mask = 1, + [0] = map_logsr, + { + shift = 21, mask = 1, + [0] = map_addsubsh, map_addsubex + } + }, + { + shift = 21, mask = 15, + [0] = map_addsubc, false, map_ccomp, false, map_csel, false, + { + shift = 30, mask = 1, + [0] = map_data2s, map_data1s + }, + false, map_data3s, map_data3s, map_data3s, map_data3s, map_data3s, + map_data3s, map_data3s, map_data3s + } +} + +local map_lrl = { -- Load register, literal. + shift = 26, mask = 1, + [0] = { + shift = 30, mask = 3, + [0] = "ldrDwB", "ldrDxB", "ldrswDxB" + }, + { + shift = 30, mask = 3, + [0] = "ldrDsB", "ldrDdB" + } +} + +local map_lsriind = { -- Load/store register, immediate pre/post-indexed. + shift = 30, mask = 3, + [0] = { + shift = 26, mask = 1, + [0] = { + shift = 22, mask = 3, + [0] = "strbDwzL", "ldrbDwzL", "ldrsbDxzL", "ldrsbDwzL" + } + }, + { + shift = 26, mask = 1, + [0] = { + shift = 22, mask = 3, + [0] = "strhDwzL", "ldrhDwzL", "ldrshDxzL", "ldrshDwzL" + } + }, + { + shift = 26, mask = 1, + [0] = { + shift = 22, mask = 3, + [0] = "strDwzL", "ldrDwzL", "ldrswDxzL" + }, + { + shift = 22, mask = 3, + [0] = "strDszL", "ldrDszL" + } + }, + { + shift = 26, mask = 1, + [0] = { + shift = 22, mask = 3, + [0] = "strDxzL", "ldrDxzL" + }, + { + shift = 22, mask = 3, + [0] = "strDdzL", "ldrDdzL" + } + } +} + +local map_lsriro = { + shift = 21, mask = 1, + [0] = { -- Load/store register immediate. + shift = 10, mask = 3, + [0] = { -- Unscaled immediate. + shift = 26, mask = 1, + [0] = { + shift = 30, mask = 3, + [0] = { + shift = 22, mask = 3, + [0] = "sturbDwK", "ldurbDwK" + }, + { + shift = 22, mask = 3, + [0] = "sturhDwK", "ldurhDwK" + }, + { + shift = 22, mask = 3, + [0] = "sturDwK", "ldurDwK" + }, + { + shift = 22, mask = 3, + [0] = "sturDxK", "ldurDxK" + } + } + }, map_lsriind, false, map_lsriind + }, + { -- Load/store register, register offset. + shift = 10, mask = 3, + [2] = { + shift = 26, mask = 1, + [0] = { + shift = 30, mask = 3, + [0] = { + shift = 22, mask = 3, + [0] = "strbDwO", "ldrbDwO", "ldrsbDxO", "ldrsbDwO" + }, + { + shift = 22, mask = 3, + [0] = "strhDwO", "ldrhDwO", "ldrshDxO", "ldrshDwO" + }, + { + shift = 22, mask = 3, + [0] = "strDwO", "ldrDwO", "ldrswDxO" + }, + { + shift = 22, mask = 3, + [0] = "strDxO", "ldrDxO" + } + }, + { + shift = 30, mask = 3, + [2] = { + shift = 22, mask = 3, + [0] = "strDsO", "ldrDsO" + }, + [3] = { + shift = 22, mask = 3, + [0] = "strDdO", "ldrDdO" + } + } + } + } +} + +local map_lsp = { -- Load/store register pair, offset. + shift = 22, mask = 1, + [0] = { + shift = 30, mask = 3, + [0] = { + shift = 26, mask = 1, + [0] = "stpDzAzwP", "stpDzAzsP", + }, + { + shift = 26, mask = 1, + "stpDzAzdP" + }, + { + shift = 26, mask = 1, + [0] = "stpDzAzxP" + } + }, + { + shift = 30, mask = 3, + [0] = { + shift = 26, mask = 1, + [0] = "ldpDzAzwP", "ldpDzAzsP", + }, + { + shift = 26, mask = 1, + [0] = "ldpswDAxP", "ldpDzAzdP" + }, + { + shift = 26, mask = 1, + [0] = "ldpDzAzxP" + } + } +} + +local map_ls = { -- Loads and stores. + shift = 24, mask = 0x31, + [0x10] = map_lrl, [0x30] = map_lsriro, + [0x20] = { + shift = 23, mask = 3, + map_lsp, map_lsp, map_lsp + }, + [0x21] = { + shift = 23, mask = 3, + map_lsp, map_lsp, map_lsp + }, + [0x31] = { + shift = 26, mask = 1, + [0] = { + shift = 30, mask = 3, + [0] = { + shift = 22, mask = 3, + [0] = "strbDwzU", "ldrbDwzU" + }, + { + shift = 22, mask = 3, + [0] = "strhDwzU", "ldrhDwzU" + }, + { + shift = 22, mask = 3, + [0] = "strDwzU", "ldrDwzU" + }, + { + shift = 22, mask = 3, + [0] = "strDxzU", "ldrDxzU" + } + }, + { + shift = 30, mask = 3, + [2] = { + shift = 22, mask = 3, + [0] = "strDszU", "ldrDszU" + }, + [3] = { + shift = 22, mask = 3, + [0] = "strDdzU", "ldrDdzU" + } + } + }, +} + +local map_datafp = { -- Data processing, SIMD and FP. + shift = 28, mask = 7, + { -- 001 + shift = 24, mask = 1, + [0] = { + shift = 21, mask = 1, + { + shift = 10, mask = 3, + [0] = { + shift = 12, mask = 1, + [0] = { + shift = 13, mask = 1, + [0] = { + shift = 14, mask = 1, + [0] = { + shift = 15, mask = 1, + [0] = { -- FP/int conversion. + shift = 31, mask = 1, + [0] = { + shift = 16, mask = 0xff, + [0x20] = "fcvtnsDwNs", [0x21] = "fcvtnuDwNs", + [0x22] = "scvtfDsNw", [0x23] = "ucvtfDsNw", + [0x24] = "fcvtasDwNs", [0x25] = "fcvtauDwNs", + [0x26] = "fmovDwNs", [0x27] = "fmovDsNw", + [0x28] = "fcvtpsDwNs", [0x29] = "fcvtpuDwNs", + [0x30] = "fcvtmsDwNs", [0x31] = "fcvtmuDwNs", + [0x38] = "fcvtzsDwNs", [0x39] = "fcvtzuDwNs", + [0x60] = "fcvtnsDwNd", [0x61] = "fcvtnuDwNd", + [0x62] = "scvtfDdNw", [0x63] = "ucvtfDdNw", + [0x64] = "fcvtasDwNd", [0x65] = "fcvtauDwNd", + [0x68] = "fcvtpsDwNd", [0x69] = "fcvtpuDwNd", + [0x70] = "fcvtmsDwNd", [0x71] = "fcvtmuDwNd", + [0x78] = "fcvtzsDwNd", [0x79] = "fcvtzuDwNd" + }, + { + shift = 16, mask = 0xff, + [0x20] = "fcvtnsDxNs", [0x21] = "fcvtnuDxNs", + [0x22] = "scvtfDsNx", [0x23] = "ucvtfDsNx", + [0x24] = "fcvtasDxNs", [0x25] = "fcvtauDxNs", + [0x28] = "fcvtpsDxNs", [0x29] = "fcvtpuDxNs", + [0x30] = "fcvtmsDxNs", [0x31] = "fcvtmuDxNs", + [0x38] = "fcvtzsDxNs", [0x39] = "fcvtzuDxNs", + [0x60] = "fcvtnsDxNd", [0x61] = "fcvtnuDxNd", + [0x62] = "scvtfDdNx", [0x63] = "ucvtfDdNx", + [0x64] = "fcvtasDxNd", [0x65] = "fcvtauDxNd", + [0x66] = "fmovDxNd", [0x67] = "fmovDdNx", + [0x68] = "fcvtpsDxNd", [0x69] = "fcvtpuDxNd", + [0x70] = "fcvtmsDxNd", [0x71] = "fcvtmuDxNd", + [0x78] = "fcvtzsDxNd", [0x79] = "fcvtzuDxNd" + } + } + }, + { -- FP data-processing, 1 source. + shift = 31, mask = 1, + [0] = { + shift = 22, mask = 3, + [0] = { + shift = 15, mask = 63, + [0] = "fmovDNf", "fabsDNf", "fnegDNf", + "fsqrtDNf", false, "fcvtDdNs", false, false, + "frintnDNf", "frintpDNf", "frintmDNf", "frintzDNf", + "frintaDNf", false, "frintxDNf", "frintiDNf", + }, + { + shift = 15, mask = 63, + [0] = "fmovDNf", "fabsDNf", "fnegDNf", + "fsqrtDNf", "fcvtDsNd", false, false, false, + "frintnDNf", "frintpDNf", "frintmDNf", "frintzDNf", + "frintaDNf", false, "frintxDNf", "frintiDNf", + } + } + } + }, + { -- FP compare. + shift = 31, mask = 1, + [0] = { + shift = 14, mask = 3, + [0] = { + shift = 23, mask = 1, + [0] = { + shift = 0, mask = 31, + [0] = "fcmpNMf", [8] = "fcmpNZf", + [16] = "fcmpeNMf", [24] = "fcmpeNZf", + } + } + } + } + }, + { -- FP immediate. + shift = 31, mask = 1, + [0] = { + shift = 5, mask = 31, + [0] = { + shift = 23, mask = 1, + [0] = "fmovDFf" + } + } + } + }, + { -- FP conditional compare. + shift = 31, mask = 1, + [0] = { + shift = 23, mask = 1, + [0] = { + shift = 4, mask = 1, + [0] = "fccmpNMVCf", "fccmpeNMVCf" + } + } + }, + { -- FP data-processing, 2 sources. + shift = 31, mask = 1, + [0] = { + shift = 23, mask = 1, + [0] = { + shift = 12, mask = 15, + [0] = "fmulDNMf", "fdivDNMf", "faddDNMf", "fsubDNMf", + "fmaxDNMf", "fminDNMf", "fmaxnmDNMf", "fminnmDNMf", + "fnmulDNMf" + } + } + }, + { -- FP conditional select. + shift = 31, mask = 1, + [0] = { + shift = 23, mask = 1, + [0] = "fcselDNMCf" + } + } + } + }, + { -- FP data-processing, 3 sources. + shift = 31, mask = 1, + [0] = { + shift = 15, mask = 1, + [0] = { + shift = 21, mask = 5, + [0] = "fmaddDNMAf", "fnmaddDNMAf" + }, + { + shift = 21, mask = 5, + [0] = "fmsubDNMAf", "fnmsubDNMAf" + } + } + } + } +} + +local map_br = { -- Branches, exception generating and system instructions. + shift = 29, mask = 7, + [0] = "bB", + { -- Compare & branch, immediate. + shift = 24, mask = 3, + [0] = "cbzDBg", "cbnzDBg", "tbzDTBw", "tbnzDTBw" + }, + { -- Conditional branch, immediate. + shift = 24, mask = 3, + [0] = { + shift = 4, mask = 1, + [0] = { + shift = 0, mask = 15, + [0] = "beqB", "bneB", "bhsB", "bloB", "bmiB", "bplB", "bvsB", "bvcB", + "bhiB", "blsB", "bgeB", "bltB", "bgtB", "bleB", "balB" + } + } + }, false, "blB", + { -- Compare & branch, immediate. + shift = 24, mask = 3, + [0] = "cbzDBg", "cbnzDBg", "tbzDTBx", "tbnzDTBx" + }, + { + shift = 24, mask = 3, + [0] = { -- Exception generation. + shift = 0, mask = 0xe0001f, + [0x200000] = "brkW" + }, + { -- System instructions. + shift = 0, mask = 0x3fffff, + [0x03201f] = "nop" + }, + { -- Unconditional branch, register. + shift = 0, mask = 0xfffc1f, + [0x1f0000] = "brNx", [0x3f0000] = "blrNx", + [0x5f0000] = "retNx" + }, + } +} + +local map_init = { + shift = 25, mask = 15, + [0] = false, false, false, false, map_ls, map_datar, map_ls, map_datafp, + map_datai, map_datai, map_br, map_br, map_ls, map_datar, map_ls, map_datafp +} + +------------------------------------------------------------------------------ + +local map_regs = { x = {}, w = {}, d = {}, s = {} } + +for i=0,30 do + map_regs.x[i] = "x"..i + map_regs.w[i] = "w"..i + map_regs.d[i] = "d"..i + map_regs.s[i] = "s"..i +end +map_regs.x[31] = "sp" +map_regs.w[31] = "wsp" +map_regs.d[31] = "d31" +map_regs.s[31] = "s31" + +local map_cond = { + [0] = "eq", "ne", "cs", "cc", "mi", "pl", "vs", "vc", + "hi", "ls", "ge", "lt", "gt", "le", "al", +} + +local map_shift = { [0] = "lsl", "lsr", "asr", } + +local map_extend = { + [0] = "uxtb", "uxth", "uxtw", "uxtx", "sxtb", "sxth", "sxtw", "sxtx", +} + +------------------------------------------------------------------------------ + +-- Output a nicely formatted line with an opcode and operands. +local function putop(ctx, text, operands) + local pos = ctx.pos + local extra = "" + if ctx.rel then + local sym = ctx.symtab[ctx.rel] + if sym then + extra = "\t->"..sym + end + end + if ctx.hexdump > 0 then + ctx.out(format("%08x %s %-5s %s%s\n", + ctx.addr+pos, tohex(ctx.op), text, concat(operands, ", "), extra)) + else + ctx.out(format("%08x %-5s %s%s\n", + ctx.addr+pos, text, concat(operands, ", "), extra)) + end + ctx.pos = pos + 4 +end + +-- Fallback for unknown opcodes. +local function unknown(ctx) + return putop(ctx, ".long", { "0x"..tohex(ctx.op) }) +end + +local function match_reg(p, pat, regnum) + return map_regs[match(pat, p.."%w-([xwds])")][regnum] +end + +local function fmt_hex32(x) + if x < 0 then + return tohex(x) + else + return format("%x", x) + end +end + +local imm13_rep = { 0x55555555, 0x11111111, 0x01010101, 0x00010001, 0x00000001 } + +local function decode_imm13(op) + local imms = band(rshift(op, 10), 63) + local immr = band(rshift(op, 16), 63) + if band(op, 0x00400000) == 0 then + local len = 5 + if imms >= 56 then + if imms >= 60 then len = 1 else len = 2 end + elseif imms >= 48 then len = 3 elseif imms >= 32 then len = 4 end + local l = lshift(1, len)-1 + local s = band(imms, l) + local r = band(immr, l) + local imm = ror(rshift(-1, 31-s), r) + if len ~= 5 then imm = band(imm, lshift(1, l)-1) + rshift(imm, 31-l) end + imm = imm * imm13_rep[len] + local ix = fmt_hex32(imm) + if rshift(op, 31) ~= 0 then + return ix..tohex(imm) + else + return ix + end + else + local lo, hi = -1, 0 + if imms < 32 then lo = rshift(-1, 31-imms) else hi = rshift(-1, 63-imms) end + if immr ~= 0 then + lo, hi = ror(lo, immr), ror(hi, immr) + local x = immr == 32 and 0 or band(bxor(lo, hi), lshift(-1, 32-immr)) + lo, hi = bxor(lo, x), bxor(hi, x) + if immr >= 32 then lo, hi = hi, lo end + end + if hi ~= 0 then + return fmt_hex32(hi)..tohex(lo) + else + return fmt_hex32(lo) + end + end +end + +local function parse_immpc(op, name) + if name == "b" or name == "bl" then + return arshift(lshift(op, 6), 4) + elseif name == "adr" or name == "adrp" then + local immlo = band(rshift(op, 29), 3) + local immhi = lshift(arshift(lshift(op, 8), 13), 2) + return bor(immhi, immlo) + elseif name == "tbz" or name == "tbnz" then + return lshift(arshift(lshift(op, 13), 18), 2) + else + return lshift(arshift(lshift(op, 8), 13), 2) + end +end + +local function parse_fpimm8(op) + local sign = band(op, 0x100000) == 0 and 1 or -1 + local exp = bxor(rshift(arshift(lshift(op, 12), 5), 24), 0x80) - 131 + local frac = 16+band(rshift(op, 13), 15) + return sign * frac * 2^exp +end + +local function prefer_bfx(sf, uns, imms, immr) + if imms < immr or imms == 31 or imms == 63 then + return false + end + if immr == 0 then + if sf == 0 and (imms == 7 or imms == 15) then + return false + end + if sf ~= 0 and uns == 0 and (imms == 7 or imms == 15 or imms == 31) then + return false + end + end + return true +end + +-- Disassemble a single instruction. +local function disass_ins(ctx) + local pos = ctx.pos + local b0, b1, b2, b3 = byte(ctx.code, pos+1, pos+4) + local op = bor(lshift(b3, 24), lshift(b2, 16), lshift(b1, 8), b0) + local operands = {} + local suffix = "" + local last, name, pat + local map_reg + ctx.op = op + ctx.rel = nil + last = nil + local opat + opat = map_init[band(rshift(op, 25), 15)] + while type(opat) ~= "string" do + if not opat then return unknown(ctx) end + opat = opat[band(rshift(op, opat.shift), opat.mask)] or opat._ + end + name, pat = match(opat, "^([a-z0-9]*)(.*)") + local altname, pat2 = match(pat, "|([a-z0-9_.|]*)(.*)") + if altname then pat = pat2 end + if sub(pat, 1, 1) == "." then + local s2, p2 = match(pat, "^([a-z0-9.]*)(.*)") + suffix = suffix..s2 + pat = p2 + end + + local rt = match(pat, "[gf]") + if rt then + if rt == "g" then + map_reg = band(op, 0x80000000) ~= 0 and map_regs.x or map_regs.w + else + map_reg = band(op, 0x400000) ~= 0 and map_regs.d or map_regs.s + end + end + + local second0, immr + + for p in gmatch(pat, ".") do + local x = nil + if p == "D" then + local regnum = band(op, 31) + x = rt and map_reg[regnum] or match_reg(p, pat, regnum) + elseif p == "N" then + local regnum = band(rshift(op, 5), 31) + x = rt and map_reg[regnum] or match_reg(p, pat, regnum) + elseif p == "M" then + local regnum = band(rshift(op, 16), 31) + x = rt and map_reg[regnum] or match_reg(p, pat, regnum) + elseif p == "A" then + local regnum = band(rshift(op, 10), 31) + x = rt and map_reg[regnum] or match_reg(p, pat, regnum) + elseif p == "B" then + local addr = ctx.addr + pos + parse_immpc(op, name) + ctx.rel = addr + x = "0x"..tohex(addr) + elseif p == "T" then + x = bor(band(rshift(op, 26), 32), band(rshift(op, 19), 31)) + elseif p == "V" then + x = band(op, 15) + elseif p == "C" then + x = map_cond[band(rshift(op, 12), 15)] + elseif p == "c" then + local rn = band(rshift(op, 5), 31) + local rm = band(rshift(op, 16), 31) + local cond = band(rshift(op, 12), 15) + local invc = bxor(cond, 1) + x = map_cond[cond] + if altname and cond ~= 14 and cond ~= 15 then + local a1, a2 = match(altname, "([^|]*)|(.*)") + if rn == rm then + local n = #operands + operands[n] = nil + x = map_cond[invc] + if rn ~= 31 then + if a1 then name = a1 else name = altname end + else + operands[n-1] = nil + name = a2 + end + end + end + elseif p == "W" then + x = band(rshift(op, 5), 0xffff) + elseif p == "Y" then + x = band(rshift(op, 5), 0xffff) + local hw = band(rshift(op, 21), 3) + if altname and (hw == 0 or x ~= 0) then + name = altname + end + elseif p == "L" then + local rn = map_regs.x[band(rshift(op, 5), 31)] + local imm9 = arshift(lshift(op, 11), 23) + if band(op, 0x800) ~= 0 then + x = "["..rn..", #"..imm9.."]!" + else + x = "["..rn.."], #"..imm9 + end + elseif p == "U" then + local rn = map_regs.x[band(rshift(op, 5), 31)] + local sz = band(rshift(op, 30), 3) + local imm12 = lshift(arshift(lshift(op, 10), 20), sz) + if imm12 ~= 0 then + x = "["..rn..", #"..imm12.."]" + else + x = "["..rn.."]" + end + elseif p == "K" then + local rn = map_regs.x[band(rshift(op, 5), 31)] + local imm9 = arshift(lshift(op, 11), 23) + if imm9 ~= 0 then + x = "["..rn..", #"..imm9.."]" + else + x = "["..rn.."]" + end + elseif p == "O" then + local rn, rm = map_regs.x[band(rshift(op, 5), 31)] + local m = band(rshift(op, 13), 1) + if m == 0 then + rm = map_regs.w[band(rshift(op, 16), 31)] + else + rm = map_regs.x[band(rshift(op, 16), 31)] + end + x = "["..rn..", "..rm + local opt = band(rshift(op, 13), 7) + local s = band(rshift(op, 12), 1) + local sz = band(rshift(op, 30), 3) + -- extension to be applied + if opt == 3 then + if s == 0 then x = x.."]" + else x = x..", lsl #"..sz.."]" end + elseif opt == 2 or opt == 6 or opt == 7 then + if s == 0 then x = x..", "..map_extend[opt].."]" + else x = x..", "..map_extend[opt].." #"..sz.."]" end + else + x = x.."]" + end + elseif p == "P" then + local opcv, sh = rshift(op, 26), 2 + if opcv >= 0x2a then sh = 4 elseif opcv >= 0x1b then sh = 3 end + local imm7 = lshift(arshift(lshift(op, 10), 25), sh) + local rn = map_regs.x[band(rshift(op, 5), 31)] + local ind = band(rshift(op, 23), 3) + if ind == 1 then + x = "["..rn.."], #"..imm7 + elseif ind == 2 then + if imm7 == 0 then + x = "["..rn.."]" + else + x = "["..rn..", #"..imm7.."]" + end + elseif ind == 3 then + x = "["..rn..", #"..imm7.."]!" + end + elseif p == "I" then + local shf = band(rshift(op, 22), 3) + local imm12 = band(rshift(op, 10), 0x0fff) + local rn, rd = band(rshift(op, 5), 31), band(op, 31) + if altname == "mov" and shf == 0 and imm12 == 0 and (rn == 31 or rd == 31) then + name = altname + x = nil + elseif shf == 0 then + x = imm12 + elseif shf == 1 then + x = imm12..", lsl #12" + end + elseif p == "i" then + x = "#0x"..decode_imm13(op) + elseif p == "1" then + immr = band(rshift(op, 16), 63) + x = immr + elseif p == "2" then + x = band(rshift(op, 10), 63) + if altname then + local a1, a2, a3, a4, a5, a6 = + match(altname, "([^|]*)|([^|]*)|([^|]*)|([^|]*)|([^|]*)|(.*)") + local sf = band(rshift(op, 26), 32) + local uns = band(rshift(op, 30), 1) + if prefer_bfx(sf, uns, x, immr) then + name = a2 + x = x - immr + 1 + elseif immr == 0 and x == 7 then + local n = #operands + operands[n] = nil + if sf ~= 0 then + operands[n-1] = gsub(operands[n-1], "x", "w") + end + last = operands[n-1] + name = a6 + x = nil + elseif immr == 0 and x == 15 then + local n = #operands + operands[n] = nil + if sf ~= 0 then + operands[n-1] = gsub(operands[n-1], "x", "w") + end + last = operands[n-1] + name = a5 + x = nil + elseif x == 31 or x == 63 then + if x == 31 and immr == 0 and name == "sbfm" then + name = a4 + local n = #operands + operands[n] = nil + if sf ~= 0 then + operands[n-1] = gsub(operands[n-1], "x", "w") + end + last = operands[n-1] + else + name = a3 + end + x = nil + elseif band(x, 31) ~= 31 and immr == x+1 and name == "ubfm" then + name = a4 + last = "#"..(sf+32 - immr) + operands[#operands] = last + x = nil + elseif x < immr then + name = a1 + last = "#"..(sf+32 - immr) + operands[#operands] = last + x = x + 1 + end + end + elseif p == "3" then + x = band(rshift(op, 10), 63) + if altname then + local a1, a2 = match(altname, "([^|]*)|(.*)") + if x < immr then + name = a1 + local sf = band(rshift(op, 26), 32) + last = "#"..(sf+32 - immr) + operands[#operands] = last + x = x + 1 + elseif x >= immr then + name = a2 + x = x - immr + 1 + end + end + elseif p == "4" then + x = band(rshift(op, 10), 63) + local rn = band(rshift(op, 5), 31) + local rm = band(rshift(op, 16), 31) + if altname and rn == rm then + local n = #operands + operands[n] = nil + last = operands[n-1] + name = altname + end + elseif p == "5" then + x = band(rshift(op, 16), 31) + elseif p == "S" then + x = band(rshift(op, 10), 63) + if x == 0 then x = nil + else x = map_shift[band(rshift(op, 22), 3)].." #"..x end + elseif p == "X" then + local opt = band(rshift(op, 13), 7) + -- Width specifier . + if opt ~= 3 and opt ~= 7 then + last = map_regs.w[band(rshift(op, 16), 31)] + operands[#operands] = last + end + x = band(rshift(op, 10), 7) + -- Extension. + if opt == 2 + band(rshift(op, 31), 1) and + band(rshift(op, second0 and 5 or 0), 31) == 31 then + if x == 0 then x = nil + else x = "lsl #"..x end + else + if x == 0 then x = map_extend[band(rshift(op, 13), 7)] + else x = map_extend[band(rshift(op, 13), 7)].." #"..x end + end + elseif p == "R" then + x = band(rshift(op,21), 3) + if x == 0 then x = nil + else x = "lsl #"..x*16 end + elseif p == "z" then + local n = #operands + if operands[n] == "sp" then operands[n] = "xzr" + elseif operands[n] == "wsp" then operands[n] = "wzr" + end + elseif p == "Z" then + x = 0 + elseif p == "F" then + x = parse_fpimm8(op) + elseif p == "g" or p == "f" or p == "x" or p == "w" or + p == "d" or p == "s" then + -- These are handled in D/N/M/A. + elseif p == "0" then + if last == "sp" or last == "wsp" then + local n = #operands + operands[n] = nil + last = operands[n-1] + if altname then + local a1, a2 = match(altname, "([^|]*)|(.*)") + if not a1 then + name = altname + elseif second0 then + name, altname = a2, a1 + else + name, altname = a1, a2 + end + end + end + second0 = true + else + assert(false) + end + if x then + last = x + if type(x) == "number" then x = "#"..x end + operands[#operands+1] = x + end + end + + return putop(ctx, name..suffix, operands) +end + +------------------------------------------------------------------------------ + +-- Disassemble a block of code. +local function disass_block(ctx, ofs, len) + if not ofs then ofs = 0 end + local stop = len and ofs+len or #ctx.code + ctx.pos = ofs + ctx.rel = nil + while ctx.pos < stop do disass_ins(ctx) end +end + +-- Extended API: create a disassembler context. Then call ctx:disass(ofs, len). +local function create(code, addr, out) + local ctx = {} + ctx.code = code + ctx.addr = addr or 0 + ctx.out = out or io.write + ctx.symtab = {} + ctx.disass = disass_block + ctx.hexdump = 8 + return ctx +end + +-- Simple API: disassemble code (a string) at address and output via out. +local function disass(code, addr, out) + create(code, addr, out):disass() +end + +-- Return register name for RID. +local function regname(r) + if r < 32 then return map_regs.x[r] end + return map_regs.d[r-32] +end + +-- Public module functions. +return { + create = create, + disass = disass, + regname = regname +} + diff --git a/lib/LuaJIT/jit/dis_arm64be.lua b/lib/LuaJIT/jit/dis_arm64be.lua new file mode 100644 index 0000000..7eb389e --- /dev/null +++ b/lib/LuaJIT/jit/dis_arm64be.lua @@ -0,0 +1,12 @@ +---------------------------------------------------------------------------- +-- LuaJIT ARM64BE disassembler wrapper module. +-- +-- Copyright (C) 2005-2017 Mike Pall. All rights reserved. +-- Released under the MIT license. See Copyright Notice in luajit.h +---------------------------------------------------------------------------- +-- ARM64 instructions are always little-endian. So just forward to the +-- common ARM64 disassembler module. All the interesting stuff is there. +------------------------------------------------------------------------------ + +return require((string.match(..., ".*%.") or "").."dis_arm64") + diff --git a/lib/LuaJIT/jit/dis_mips.lua b/lib/LuaJIT/jit/dis_mips.lua new file mode 100644 index 0000000..a12b8e6 --- /dev/null +++ b/lib/LuaJIT/jit/dis_mips.lua @@ -0,0 +1,443 @@ +---------------------------------------------------------------------------- +-- LuaJIT MIPS disassembler module. +-- +-- Copyright (C) 2005-2017 Mike Pall. All rights reserved. +-- Released under the MIT/X license. See Copyright Notice in luajit.h +---------------------------------------------------------------------------- +-- This is a helper module used by the LuaJIT machine code dumper module. +-- +-- It disassembles all standard MIPS32R1/R2 instructions. +-- Default mode is big-endian, but see: dis_mipsel.lua +------------------------------------------------------------------------------ + +local type = type +local byte, format = string.byte, string.format +local match, gmatch = string.match, string.gmatch +local concat = table.concat +local bit = require("bit") +local band, bor, tohex = bit.band, bit.bor, bit.tohex +local lshift, rshift, arshift = bit.lshift, bit.rshift, bit.arshift + +------------------------------------------------------------------------------ +-- Primary and extended opcode maps +------------------------------------------------------------------------------ + +local map_movci = { shift = 16, mask = 1, [0] = "movfDSC", "movtDSC", } +local map_srl = { shift = 21, mask = 1, [0] = "srlDTA", "rotrDTA", } +local map_srlv = { shift = 6, mask = 1, [0] = "srlvDTS", "rotrvDTS", } + +local map_special = { + shift = 0, mask = 63, + [0] = { shift = 0, mask = -1, [0] = "nop", _ = "sllDTA" }, + map_movci, map_srl, "sraDTA", + "sllvDTS", false, map_srlv, "sravDTS", + "jrS", "jalrD1S", "movzDST", "movnDST", + "syscallY", "breakY", false, "sync", + "mfhiD", "mthiS", "mfloD", "mtloS", + "dsllvDST", false, "dsrlvDST", "dsravDST", + "multST", "multuST", "divST", "divuST", + "dmultST", "dmultuST", "ddivST", "ddivuST", + "addDST", "addu|moveDST0", "subDST", "subu|neguDS0T", + "andDST", "or|moveDST0", "xorDST", "nor|notDST0", + false, false, "sltDST", "sltuDST", + "daddDST", "dadduDST", "dsubDST", "dsubuDST", + "tgeSTZ", "tgeuSTZ", "tltSTZ", "tltuSTZ", + "teqSTZ", false, "tneSTZ", false, + "dsllDTA", false, "dsrlDTA", "dsraDTA", + "dsll32DTA", false, "dsrl32DTA", "dsra32DTA", +} + +local map_special2 = { + shift = 0, mask = 63, + [0] = "maddST", "madduST", "mulDST", false, + "msubST", "msubuST", + [32] = "clzDS", [33] = "cloDS", + [63] = "sdbbpY", +} + +local map_bshfl = { + shift = 6, mask = 31, + [2] = "wsbhDT", + [16] = "sebDT", + [24] = "sehDT", +} + +local map_dbshfl = { + shift = 6, mask = 31, + [2] = "dsbhDT", + [5] = "dshdDT", +} + +local map_special3 = { + shift = 0, mask = 63, + [0] = "extTSAK", [1] = "dextmTSAP", [3] = "dextTSAK", + [4] = "insTSAL", [6] = "dinsuTSEQ", [7] = "dinsTSAL", + [32] = map_bshfl, [36] = map_dbshfl, [59] = "rdhwrTD", +} + +local map_regimm = { + shift = 16, mask = 31, + [0] = "bltzSB", "bgezSB", "bltzlSB", "bgezlSB", + false, false, false, false, + "tgeiSI", "tgeiuSI", "tltiSI", "tltiuSI", + "teqiSI", false, "tneiSI", false, + "bltzalSB", "bgezalSB", "bltzallSB", "bgezallSB", + false, false, false, false, + false, false, false, false, + false, false, false, "synciSO", +} + +local map_cop0 = { + shift = 25, mask = 1, + [0] = { + shift = 21, mask = 15, + [0] = "mfc0TDW", [4] = "mtc0TDW", + [10] = "rdpgprDT", + [11] = { shift = 5, mask = 1, [0] = "diT0", "eiT0", }, + [14] = "wrpgprDT", + }, { + shift = 0, mask = 63, + [1] = "tlbr", [2] = "tlbwi", [6] = "tlbwr", [8] = "tlbp", + [24] = "eret", [31] = "deret", + [32] = "wait", + }, +} + +local map_cop1s = { + shift = 0, mask = 63, + [0] = "add.sFGH", "sub.sFGH", "mul.sFGH", "div.sFGH", + "sqrt.sFG", "abs.sFG", "mov.sFG", "neg.sFG", + "round.l.sFG", "trunc.l.sFG", "ceil.l.sFG", "floor.l.sFG", + "round.w.sFG", "trunc.w.sFG", "ceil.w.sFG", "floor.w.sFG", + false, + { shift = 16, mask = 1, [0] = "movf.sFGC", "movt.sFGC" }, + "movz.sFGT", "movn.sFGT", + false, "recip.sFG", "rsqrt.sFG", false, + false, false, false, false, + false, false, false, false, + false, "cvt.d.sFG", false, false, + "cvt.w.sFG", "cvt.l.sFG", "cvt.ps.sFGH", false, + false, false, false, false, + false, false, false, false, + "c.f.sVGH", "c.un.sVGH", "c.eq.sVGH", "c.ueq.sVGH", + "c.olt.sVGH", "c.ult.sVGH", "c.ole.sVGH", "c.ule.sVGH", + "c.sf.sVGH", "c.ngle.sVGH", "c.seq.sVGH", "c.ngl.sVGH", + "c.lt.sVGH", "c.nge.sVGH", "c.le.sVGH", "c.ngt.sVGH", +} + +local map_cop1d = { + shift = 0, mask = 63, + [0] = "add.dFGH", "sub.dFGH", "mul.dFGH", "div.dFGH", + "sqrt.dFG", "abs.dFG", "mov.dFG", "neg.dFG", + "round.l.dFG", "trunc.l.dFG", "ceil.l.dFG", "floor.l.dFG", + "round.w.dFG", "trunc.w.dFG", "ceil.w.dFG", "floor.w.dFG", + false, + { shift = 16, mask = 1, [0] = "movf.dFGC", "movt.dFGC" }, + "movz.dFGT", "movn.dFGT", + false, "recip.dFG", "rsqrt.dFG", false, + false, false, false, false, + false, false, false, false, + "cvt.s.dFG", false, false, false, + "cvt.w.dFG", "cvt.l.dFG", false, false, + false, false, false, false, + false, false, false, false, + "c.f.dVGH", "c.un.dVGH", "c.eq.dVGH", "c.ueq.dVGH", + "c.olt.dVGH", "c.ult.dVGH", "c.ole.dVGH", "c.ule.dVGH", + "c.df.dVGH", "c.ngle.dVGH", "c.deq.dVGH", "c.ngl.dVGH", + "c.lt.dVGH", "c.nge.dVGH", "c.le.dVGH", "c.ngt.dVGH", +} + +local map_cop1ps = { + shift = 0, mask = 63, + [0] = "add.psFGH", "sub.psFGH", "mul.psFGH", false, + false, "abs.psFG", "mov.psFG", "neg.psFG", + false, false, false, false, + false, false, false, false, + false, + { shift = 16, mask = 1, [0] = "movf.psFGC", "movt.psFGC" }, + "movz.psFGT", "movn.psFGT", + false, false, false, false, + false, false, false, false, + false, false, false, false, + "cvt.s.puFG", false, false, false, + false, false, false, false, + "cvt.s.plFG", false, false, false, + "pll.psFGH", "plu.psFGH", "pul.psFGH", "puu.psFGH", + "c.f.psVGH", "c.un.psVGH", "c.eq.psVGH", "c.ueq.psVGH", + "c.olt.psVGH", "c.ult.psVGH", "c.ole.psVGH", "c.ule.psVGH", + "c.psf.psVGH", "c.ngle.psVGH", "c.pseq.psVGH", "c.ngl.psVGH", + "c.lt.psVGH", "c.nge.psVGH", "c.le.psVGH", "c.ngt.psVGH", +} + +local map_cop1w = { + shift = 0, mask = 63, + [32] = "cvt.s.wFG", [33] = "cvt.d.wFG", +} + +local map_cop1l = { + shift = 0, mask = 63, + [32] = "cvt.s.lFG", [33] = "cvt.d.lFG", +} + +local map_cop1bc = { + shift = 16, mask = 3, + [0] = "bc1fCB", "bc1tCB", "bc1flCB", "bc1tlCB", +} + +local map_cop1 = { + shift = 21, mask = 31, + [0] = "mfc1TG", "dmfc1TG", "cfc1TG", "mfhc1TG", + "mtc1TG", "dmtc1TG", "ctc1TG", "mthc1TG", + map_cop1bc, false, false, false, + false, false, false, false, + map_cop1s, map_cop1d, false, false, + map_cop1w, map_cop1l, map_cop1ps, +} + +local map_cop1x = { + shift = 0, mask = 63, + [0] = "lwxc1FSX", "ldxc1FSX", false, false, + false, "luxc1FSX", false, false, + "swxc1FSX", "sdxc1FSX", false, false, + false, "suxc1FSX", false, "prefxMSX", + false, false, false, false, + false, false, false, false, + false, false, false, false, + false, false, "alnv.psFGHS", false, + "madd.sFRGH", "madd.dFRGH", false, false, + false, false, "madd.psFRGH", false, + "msub.sFRGH", "msub.dFRGH", false, false, + false, false, "msub.psFRGH", false, + "nmadd.sFRGH", "nmadd.dFRGH", false, false, + false, false, "nmadd.psFRGH", false, + "nmsub.sFRGH", "nmsub.dFRGH", false, false, + false, false, "nmsub.psFRGH", false, +} + +local map_pri = { + [0] = map_special, map_regimm, "jJ", "jalJ", + "beq|beqz|bST00B", "bne|bnezST0B", "blezSB", "bgtzSB", + "addiTSI", "addiu|liTS0I", "sltiTSI", "sltiuTSI", + "andiTSU", "ori|liTS0U", "xoriTSU", "luiTU", + map_cop0, map_cop1, false, map_cop1x, + "beql|beqzlST0B", "bnel|bnezlST0B", "blezlSB", "bgtzlSB", + "daddiTSI", "daddiuTSI", false, false, + map_special2, "jalxJ", false, map_special3, + "lbTSO", "lhTSO", "lwlTSO", "lwTSO", + "lbuTSO", "lhuTSO", "lwrTSO", false, + "sbTSO", "shTSO", "swlTSO", "swTSO", + false, false, "swrTSO", "cacheNSO", + "llTSO", "lwc1HSO", "lwc2TSO", "prefNSO", + false, "ldc1HSO", "ldc2TSO", "ldTSO", + "scTSO", "swc1HSO", "swc2TSO", false, + false, "sdc1HSO", "sdc2TSO", "sdTSO", +} + +------------------------------------------------------------------------------ + +local map_gpr = { + [0] = "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", + "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15", + "r16", "r17", "r18", "r19", "r20", "r21", "r22", "r23", + "r24", "r25", "r26", "r27", "r28", "sp", "r30", "ra", +} + +------------------------------------------------------------------------------ + +-- Output a nicely formatted line with an opcode and operands. +local function putop(ctx, text, operands) + local pos = ctx.pos + local extra = "" + if ctx.rel then + local sym = ctx.symtab[ctx.rel] + if sym then extra = "\t->"..sym end + end + if ctx.hexdump > 0 then + ctx.out(format("%08x %s %-7s %s%s\n", + ctx.addr+pos, tohex(ctx.op), text, concat(operands, ", "), extra)) + else + ctx.out(format("%08x %-7s %s%s\n", + ctx.addr+pos, text, concat(operands, ", "), extra)) + end + ctx.pos = pos + 4 +end + +-- Fallback for unknown opcodes. +local function unknown(ctx) + return putop(ctx, ".long", { "0x"..tohex(ctx.op) }) +end + +local function get_be(ctx) + local pos = ctx.pos + local b0, b1, b2, b3 = byte(ctx.code, pos+1, pos+4) + return bor(lshift(b0, 24), lshift(b1, 16), lshift(b2, 8), b3) +end + +local function get_le(ctx) + local pos = ctx.pos + local b0, b1, b2, b3 = byte(ctx.code, pos+1, pos+4) + return bor(lshift(b3, 24), lshift(b2, 16), lshift(b1, 8), b0) +end + +-- Disassemble a single instruction. +local function disass_ins(ctx) + local op = ctx:get() + local operands = {} + local last = nil + ctx.op = op + ctx.rel = nil + + local opat = map_pri[rshift(op, 26)] + while type(opat) ~= "string" do + if not opat then return unknown(ctx) end + opat = opat[band(rshift(op, opat.shift), opat.mask)] or opat._ + end + local name, pat = match(opat, "^([a-z0-9_.]*)(.*)") + local altname, pat2 = match(pat, "|([a-z0-9_.|]*)(.*)") + if altname then pat = pat2 end + + for p in gmatch(pat, ".") do + local x = nil + if p == "S" then + x = map_gpr[band(rshift(op, 21), 31)] + elseif p == "T" then + x = map_gpr[band(rshift(op, 16), 31)] + elseif p == "D" then + x = map_gpr[band(rshift(op, 11), 31)] + elseif p == "F" then + x = "f"..band(rshift(op, 6), 31) + elseif p == "G" then + x = "f"..band(rshift(op, 11), 31) + elseif p == "H" then + x = "f"..band(rshift(op, 16), 31) + elseif p == "R" then + x = "f"..band(rshift(op, 21), 31) + elseif p == "A" then + x = band(rshift(op, 6), 31) + elseif p == "E" then + x = band(rshift(op, 6), 31) + 32 + elseif p == "M" then + x = band(rshift(op, 11), 31) + elseif p == "N" then + x = band(rshift(op, 16), 31) + elseif p == "C" then + x = band(rshift(op, 18), 7) + if x == 0 then x = nil end + elseif p == "K" then + x = band(rshift(op, 11), 31) + 1 + elseif p == "P" then + x = band(rshift(op, 11), 31) + 33 + elseif p == "L" then + x = band(rshift(op, 11), 31) - last + 1 + elseif p == "Q" then + x = band(rshift(op, 11), 31) - last + 33 + elseif p == "I" then + x = arshift(lshift(op, 16), 16) + elseif p == "U" then + x = band(op, 0xffff) + elseif p == "O" then + local disp = arshift(lshift(op, 16), 16) + operands[#operands] = format("%d(%s)", disp, last) + elseif p == "X" then + local index = map_gpr[band(rshift(op, 16), 31)] + operands[#operands] = format("%s(%s)", index, last) + elseif p == "B" then + x = ctx.addr + ctx.pos + arshift(lshift(op, 16), 16)*4 + 4 + ctx.rel = x + x = format("0x%08x", x) + elseif p == "J" then + local a = ctx.addr + ctx.pos + x = a - band(a, 0x0fffffff) + band(op, 0x03ffffff)*4 + ctx.rel = x + x = format("0x%08x", x) + elseif p == "V" then + x = band(rshift(op, 8), 7) + if x == 0 then x = nil end + elseif p == "W" then + x = band(op, 7) + if x == 0 then x = nil end + elseif p == "Y" then + x = band(rshift(op, 6), 0x000fffff) + if x == 0 then x = nil end + elseif p == "Z" then + x = band(rshift(op, 6), 1023) + if x == 0 then x = nil end + elseif p == "0" then + if last == "r0" or last == 0 then + local n = #operands + operands[n] = nil + last = operands[n-1] + if altname then + local a1, a2 = match(altname, "([^|]*)|(.*)") + if a1 then name, altname = a1, a2 + else name = altname end + end + end + elseif p == "1" then + if last == "ra" then + operands[#operands] = nil + end + else + assert(false) + end + if x then operands[#operands+1] = x; last = x end + end + + return putop(ctx, name, operands) +end + +------------------------------------------------------------------------------ + +-- Disassemble a block of code. +local function disass_block(ctx, ofs, len) + if not ofs then ofs = 0 end + local stop = len and ofs+len or #ctx.code + stop = stop - stop % 4 + ctx.pos = ofs - ofs % 4 + ctx.rel = nil + while ctx.pos < stop do disass_ins(ctx) end +end + +-- Extended API: create a disassembler context. Then call ctx:disass(ofs, len). +local function create(code, addr, out) + local ctx = {} + ctx.code = code + ctx.addr = addr or 0 + ctx.out = out or io.write + ctx.symtab = {} + ctx.disass = disass_block + ctx.hexdump = 8 + ctx.get = get_be + return ctx +end + +local function create_el(code, addr, out) + local ctx = create(code, addr, out) + ctx.get = get_le + return ctx +end + +-- Simple API: disassemble code (a string) at address and output via out. +local function disass(code, addr, out) + create(code, addr, out):disass() +end + +local function disass_el(code, addr, out) + create_el(code, addr, out):disass() +end + +-- Return register name for RID. +local function regname(r) + if r < 32 then return map_gpr[r] end + return "f"..(r-32) +end + +-- Public module functions. +return { + create = create, + create_el = create_el, + disass = disass, + disass_el = disass_el, + regname = regname +} + diff --git a/lib/LuaJIT/jit/dis_mips64.lua b/lib/LuaJIT/jit/dis_mips64.lua new file mode 100644 index 0000000..c437492 --- /dev/null +++ b/lib/LuaJIT/jit/dis_mips64.lua @@ -0,0 +1,17 @@ +---------------------------------------------------------------------------- +-- LuaJIT MIPS64 disassembler wrapper module. +-- +-- Copyright (C) 2005-2017 Mike Pall. All rights reserved. +-- Released under the MIT license. See Copyright Notice in luajit.h +---------------------------------------------------------------------------- +-- This module just exports the big-endian functions from the +-- MIPS disassembler module. All the interesting stuff is there. +------------------------------------------------------------------------------ + +local dis_mips = require((string.match(..., ".*%.") or "").."dis_mips") +return { + create = dis_mips.create, + disass = dis_mips.disass, + regname = dis_mips.regname +} + diff --git a/lib/LuaJIT/jit/dis_mips64el.lua b/lib/LuaJIT/jit/dis_mips64el.lua new file mode 100644 index 0000000..2b1470a --- /dev/null +++ b/lib/LuaJIT/jit/dis_mips64el.lua @@ -0,0 +1,17 @@ +---------------------------------------------------------------------------- +-- LuaJIT MIPS64EL disassembler wrapper module. +-- +-- Copyright (C) 2005-2017 Mike Pall. All rights reserved. +-- Released under the MIT license. See Copyright Notice in luajit.h +---------------------------------------------------------------------------- +-- This module just exports the little-endian functions from the +-- MIPS disassembler module. All the interesting stuff is there. +------------------------------------------------------------------------------ + +local dis_mips = require((string.match(..., ".*%.") or "").."dis_mips") +return { + create = dis_mips.create_el, + disass = dis_mips.disass_el, + regname = dis_mips.regname +} + diff --git a/lib/LuaJIT/jit/dis_mipsel.lua b/lib/LuaJIT/jit/dis_mipsel.lua new file mode 100644 index 0000000..f69b11f --- /dev/null +++ b/lib/LuaJIT/jit/dis_mipsel.lua @@ -0,0 +1,17 @@ +---------------------------------------------------------------------------- +-- LuaJIT MIPSEL disassembler wrapper module. +-- +-- Copyright (C) 2005-2017 Mike Pall. All rights reserved. +-- Released under the MIT license. See Copyright Notice in luajit.h +---------------------------------------------------------------------------- +-- This module just exports the little-endian functions from the +-- MIPS disassembler module. All the interesting stuff is there. +------------------------------------------------------------------------------ + +local dis_mips = require((string.match(..., ".*%.") or "").."dis_mips") +return { + create = dis_mips.create_el, + disass = dis_mips.disass_el, + regname = dis_mips.regname +} + diff --git a/lib/LuaJIT/jit/dis_ppc.lua b/lib/LuaJIT/jit/dis_ppc.lua new file mode 100644 index 0000000..2aeb1b2 --- /dev/null +++ b/lib/LuaJIT/jit/dis_ppc.lua @@ -0,0 +1,591 @@ +---------------------------------------------------------------------------- +-- LuaJIT PPC disassembler module. +-- +-- Copyright (C) 2005-2017 Mike Pall. All rights reserved. +-- Released under the MIT/X license. See Copyright Notice in luajit.h +---------------------------------------------------------------------------- +-- This is a helper module used by the LuaJIT machine code dumper module. +-- +-- It disassembles all common, non-privileged 32/64 bit PowerPC instructions +-- plus the e500 SPE instructions and some Cell/Xenon extensions. +-- +-- NYI: VMX, VMX128 +------------------------------------------------------------------------------ + +local type = type +local byte, format = string.byte, string.format +local match, gmatch, gsub = string.match, string.gmatch, string.gsub +local concat = table.concat +local bit = require("bit") +local band, bor, tohex = bit.band, bit.bor, bit.tohex +local lshift, rshift, arshift = bit.lshift, bit.rshift, bit.arshift + +------------------------------------------------------------------------------ +-- Primary and extended opcode maps +------------------------------------------------------------------------------ + +local map_crops = { + shift = 1, mask = 1023, + [0] = "mcrfXX", + [33] = "crnor|crnotCCC=", [129] = "crandcCCC", + [193] = "crxor|crclrCCC%", [225] = "crnandCCC", + [257] = "crandCCC", [289] = "creqv|crsetCCC%", + [417] = "crorcCCC", [449] = "cror|crmoveCCC=", + [16] = "b_lrKB", [528] = "b_ctrKB", + [150] = "isync", +} + +local map_rlwinm = setmetatable({ + shift = 0, mask = -1, +}, +{ __index = function(t, x) + local rot = band(rshift(x, 11), 31) + local mb = band(rshift(x, 6), 31) + local me = band(rshift(x, 1), 31) + if mb == 0 and me == 31-rot then + return "slwiRR~A." + elseif me == 31 and mb == 32-rot then + return "srwiRR~-A." + else + return "rlwinmRR~AAA." + end + end +}) + +local map_rld = { + shift = 2, mask = 7, + [0] = "rldiclRR~HM.", "rldicrRR~HM.", "rldicRR~HM.", "rldimiRR~HM.", + { + shift = 1, mask = 1, + [0] = "rldclRR~RM.", "rldcrRR~RM.", + }, +} + +local map_ext = setmetatable({ + shift = 1, mask = 1023, + + [0] = "cmp_YLRR", [32] = "cmpl_YLRR", + [4] = "twARR", [68] = "tdARR", + + [8] = "subfcRRR.", [40] = "subfRRR.", + [104] = "negRR.", [136] = "subfeRRR.", + [200] = "subfzeRR.", [232] = "subfmeRR.", + [520] = "subfcoRRR.", [552] = "subfoRRR.", + [616] = "negoRR.", [648] = "subfeoRRR.", + [712] = "subfzeoRR.", [744] = "subfmeoRR.", + + [9] = "mulhduRRR.", [73] = "mulhdRRR.", [233] = "mulldRRR.", + [457] = "divduRRR.", [489] = "divdRRR.", + [745] = "mulldoRRR.", + [969] = "divduoRRR.", [1001] = "divdoRRR.", + + [10] = "addcRRR.", [138] = "addeRRR.", + [202] = "addzeRR.", [234] = "addmeRR.", [266] = "addRRR.", + [522] = "addcoRRR.", [650] = "addeoRRR.", + [714] = "addzeoRR.", [746] = "addmeoRR.", [778] = "addoRRR.", + + [11] = "mulhwuRRR.", [75] = "mulhwRRR.", [235] = "mullwRRR.", + [459] = "divwuRRR.", [491] = "divwRRR.", + [747] = "mullwoRRR.", + [971] = "divwouRRR.", [1003] = "divwoRRR.", + + [15] = "iselltRRR", [47] = "iselgtRRR", [79] = "iseleqRRR", + + [144] = { shift = 20, mask = 1, [0] = "mtcrfRZ~", "mtocrfRZ~", }, + [19] = { shift = 20, mask = 1, [0] = "mfcrR", "mfocrfRZ", }, + [371] = { shift = 11, mask = 1023, [392] = "mftbR", [424] = "mftbuR", }, + [339] = { + shift = 11, mask = 1023, + [32] = "mferR", [256] = "mflrR", [288] = "mfctrR", [16] = "mfspefscrR", + }, + [467] = { + shift = 11, mask = 1023, + [32] = "mtxerR", [256] = "mtlrR", [288] = "mtctrR", [16] = "mtspefscrR", + }, + + [20] = "lwarxRR0R", [84] = "ldarxRR0R", + + [21] = "ldxRR0R", [53] = "lduxRRR", + [149] = "stdxRR0R", [181] = "stduxRRR", + [341] = "lwaxRR0R", [373] = "lwauxRRR", + + [23] = "lwzxRR0R", [55] = "lwzuxRRR", + [87] = "lbzxRR0R", [119] = "lbzuxRRR", + [151] = "stwxRR0R", [183] = "stwuxRRR", + [215] = "stbxRR0R", [247] = "stbuxRRR", + [279] = "lhzxRR0R", [311] = "lhzuxRRR", + [343] = "lhaxRR0R", [375] = "lhauxRRR", + [407] = "sthxRR0R", [439] = "sthuxRRR", + + [54] = "dcbst-R0R", [86] = "dcbf-R0R", + [150] = "stwcxRR0R.", [214] = "stdcxRR0R.", + [246] = "dcbtst-R0R", [278] = "dcbt-R0R", + [310] = "eciwxRR0R", [438] = "ecowxRR0R", + [470] = "dcbi-RR", + + [598] = { + shift = 21, mask = 3, + [0] = "sync", "lwsync", "ptesync", + }, + [758] = "dcba-RR", + [854] = "eieio", [982] = "icbi-R0R", [1014] = "dcbz-R0R", + + [26] = "cntlzwRR~", [58] = "cntlzdRR~", + [122] = "popcntbRR~", + [154] = "prtywRR~", [186] = "prtydRR~", + + [28] = "andRR~R.", [60] = "andcRR~R.", [124] = "nor|notRR~R=.", + [284] = "eqvRR~R.", [316] = "xorRR~R.", + [412] = "orcRR~R.", [444] = "or|mrRR~R=.", [476] = "nandRR~R.", + [508] = "cmpbRR~R", + + [512] = "mcrxrX", + + [532] = "ldbrxRR0R", [660] = "stdbrxRR0R", + + [533] = "lswxRR0R", [597] = "lswiRR0A", + [661] = "stswxRR0R", [725] = "stswiRR0A", + + [534] = "lwbrxRR0R", [662] = "stwbrxRR0R", + [790] = "lhbrxRR0R", [918] = "sthbrxRR0R", + + [535] = "lfsxFR0R", [567] = "lfsuxFRR", + [599] = "lfdxFR0R", [631] = "lfduxFRR", + [663] = "stfsxFR0R", [695] = "stfsuxFRR", + [727] = "stfdxFR0R", [759] = "stfduxFR0R", + [855] = "lfiwaxFR0R", + [983] = "stfiwxFR0R", + + [24] = "slwRR~R.", + + [27] = "sldRR~R.", [536] = "srwRR~R.", + [792] = "srawRR~R.", [824] = "srawiRR~A.", + + [794] = "sradRR~R.", [826] = "sradiRR~H.", [827] = "sradiRR~H.", + [922] = "extshRR~.", [954] = "extsbRR~.", [986] = "extswRR~.", + + [539] = "srdRR~R.", +}, +{ __index = function(t, x) + if band(x, 31) == 15 then return "iselRRRC" end + end +}) + +local map_ld = { + shift = 0, mask = 3, + [0] = "ldRRE", "lduRRE", "lwaRRE", +} + +local map_std = { + shift = 0, mask = 3, + [0] = "stdRRE", "stduRRE", +} + +local map_fps = { + shift = 5, mask = 1, + { + shift = 1, mask = 15, + [0] = false, false, "fdivsFFF.", false, + "fsubsFFF.", "faddsFFF.", "fsqrtsF-F.", false, + "fresF-F.", "fmulsFF-F.", "frsqrtesF-F.", false, + "fmsubsFFFF~.", "fmaddsFFFF~.", "fnmsubsFFFF~.", "fnmaddsFFFF~.", + } +} + +local map_fpd = { + shift = 5, mask = 1, + [0] = { + shift = 1, mask = 1023, + [0] = "fcmpuXFF", [32] = "fcmpoXFF", [64] = "mcrfsXX", + [38] = "mtfsb1A.", [70] = "mtfsb0A.", [134] = "mtfsfiA>>-A>", + [8] = "fcpsgnFFF.", [40] = "fnegF-F.", [72] = "fmrF-F.", + [136] = "fnabsF-F.", [264] = "fabsF-F.", + [12] = "frspF-F.", + [14] = "fctiwF-F.", [15] = "fctiwzF-F.", + [583] = "mffsF.", [711] = "mtfsfZF.", + [392] = "frinF-F.", [424] = "frizF-F.", + [456] = "fripF-F.", [488] = "frimF-F.", + [814] = "fctidF-F.", [815] = "fctidzF-F.", [846] = "fcfidF-F.", + }, + { + shift = 1, mask = 15, + [0] = false, false, "fdivFFF.", false, + "fsubFFF.", "faddFFF.", "fsqrtF-F.", "fselFFFF~.", + "freF-F.", "fmulFF-F.", "frsqrteF-F.", false, + "fmsubFFFF~.", "fmaddFFFF~.", "fnmsubFFFF~.", "fnmaddFFFF~.", + } +} + +local map_spe = { + shift = 0, mask = 2047, + + [512] = "evaddwRRR", [514] = "evaddiwRAR~", + [516] = "evsubwRRR~", [518] = "evsubiwRAR~", + [520] = "evabsRR", [521] = "evnegRR", + [522] = "evextsbRR", [523] = "evextshRR", [524] = "evrndwRR", + [525] = "evcntlzwRR", [526] = "evcntlswRR", + + [527] = "brincRRR", + + [529] = "evandRRR", [530] = "evandcRRR", [534] = "evxorRRR", + [535] = "evor|evmrRRR=", [536] = "evnor|evnotRRR=", + [537] = "eveqvRRR", [539] = "evorcRRR", [542] = "evnandRRR", + + [544] = "evsrwuRRR", [545] = "evsrwsRRR", + [546] = "evsrwiuRRA", [547] = "evsrwisRRA", + [548] = "evslwRRR", [550] = "evslwiRRA", + [552] = "evrlwRRR", [553] = "evsplatiRS", + [554] = "evrlwiRRA", [555] = "evsplatfiRS", + [556] = "evmergehiRRR", [557] = "evmergeloRRR", + [558] = "evmergehiloRRR", [559] = "evmergelohiRRR", + + [560] = "evcmpgtuYRR", [561] = "evcmpgtsYRR", + [562] = "evcmpltuYRR", [563] = "evcmpltsYRR", + [564] = "evcmpeqYRR", + + [632] = "evselRRR", [633] = "evselRRRW", + [634] = "evselRRRW", [635] = "evselRRRW", + [636] = "evselRRRW", [637] = "evselRRRW", + [638] = "evselRRRW", [639] = "evselRRRW", + + [640] = "evfsaddRRR", [641] = "evfssubRRR", + [644] = "evfsabsRR", [645] = "evfsnabsRR", [646] = "evfsnegRR", + [648] = "evfsmulRRR", [649] = "evfsdivRRR", + [652] = "evfscmpgtYRR", [653] = "evfscmpltYRR", [654] = "evfscmpeqYRR", + [656] = "evfscfuiR-R", [657] = "evfscfsiR-R", + [658] = "evfscfufR-R", [659] = "evfscfsfR-R", + [660] = "evfsctuiR-R", [661] = "evfsctsiR-R", + [662] = "evfsctufR-R", [663] = "evfsctsfR-R", + [664] = "evfsctuizR-R", [666] = "evfsctsizR-R", + [668] = "evfststgtYRR", [669] = "evfststltYRR", [670] = "evfststeqYRR", + + [704] = "efsaddRRR", [705] = "efssubRRR", + [708] = "efsabsRR", [709] = "efsnabsRR", [710] = "efsnegRR", + [712] = "efsmulRRR", [713] = "efsdivRRR", + [716] = "efscmpgtYRR", [717] = "efscmpltYRR", [718] = "efscmpeqYRR", + [719] = "efscfdR-R", + [720] = "efscfuiR-R", [721] = "efscfsiR-R", + [722] = "efscfufR-R", [723] = "efscfsfR-R", + [724] = "efsctuiR-R", [725] = "efsctsiR-R", + [726] = "efsctufR-R", [727] = "efsctsfR-R", + [728] = "efsctuizR-R", [730] = "efsctsizR-R", + [732] = "efststgtYRR", [733] = "efststltYRR", [734] = "efststeqYRR", + + [736] = "efdaddRRR", [737] = "efdsubRRR", + [738] = "efdcfuidR-R", [739] = "efdcfsidR-R", + [740] = "efdabsRR", [741] = "efdnabsRR", [742] = "efdnegRR", + [744] = "efdmulRRR", [745] = "efddivRRR", + [746] = "efdctuidzR-R", [747] = "efdctsidzR-R", + [748] = "efdcmpgtYRR", [749] = "efdcmpltYRR", [750] = "efdcmpeqYRR", + [751] = "efdcfsR-R", + [752] = "efdcfuiR-R", [753] = "efdcfsiR-R", + [754] = "efdcfufR-R", [755] = "efdcfsfR-R", + [756] = "efdctuiR-R", [757] = "efdctsiR-R", + [758] = "efdctufR-R", [759] = "efdctsfR-R", + [760] = "efdctuizR-R", [762] = "efdctsizR-R", + [764] = "efdtstgtYRR", [765] = "efdtstltYRR", [766] = "efdtsteqYRR", + + [768] = "evlddxRR0R", [769] = "evlddRR8", + [770] = "evldwxRR0R", [771] = "evldwRR8", + [772] = "evldhxRR0R", [773] = "evldhRR8", + [776] = "evlhhesplatxRR0R", [777] = "evlhhesplatRR2", + [780] = "evlhhousplatxRR0R", [781] = "evlhhousplatRR2", + [782] = "evlhhossplatxRR0R", [783] = "evlhhossplatRR2", + [784] = "evlwhexRR0R", [785] = "evlwheRR4", + [788] = "evlwhouxRR0R", [789] = "evlwhouRR4", + [790] = "evlwhosxRR0R", [791] = "evlwhosRR4", + [792] = "evlwwsplatxRR0R", [793] = "evlwwsplatRR4", + [796] = "evlwhsplatxRR0R", [797] = "evlwhsplatRR4", + + [800] = "evstddxRR0R", [801] = "evstddRR8", + [802] = "evstdwxRR0R", [803] = "evstdwRR8", + [804] = "evstdhxRR0R", [805] = "evstdhRR8", + [816] = "evstwhexRR0R", [817] = "evstwheRR4", + [820] = "evstwhoxRR0R", [821] = "evstwhoRR4", + [824] = "evstwwexRR0R", [825] = "evstwweRR4", + [828] = "evstwwoxRR0R", [829] = "evstwwoRR4", + + [1027] = "evmhessfRRR", [1031] = "evmhossfRRR", [1032] = "evmheumiRRR", + [1033] = "evmhesmiRRR", [1035] = "evmhesmfRRR", [1036] = "evmhoumiRRR", + [1037] = "evmhosmiRRR", [1039] = "evmhosmfRRR", [1059] = "evmhessfaRRR", + [1063] = "evmhossfaRRR", [1064] = "evmheumiaRRR", [1065] = "evmhesmiaRRR", + [1067] = "evmhesmfaRRR", [1068] = "evmhoumiaRRR", [1069] = "evmhosmiaRRR", + [1071] = "evmhosmfaRRR", [1095] = "evmwhssfRRR", [1096] = "evmwlumiRRR", + [1100] = "evmwhumiRRR", [1101] = "evmwhsmiRRR", [1103] = "evmwhsmfRRR", + [1107] = "evmwssfRRR", [1112] = "evmwumiRRR", [1113] = "evmwsmiRRR", + [1115] = "evmwsmfRRR", [1127] = "evmwhssfaRRR", [1128] = "evmwlumiaRRR", + [1132] = "evmwhumiaRRR", [1133] = "evmwhsmiaRRR", [1135] = "evmwhsmfaRRR", + [1139] = "evmwssfaRRR", [1144] = "evmwumiaRRR", [1145] = "evmwsmiaRRR", + [1147] = "evmwsmfaRRR", + + [1216] = "evaddusiaawRR", [1217] = "evaddssiaawRR", + [1218] = "evsubfusiaawRR", [1219] = "evsubfssiaawRR", + [1220] = "evmraRR", + [1222] = "evdivwsRRR", [1223] = "evdivwuRRR", + [1224] = "evaddumiaawRR", [1225] = "evaddsmiaawRR", + [1226] = "evsubfumiaawRR", [1227] = "evsubfsmiaawRR", + + [1280] = "evmheusiaawRRR", [1281] = "evmhessiaawRRR", + [1283] = "evmhessfaawRRR", [1284] = "evmhousiaawRRR", + [1285] = "evmhossiaawRRR", [1287] = "evmhossfaawRRR", + [1288] = "evmheumiaawRRR", [1289] = "evmhesmiaawRRR", + [1291] = "evmhesmfaawRRR", [1292] = "evmhoumiaawRRR", + [1293] = "evmhosmiaawRRR", [1295] = "evmhosmfaawRRR", + [1320] = "evmhegumiaaRRR", [1321] = "evmhegsmiaaRRR", + [1323] = "evmhegsmfaaRRR", [1324] = "evmhogumiaaRRR", + [1325] = "evmhogsmiaaRRR", [1327] = "evmhogsmfaaRRR", + [1344] = "evmwlusiaawRRR", [1345] = "evmwlssiaawRRR", + [1352] = "evmwlumiaawRRR", [1353] = "evmwlsmiaawRRR", + [1363] = "evmwssfaaRRR", [1368] = "evmwumiaaRRR", + [1369] = "evmwsmiaaRRR", [1371] = "evmwsmfaaRRR", + [1408] = "evmheusianwRRR", [1409] = "evmhessianwRRR", + [1411] = "evmhessfanwRRR", [1412] = "evmhousianwRRR", + [1413] = "evmhossianwRRR", [1415] = "evmhossfanwRRR", + [1416] = "evmheumianwRRR", [1417] = "evmhesmianwRRR", + [1419] = "evmhesmfanwRRR", [1420] = "evmhoumianwRRR", + [1421] = "evmhosmianwRRR", [1423] = "evmhosmfanwRRR", + [1448] = "evmhegumianRRR", [1449] = "evmhegsmianRRR", + [1451] = "evmhegsmfanRRR", [1452] = "evmhogumianRRR", + [1453] = "evmhogsmianRRR", [1455] = "evmhogsmfanRRR", + [1472] = "evmwlusianwRRR", [1473] = "evmwlssianwRRR", + [1480] = "evmwlumianwRRR", [1481] = "evmwlsmianwRRR", + [1491] = "evmwssfanRRR", [1496] = "evmwumianRRR", + [1497] = "evmwsmianRRR", [1499] = "evmwsmfanRRR", +} + +local map_pri = { + [0] = false, false, "tdiARI", "twiARI", + map_spe, false, false, "mulliRRI", + "subficRRI", false, "cmpl_iYLRU", "cmp_iYLRI", + "addicRRI", "addic.RRI", "addi|liRR0I", "addis|lisRR0I", + "b_KBJ", "sc", "bKJ", map_crops, + "rlwimiRR~AAA.", map_rlwinm, false, "rlwnmRR~RAA.", + "oriNRR~U", "orisRR~U", "xoriRR~U", "xorisRR~U", + "andi.RR~U", "andis.RR~U", map_rld, map_ext, + "lwzRRD", "lwzuRRD", "lbzRRD", "lbzuRRD", + "stwRRD", "stwuRRD", "stbRRD", "stbuRRD", + "lhzRRD", "lhzuRRD", "lhaRRD", "lhauRRD", + "sthRRD", "sthuRRD", "lmwRRD", "stmwRRD", + "lfsFRD", "lfsuFRD", "lfdFRD", "lfduFRD", + "stfsFRD", "stfsuFRD", "stfdFRD", "stfduFRD", + false, false, map_ld, map_fps, + false, false, map_std, map_fpd, +} + +------------------------------------------------------------------------------ + +local map_gpr = { + [0] = "r0", "sp", "r2", "r3", "r4", "r5", "r6", "r7", + "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15", + "r16", "r17", "r18", "r19", "r20", "r21", "r22", "r23", + "r24", "r25", "r26", "r27", "r28", "r29", "r30", "r31", +} + +local map_cond = { [0] = "lt", "gt", "eq", "so", "ge", "le", "ne", "ns", } + +-- Format a condition bit. +local function condfmt(cond) + if cond <= 3 then + return map_cond[band(cond, 3)] + else + return format("4*cr%d+%s", rshift(cond, 2), map_cond[band(cond, 3)]) + end +end + +------------------------------------------------------------------------------ + +-- Output a nicely formatted line with an opcode and operands. +local function putop(ctx, text, operands) + local pos = ctx.pos + local extra = "" + if ctx.rel then + local sym = ctx.symtab[ctx.rel] + if sym then extra = "\t->"..sym end + end + if ctx.hexdump > 0 then + ctx.out(format("%08x %s %-7s %s%s\n", + ctx.addr+pos, tohex(ctx.op), text, concat(operands, ", "), extra)) + else + ctx.out(format("%08x %-7s %s%s\n", + ctx.addr+pos, text, concat(operands, ", "), extra)) + end + ctx.pos = pos + 4 +end + +-- Fallback for unknown opcodes. +local function unknown(ctx) + return putop(ctx, ".long", { "0x"..tohex(ctx.op) }) +end + +-- Disassemble a single instruction. +local function disass_ins(ctx) + local pos = ctx.pos + local b0, b1, b2, b3 = byte(ctx.code, pos+1, pos+4) + local op = bor(lshift(b0, 24), lshift(b1, 16), lshift(b2, 8), b3) + local operands = {} + local last = nil + local rs = 21 + ctx.op = op + ctx.rel = nil + + local opat = map_pri[rshift(b0, 2)] + while type(opat) ~= "string" do + if not opat then return unknown(ctx) end + opat = opat[band(rshift(op, opat.shift), opat.mask)] + end + local name, pat = match(opat, "^([a-z0-9_.]*)(.*)") + local altname, pat2 = match(pat, "|([a-z0-9_.]*)(.*)") + if altname then pat = pat2 end + + for p in gmatch(pat, ".") do + local x = nil + if p == "R" then + x = map_gpr[band(rshift(op, rs), 31)] + rs = rs - 5 + elseif p == "F" then + x = "f"..band(rshift(op, rs), 31) + rs = rs - 5 + elseif p == "A" then + x = band(rshift(op, rs), 31) + rs = rs - 5 + elseif p == "S" then + x = arshift(lshift(op, 27-rs), 27) + rs = rs - 5 + elseif p == "I" then + x = arshift(lshift(op, 16), 16) + elseif p == "U" then + x = band(op, 0xffff) + elseif p == "D" or p == "E" then + local disp = arshift(lshift(op, 16), 16) + if p == "E" then disp = band(disp, -4) end + if last == "r0" then last = "0" end + operands[#operands] = format("%d(%s)", disp, last) + elseif p >= "2" and p <= "8" then + local disp = band(rshift(op, rs), 31) * p + if last == "r0" then last = "0" end + operands[#operands] = format("%d(%s)", disp, last) + elseif p == "H" then + x = band(rshift(op, rs), 31) + lshift(band(op, 2), 4) + rs = rs - 5 + elseif p == "M" then + x = band(rshift(op, rs), 31) + band(op, 0x20) + elseif p == "C" then + x = condfmt(band(rshift(op, rs), 31)) + rs = rs - 5 + elseif p == "B" then + local bo = rshift(op, 21) + local cond = band(rshift(op, 16), 31) + local cn = "" + rs = rs - 10 + if band(bo, 4) == 0 then + cn = band(bo, 2) == 0 and "dnz" or "dz" + if band(bo, 0x10) == 0 then + cn = cn..(band(bo, 8) == 0 and "f" or "t") + end + if band(bo, 0x10) == 0 then x = condfmt(cond) end + name = name..(band(bo, 1) == band(rshift(op, 15), 1) and "-" or "+") + elseif band(bo, 0x10) == 0 then + cn = map_cond[band(cond, 3) + (band(bo, 8) == 0 and 4 or 0)] + if cond > 3 then x = "cr"..rshift(cond, 2) end + name = name..(band(bo, 1) == band(rshift(op, 15), 1) and "-" or "+") + end + name = gsub(name, "_", cn) + elseif p == "J" then + x = arshift(lshift(op, 27-rs), 29-rs)*4 + if band(op, 2) == 0 then x = ctx.addr + pos + x end + ctx.rel = x + x = "0x"..tohex(x) + elseif p == "K" then + if band(op, 1) ~= 0 then name = name.."l" end + if band(op, 2) ~= 0 then name = name.."a" end + elseif p == "X" or p == "Y" then + x = band(rshift(op, rs+2), 7) + if x == 0 and p == "Y" then x = nil else x = "cr"..x end + rs = rs - 5 + elseif p == "W" then + x = "cr"..band(op, 7) + elseif p == "Z" then + x = band(rshift(op, rs-4), 255) + rs = rs - 10 + elseif p == ">" then + operands[#operands] = rshift(operands[#operands], 1) + elseif p == "0" then + if last == "r0" then + operands[#operands] = nil + if altname then name = altname end + end + elseif p == "L" then + name = gsub(name, "_", band(op, 0x00200000) ~= 0 and "d" or "w") + elseif p == "." then + if band(op, 1) == 1 then name = name.."." end + elseif p == "N" then + if op == 0x60000000 then name = "nop"; break end + elseif p == "~" then + local n = #operands + operands[n-1], operands[n] = operands[n], operands[n-1] + elseif p == "=" then + local n = #operands + if last == operands[n-1] then + operands[n] = nil + name = altname + end + elseif p == "%" then + local n = #operands + if last == operands[n-1] and last == operands[n-2] then + operands[n] = nil + operands[n-1] = nil + name = altname + end + elseif p == "-" then + rs = rs - 5 + else + assert(false) + end + if x then operands[#operands+1] = x; last = x end + end + + return putop(ctx, name, operands) +end + +------------------------------------------------------------------------------ + +-- Disassemble a block of code. +local function disass_block(ctx, ofs, len) + if not ofs then ofs = 0 end + local stop = len and ofs+len or #ctx.code + stop = stop - stop % 4 + ctx.pos = ofs - ofs % 4 + ctx.rel = nil + while ctx.pos < stop do disass_ins(ctx) end +end + +-- Extended API: create a disassembler context. Then call ctx:disass(ofs, len). +local function create(code, addr, out) + local ctx = {} + ctx.code = code + ctx.addr = addr or 0 + ctx.out = out or io.write + ctx.symtab = {} + ctx.disass = disass_block + ctx.hexdump = 8 + return ctx +end + +-- Simple API: disassemble code (a string) at address and output via out. +local function disass(code, addr, out) + create(code, addr, out):disass() +end + +-- Return register name for RID. +local function regname(r) + if r < 32 then return map_gpr[r] end + return "f"..(r-32) +end + +-- Public module functions. +return { + create = create, + disass = disass, + regname = regname +} + diff --git a/lib/LuaJIT/jit/dis_x64.lua b/lib/LuaJIT/jit/dis_x64.lua new file mode 100644 index 0000000..d5714ee --- /dev/null +++ b/lib/LuaJIT/jit/dis_x64.lua @@ -0,0 +1,17 @@ +---------------------------------------------------------------------------- +-- LuaJIT x64 disassembler wrapper module. +-- +-- Copyright (C) 2005-2017 Mike Pall. All rights reserved. +-- Released under the MIT license. See Copyright Notice in luajit.h +---------------------------------------------------------------------------- +-- This module just exports the 64 bit functions from the combined +-- x86/x64 disassembler module. All the interesting stuff is there. +------------------------------------------------------------------------------ + +local dis_x86 = require((string.match(..., ".*%.") or "").."dis_x86") +return { + create = dis_x86.create64, + disass = dis_x86.disass64, + regname = dis_x86.regname64 +} + diff --git a/lib/LuaJIT/jit/dis_x86.lua b/lib/LuaJIT/jit/dis_x86.lua new file mode 100644 index 0000000..3a68c93 --- /dev/null +++ b/lib/LuaJIT/jit/dis_x86.lua @@ -0,0 +1,953 @@ +---------------------------------------------------------------------------- +-- LuaJIT x86/x64 disassembler module. +-- +-- Copyright (C) 2005-2017 Mike Pall. All rights reserved. +-- Released under the MIT license. See Copyright Notice in luajit.h +---------------------------------------------------------------------------- +-- This is a helper module used by the LuaJIT machine code dumper module. +-- +-- Sending small code snippets to an external disassembler and mixing the +-- output with our own stuff was too fragile. So I had to bite the bullet +-- and write yet another x86 disassembler. Oh well ... +-- +-- The output format is very similar to what ndisasm generates. But it has +-- been developed independently by looking at the opcode tables from the +-- Intel and AMD manuals. The supported instruction set is quite extensive +-- and reflects what a current generation Intel or AMD CPU implements in +-- 32 bit and 64 bit mode. Yes, this includes MMX, SSE, SSE2, SSE3, SSSE3, +-- SSE4.1, SSE4.2, SSE4a, AVX, AVX2 and even privileged and hypervisor +-- (VMX/SVM) instructions. +-- +-- Notes: +-- * The (useless) a16 prefix, 3DNow and pre-586 opcodes are unsupported. +-- * No attempt at optimization has been made -- it's fast enough for my needs. +------------------------------------------------------------------------------ + +local type = type +local sub, byte, format = string.sub, string.byte, string.format +local match, gmatch, gsub = string.match, string.gmatch, string.gsub +local lower, rep = string.lower, string.rep +local bit = require("bit") +local tohex = bit.tohex + +-- Map for 1st opcode byte in 32 bit mode. Ugly? Well ... read on. +local map_opc1_32 = { +--0x +[0]="addBmr","addVmr","addBrm","addVrm","addBai","addVai","push es","pop es", +"orBmr","orVmr","orBrm","orVrm","orBai","orVai","push cs","opc2*", +--1x +"adcBmr","adcVmr","adcBrm","adcVrm","adcBai","adcVai","push ss","pop ss", +"sbbBmr","sbbVmr","sbbBrm","sbbVrm","sbbBai","sbbVai","push ds","pop ds", +--2x +"andBmr","andVmr","andBrm","andVrm","andBai","andVai","es:seg","daa", +"subBmr","subVmr","subBrm","subVrm","subBai","subVai","cs:seg","das", +--3x +"xorBmr","xorVmr","xorBrm","xorVrm","xorBai","xorVai","ss:seg","aaa", +"cmpBmr","cmpVmr","cmpBrm","cmpVrm","cmpBai","cmpVai","ds:seg","aas", +--4x +"incVR","incVR","incVR","incVR","incVR","incVR","incVR","incVR", +"decVR","decVR","decVR","decVR","decVR","decVR","decVR","decVR", +--5x +"pushUR","pushUR","pushUR","pushUR","pushUR","pushUR","pushUR","pushUR", +"popUR","popUR","popUR","popUR","popUR","popUR","popUR","popUR", +--6x +"sz*pushaw,pusha","sz*popaw,popa","boundVrm","arplWmr", +"fs:seg","gs:seg","o16:","a16", +"pushUi","imulVrmi","pushBs","imulVrms", +"insb","insVS","outsb","outsVS", +--7x +"joBj","jnoBj","jbBj","jnbBj","jzBj","jnzBj","jbeBj","jaBj", +"jsBj","jnsBj","jpeBj","jpoBj","jlBj","jgeBj","jleBj","jgBj", +--8x +"arith!Bmi","arith!Vmi","arith!Bmi","arith!Vms", +"testBmr","testVmr","xchgBrm","xchgVrm", +"movBmr","movVmr","movBrm","movVrm", +"movVmg","leaVrm","movWgm","popUm", +--9x +"nop*xchgVaR|pause|xchgWaR|repne nop","xchgVaR","xchgVaR","xchgVaR", +"xchgVaR","xchgVaR","xchgVaR","xchgVaR", +"sz*cbw,cwde,cdqe","sz*cwd,cdq,cqo","call farViw","wait", +"sz*pushfw,pushf","sz*popfw,popf","sahf","lahf", +--Ax +"movBao","movVao","movBoa","movVoa", +"movsb","movsVS","cmpsb","cmpsVS", +"testBai","testVai","stosb","stosVS", +"lodsb","lodsVS","scasb","scasVS", +--Bx +"movBRi","movBRi","movBRi","movBRi","movBRi","movBRi","movBRi","movBRi", +"movVRI","movVRI","movVRI","movVRI","movVRI","movVRI","movVRI","movVRI", +--Cx +"shift!Bmu","shift!Vmu","retBw","ret","vex*3$lesVrm","vex*2$ldsVrm","movBmi","movVmi", +"enterBwu","leave","retfBw","retf","int3","intBu","into","iretVS", +--Dx +"shift!Bm1","shift!Vm1","shift!Bmc","shift!Vmc","aamBu","aadBu","salc","xlatb", +"fp*0","fp*1","fp*2","fp*3","fp*4","fp*5","fp*6","fp*7", +--Ex +"loopneBj","loopeBj","loopBj","sz*jcxzBj,jecxzBj,jrcxzBj", +"inBau","inVau","outBua","outVua", +"callVj","jmpVj","jmp farViw","jmpBj","inBad","inVad","outBda","outVda", +--Fx +"lock:","int1","repne:rep","rep:","hlt","cmc","testb!Bm","testv!Vm", +"clc","stc","cli","sti","cld","std","incb!Bm","incd!Vm", +} +assert(#map_opc1_32 == 255) + +-- Map for 1st opcode byte in 64 bit mode (overrides only). +local map_opc1_64 = setmetatable({ + [0x06]=false, [0x07]=false, [0x0e]=false, + [0x16]=false, [0x17]=false, [0x1e]=false, [0x1f]=false, + [0x27]=false, [0x2f]=false, [0x37]=false, [0x3f]=false, + [0x60]=false, [0x61]=false, [0x62]=false, [0x63]="movsxdVrDmt", [0x67]="a32:", + [0x40]="rex*", [0x41]="rex*b", [0x42]="rex*x", [0x43]="rex*xb", + [0x44]="rex*r", [0x45]="rex*rb", [0x46]="rex*rx", [0x47]="rex*rxb", + [0x48]="rex*w", [0x49]="rex*wb", [0x4a]="rex*wx", [0x4b]="rex*wxb", + [0x4c]="rex*wr", [0x4d]="rex*wrb", [0x4e]="rex*wrx", [0x4f]="rex*wrxb", + [0x82]=false, [0x9a]=false, [0xc4]="vex*3", [0xc5]="vex*2", [0xce]=false, + [0xd4]=false, [0xd5]=false, [0xd6]=false, [0xea]=false, +}, { __index = map_opc1_32 }) + +-- Map for 2nd opcode byte (0F xx). True CISC hell. Hey, I told you. +-- Prefix dependent MMX/SSE opcodes: (none)|rep|o16|repne, -|F3|66|F2 +local map_opc2 = { +--0x +[0]="sldt!Dmp","sgdt!Ump","larVrm","lslVrm",nil,"syscall","clts","sysret", +"invd","wbinvd",nil,"ud1",nil,"$prefetch!Bm","femms","3dnowMrmu", +--1x +"movupsXrm|movssXrvm|movupdXrm|movsdXrvm", +"movupsXmr|movssXmvr|movupdXmr|movsdXmvr", +"movhlpsXrm$movlpsXrm|movsldupXrm|movlpdXrm|movddupXrm", +"movlpsXmr||movlpdXmr", +"unpcklpsXrvm||unpcklpdXrvm", +"unpckhpsXrvm||unpckhpdXrvm", +"movlhpsXrm$movhpsXrm|movshdupXrm|movhpdXrm", +"movhpsXmr||movhpdXmr", +"$prefetcht!Bm","hintnopVm","hintnopVm","hintnopVm", +"hintnopVm","hintnopVm","hintnopVm","hintnopVm", +--2x +"movUmx$","movUmy$","movUxm$","movUym$","movUmz$",nil,"movUzm$",nil, +"movapsXrm||movapdXrm", +"movapsXmr||movapdXmr", +"cvtpi2psXrMm|cvtsi2ssXrvVmt|cvtpi2pdXrMm|cvtsi2sdXrvVmt", +"movntpsXmr|movntssXmr|movntpdXmr|movntsdXmr", +"cvttps2piMrXm|cvttss2siVrXm|cvttpd2piMrXm|cvttsd2siVrXm", +"cvtps2piMrXm|cvtss2siVrXm|cvtpd2piMrXm|cvtsd2siVrXm", +"ucomissXrm||ucomisdXrm", +"comissXrm||comisdXrm", +--3x +"wrmsr","rdtsc","rdmsr","rdpmc","sysenter","sysexit",nil,"getsec", +"opc3*38",nil,"opc3*3a",nil,nil,nil,nil,nil, +--4x +"cmovoVrm","cmovnoVrm","cmovbVrm","cmovnbVrm", +"cmovzVrm","cmovnzVrm","cmovbeVrm","cmovaVrm", +"cmovsVrm","cmovnsVrm","cmovpeVrm","cmovpoVrm", +"cmovlVrm","cmovgeVrm","cmovleVrm","cmovgVrm", +--5x +"movmskpsVrXm$||movmskpdVrXm$","sqrtpsXrm|sqrtssXrm|sqrtpdXrm|sqrtsdXrm", +"rsqrtpsXrm|rsqrtssXrvm","rcppsXrm|rcpssXrvm", +"andpsXrvm||andpdXrvm","andnpsXrvm||andnpdXrvm", +"orpsXrvm||orpdXrvm","xorpsXrvm||xorpdXrvm", +"addpsXrvm|addssXrvm|addpdXrvm|addsdXrvm","mulpsXrvm|mulssXrvm|mulpdXrvm|mulsdXrvm", +"cvtps2pdXrm|cvtss2sdXrvm|cvtpd2psXrm|cvtsd2ssXrvm", +"cvtdq2psXrm|cvttps2dqXrm|cvtps2dqXrm", +"subpsXrvm|subssXrvm|subpdXrvm|subsdXrvm","minpsXrvm|minssXrvm|minpdXrvm|minsdXrvm", +"divpsXrvm|divssXrvm|divpdXrvm|divsdXrvm","maxpsXrvm|maxssXrvm|maxpdXrvm|maxsdXrvm", +--6x +"punpcklbwPrvm","punpcklwdPrvm","punpckldqPrvm","packsswbPrvm", +"pcmpgtbPrvm","pcmpgtwPrvm","pcmpgtdPrvm","packuswbPrvm", +"punpckhbwPrvm","punpckhwdPrvm","punpckhdqPrvm","packssdwPrvm", +"||punpcklqdqXrvm","||punpckhqdqXrvm", +"movPrVSm","movqMrm|movdquXrm|movdqaXrm", +--7x +"pshufwMrmu|pshufhwXrmu|pshufdXrmu|pshuflwXrmu","pshiftw!Pvmu", +"pshiftd!Pvmu","pshiftq!Mvmu||pshiftdq!Xvmu", +"pcmpeqbPrvm","pcmpeqwPrvm","pcmpeqdPrvm","emms*|", +"vmreadUmr||extrqXmuu$|insertqXrmuu$","vmwriteUrm||extrqXrm$|insertqXrm$", +nil,nil, +"||haddpdXrvm|haddpsXrvm","||hsubpdXrvm|hsubpsXrvm", +"movVSmMr|movqXrm|movVSmXr","movqMmr|movdquXmr|movdqaXmr", +--8x +"joVj","jnoVj","jbVj","jnbVj","jzVj","jnzVj","jbeVj","jaVj", +"jsVj","jnsVj","jpeVj","jpoVj","jlVj","jgeVj","jleVj","jgVj", +--9x +"setoBm","setnoBm","setbBm","setnbBm","setzBm","setnzBm","setbeBm","setaBm", +"setsBm","setnsBm","setpeBm","setpoBm","setlBm","setgeBm","setleBm","setgBm", +--Ax +"push fs","pop fs","cpuid","btVmr","shldVmru","shldVmrc",nil,nil, +"push gs","pop gs","rsm","btsVmr","shrdVmru","shrdVmrc","fxsave!Dmp","imulVrm", +--Bx +"cmpxchgBmr","cmpxchgVmr","$lssVrm","btrVmr", +"$lfsVrm","$lgsVrm","movzxVrBmt","movzxVrWmt", +"|popcntVrm","ud2Dp","bt!Vmu","btcVmr", +"bsfVrm","bsrVrm|lzcntVrm|bsrWrm","movsxVrBmt","movsxVrWmt", +--Cx +"xaddBmr","xaddVmr", +"cmppsXrvmu|cmpssXrvmu|cmppdXrvmu|cmpsdXrvmu","$movntiVmr|", +"pinsrwPrvWmu","pextrwDrPmu", +"shufpsXrvmu||shufpdXrvmu","$cmpxchg!Qmp", +"bswapVR","bswapVR","bswapVR","bswapVR","bswapVR","bswapVR","bswapVR","bswapVR", +--Dx +"||addsubpdXrvm|addsubpsXrvm","psrlwPrvm","psrldPrvm","psrlqPrvm", +"paddqPrvm","pmullwPrvm", +"|movq2dqXrMm|movqXmr|movdq2qMrXm$","pmovmskbVrMm||pmovmskbVrXm", +"psubusbPrvm","psubuswPrvm","pminubPrvm","pandPrvm", +"paddusbPrvm","padduswPrvm","pmaxubPrvm","pandnPrvm", +--Ex +"pavgbPrvm","psrawPrvm","psradPrvm","pavgwPrvm", +"pmulhuwPrvm","pmulhwPrvm", +"|cvtdq2pdXrm|cvttpd2dqXrm|cvtpd2dqXrm","$movntqMmr||$movntdqXmr", +"psubsbPrvm","psubswPrvm","pminswPrvm","porPrvm", +"paddsbPrvm","paddswPrvm","pmaxswPrvm","pxorPrvm", +--Fx +"|||lddquXrm","psllwPrvm","pslldPrvm","psllqPrvm", +"pmuludqPrvm","pmaddwdPrvm","psadbwPrvm","maskmovqMrm||maskmovdquXrm$", +"psubbPrvm","psubwPrvm","psubdPrvm","psubqPrvm", +"paddbPrvm","paddwPrvm","padddPrvm","ud", +} +assert(map_opc2[255] == "ud") + +-- Map for three-byte opcodes. Can't wait for their next invention. +local map_opc3 = { +["38"] = { -- [66] 0f 38 xx +--0x +[0]="pshufbPrvm","phaddwPrvm","phadddPrvm","phaddswPrvm", +"pmaddubswPrvm","phsubwPrvm","phsubdPrvm","phsubswPrvm", +"psignbPrvm","psignwPrvm","psigndPrvm","pmulhrswPrvm", +"||permilpsXrvm","||permilpdXrvm",nil,nil, +--1x +"||pblendvbXrma",nil,nil,nil, +"||blendvpsXrma","||blendvpdXrma","||permpsXrvm","||ptestXrm", +"||broadcastssXrm","||broadcastsdXrm","||broadcastf128XrlXm",nil, +"pabsbPrm","pabswPrm","pabsdPrm",nil, +--2x +"||pmovsxbwXrm","||pmovsxbdXrm","||pmovsxbqXrm","||pmovsxwdXrm", +"||pmovsxwqXrm","||pmovsxdqXrm",nil,nil, +"||pmuldqXrvm","||pcmpeqqXrvm","||$movntdqaXrm","||packusdwXrvm", +"||maskmovpsXrvm","||maskmovpdXrvm","||maskmovpsXmvr","||maskmovpdXmvr", +--3x +"||pmovzxbwXrm","||pmovzxbdXrm","||pmovzxbqXrm","||pmovzxwdXrm", +"||pmovzxwqXrm","||pmovzxdqXrm","||permdXrvm","||pcmpgtqXrvm", +"||pminsbXrvm","||pminsdXrvm","||pminuwXrvm","||pminudXrvm", +"||pmaxsbXrvm","||pmaxsdXrvm","||pmaxuwXrvm","||pmaxudXrvm", +--4x +"||pmulddXrvm","||phminposuwXrm",nil,nil, +nil,"||psrlvVSXrvm","||psravdXrvm","||psllvVSXrvm", +--5x +[0x58] = "||pbroadcastdXrlXm",[0x59] = "||pbroadcastqXrlXm", +[0x5a] = "||broadcasti128XrlXm", +--7x +[0x78] = "||pbroadcastbXrlXm",[0x79] = "||pbroadcastwXrlXm", +--8x +[0x8c] = "||pmaskmovXrvVSm", +[0x8e] = "||pmaskmovVSmXvr", +--9x +[0x96] = "||fmaddsub132pHXrvm",[0x97] = "||fmsubadd132pHXrvm", +[0x98] = "||fmadd132pHXrvm",[0x99] = "||fmadd132sHXrvm", +[0x9a] = "||fmsub132pHXrvm",[0x9b] = "||fmsub132sHXrvm", +[0x9c] = "||fnmadd132pHXrvm",[0x9d] = "||fnmadd132sHXrvm", +[0x9e] = "||fnmsub132pHXrvm",[0x9f] = "||fnmsub132sHXrvm", +--Ax +[0xa6] = "||fmaddsub213pHXrvm",[0xa7] = "||fmsubadd213pHXrvm", +[0xa8] = "||fmadd213pHXrvm",[0xa9] = "||fmadd213sHXrvm", +[0xaa] = "||fmsub213pHXrvm",[0xab] = "||fmsub213sHXrvm", +[0xac] = "||fnmadd213pHXrvm",[0xad] = "||fnmadd213sHXrvm", +[0xae] = "||fnmsub213pHXrvm",[0xaf] = "||fnmsub213sHXrvm", +--Bx +[0xb6] = "||fmaddsub231pHXrvm",[0xb7] = "||fmsubadd231pHXrvm", +[0xb8] = "||fmadd231pHXrvm",[0xb9] = "||fmadd231sHXrvm", +[0xba] = "||fmsub231pHXrvm",[0xbb] = "||fmsub231sHXrvm", +[0xbc] = "||fnmadd231pHXrvm",[0xbd] = "||fnmadd231sHXrvm", +[0xbe] = "||fnmsub231pHXrvm",[0xbf] = "||fnmsub231sHXrvm", +--Dx +[0xdc] = "||aesencXrvm", [0xdd] = "||aesenclastXrvm", +[0xde] = "||aesdecXrvm", [0xdf] = "||aesdeclastXrvm", +--Fx +[0xf0] = "|||crc32TrBmt",[0xf1] = "|||crc32TrVmt", +[0xf7] = "| sarxVrmv| shlxVrmv| shrxVrmv", +}, + +["3a"] = { -- [66] 0f 3a xx +--0x +[0x00]="||permqXrmu","||permpdXrmu","||pblenddXrvmu",nil, +"||permilpsXrmu","||permilpdXrmu","||perm2f128Xrvmu",nil, +"||roundpsXrmu","||roundpdXrmu","||roundssXrvmu","||roundsdXrvmu", +"||blendpsXrvmu","||blendpdXrvmu","||pblendwXrvmu","palignrPrvmu", +--1x +nil,nil,nil,nil, +"||pextrbVmXru","||pextrwVmXru","||pextrVmSXru","||extractpsVmXru", +"||insertf128XrvlXmu","||extractf128XlXmYru",nil,nil, +nil,nil,nil,nil, +--2x +"||pinsrbXrvVmu","||insertpsXrvmu","||pinsrXrvVmuS",nil, +--3x +[0x38] = "||inserti128Xrvmu",[0x39] = "||extracti128XlXmYru", +--4x +[0x40] = "||dppsXrvmu", +[0x41] = "||dppdXrvmu", +[0x42] = "||mpsadbwXrvmu", +[0x44] = "||pclmulqdqXrvmu", +[0x46] = "||perm2i128Xrvmu", +[0x4a] = "||blendvpsXrvmb",[0x4b] = "||blendvpdXrvmb", +[0x4c] = "||pblendvbXrvmb", +--6x +[0x60] = "||pcmpestrmXrmu",[0x61] = "||pcmpestriXrmu", +[0x62] = "||pcmpistrmXrmu",[0x63] = "||pcmpistriXrmu", +[0xdf] = "||aeskeygenassistXrmu", +--Fx +[0xf0] = "||| rorxVrmu", +}, +} + +-- Map for VMX/SVM opcodes 0F 01 C0-FF (sgdt group with register operands). +local map_opcvm = { +[0xc1]="vmcall",[0xc2]="vmlaunch",[0xc3]="vmresume",[0xc4]="vmxoff", +[0xc8]="monitor",[0xc9]="mwait", +[0xd8]="vmrun",[0xd9]="vmmcall",[0xda]="vmload",[0xdb]="vmsave", +[0xdc]="stgi",[0xdd]="clgi",[0xde]="skinit",[0xdf]="invlpga", +[0xf8]="swapgs",[0xf9]="rdtscp", +} + +-- Map for FP opcodes. And you thought stack machines are simple? +local map_opcfp = { +-- D8-DF 00-BF: opcodes with a memory operand. +-- D8 +[0]="faddFm","fmulFm","fcomFm","fcompFm","fsubFm","fsubrFm","fdivFm","fdivrFm", +"fldFm",nil,"fstFm","fstpFm","fldenvVm","fldcwWm","fnstenvVm","fnstcwWm", +-- DA +"fiaddDm","fimulDm","ficomDm","ficompDm", +"fisubDm","fisubrDm","fidivDm","fidivrDm", +-- DB +"fildDm","fisttpDm","fistDm","fistpDm",nil,"fld twordFmp",nil,"fstp twordFmp", +-- DC +"faddGm","fmulGm","fcomGm","fcompGm","fsubGm","fsubrGm","fdivGm","fdivrGm", +-- DD +"fldGm","fisttpQm","fstGm","fstpGm","frstorDmp",nil,"fnsaveDmp","fnstswWm", +-- DE +"fiaddWm","fimulWm","ficomWm","ficompWm", +"fisubWm","fisubrWm","fidivWm","fidivrWm", +-- DF +"fildWm","fisttpWm","fistWm","fistpWm", +"fbld twordFmp","fildQm","fbstp twordFmp","fistpQm", +-- xx C0-FF: opcodes with a pseudo-register operand. +-- D8 +"faddFf","fmulFf","fcomFf","fcompFf","fsubFf","fsubrFf","fdivFf","fdivrFf", +-- D9 +"fldFf","fxchFf",{"fnop"},nil, +{"fchs","fabs",nil,nil,"ftst","fxam"}, +{"fld1","fldl2t","fldl2e","fldpi","fldlg2","fldln2","fldz"}, +{"f2xm1","fyl2x","fptan","fpatan","fxtract","fprem1","fdecstp","fincstp"}, +{"fprem","fyl2xp1","fsqrt","fsincos","frndint","fscale","fsin","fcos"}, +-- DA +"fcmovbFf","fcmoveFf","fcmovbeFf","fcmovuFf",nil,{nil,"fucompp"},nil,nil, +-- DB +"fcmovnbFf","fcmovneFf","fcmovnbeFf","fcmovnuFf", +{nil,nil,"fnclex","fninit"},"fucomiFf","fcomiFf",nil, +-- DC +"fadd toFf","fmul toFf",nil,nil, +"fsub toFf","fsubr toFf","fdivr toFf","fdiv toFf", +-- DD +"ffreeFf",nil,"fstFf","fstpFf","fucomFf","fucompFf",nil,nil, +-- DE +"faddpFf","fmulpFf",nil,{nil,"fcompp"}, +"fsubrpFf","fsubpFf","fdivrpFf","fdivpFf", +-- DF +nil,nil,nil,nil,{"fnstsw ax"},"fucomipFf","fcomipFf",nil, +} +assert(map_opcfp[126] == "fcomipFf") + +-- Map for opcode groups. The subkey is sp from the ModRM byte. +local map_opcgroup = { + arith = { "add", "or", "adc", "sbb", "and", "sub", "xor", "cmp" }, + shift = { "rol", "ror", "rcl", "rcr", "shl", "shr", "sal", "sar" }, + testb = { "testBmi", "testBmi", "not", "neg", "mul", "imul", "div", "idiv" }, + testv = { "testVmi", "testVmi", "not", "neg", "mul", "imul", "div", "idiv" }, + incb = { "inc", "dec" }, + incd = { "inc", "dec", "callUmp", "$call farDmp", + "jmpUmp", "$jmp farDmp", "pushUm" }, + sldt = { "sldt", "str", "lldt", "ltr", "verr", "verw" }, + sgdt = { "vm*$sgdt", "vm*$sidt", "$lgdt", "vm*$lidt", + "smsw", nil, "lmsw", "vm*$invlpg" }, + bt = { nil, nil, nil, nil, "bt", "bts", "btr", "btc" }, + cmpxchg = { nil, "sz*,cmpxchg8bQmp,cmpxchg16bXmp", nil, nil, + nil, nil, "vmptrld|vmxon|vmclear", "vmptrst" }, + pshiftw = { nil, nil, "psrlw", nil, "psraw", nil, "psllw" }, + pshiftd = { nil, nil, "psrld", nil, "psrad", nil, "pslld" }, + pshiftq = { nil, nil, "psrlq", nil, nil, nil, "psllq" }, + pshiftdq = { nil, nil, "psrlq", "psrldq", nil, nil, "psllq", "pslldq" }, + fxsave = { "$fxsave", "$fxrstor", "$ldmxcsr", "$stmxcsr", + nil, "lfenceDp$", "mfenceDp$", "sfenceDp$clflush" }, + prefetch = { "prefetch", "prefetchw" }, + prefetcht = { "prefetchnta", "prefetcht0", "prefetcht1", "prefetcht2" }, +} + +------------------------------------------------------------------------------ + +-- Maps for register names. +local map_regs = { + B = { "al", "cl", "dl", "bl", "ah", "ch", "dh", "bh", + "r8b", "r9b", "r10b", "r11b", "r12b", "r13b", "r14b", "r15b" }, + B64 = { "al", "cl", "dl", "bl", "spl", "bpl", "sil", "dil", + "r8b", "r9b", "r10b", "r11b", "r12b", "r13b", "r14b", "r15b" }, + W = { "ax", "cx", "dx", "bx", "sp", "bp", "si", "di", + "r8w", "r9w", "r10w", "r11w", "r12w", "r13w", "r14w", "r15w" }, + D = { "eax", "ecx", "edx", "ebx", "esp", "ebp", "esi", "edi", + "r8d", "r9d", "r10d", "r11d", "r12d", "r13d", "r14d", "r15d" }, + Q = { "rax", "rcx", "rdx", "rbx", "rsp", "rbp", "rsi", "rdi", + "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15" }, + M = { "mm0", "mm1", "mm2", "mm3", "mm4", "mm5", "mm6", "mm7", + "mm0", "mm1", "mm2", "mm3", "mm4", "mm5", "mm6", "mm7" }, -- No x64 ext! + X = { "xmm0", "xmm1", "xmm2", "xmm3", "xmm4", "xmm5", "xmm6", "xmm7", + "xmm8", "xmm9", "xmm10", "xmm11", "xmm12", "xmm13", "xmm14", "xmm15" }, + Y = { "ymm0", "ymm1", "ymm2", "ymm3", "ymm4", "ymm5", "ymm6", "ymm7", + "ymm8", "ymm9", "ymm10", "ymm11", "ymm12", "ymm13", "ymm14", "ymm15" }, +} +local map_segregs = { "es", "cs", "ss", "ds", "fs", "gs", "segr6", "segr7" } + +-- Maps for size names. +local map_sz2n = { + B = 1, W = 2, D = 4, Q = 8, M = 8, X = 16, Y = 32, +} +local map_sz2prefix = { + B = "byte", W = "word", D = "dword", + Q = "qword", + M = "qword", X = "xword", Y = "yword", + F = "dword", G = "qword", -- No need for sizes/register names for these two. +} + +------------------------------------------------------------------------------ + +-- Output a nicely formatted line with an opcode and operands. +local function putop(ctx, text, operands) + local code, pos, hex = ctx.code, ctx.pos, "" + local hmax = ctx.hexdump + if hmax > 0 then + for i=ctx.start,pos-1 do + hex = hex..format("%02X", byte(code, i, i)) + end + if #hex > hmax then hex = sub(hex, 1, hmax)..". " + else hex = hex..rep(" ", hmax-#hex+2) end + end + if operands then text = text.." "..operands end + if ctx.o16 then text = "o16 "..text; ctx.o16 = false end + if ctx.a32 then text = "a32 "..text; ctx.a32 = false end + if ctx.rep then text = ctx.rep.." "..text; ctx.rep = false end + if ctx.rex then + local t = (ctx.rexw and "w" or "")..(ctx.rexr and "r" or "").. + (ctx.rexx and "x" or "")..(ctx.rexb and "b" or "").. + (ctx.vexl and "l" or "") + if ctx.vexv and ctx.vexv ~= 0 then t = t.."v"..ctx.vexv end + if t ~= "" then text = ctx.rex.."."..t.." "..gsub(text, "^ ", "") + elseif ctx.rex == "vex" then text = gsub("v"..text, "^v ", "") end + ctx.rexw = false; ctx.rexr = false; ctx.rexx = false; ctx.rexb = false + ctx.rex = false; ctx.vexl = false; ctx.vexv = false + end + if ctx.seg then + local text2, n = gsub(text, "%[", "["..ctx.seg..":") + if n == 0 then text = ctx.seg.." "..text else text = text2 end + ctx.seg = false + end + if ctx.lock then text = "lock "..text; ctx.lock = false end + local imm = ctx.imm + if imm then + local sym = ctx.symtab[imm] + if sym then text = text.."\t->"..sym end + end + ctx.out(format("%08x %s%s\n", ctx.addr+ctx.start, hex, text)) + ctx.mrm = false + ctx.vexv = false + ctx.start = pos + ctx.imm = nil +end + +-- Clear all prefix flags. +local function clearprefixes(ctx) + ctx.o16 = false; ctx.seg = false; ctx.lock = false; ctx.rep = false + ctx.rexw = false; ctx.rexr = false; ctx.rexx = false; ctx.rexb = false + ctx.rex = false; ctx.a32 = false; ctx.vexl = false +end + +-- Fallback for incomplete opcodes at the end. +local function incomplete(ctx) + ctx.pos = ctx.stop+1 + clearprefixes(ctx) + return putop(ctx, "(incomplete)") +end + +-- Fallback for unknown opcodes. +local function unknown(ctx) + clearprefixes(ctx) + return putop(ctx, "(unknown)") +end + +-- Return an immediate of the specified size. +local function getimm(ctx, pos, n) + if pos+n-1 > ctx.stop then return incomplete(ctx) end + local code = ctx.code + if n == 1 then + local b1 = byte(code, pos, pos) + return b1 + elseif n == 2 then + local b1, b2 = byte(code, pos, pos+1) + return b1+b2*256 + else + local b1, b2, b3, b4 = byte(code, pos, pos+3) + local imm = b1+b2*256+b3*65536+b4*16777216 + ctx.imm = imm + return imm + end +end + +-- Process pattern string and generate the operands. +local function putpat(ctx, name, pat) + local operands, regs, sz, mode, sp, rm, sc, rx, sdisp + local code, pos, stop, vexl = ctx.code, ctx.pos, ctx.stop, ctx.vexl + + -- Chars used: 1DFGHIMPQRSTUVWXYabcdfgijlmoprstuvwxyz + for p in gmatch(pat, ".") do + local x = nil + if p == "V" or p == "U" then + if ctx.rexw then sz = "Q"; ctx.rexw = false + elseif ctx.o16 then sz = "W"; ctx.o16 = false + elseif p == "U" and ctx.x64 then sz = "Q" + else sz = "D" end + regs = map_regs[sz] + elseif p == "T" then + if ctx.rexw then sz = "Q"; ctx.rexw = false else sz = "D" end + regs = map_regs[sz] + elseif p == "B" then + sz = "B" + regs = ctx.rex and map_regs.B64 or map_regs.B + elseif match(p, "[WDQMXYFG]") then + sz = p + if sz == "X" and vexl then sz = "Y"; ctx.vexl = false end + regs = map_regs[sz] + elseif p == "P" then + sz = ctx.o16 and "X" or "M"; ctx.o16 = false + if sz == "X" and vexl then sz = "Y"; ctx.vexl = false end + regs = map_regs[sz] + elseif p == "H" then + name = name..(ctx.rexw and "d" or "s") + ctx.rexw = false + elseif p == "S" then + name = name..lower(sz) + elseif p == "s" then + local imm = getimm(ctx, pos, 1); if not imm then return end + x = imm <= 127 and format("+0x%02x", imm) + or format("-0x%02x", 256-imm) + pos = pos+1 + elseif p == "u" then + local imm = getimm(ctx, pos, 1); if not imm then return end + x = format("0x%02x", imm) + pos = pos+1 + elseif p == "b" then + local imm = getimm(ctx, pos, 1); if not imm then return end + x = regs[imm/16+1] + pos = pos+1 + elseif p == "w" then + local imm = getimm(ctx, pos, 2); if not imm then return end + x = format("0x%x", imm) + pos = pos+2 + elseif p == "o" then -- [offset] + if ctx.x64 then + local imm1 = getimm(ctx, pos, 4); if not imm1 then return end + local imm2 = getimm(ctx, pos+4, 4); if not imm2 then return end + x = format("[0x%08x%08x]", imm2, imm1) + pos = pos+8 + else + local imm = getimm(ctx, pos, 4); if not imm then return end + x = format("[0x%08x]", imm) + pos = pos+4 + end + elseif p == "i" or p == "I" then + local n = map_sz2n[sz] + if n == 8 and ctx.x64 and p == "I" then + local imm1 = getimm(ctx, pos, 4); if not imm1 then return end + local imm2 = getimm(ctx, pos+4, 4); if not imm2 then return end + x = format("0x%08x%08x", imm2, imm1) + else + if n == 8 then n = 4 end + local imm = getimm(ctx, pos, n); if not imm then return end + if sz == "Q" and (imm < 0 or imm > 0x7fffffff) then + imm = (0xffffffff+1)-imm + x = format(imm > 65535 and "-0x%08x" or "-0x%x", imm) + else + x = format(imm > 65535 and "0x%08x" or "0x%x", imm) + end + end + pos = pos+n + elseif p == "j" then + local n = map_sz2n[sz] + if n == 8 then n = 4 end + local imm = getimm(ctx, pos, n); if not imm then return end + if sz == "B" and imm > 127 then imm = imm-256 + elseif imm > 2147483647 then imm = imm-4294967296 end + pos = pos+n + imm = imm + pos + ctx.addr + if imm > 4294967295 and not ctx.x64 then imm = imm-4294967296 end + ctx.imm = imm + if sz == "W" then + x = format("word 0x%04x", imm%65536) + elseif ctx.x64 then + local lo = imm % 0x1000000 + x = format("0x%02x%06x", (imm-lo) / 0x1000000, lo) + else + x = "0x"..tohex(imm) + end + elseif p == "R" then + local r = byte(code, pos-1, pos-1)%8 + if ctx.rexb then r = r + 8; ctx.rexb = false end + x = regs[r+1] + elseif p == "a" then x = regs[1] + elseif p == "c" then x = "cl" + elseif p == "d" then x = "dx" + elseif p == "1" then x = "1" + else + if not mode then + mode = ctx.mrm + if not mode then + if pos > stop then return incomplete(ctx) end + mode = byte(code, pos, pos) + pos = pos+1 + end + rm = mode%8; mode = (mode-rm)/8 + sp = mode%8; mode = (mode-sp)/8 + sdisp = "" + if mode < 3 then + if rm == 4 then + if pos > stop then return incomplete(ctx) end + sc = byte(code, pos, pos) + pos = pos+1 + rm = sc%8; sc = (sc-rm)/8 + rx = sc%8; sc = (sc-rx)/8 + if ctx.rexx then rx = rx + 8; ctx.rexx = false end + if rx == 4 then rx = nil end + end + if mode > 0 or rm == 5 then + local dsz = mode + if dsz ~= 1 then dsz = 4 end + local disp = getimm(ctx, pos, dsz); if not disp then return end + if mode == 0 then rm = nil end + if rm or rx or (not sc and ctx.x64 and not ctx.a32) then + if dsz == 1 and disp > 127 then + sdisp = format("-0x%x", 256-disp) + elseif disp >= 0 and disp <= 0x7fffffff then + sdisp = format("+0x%x", disp) + else + sdisp = format("-0x%x", (0xffffffff+1)-disp) + end + else + sdisp = format(ctx.x64 and not ctx.a32 and + not (disp >= 0 and disp <= 0x7fffffff) + and "0xffffffff%08x" or "0x%08x", disp) + end + pos = pos+dsz + end + end + if rm and ctx.rexb then rm = rm + 8; ctx.rexb = false end + if ctx.rexr then sp = sp + 8; ctx.rexr = false end + end + if p == "m" then + if mode == 3 then x = regs[rm+1] + else + local aregs = ctx.a32 and map_regs.D or ctx.aregs + local srm, srx = "", "" + if rm then srm = aregs[rm+1] + elseif not sc and ctx.x64 and not ctx.a32 then srm = "rip" end + ctx.a32 = false + if rx then + if rm then srm = srm.."+" end + srx = aregs[rx+1] + if sc > 0 then srx = srx.."*"..(2^sc) end + end + x = format("[%s%s%s]", srm, srx, sdisp) + end + if mode < 3 and + (not match(pat, "[aRrgp]") or match(pat, "t")) then -- Yuck. + x = map_sz2prefix[sz].." "..x + end + elseif p == "r" then x = regs[sp+1] + elseif p == "g" then x = map_segregs[sp+1] + elseif p == "p" then -- Suppress prefix. + elseif p == "f" then x = "st"..rm + elseif p == "x" then + if sp == 0 and ctx.lock and not ctx.x64 then + x = "CR8"; ctx.lock = false + else + x = "CR"..sp + end + elseif p == "v" then + if ctx.vexv then + x = regs[ctx.vexv+1]; ctx.vexv = false + end + elseif p == "y" then x = "DR"..sp + elseif p == "z" then x = "TR"..sp + elseif p == "l" then vexl = false + elseif p == "t" then + else + error("bad pattern `"..pat.."'") + end + end + if x then operands = operands and operands..", "..x or x end + end + ctx.pos = pos + return putop(ctx, name, operands) +end + +-- Forward declaration. +local map_act + +-- Fetch and cache MRM byte. +local function getmrm(ctx) + local mrm = ctx.mrm + if not mrm then + local pos = ctx.pos + if pos > ctx.stop then return nil end + mrm = byte(ctx.code, pos, pos) + ctx.pos = pos+1 + ctx.mrm = mrm + end + return mrm +end + +-- Dispatch to handler depending on pattern. +local function dispatch(ctx, opat, patgrp) + if not opat then return unknown(ctx) end + if match(opat, "%|") then -- MMX/SSE variants depending on prefix. + local p + if ctx.rep then + p = ctx.rep=="rep" and "%|([^%|]*)" or "%|[^%|]*%|[^%|]*%|([^%|]*)" + ctx.rep = false + elseif ctx.o16 then p = "%|[^%|]*%|([^%|]*)"; ctx.o16 = false + else p = "^[^%|]*" end + opat = match(opat, p) + if not opat then return unknown(ctx) end +-- ctx.rep = false; ctx.o16 = false + --XXX fails for 66 f2 0f 38 f1 06 crc32 eax,WORD PTR [esi] + --XXX remove in branches? + end + if match(opat, "%$") then -- reg$mem variants. + local mrm = getmrm(ctx); if not mrm then return incomplete(ctx) end + opat = match(opat, mrm >= 192 and "^[^%$]*" or "%$(.*)") + if opat == "" then return unknown(ctx) end + end + if opat == "" then return unknown(ctx) end + local name, pat = match(opat, "^([a-z0-9 ]*)(.*)") + if pat == "" and patgrp then pat = patgrp end + return map_act[sub(pat, 1, 1)](ctx, name, pat) +end + +-- Get a pattern from an opcode map and dispatch to handler. +local function dispatchmap(ctx, opcmap) + local pos = ctx.pos + local opat = opcmap[byte(ctx.code, pos, pos)] + pos = pos + 1 + ctx.pos = pos + return dispatch(ctx, opat) +end + +-- Map for action codes. The key is the first char after the name. +map_act = { + -- Simple opcodes without operands. + [""] = function(ctx, name, pat) + return putop(ctx, name) + end, + + -- Operand size chars fall right through. + B = putpat, W = putpat, D = putpat, Q = putpat, + V = putpat, U = putpat, T = putpat, + M = putpat, X = putpat, P = putpat, + F = putpat, G = putpat, Y = putpat, + H = putpat, + + -- Collect prefixes. + [":"] = function(ctx, name, pat) + ctx[pat == ":" and name or sub(pat, 2)] = name + if ctx.pos - ctx.start > 5 then return unknown(ctx) end -- Limit #prefixes. + end, + + -- Chain to special handler specified by name. + ["*"] = function(ctx, name, pat) + return map_act[name](ctx, name, sub(pat, 2)) + end, + + -- Use named subtable for opcode group. + ["!"] = function(ctx, name, pat) + local mrm = getmrm(ctx); if not mrm then return incomplete(ctx) end + return dispatch(ctx, map_opcgroup[name][((mrm-(mrm%8))/8)%8+1], sub(pat, 2)) + end, + + -- o16,o32[,o64] variants. + sz = function(ctx, name, pat) + if ctx.o16 then ctx.o16 = false + else + pat = match(pat, ",(.*)") + if ctx.rexw then + local p = match(pat, ",(.*)") + if p then pat = p; ctx.rexw = false end + end + end + pat = match(pat, "^[^,]*") + return dispatch(ctx, pat) + end, + + -- Two-byte opcode dispatch. + opc2 = function(ctx, name, pat) + return dispatchmap(ctx, map_opc2) + end, + + -- Three-byte opcode dispatch. + opc3 = function(ctx, name, pat) + return dispatchmap(ctx, map_opc3[pat]) + end, + + -- VMX/SVM dispatch. + vm = function(ctx, name, pat) + return dispatch(ctx, map_opcvm[ctx.mrm]) + end, + + -- Floating point opcode dispatch. + fp = function(ctx, name, pat) + local mrm = getmrm(ctx); if not mrm then return incomplete(ctx) end + local rm = mrm%8 + local idx = pat*8 + ((mrm-rm)/8)%8 + if mrm >= 192 then idx = idx + 64 end + local opat = map_opcfp[idx] + if type(opat) == "table" then opat = opat[rm+1] end + return dispatch(ctx, opat) + end, + + -- REX prefix. + rex = function(ctx, name, pat) + if ctx.rex then return unknown(ctx) end -- Only 1 REX or VEX prefix allowed. + for p in gmatch(pat, ".") do ctx["rex"..p] = true end + ctx.rex = "rex" + end, + + -- VEX prefix. + vex = function(ctx, name, pat) + if ctx.rex then return unknown(ctx) end -- Only 1 REX or VEX prefix allowed. + ctx.rex = "vex" + local pos = ctx.pos + if ctx.mrm then + ctx.mrm = nil + pos = pos-1 + end + local b = byte(ctx.code, pos, pos) + if not b then return incomplete(ctx) end + pos = pos+1 + if b < 128 then ctx.rexr = true end + local m = 1 + if pat == "3" then + m = b%32; b = (b-m)/32 + local nb = b%2; b = (b-nb)/2 + if nb == 0 then ctx.rexb = true end + local nx = b%2 + if nx == 0 then ctx.rexx = true end + b = byte(ctx.code, pos, pos) + if not b then return incomplete(ctx) end + pos = pos+1 + if b >= 128 then ctx.rexw = true end + end + ctx.pos = pos + local map + if m == 1 then map = map_opc2 + elseif m == 2 then map = map_opc3["38"] + elseif m == 3 then map = map_opc3["3a"] + else return unknown(ctx) end + local p = b%4; b = (b-p)/4 + if p == 1 then ctx.o16 = "o16" + elseif p == 2 then ctx.rep = "rep" + elseif p == 3 then ctx.rep = "repne" end + local l = b%2; b = (b-l)/2 + if l ~= 0 then ctx.vexl = true end + ctx.vexv = (-1-b)%16 + return dispatchmap(ctx, map) + end, + + -- Special case for nop with REX prefix. + nop = function(ctx, name, pat) + return dispatch(ctx, ctx.rex and pat or "nop") + end, + + -- Special case for 0F 77. + emms = function(ctx, name, pat) + if ctx.rex ~= "vex" then + return putop(ctx, "emms") + elseif ctx.vexl then + ctx.vexl = false + return putop(ctx, "zeroall") + else + return putop(ctx, "zeroupper") + end + end, +} + +------------------------------------------------------------------------------ + +-- Disassemble a block of code. +local function disass_block(ctx, ofs, len) + if not ofs then ofs = 0 end + local stop = len and ofs+len or #ctx.code + ofs = ofs + 1 + ctx.start = ofs + ctx.pos = ofs + ctx.stop = stop + ctx.imm = nil + ctx.mrm = false + clearprefixes(ctx) + while ctx.pos <= stop do dispatchmap(ctx, ctx.map1) end + if ctx.pos ~= ctx.start then incomplete(ctx) end +end + +-- Extended API: create a disassembler context. Then call ctx:disass(ofs, len). +local function create(code, addr, out) + local ctx = {} + ctx.code = code + ctx.addr = (addr or 0) - 1 + ctx.out = out or io.write + ctx.symtab = {} + ctx.disass = disass_block + ctx.hexdump = 16 + ctx.x64 = false + ctx.map1 = map_opc1_32 + ctx.aregs = map_regs.D + return ctx +end + +local function create64(code, addr, out) + local ctx = create(code, addr, out) + ctx.x64 = true + ctx.map1 = map_opc1_64 + ctx.aregs = map_regs.Q + return ctx +end + +-- Simple API: disassemble code (a string) at address and output via out. +local function disass(code, addr, out) + create(code, addr, out):disass() +end + +local function disass64(code, addr, out) + create64(code, addr, out):disass() +end + +-- Return register name for RID. +local function regname(r) + if r < 8 then return map_regs.D[r+1] end + return map_regs.X[r-7] +end + +local function regname64(r) + if r < 16 then return map_regs.Q[r+1] end + return map_regs.X[r-15] +end + +-- Public module functions. +return { + create = create, + create64 = create64, + disass = disass, + disass64 = disass64, + regname = regname, + regname64 = regname64 +} + diff --git a/lib/LuaJIT/jit/dump.lua b/lib/LuaJIT/jit/dump.lua new file mode 100644 index 0000000..2bea652 --- /dev/null +++ b/lib/LuaJIT/jit/dump.lua @@ -0,0 +1,712 @@ +---------------------------------------------------------------------------- +-- LuaJIT compiler dump module. +-- +-- Copyright (C) 2005-2017 Mike Pall. All rights reserved. +-- Released under the MIT license. See Copyright Notice in luajit.h +---------------------------------------------------------------------------- +-- +-- This module can be used to debug the JIT compiler itself. It dumps the +-- code representations and structures used in various compiler stages. +-- +-- Example usage: +-- +-- luajit -jdump -e "local x=0; for i=1,1e6 do x=x+i end; print(x)" +-- luajit -jdump=im -e "for i=1,1000 do for j=1,1000 do end end" | less -R +-- luajit -jdump=is myapp.lua | less -R +-- luajit -jdump=-b myapp.lua +-- luajit -jdump=+aH,myapp.html myapp.lua +-- luajit -jdump=ixT,myapp.dump myapp.lua +-- +-- The first argument specifies the dump mode. The second argument gives +-- the output file name. Default output is to stdout, unless the environment +-- variable LUAJIT_DUMPFILE is set. The file is overwritten every time the +-- module is started. +-- +-- Different features can be turned on or off with the dump mode. If the +-- mode starts with a '+', the following features are added to the default +-- set of features; a '-' removes them. Otherwise the features are replaced. +-- +-- The following dump features are available (* marks the default): +-- +-- * t Print a line for each started, ended or aborted trace (see also -jv). +-- * b Dump the traced bytecode. +-- * i Dump the IR (intermediate representation). +-- r Augment the IR with register/stack slots. +-- s Dump the snapshot map. +-- * m Dump the generated machine code. +-- x Print each taken trace exit. +-- X Print each taken trace exit and the contents of all registers. +-- a Print the IR of aborted traces, too. +-- +-- The output format can be set with the following characters: +-- +-- T Plain text output. +-- A ANSI-colored text output +-- H Colorized HTML + CSS output. +-- +-- The default output format is plain text. It's set to ANSI-colored text +-- if the COLORTERM variable is set. Note: this is independent of any output +-- redirection, which is actually considered a feature. +-- +-- You probably want to use less -R to enjoy viewing ANSI-colored text from +-- a pipe or a file. Add this to your ~/.bashrc: export LESS="-R" +-- +------------------------------------------------------------------------------ + +-- Cache some library functions and objects. +local jit = require("jit") +assert(jit.version_num == 20100, "LuaJIT core/library version mismatch") +local jutil = require("jit.util") +local vmdef = require("jit.vmdef") +local funcinfo, funcbc = jutil.funcinfo, jutil.funcbc +local traceinfo, traceir, tracek = jutil.traceinfo, jutil.traceir, jutil.tracek +local tracemc, tracesnap = jutil.tracemc, jutil.tracesnap +local traceexitstub, ircalladdr = jutil.traceexitstub, jutil.ircalladdr +local bit = require("bit") +local band, shr, tohex = bit.band, bit.rshift, bit.tohex +local sub, gsub, format = string.sub, string.gsub, string.format +local byte, rep = string.byte, string.rep +local type, tostring = type, tostring +local stdout, stderr = io.stdout, io.stderr + +-- Load other modules on-demand. +local bcline, disass + +-- Active flag, output file handle and dump mode. +local active, out, dumpmode + +------------------------------------------------------------------------------ + +local symtabmt = { __index = false } +local symtab = {} +local nexitsym = 0 + +-- Fill nested symbol table with per-trace exit stub addresses. +local function fillsymtab_tr(tr, nexit) + local t = {} + symtabmt.__index = t + if jit.arch:sub(1, 4) == "mips" then + t[traceexitstub(tr, 0)] = "exit" + return + end + for i=0,nexit-1 do + local addr = traceexitstub(tr, i) + if addr < 0 then addr = addr + 2^32 end + t[addr] = tostring(i) + end + local addr = traceexitstub(tr, nexit) + if addr then t[addr] = "stack_check" end +end + +-- Fill symbol table with trace exit stub addresses. +local function fillsymtab(tr, nexit) + local t = symtab + if nexitsym == 0 then + local ircall = vmdef.ircall + for i=0,#ircall do + local addr = ircalladdr(i) + if addr ~= 0 then + if addr < 0 then addr = addr + 2^32 end + t[addr] = ircall[i] + end + end + end + if nexitsym == 1000000 then -- Per-trace exit stubs. + fillsymtab_tr(tr, nexit) + elseif nexit > nexitsym then -- Shared exit stubs. + for i=nexitsym,nexit-1 do + local addr = traceexitstub(i) + if addr == nil then -- Fall back to per-trace exit stubs. + fillsymtab_tr(tr, nexit) + setmetatable(symtab, symtabmt) + nexit = 1000000 + break + end + if addr < 0 then addr = addr + 2^32 end + t[addr] = tostring(i) + end + nexitsym = nexit + end + return t +end + +local function dumpwrite(s) + out:write(s) +end + +-- Disassemble machine code. +local function dump_mcode(tr) + local info = traceinfo(tr) + if not info then return end + local mcode, addr, loop = tracemc(tr) + if not mcode then return end + if not disass then disass = require("jit.dis_"..jit.arch) end + if addr < 0 then addr = addr + 2^32 end + out:write("---- TRACE ", tr, " mcode ", #mcode, "\n") + local ctx = disass.create(mcode, addr, dumpwrite) + ctx.hexdump = 0 + ctx.symtab = fillsymtab(tr, info.nexit) + if loop ~= 0 then + symtab[addr+loop] = "LOOP" + ctx:disass(0, loop) + out:write("->LOOP:\n") + ctx:disass(loop, #mcode-loop) + symtab[addr+loop] = nil + else + ctx:disass(0, #mcode) + end +end + +------------------------------------------------------------------------------ + +local irtype_text = { + [0] = "nil", + "fal", + "tru", + "lud", + "str", + "p32", + "thr", + "pro", + "fun", + "p64", + "cdt", + "tab", + "udt", + "flt", + "num", + "i8 ", + "u8 ", + "i16", + "u16", + "int", + "u32", + "i64", + "u64", + "sfp", +} + +local colortype_ansi = { + [0] = "%s", + "%s", + "%s", + "\027[36m%s\027[m", + "\027[32m%s\027[m", + "%s", + "\027[1m%s\027[m", + "%s", + "\027[1m%s\027[m", + "%s", + "\027[33m%s\027[m", + "\027[31m%s\027[m", + "\027[36m%s\027[m", + "\027[34m%s\027[m", + "\027[34m%s\027[m", + "\027[35m%s\027[m", + "\027[35m%s\027[m", + "\027[35m%s\027[m", + "\027[35m%s\027[m", + "\027[35m%s\027[m", + "\027[35m%s\027[m", + "\027[35m%s\027[m", + "\027[35m%s\027[m", + "\027[35m%s\027[m", +} + +local function colorize_text(s) + return s +end + +local function colorize_ansi(s, t) + return format(colortype_ansi[t], s) +end + +local irtype_ansi = setmetatable({}, + { __index = function(tab, t) + local s = colorize_ansi(irtype_text[t], t); tab[t] = s; return s; end }) + +local html_escape = { ["<"] = "<", [">"] = ">", ["&"] = "&", } + +local function colorize_html(s, t) + s = gsub(s, "[<>&]", html_escape) + return format('%s', irtype_text[t], s) +end + +local irtype_html = setmetatable({}, + { __index = function(tab, t) + local s = colorize_html(irtype_text[t], t); tab[t] = s; return s; end }) + +local header_html = [[ + +]] + +local colorize, irtype + +-- Lookup tables to convert some literals into names. +local litname = { + ["SLOAD "] = setmetatable({}, { __index = function(t, mode) + local s = "" + if band(mode, 1) ~= 0 then s = s.."P" end + if band(mode, 2) ~= 0 then s = s.."F" end + if band(mode, 4) ~= 0 then s = s.."T" end + if band(mode, 8) ~= 0 then s = s.."C" end + if band(mode, 16) ~= 0 then s = s.."R" end + if band(mode, 32) ~= 0 then s = s.."I" end + t[mode] = s + return s + end}), + ["XLOAD "] = { [0] = "", "R", "V", "RV", "U", "RU", "VU", "RVU", }, + ["CONV "] = setmetatable({}, { __index = function(t, mode) + local s = irtype[band(mode, 31)] + s = irtype[band(shr(mode, 5), 31)].."."..s + if band(mode, 0x800) ~= 0 then s = s.." sext" end + local c = shr(mode, 14) + if c == 2 then s = s.." index" elseif c == 3 then s = s.." check" end + t[mode] = s + return s + end}), + ["FLOAD "] = vmdef.irfield, + ["FREF "] = vmdef.irfield, + ["FPMATH"] = vmdef.irfpm, + ["BUFHDR"] = { [0] = "RESET", "APPEND" }, + ["TOSTR "] = { [0] = "INT", "NUM", "CHAR" }, +} + +local function ctlsub(c) + if c == "\n" then return "\\n" + elseif c == "\r" then return "\\r" + elseif c == "\t" then return "\\t" + else return format("\\%03d", byte(c)) + end +end + +local function fmtfunc(func, pc) + local fi = funcinfo(func, pc) + if fi.loc then + return fi.loc + elseif fi.ffid then + return vmdef.ffnames[fi.ffid] + elseif fi.addr then + return format("C:%x", fi.addr) + else + return "(?)" + end +end + +local function formatk(tr, idx, sn) + local k, t, slot = tracek(tr, idx) + local tn = type(k) + local s + if tn == "number" then + if band(sn or 0, 0x30000) ~= 0 then + s = band(sn, 0x20000) ~= 0 and "contpc" or "ftsz" + elseif k == 2^52+2^51 then + s = "bias" + else + s = format(0 < k and k < 0x1p-1026 and "%+a" or "%+.14g", k) + end + elseif tn == "string" then + s = format(#k > 20 and '"%.20s"~' or '"%s"', gsub(k, "%c", ctlsub)) + elseif tn == "function" then + s = fmtfunc(k) + elseif tn == "table" then + s = format("{%p}", k) + elseif tn == "userdata" then + if t == 12 then + s = format("userdata:%p", k) + else + s = format("[%p]", k) + if s == "[NULL]" then s = "NULL" end + end + elseif t == 21 then -- int64_t + s = sub(tostring(k), 1, -3) + if sub(s, 1, 1) ~= "-" then s = "+"..s end + elseif sn == 0x1057fff then -- SNAP(1, SNAP_FRAME | SNAP_NORESTORE, REF_NIL) + return "----" -- Special case for LJ_FR2 slot 1. + else + s = tostring(k) -- For primitives. + end + s = colorize(format("%-4s", s), t) + if slot then + s = format("%s @%d", s, slot) + end + return s +end + +local function printsnap(tr, snap) + local n = 2 + for s=0,snap[1]-1 do + local sn = snap[n] + if shr(sn, 24) == s then + n = n + 1 + local ref = band(sn, 0xffff) - 0x8000 -- REF_BIAS + if ref < 0 then + out:write(formatk(tr, ref, sn)) + elseif band(sn, 0x80000) ~= 0 then -- SNAP_SOFTFPNUM + out:write(colorize(format("%04d/%04d", ref, ref+1), 14)) + else + local m, ot, op1, op2 = traceir(tr, ref) + out:write(colorize(format("%04d", ref), band(ot, 31))) + end + out:write(band(sn, 0x10000) == 0 and " " or "|") -- SNAP_FRAME + else + out:write("---- ") + end + end + out:write("]\n") +end + +-- Dump snapshots (not interleaved with IR). +local function dump_snap(tr) + out:write("---- TRACE ", tr, " snapshots\n") + for i=0,1000000000 do + local snap = tracesnap(tr, i) + if not snap then break end + out:write(format("#%-3d %04d [ ", i, snap[0])) + printsnap(tr, snap) + end +end + +-- Return a register name or stack slot for a rid/sp location. +local function ridsp_name(ridsp, ins) + if not disass then disass = require("jit.dis_"..jit.arch) end + local rid, slot = band(ridsp, 0xff), shr(ridsp, 8) + if rid == 253 or rid == 254 then + return (slot == 0 or slot == 255) and " {sink" or format(" {%04d", ins-slot) + end + if ridsp > 255 then return format("[%x]", slot*4) end + if rid < 128 then return disass.regname(rid) end + return "" +end + +-- Dump CALL* function ref and return optional ctype. +local function dumpcallfunc(tr, ins) + local ctype + if ins > 0 then + local m, ot, op1, op2 = traceir(tr, ins) + if band(ot, 31) == 0 then -- nil type means CARG(func, ctype). + ins = op1 + ctype = formatk(tr, op2) + end + end + if ins < 0 then + out:write(format("[0x%x](", tonumber((tracek(tr, ins))))) + else + out:write(format("%04d (", ins)) + end + return ctype +end + +-- Recursively gather CALL* args and dump them. +local function dumpcallargs(tr, ins) + if ins < 0 then + out:write(formatk(tr, ins)) + else + local m, ot, op1, op2 = traceir(tr, ins) + local oidx = 6*shr(ot, 8) + local op = sub(vmdef.irnames, oidx+1, oidx+6) + if op == "CARG " then + dumpcallargs(tr, op1) + if op2 < 0 then + out:write(" ", formatk(tr, op2)) + else + out:write(" ", format("%04d", op2)) + end + else + out:write(format("%04d", ins)) + end + end +end + +-- Dump IR and interleaved snapshots. +local function dump_ir(tr, dumpsnap, dumpreg) + local info = traceinfo(tr) + if not info then return end + local nins = info.nins + out:write("---- TRACE ", tr, " IR\n") + local irnames = vmdef.irnames + local snapref = 65536 + local snap, snapno + if dumpsnap then + snap = tracesnap(tr, 0) + snapref = snap[0] + snapno = 0 + end + for ins=1,nins do + if ins >= snapref then + if dumpreg then + out:write(format(".... SNAP #%-3d [ ", snapno)) + else + out:write(format(".... SNAP #%-3d [ ", snapno)) + end + printsnap(tr, snap) + snapno = snapno + 1 + snap = tracesnap(tr, snapno) + snapref = snap and snap[0] or 65536 + end + local m, ot, op1, op2, ridsp = traceir(tr, ins) + local oidx, t = 6*shr(ot, 8), band(ot, 31) + local op = sub(irnames, oidx+1, oidx+6) + if op == "LOOP " then + if dumpreg then + out:write(format("%04d ------------ LOOP ------------\n", ins)) + else + out:write(format("%04d ------ LOOP ------------\n", ins)) + end + elseif op ~= "NOP " and op ~= "CARG " and + (dumpreg or op ~= "RENAME") then + local rid = band(ridsp, 255) + if dumpreg then + out:write(format("%04d %-6s", ins, ridsp_name(ridsp, ins))) + else + out:write(format("%04d ", ins)) + end + out:write(format("%s%s %s %s ", + (rid == 254 or rid == 253) and "}" or + (band(ot, 128) == 0 and " " or ">"), + band(ot, 64) == 0 and " " or "+", + irtype[t], op)) + local m1, m2 = band(m, 3), band(m, 3*4) + if sub(op, 1, 4) == "CALL" then + local ctype + if m2 == 1*4 then -- op2 == IRMlit + out:write(format("%-10s (", vmdef.ircall[op2])) + else + ctype = dumpcallfunc(tr, op2) + end + if op1 ~= -1 then dumpcallargs(tr, op1) end + out:write(")") + if ctype then out:write(" ctype ", ctype) end + elseif op == "CNEW " and op2 == -1 then + out:write(formatk(tr, op1)) + elseif m1 ~= 3 then -- op1 != IRMnone + if op1 < 0 then + out:write(formatk(tr, op1)) + else + out:write(format(m1 == 0 and "%04d" or "#%-3d", op1)) + end + if m2 ~= 3*4 then -- op2 != IRMnone + if m2 == 1*4 then -- op2 == IRMlit + local litn = litname[op] + if litn and litn[op2] then + out:write(" ", litn[op2]) + elseif op == "UREFO " or op == "UREFC " then + out:write(format(" #%-3d", shr(op2, 8))) + else + out:write(format(" #%-3d", op2)) + end + elseif op2 < 0 then + out:write(" ", formatk(tr, op2)) + else + out:write(format(" %04d", op2)) + end + end + end + out:write("\n") + end + end + if snap then + if dumpreg then + out:write(format(".... SNAP #%-3d [ ", snapno)) + else + out:write(format(".... SNAP #%-3d [ ", snapno)) + end + printsnap(tr, snap) + end +end + +------------------------------------------------------------------------------ + +local recprefix = "" +local recdepth = 0 + +-- Format trace error message. +local function fmterr(err, info) + if type(err) == "number" then + if type(info) == "function" then info = fmtfunc(info) end + err = format(vmdef.traceerr[err], info) + end + return err +end + +-- Dump trace states. +local function dump_trace(what, tr, func, pc, otr, oex) + if what == "stop" or (what == "abort" and dumpmode.a) then + if dumpmode.i then dump_ir(tr, dumpmode.s, dumpmode.r and what == "stop") + elseif dumpmode.s then dump_snap(tr) end + if dumpmode.m then dump_mcode(tr) end + end + if what == "start" then + if dumpmode.H then out:write('
\n') end
+    out:write("---- TRACE ", tr, " ", what)
+    if otr then out:write(" ", otr, "/", oex == -1 and "stitch" or oex) end
+    out:write(" ", fmtfunc(func, pc), "\n")
+  elseif what == "stop" or what == "abort" then
+    out:write("---- TRACE ", tr, " ", what)
+    if what == "abort" then
+      out:write(" ", fmtfunc(func, pc), " -- ", fmterr(otr, oex), "\n")
+    else
+      local info = traceinfo(tr)
+      local link, ltype = info.link, info.linktype
+      if link == tr or link == 0 then
+	out:write(" -> ", ltype, "\n")
+      elseif ltype == "root" then
+	out:write(" -> ", link, "\n")
+      else
+	out:write(" -> ", link, " ", ltype, "\n")
+      end
+    end
+    if dumpmode.H then out:write("
\n\n") else out:write("\n") end + else + if what == "flush" then symtab, nexitsym = {}, 0 end + out:write("---- TRACE ", what, "\n\n") + end + out:flush() +end + +-- Dump recorded bytecode. +local function dump_record(tr, func, pc, depth, callee) + if depth ~= recdepth then + recdepth = depth + recprefix = rep(" .", depth) + end + local line + if pc >= 0 then + line = bcline(func, pc, recprefix) + if dumpmode.H then line = gsub(line, "[<>&]", html_escape) end + else + line = "0000 "..recprefix.." FUNCC \n" + callee = func + end + if pc <= 0 then + out:write(sub(line, 1, -2), " ; ", fmtfunc(func), "\n") + else + out:write(line) + end + if pc >= 0 and band(funcbc(func, pc), 0xff) < 16 then -- ORDER BC + out:write(bcline(func, pc+1, recprefix)) -- Write JMP for cond. + end +end + +------------------------------------------------------------------------------ + +-- Dump taken trace exits. +local function dump_texit(tr, ex, ngpr, nfpr, ...) + out:write("---- TRACE ", tr, " exit ", ex, "\n") + if dumpmode.X then + local regs = {...} + if jit.arch == "x64" then + for i=1,ngpr do + out:write(format(" %016x", regs[i])) + if i % 4 == 0 then out:write("\n") end + end + else + for i=1,ngpr do + out:write(" ", tohex(regs[i])) + if i % 8 == 0 then out:write("\n") end + end + end + if jit.arch == "mips" or jit.arch == "mipsel" then + for i=1,nfpr,2 do + out:write(format(" %+17.14g", regs[ngpr+i])) + if i % 8 == 7 then out:write("\n") end + end + else + for i=1,nfpr do + out:write(format(" %+17.14g", regs[ngpr+i])) + if i % 4 == 0 then out:write("\n") end + end + end + end +end + +------------------------------------------------------------------------------ + +-- Detach dump handlers. +local function dumpoff() + if active then + active = false + jit.attach(dump_texit) + jit.attach(dump_record) + jit.attach(dump_trace) + if out and out ~= stdout and out ~= stderr then out:close() end + out = nil + end +end + +-- Open the output file and attach dump handlers. +local function dumpon(opt, outfile) + if active then dumpoff() end + + local term = os.getenv("TERM") + local colormode = (term and term:match("color") or os.getenv("COLORTERM")) and "A" or "T" + if opt then + opt = gsub(opt, "[TAH]", function(mode) colormode = mode; return ""; end) + end + + local m = { t=true, b=true, i=true, m=true, } + if opt and opt ~= "" then + local o = sub(opt, 1, 1) + if o ~= "+" and o ~= "-" then m = {} end + for i=1,#opt do m[sub(opt, i, i)] = (o ~= "-") end + end + dumpmode = m + + if m.t or m.b or m.i or m.s or m.m then + jit.attach(dump_trace, "trace") + end + if m.b then + jit.attach(dump_record, "record") + if not bcline then bcline = require("jit.bc").line end + end + if m.x or m.X then + jit.attach(dump_texit, "texit") + end + + if not outfile then outfile = os.getenv("LUAJIT_DUMPFILE") end + if outfile then + out = outfile == "-" and stdout or assert(io.open(outfile, "w")) + else + out = stdout + end + + m[colormode] = true + if colormode == "A" then + colorize = colorize_ansi + irtype = irtype_ansi + elseif colormode == "H" then + colorize = colorize_html + irtype = irtype_html + out:write(header_html) + else + colorize = colorize_text + irtype = irtype_text + end + + active = true +end + +-- Public module functions. +return { + on = dumpon, + off = dumpoff, + start = dumpon -- For -j command line option. +} + diff --git a/lib/LuaJIT/jit/p.lua b/lib/LuaJIT/jit/p.lua new file mode 100644 index 0000000..7be1058 --- /dev/null +++ b/lib/LuaJIT/jit/p.lua @@ -0,0 +1,311 @@ +---------------------------------------------------------------------------- +-- LuaJIT profiler. +-- +-- Copyright (C) 2005-2017 Mike Pall. All rights reserved. +-- Released under the MIT license. See Copyright Notice in luajit.h +---------------------------------------------------------------------------- +-- +-- This module is a simple command line interface to the built-in +-- low-overhead profiler of LuaJIT. +-- +-- The lower-level API of the profiler is accessible via the "jit.profile" +-- module or the luaJIT_profile_* C API. +-- +-- Example usage: +-- +-- luajit -jp myapp.lua +-- luajit -jp=s myapp.lua +-- luajit -jp=-s myapp.lua +-- luajit -jp=vl myapp.lua +-- luajit -jp=G,profile.txt myapp.lua +-- +-- The following dump features are available: +-- +-- f Stack dump: function name, otherwise module:line. Default mode. +-- F Stack dump: ditto, but always prepend module. +-- l Stack dump: module:line. +-- stack dump depth (callee < caller). Default: 1. +-- - Inverse stack dump depth (caller > callee). +-- s Split stack dump after first stack level. Implies abs(depth) >= 2. +-- p Show full path for module names. +-- v Show VM states. Can be combined with stack dumps, e.g. vf or fv. +-- z Show zones. Can be combined with stack dumps, e.g. zf or fz. +-- r Show raw sample counts. Default: show percentages. +-- a Annotate excerpts from source code files. +-- A Annotate complete source code files. +-- G Produce raw output suitable for graphical tools (e.g. flame graphs). +-- m Minimum sample percentage to be shown. Default: 3. +-- i Sampling interval in milliseconds. Default: 10. +-- +---------------------------------------------------------------------------- + +-- Cache some library functions and objects. +local jit = require("jit") +assert(jit.version_num == 20100, "LuaJIT core/library version mismatch") +local profile = require("jit.profile") +local vmdef = require("jit.vmdef") +local math = math +local pairs, ipairs, tonumber, floor = pairs, ipairs, tonumber, math.floor +local sort, format = table.sort, string.format +local stdout = io.stdout +local zone -- Load jit.zone module on demand. + +-- Output file handle. +local out + +------------------------------------------------------------------------------ + +local prof_ud +local prof_states, prof_split, prof_min, prof_raw, prof_fmt, prof_depth +local prof_ann, prof_count1, prof_count2, prof_samples + +local map_vmmode = { + N = "Compiled", + I = "Interpreted", + C = "C code", + G = "Garbage Collector", + J = "JIT Compiler", +} + +-- Profiler callback. +local function prof_cb(th, samples, vmmode) + prof_samples = prof_samples + samples + local key_stack, key_stack2, key_state + -- Collect keys for sample. + if prof_states then + if prof_states == "v" then + key_state = map_vmmode[vmmode] or vmmode + else + key_state = zone:get() or "(none)" + end + end + if prof_fmt then + key_stack = profile.dumpstack(th, prof_fmt, prof_depth) + key_stack = key_stack:gsub("%[builtin#(%d+)%]", function(x) + return vmdef.ffnames[tonumber(x)] + end) + if prof_split == 2 then + local k1, k2 = key_stack:match("(.-) [<>] (.*)") + if k2 then key_stack, key_stack2 = k1, k2 end + elseif prof_split == 3 then + key_stack2 = profile.dumpstack(th, "l", 1) + end + end + -- Order keys. + local k1, k2 + if prof_split == 1 then + if key_state then + k1 = key_state + if key_stack then k2 = key_stack end + end + elseif key_stack then + k1 = key_stack + if key_stack2 then k2 = key_stack2 elseif key_state then k2 = key_state end + end + -- Coalesce samples in one or two levels. + if k1 then + local t1 = prof_count1 + t1[k1] = (t1[k1] or 0) + samples + if k2 then + local t2 = prof_count2 + local t3 = t2[k1] + if not t3 then t3 = {}; t2[k1] = t3 end + t3[k2] = (t3[k2] or 0) + samples + end + end +end + +------------------------------------------------------------------------------ + +-- Show top N list. +local function prof_top(count1, count2, samples, indent) + local t, n = {}, 0 + for k in pairs(count1) do + n = n + 1 + t[n] = k + end + sort(t, function(a, b) return count1[a] > count1[b] end) + for i=1,n do + local k = t[i] + local v = count1[k] + local pct = floor(v*100/samples + 0.5) + if pct < prof_min then break end + if not prof_raw then + out:write(format("%s%2d%% %s\n", indent, pct, k)) + elseif prof_raw == "r" then + out:write(format("%s%5d %s\n", indent, v, k)) + else + out:write(format("%s %d\n", k, v)) + end + if count2 then + local r = count2[k] + if r then + prof_top(r, nil, v, (prof_split == 3 or prof_split == 1) and " -- " or + (prof_depth < 0 and " -> " or " <- ")) + end + end + end +end + +-- Annotate source code +local function prof_annotate(count1, samples) + local files = {} + local ms = 0 + for k, v in pairs(count1) do + local pct = floor(v*100/samples + 0.5) + ms = math.max(ms, v) + if pct >= prof_min then + local file, line = k:match("^(.*):(%d+)$") + if not file then file = k; line = 0 end + local fl = files[file] + if not fl then fl = {}; files[file] = fl; files[#files+1] = file end + line = tonumber(line) + fl[line] = prof_raw and v or pct + end + end + sort(files) + local fmtv, fmtn = " %3d%% | %s\n", " | %s\n" + if prof_raw then + local n = math.max(5, math.ceil(math.log10(ms))) + fmtv = "%"..n.."d | %s\n" + fmtn = (" "):rep(n).." | %s\n" + end + local ann = prof_ann + for _, file in ipairs(files) do + local f0 = file:byte() + if f0 == 40 or f0 == 91 then + out:write(format("\n====== %s ======\n[Cannot annotate non-file]\n", file)) + break + end + local fp, err = io.open(file) + if not fp then + out:write(format("====== ERROR: %s: %s\n", file, err)) + break + end + out:write(format("\n====== %s ======\n", file)) + local fl = files[file] + local n, show = 1, false + if ann ~= 0 then + for i=1,ann do + if fl[i] then show = true; out:write("@@ 1 @@\n"); break end + end + end + for line in fp:lines() do + if line:byte() == 27 then + out:write("[Cannot annotate bytecode file]\n") + break + end + local v = fl[n] + if ann ~= 0 then + local v2 = fl[n+ann] + if show then + if v2 then show = n+ann elseif v then show = n + elseif show+ann < n then show = false end + elseif v2 then + show = n+ann + out:write(format("@@ %d @@\n", n)) + end + if not show then goto next end + end + if v then + out:write(format(fmtv, v, line)) + else + out:write(format(fmtn, line)) + end + ::next:: + n = n + 1 + end + fp:close() + end +end + +------------------------------------------------------------------------------ + +-- Finish profiling and dump result. +local function prof_finish() + if prof_ud then + profile.stop() + local samples = prof_samples + if samples == 0 then + if prof_raw ~= true then out:write("[No samples collected]\n") end + return + end + if prof_ann then + prof_annotate(prof_count1, samples) + else + prof_top(prof_count1, prof_count2, samples, "") + end + prof_count1 = nil + prof_count2 = nil + prof_ud = nil + end +end + +-- Start profiling. +local function prof_start(mode) + local interval = "" + mode = mode:gsub("i%d*", function(s) interval = s; return "" end) + prof_min = 3 + mode = mode:gsub("m(%d+)", function(s) prof_min = tonumber(s); return "" end) + prof_depth = 1 + mode = mode:gsub("%-?%d+", function(s) prof_depth = tonumber(s); return "" end) + local m = {} + for c in mode:gmatch(".") do m[c] = c end + prof_states = m.z or m.v + if prof_states == "z" then zone = require("jit.zone") end + local scope = m.l or m.f or m.F or (prof_states and "" or "f") + local flags = (m.p or "") + prof_raw = m.r + if m.s then + prof_split = 2 + if prof_depth == -1 or m["-"] then prof_depth = -2 + elseif prof_depth == 1 then prof_depth = 2 end + elseif mode:find("[fF].*l") then + scope = "l" + prof_split = 3 + else + prof_split = (scope == "" or mode:find("[zv].*[lfF]")) and 1 or 0 + end + prof_ann = m.A and 0 or (m.a and 3) + if prof_ann then + scope = "l" + prof_fmt = "pl" + prof_split = 0 + prof_depth = 1 + elseif m.G and scope ~= "" then + prof_fmt = flags..scope.."Z;" + prof_depth = -100 + prof_raw = true + prof_min = 0 + elseif scope == "" then + prof_fmt = false + else + local sc = prof_split == 3 and m.f or m.F or scope + prof_fmt = flags..sc..(prof_depth >= 0 and "Z < " or "Z > ") + end + prof_count1 = {} + prof_count2 = {} + prof_samples = 0 + profile.start(scope:lower()..interval, prof_cb) + prof_ud = newproxy(true) + getmetatable(prof_ud).__gc = prof_finish +end + +------------------------------------------------------------------------------ + +local function start(mode, outfile) + if not outfile then outfile = os.getenv("LUAJIT_PROFILEFILE") end + if outfile then + out = outfile == "-" and stdout or assert(io.open(outfile, "w")) + else + out = stdout + end + prof_start(mode or "f") +end + +-- Public module functions. +return { + start = start, -- For -j command line option. + stop = prof_finish +} + diff --git a/lib/LuaJIT/jit/v.lua b/lib/LuaJIT/jit/v.lua new file mode 100644 index 0000000..934de98 --- /dev/null +++ b/lib/LuaJIT/jit/v.lua @@ -0,0 +1,170 @@ +---------------------------------------------------------------------------- +-- Verbose mode of the LuaJIT compiler. +-- +-- Copyright (C) 2005-2017 Mike Pall. All rights reserved. +-- Released under the MIT license. See Copyright Notice in luajit.h +---------------------------------------------------------------------------- +-- +-- This module shows verbose information about the progress of the +-- JIT compiler. It prints one line for each generated trace. This module +-- is useful to see which code has been compiled or where the compiler +-- punts and falls back to the interpreter. +-- +-- Example usage: +-- +-- luajit -jv -e "for i=1,1000 do for j=1,1000 do end end" +-- luajit -jv=myapp.out myapp.lua +-- +-- Default output is to stderr. To redirect the output to a file, pass a +-- filename as an argument (use '-' for stdout) or set the environment +-- variable LUAJIT_VERBOSEFILE. The file is overwritten every time the +-- module is started. +-- +-- The output from the first example should look like this: +-- +-- [TRACE 1 (command line):1 loop] +-- [TRACE 2 (1/3) (command line):1 -> 1] +-- +-- The first number in each line is the internal trace number. Next are +-- the file name ('(command line)') and the line number (':1') where the +-- trace has started. Side traces also show the parent trace number and +-- the exit number where they are attached to in parentheses ('(1/3)'). +-- An arrow at the end shows where the trace links to ('-> 1'), unless +-- it loops to itself. +-- +-- In this case the inner loop gets hot and is traced first, generating +-- a root trace. Then the last exit from the 1st trace gets hot, too, +-- and triggers generation of the 2nd trace. The side trace follows the +-- path along the outer loop and *around* the inner loop, back to its +-- start, and then links to the 1st trace. Yes, this may seem unusual, +-- if you know how traditional compilers work. Trace compilers are full +-- of surprises like this -- have fun! :-) +-- +-- Aborted traces are shown like this: +-- +-- [TRACE --- foo.lua:44 -- leaving loop in root trace at foo:lua:50] +-- +-- Don't worry -- trace aborts are quite common, even in programs which +-- can be fully compiled. The compiler may retry several times until it +-- finds a suitable trace. +-- +-- Of course this doesn't work with features that are not-yet-implemented +-- (NYI error messages). The VM simply falls back to the interpreter. This +-- may not matter at all if the particular trace is not very high up in +-- the CPU usage profile. Oh, and the interpreter is quite fast, too. +-- +-- Also check out the -jdump module, which prints all the gory details. +-- +------------------------------------------------------------------------------ + +-- Cache some library functions and objects. +local jit = require("jit") +assert(jit.version_num == 20100, "LuaJIT core/library version mismatch") +local jutil = require("jit.util") +local vmdef = require("jit.vmdef") +local funcinfo, traceinfo = jutil.funcinfo, jutil.traceinfo +local type, format = type, string.format +local stdout, stderr = io.stdout, io.stderr + +-- Active flag and output file handle. +local active, out + +------------------------------------------------------------------------------ + +local startloc, startex + +local function fmtfunc(func, pc) + local fi = funcinfo(func, pc) + if fi.loc then + return fi.loc + elseif fi.ffid then + return vmdef.ffnames[fi.ffid] + elseif fi.addr then + return format("C:%x", fi.addr) + else + return "(?)" + end +end + +-- Format trace error message. +local function fmterr(err, info) + if type(err) == "number" then + if type(info) == "function" then info = fmtfunc(info) end + err = format(vmdef.traceerr[err], info) + end + return err +end + +-- Dump trace states. +local function dump_trace(what, tr, func, pc, otr, oex) + if what == "start" then + startloc = fmtfunc(func, pc) + startex = otr and "("..otr.."/"..(oex == -1 and "stitch" or oex)..") " or "" + else + if what == "abort" then + local loc = fmtfunc(func, pc) + if loc ~= startloc then + out:write(format("[TRACE --- %s%s -- %s at %s]\n", + startex, startloc, fmterr(otr, oex), loc)) + else + out:write(format("[TRACE --- %s%s -- %s]\n", + startex, startloc, fmterr(otr, oex))) + end + elseif what == "stop" then + local info = traceinfo(tr) + local link, ltype = info.link, info.linktype + if ltype == "interpreter" then + out:write(format("[TRACE %3s %s%s -- fallback to interpreter]\n", + tr, startex, startloc)) + elseif ltype == "stitch" then + out:write(format("[TRACE %3s %s%s %s %s]\n", + tr, startex, startloc, ltype, fmtfunc(func, pc))) + elseif link == tr or link == 0 then + out:write(format("[TRACE %3s %s%s %s]\n", + tr, startex, startloc, ltype)) + elseif ltype == "root" then + out:write(format("[TRACE %3s %s%s -> %d]\n", + tr, startex, startloc, link)) + else + out:write(format("[TRACE %3s %s%s -> %d %s]\n", + tr, startex, startloc, link, ltype)) + end + else + out:write(format("[TRACE %s]\n", what)) + end + out:flush() + end +end + +------------------------------------------------------------------------------ + +-- Detach dump handlers. +local function dumpoff() + if active then + active = false + jit.attach(dump_trace) + if out and out ~= stdout and out ~= stderr then out:close() end + out = nil + end +end + +-- Open the output file and attach dump handlers. +local function dumpon(outfile) + if active then dumpoff() end + if not outfile then outfile = os.getenv("LUAJIT_VERBOSEFILE") end + if outfile then + out = outfile == "-" and stdout or assert(io.open(outfile, "w")) + else + out = stderr + end + jit.attach(dump_trace, "trace") + active = true +end + +-- Public module functions. +return { + on = dumpon, + off = dumpoff, + start = dumpon -- For -j command line option. +} + diff --git a/lib/LuaJIT/jit/zone.lua b/lib/LuaJIT/jit/zone.lua new file mode 100644 index 0000000..fa702c4 --- /dev/null +++ b/lib/LuaJIT/jit/zone.lua @@ -0,0 +1,45 @@ +---------------------------------------------------------------------------- +-- LuaJIT profiler zones. +-- +-- Copyright (C) 2005-2017 Mike Pall. All rights reserved. +-- Released under the MIT license. See Copyright Notice in luajit.h +---------------------------------------------------------------------------- +-- +-- This module implements a simple hierarchical zone model. +-- +-- Example usage: +-- +-- local zone = require("jit.zone") +-- zone("AI") +-- ... +-- zone("A*") +-- ... +-- print(zone:get()) --> "A*" +-- ... +-- zone() +-- ... +-- print(zone:get()) --> "AI" +-- ... +-- zone() +-- +---------------------------------------------------------------------------- + +local remove = table.remove + +return setmetatable({ + flush = function(t) + for i=#t,1,-1 do t[i] = nil end + end, + get = function(t) + return t[#t] + end +}, { + __call = function(t, zone) + if zone then + t[#t+1] = zone + else + return (assert(remove(t), "empty zone stack")) + end + end +}) + diff --git a/lib/LuaJIT/lauxlib.h b/lib/LuaJIT/lauxlib.h new file mode 100644 index 0000000..a44f027 --- /dev/null +++ b/lib/LuaJIT/lauxlib.h @@ -0,0 +1,161 @@ +/* +** $Id: lauxlib.h,v 1.88.1.1 2007/12/27 13:02:25 roberto Exp $ +** Auxiliary functions for building Lua libraries +** See Copyright Notice in lua.h +*/ + + +#ifndef lauxlib_h +#define lauxlib_h + + +#include +#include + +#include "lua.h" + + +/* extra error code for `luaL_load' */ +#define LUA_ERRFILE (LUA_ERRERR+1) + +typedef struct luaL_Reg { + const char *name; + lua_CFunction func; +} luaL_Reg; + +LUALIB_API void (luaL_openlib) (lua_State *L, const char *libname, + const luaL_Reg *l, int nup); +LUALIB_API void (luaL_register) (lua_State *L, const char *libname, + const luaL_Reg *l); +LUALIB_API int (luaL_getmetafield) (lua_State *L, int obj, const char *e); +LUALIB_API int (luaL_callmeta) (lua_State *L, int obj, const char *e); +LUALIB_API int (luaL_typerror) (lua_State *L, int narg, const char *tname); +LUALIB_API int (luaL_argerror) (lua_State *L, int numarg, const char *extramsg); +LUALIB_API const char *(luaL_checklstring) (lua_State *L, int numArg, + size_t *l); +LUALIB_API const char *(luaL_optlstring) (lua_State *L, int numArg, + const char *def, size_t *l); +LUALIB_API lua_Number (luaL_checknumber) (lua_State *L, int numArg); +LUALIB_API lua_Number (luaL_optnumber) (lua_State *L, int nArg, lua_Number def); + +LUALIB_API lua_Integer (luaL_checkinteger) (lua_State *L, int numArg); +LUALIB_API lua_Integer (luaL_optinteger) (lua_State *L, int nArg, + lua_Integer def); + +LUALIB_API void (luaL_checkstack) (lua_State *L, int sz, const char *msg); +LUALIB_API void (luaL_checktype) (lua_State *L, int narg, int t); +LUALIB_API void (luaL_checkany) (lua_State *L, int narg); + +LUALIB_API int (luaL_newmetatable) (lua_State *L, const char *tname); +LUALIB_API void *(luaL_checkudata) (lua_State *L, int ud, const char *tname); + +LUALIB_API void (luaL_where) (lua_State *L, int lvl); +LUALIB_API int (luaL_error) (lua_State *L, const char *fmt, ...); + +LUALIB_API int (luaL_checkoption) (lua_State *L, int narg, const char *def, + const char *const lst[]); + +/* pre-defined references */ +#define LUA_NOREF (-2) +#define LUA_REFNIL (-1) + +LUALIB_API int (luaL_ref) (lua_State *L, int t); +LUALIB_API void (luaL_unref) (lua_State *L, int t, int ref); + +LUALIB_API int (luaL_loadfile) (lua_State *L, const char *filename); +LUALIB_API int (luaL_loadbuffer) (lua_State *L, const char *buff, size_t sz, + const char *name); +LUALIB_API int (luaL_loadstring) (lua_State *L, const char *s); + +LUALIB_API lua_State *(luaL_newstate) (void); + + +LUALIB_API const char *(luaL_gsub) (lua_State *L, const char *s, const char *p, + const char *r); + +LUALIB_API const char *(luaL_findtable) (lua_State *L, int idx, + const char *fname, int szhint); + +/* From Lua 5.2. */ +LUALIB_API int luaL_fileresult(lua_State *L, int stat, const char *fname); +LUALIB_API int luaL_execresult(lua_State *L, int stat); +LUALIB_API int (luaL_loadfilex) (lua_State *L, const char *filename, + const char *mode); +LUALIB_API int (luaL_loadbufferx) (lua_State *L, const char *buff, size_t sz, + const char *name, const char *mode); +LUALIB_API void luaL_traceback (lua_State *L, lua_State *L1, const char *msg, + int level); +LUALIB_API void (luaL_setfuncs) (lua_State *L, const luaL_Reg *l, int nup); +LUALIB_API void (luaL_pushmodule) (lua_State *L, const char *modname, + int sizehint); +LUALIB_API void *(luaL_testudata) (lua_State *L, int ud, const char *tname); +LUALIB_API void (luaL_setmetatable) (lua_State *L, const char *tname); + + +/* +** =============================================================== +** some useful macros +** =============================================================== +*/ + +#define luaL_argcheck(L, cond,numarg,extramsg) \ + ((void)((cond) || luaL_argerror(L, (numarg), (extramsg)))) +#define luaL_checkstring(L,n) (luaL_checklstring(L, (n), NULL)) +#define luaL_optstring(L,n,d) (luaL_optlstring(L, (n), (d), NULL)) +#define luaL_checkint(L,n) ((int)luaL_checkinteger(L, (n))) +#define luaL_optint(L,n,d) ((int)luaL_optinteger(L, (n), (d))) +#define luaL_checklong(L,n) ((long)luaL_checkinteger(L, (n))) +#define luaL_optlong(L,n,d) ((long)luaL_optinteger(L, (n), (d))) + +#define luaL_typename(L,i) lua_typename(L, lua_type(L,(i))) + +#define luaL_dofile(L, fn) \ + (luaL_loadfile(L, fn) || lua_pcall(L, 0, LUA_MULTRET, 0)) + +#define luaL_dostring(L, s) \ + (luaL_loadstring(L, s) || lua_pcall(L, 0, LUA_MULTRET, 0)) + +#define luaL_getmetatable(L,n) (lua_getfield(L, LUA_REGISTRYINDEX, (n))) + +#define luaL_opt(L,f,n,d) (lua_isnoneornil(L,(n)) ? (d) : f(L,(n))) + +/* From Lua 5.2. */ +#define luaL_newlibtable(L, l) \ + lua_createtable(L, 0, sizeof(l)/sizeof((l)[0]) - 1) +#define luaL_newlib(L, l) (luaL_newlibtable(L, l), luaL_setfuncs(L, l, 0)) + +/* +** {====================================================== +** Generic Buffer manipulation +** ======================================================= +*/ + + + +typedef struct luaL_Buffer { + char *p; /* current position in buffer */ + int lvl; /* number of strings in the stack (level) */ + lua_State *L; + char buffer[LUAL_BUFFERSIZE]; +} luaL_Buffer; + +#define luaL_addchar(B,c) \ + ((void)((B)->p < ((B)->buffer+LUAL_BUFFERSIZE) || luaL_prepbuffer(B)), \ + (*(B)->p++ = (char)(c))) + +/* compatibility only */ +#define luaL_putchar(B,c) luaL_addchar(B,c) + +#define luaL_addsize(B,n) ((B)->p += (n)) + +LUALIB_API void (luaL_buffinit) (lua_State *L, luaL_Buffer *B); +LUALIB_API char *(luaL_prepbuffer) (luaL_Buffer *B); +LUALIB_API void (luaL_addlstring) (luaL_Buffer *B, const char *s, size_t l); +LUALIB_API void (luaL_addstring) (luaL_Buffer *B, const char *s); +LUALIB_API void (luaL_addvalue) (luaL_Buffer *B); +LUALIB_API void (luaL_pushresult) (luaL_Buffer *B); + + +/* }====================================================== */ + +#endif diff --git a/lib/LuaJIT/lib_aux.c b/lib/LuaJIT/lib_aux.c new file mode 100644 index 0000000..2682a38 --- /dev/null +++ b/lib/LuaJIT/lib_aux.c @@ -0,0 +1,374 @@ +/* +** Auxiliary library for the Lua/C API. +** Copyright (C) 2005-2017 Mike Pall. See Copyright Notice in luajit.h +** +** Major parts taken verbatim or adapted from the Lua interpreter. +** Copyright (C) 1994-2008 Lua.org, PUC-Rio. See Copyright Notice in lua.h +*/ + +#include +#include +#include + +#define lib_aux_c +#define LUA_LIB + +#include "lua.h" +#include "lauxlib.h" + +#include "lj_obj.h" +#include "lj_err.h" +#include "lj_state.h" +#include "lj_trace.h" +#include "lj_lib.h" + +#if LJ_TARGET_POSIX +#include +#endif + +/* -- I/O error handling -------------------------------------------------- */ + +LUALIB_API int luaL_fileresult(lua_State *L, int stat, const char *fname) +{ + if (stat) { + setboolV(L->top++, 1); + return 1; + } else { + int en = errno; /* Lua API calls may change this value. */ + setnilV(L->top++); + if (fname) + lua_pushfstring(L, "%s: %s", fname, strerror(en)); + else + lua_pushfstring(L, "%s", strerror(en)); + setintV(L->top++, en); + lj_trace_abort(G(L)); + return 3; + } +} + +LUALIB_API int luaL_execresult(lua_State *L, int stat) +{ + if (stat != -1) { +#if LJ_TARGET_POSIX + if (WIFSIGNALED(stat)) { + stat = WTERMSIG(stat); + setnilV(L->top++); + lua_pushliteral(L, "signal"); + } else { + if (WIFEXITED(stat)) + stat = WEXITSTATUS(stat); + if (stat == 0) + setboolV(L->top++, 1); + else + setnilV(L->top++); + lua_pushliteral(L, "exit"); + } +#else + if (stat == 0) + setboolV(L->top++, 1); + else + setnilV(L->top++); + lua_pushliteral(L, "exit"); +#endif + setintV(L->top++, stat); + return 3; + } + return luaL_fileresult(L, 0, NULL); +} + +/* -- Module registration ------------------------------------------------- */ + +LUALIB_API const char *luaL_findtable(lua_State *L, int idx, + const char *fname, int szhint) +{ + const char *e; + lua_pushvalue(L, idx); + do { + e = strchr(fname, '.'); + if (e == NULL) e = fname + strlen(fname); + lua_pushlstring(L, fname, (size_t)(e - fname)); + lua_rawget(L, -2); + if (lua_isnil(L, -1)) { /* no such field? */ + lua_pop(L, 1); /* remove this nil */ + lua_createtable(L, 0, (*e == '.' ? 1 : szhint)); /* new table for field */ + lua_pushlstring(L, fname, (size_t)(e - fname)); + lua_pushvalue(L, -2); + lua_settable(L, -4); /* set new table into field */ + } else if (!lua_istable(L, -1)) { /* field has a non-table value? */ + lua_pop(L, 2); /* remove table and value */ + return fname; /* return problematic part of the name */ + } + lua_remove(L, -2); /* remove previous table */ + fname = e + 1; + } while (*e == '.'); + return NULL; +} + +static int libsize(const luaL_Reg *l) +{ + int size = 0; + for (; l && l->name; l++) size++; + return size; +} + +LUALIB_API void luaL_pushmodule(lua_State *L, const char *modname, int sizehint) +{ + luaL_findtable(L, LUA_REGISTRYINDEX, "_LOADED", 16); + lua_getfield(L, -1, modname); + if (!lua_istable(L, -1)) { + lua_pop(L, 1); + if (luaL_findtable(L, LUA_GLOBALSINDEX, modname, sizehint) != NULL) + lj_err_callerv(L, LJ_ERR_BADMODN, modname); + lua_pushvalue(L, -1); + lua_setfield(L, -3, modname); /* _LOADED[modname] = new table. */ + } + lua_remove(L, -2); /* Remove _LOADED table. */ +} + +LUALIB_API void luaL_openlib(lua_State *L, const char *libname, + const luaL_Reg *l, int nup) +{ + lj_lib_checkfpu(L); + if (libname) { + luaL_pushmodule(L, libname, libsize(l)); + lua_insert(L, -(nup + 1)); /* Move module table below upvalues. */ + } + if (l) + luaL_setfuncs(L, l, nup); + else + lua_pop(L, nup); /* Remove upvalues. */ +} + +LUALIB_API void luaL_register(lua_State *L, const char *libname, + const luaL_Reg *l) +{ + luaL_openlib(L, libname, l, 0); +} + +LUALIB_API void luaL_setfuncs(lua_State *L, const luaL_Reg *l, int nup) +{ + luaL_checkstack(L, nup, "too many upvalues"); + for (; l->name; l++) { + int i; + for (i = 0; i < nup; i++) /* Copy upvalues to the top. */ + lua_pushvalue(L, -nup); + lua_pushcclosure(L, l->func, nup); + lua_setfield(L, -(nup + 2), l->name); + } + lua_pop(L, nup); /* Remove upvalues. */ +} + +LUALIB_API const char *luaL_gsub(lua_State *L, const char *s, + const char *p, const char *r) +{ + const char *wild; + size_t l = strlen(p); + luaL_Buffer b; + luaL_buffinit(L, &b); + while ((wild = strstr(s, p)) != NULL) { + luaL_addlstring(&b, s, (size_t)(wild - s)); /* push prefix */ + luaL_addstring(&b, r); /* push replacement in place of pattern */ + s = wild + l; /* continue after `p' */ + } + luaL_addstring(&b, s); /* push last suffix */ + luaL_pushresult(&b); + return lua_tostring(L, -1); +} + +/* -- Buffer handling ----------------------------------------------------- */ + +#define bufflen(B) ((size_t)((B)->p - (B)->buffer)) +#define bufffree(B) ((size_t)(LUAL_BUFFERSIZE - bufflen(B))) + +static int emptybuffer(luaL_Buffer *B) +{ + size_t l = bufflen(B); + if (l == 0) + return 0; /* put nothing on stack */ + lua_pushlstring(B->L, B->buffer, l); + B->p = B->buffer; + B->lvl++; + return 1; +} + +static void adjuststack(luaL_Buffer *B) +{ + if (B->lvl > 1) { + lua_State *L = B->L; + int toget = 1; /* number of levels to concat */ + size_t toplen = lua_strlen(L, -1); + do { + size_t l = lua_strlen(L, -(toget+1)); + if (!(B->lvl - toget + 1 >= LUA_MINSTACK/2 || toplen > l)) + break; + toplen += l; + toget++; + } while (toget < B->lvl); + lua_concat(L, toget); + B->lvl = B->lvl - toget + 1; + } +} + +LUALIB_API char *luaL_prepbuffer(luaL_Buffer *B) +{ + if (emptybuffer(B)) + adjuststack(B); + return B->buffer; +} + +LUALIB_API void luaL_addlstring(luaL_Buffer *B, const char *s, size_t l) +{ + if (l <= bufffree(B)) { + memcpy(B->p, s, l); + B->p += l; + } else { + emptybuffer(B); + lua_pushlstring(B->L, s, l); + B->lvl++; + adjuststack(B); + } +} + +LUALIB_API void luaL_addstring(luaL_Buffer *B, const char *s) +{ + luaL_addlstring(B, s, strlen(s)); +} + +LUALIB_API void luaL_pushresult(luaL_Buffer *B) +{ + emptybuffer(B); + lua_concat(B->L, B->lvl); + B->lvl = 1; +} + +LUALIB_API void luaL_addvalue(luaL_Buffer *B) +{ + lua_State *L = B->L; + size_t vl; + const char *s = lua_tolstring(L, -1, &vl); + if (vl <= bufffree(B)) { /* fit into buffer? */ + memcpy(B->p, s, vl); /* put it there */ + B->p += vl; + lua_pop(L, 1); /* remove from stack */ + } else { + if (emptybuffer(B)) + lua_insert(L, -2); /* put buffer before new value */ + B->lvl++; /* add new value into B stack */ + adjuststack(B); + } +} + +LUALIB_API void luaL_buffinit(lua_State *L, luaL_Buffer *B) +{ + B->L = L; + B->p = B->buffer; + B->lvl = 0; +} + +/* -- Reference management ------------------------------------------------ */ + +#define FREELIST_REF 0 + +/* Convert a stack index to an absolute index. */ +#define abs_index(L, i) \ + ((i) > 0 || (i) <= LUA_REGISTRYINDEX ? (i) : lua_gettop(L) + (i) + 1) + +LUALIB_API int luaL_ref(lua_State *L, int t) +{ + int ref; + t = abs_index(L, t); + if (lua_isnil(L, -1)) { + lua_pop(L, 1); /* remove from stack */ + return LUA_REFNIL; /* `nil' has a unique fixed reference */ + } + lua_rawgeti(L, t, FREELIST_REF); /* get first free element */ + ref = (int)lua_tointeger(L, -1); /* ref = t[FREELIST_REF] */ + lua_pop(L, 1); /* remove it from stack */ + if (ref != 0) { /* any free element? */ + lua_rawgeti(L, t, ref); /* remove it from list */ + lua_rawseti(L, t, FREELIST_REF); /* (t[FREELIST_REF] = t[ref]) */ + } else { /* no free elements */ + ref = (int)lua_objlen(L, t); + ref++; /* create new reference */ + } + lua_rawseti(L, t, ref); + return ref; +} + +LUALIB_API void luaL_unref(lua_State *L, int t, int ref) +{ + if (ref >= 0) { + t = abs_index(L, t); + lua_rawgeti(L, t, FREELIST_REF); + lua_rawseti(L, t, ref); /* t[ref] = t[FREELIST_REF] */ + lua_pushinteger(L, ref); + lua_rawseti(L, t, FREELIST_REF); /* t[FREELIST_REF] = ref */ + } +} + +/* -- Default allocator and panic function -------------------------------- */ + +static int panic(lua_State *L) +{ + const char *s = lua_tostring(L, -1); + fputs("PANIC: unprotected error in call to Lua API (", stderr); + fputs(s ? s : "?", stderr); + fputc(')', stderr); fputc('\n', stderr); + fflush(stderr); + return 0; +} + +#ifdef LUAJIT_USE_SYSMALLOC + +#if LJ_64 && !LJ_GC64 && !defined(LUAJIT_USE_VALGRIND) +#error "Must use builtin allocator for 64 bit target" +#endif + +static void *mem_alloc(void *ud, void *ptr, size_t osize, size_t nsize) +{ + (void)ud; + (void)osize; + if (nsize == 0) { + free(ptr); + return NULL; + } else { + return realloc(ptr, nsize); + } +} + +LUALIB_API lua_State *luaL_newstate(void) +{ + lua_State *L = lua_newstate(mem_alloc, NULL); + if (L) G(L)->panic = panic; + return L; +} + +#else + +#include "lj_alloc.h" + +LUALIB_API lua_State *luaL_newstate(void) +{ + lua_State *L; + void *ud = lj_alloc_create(); + if (ud == NULL) return NULL; +#if LJ_64 && !LJ_GC64 + L = lj_state_newstate(lj_alloc_f, ud); +#else + L = lua_newstate(lj_alloc_f, ud); +#endif + if (L) G(L)->panic = panic; + return L; +} + +#if LJ_64 && !LJ_GC64 +LUA_API lua_State *lua_newstate(lua_Alloc f, void *ud) +{ + UNUSED(f); UNUSED(ud); + fputs("Must use luaL_newstate() for 64 bit target\n", stderr); + return NULL; +} +#endif + +#endif + diff --git a/lib/LuaJIT/lib_aux.o b/lib/LuaJIT/lib_aux.o new file mode 100644 index 0000000000000000000000000000000000000000..fd7d45f2a185b751b38b18fa17f7e3f817f6368f GIT binary patch literal 10632 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 MNgJ(~{dDU87d*({1J- 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 G`Tq+L;IN+n literal 0 HcmV?d00001 diff --git a/lib/LuaJIT/lib_base.c b/lib/LuaJIT/lib_base.c new file mode 100644 index 0000000..1cd8305 --- /dev/null +++ b/lib/LuaJIT/lib_base.c @@ -0,0 +1,679 @@ +/* +** Base and coroutine library. +** Copyright (C) 2005-2017 Mike Pall. See Copyright Notice in luajit.h +** +** Major portions taken verbatim or adapted from the Lua interpreter. +** Copyright (C) 1994-2011 Lua.org, PUC-Rio. See Copyright Notice in lua.h +*/ + +#include + +#define lib_base_c +#define LUA_LIB + +#include "lua.h" +#include "lauxlib.h" +#include "lualib.h" + +#include "lj_obj.h" +#include "lj_gc.h" +#include "lj_err.h" +#include "lj_debug.h" +#include "lj_str.h" +#include "lj_tab.h" +#include "lj_meta.h" +#include "lj_state.h" +#include "lj_frame.h" +#if LJ_HASFFI +#include "lj_ctype.h" +#include "lj_cconv.h" +#endif +#include "lj_bc.h" +#include "lj_ff.h" +#include "lj_dispatch.h" +#include "lj_char.h" +#include "lj_strscan.h" +#include "lj_strfmt.h" +#include "lj_lib.h" + +/* -- Base library: checks ------------------------------------------------ */ + +#define LJLIB_MODULE_base + +LJLIB_ASM(assert) LJLIB_REC(.) +{ + lj_lib_checkany(L, 1); + if (L->top == L->base+1) + lj_err_caller(L, LJ_ERR_ASSERT); + else if (tvisstr(L->base+1) || tvisnumber(L->base+1)) + lj_err_callermsg(L, strdata(lj_lib_checkstr(L, 2))); + else + lj_err_run(L); + return FFH_UNREACHABLE; +} + +/* ORDER LJ_T */ +LJLIB_PUSH("nil") +LJLIB_PUSH("boolean") +LJLIB_PUSH(top-1) /* boolean */ +LJLIB_PUSH("userdata") +LJLIB_PUSH("string") +LJLIB_PUSH("upval") +LJLIB_PUSH("thread") +LJLIB_PUSH("proto") +LJLIB_PUSH("function") +LJLIB_PUSH("trace") +LJLIB_PUSH("cdata") +LJLIB_PUSH("table") +LJLIB_PUSH(top-9) /* userdata */ +LJLIB_PUSH("number") +LJLIB_ASM_(type) LJLIB_REC(.) +/* Recycle the lj_lib_checkany(L, 1) from assert. */ + +/* -- Base library: iterators --------------------------------------------- */ + +/* This solves a circular dependency problem -- change FF_next_N as needed. */ +LJ_STATIC_ASSERT((int)FF_next == FF_next_N); + +LJLIB_ASM(next) +{ + lj_lib_checktab(L, 1); + return FFH_UNREACHABLE; +} + +#if LJ_52 || LJ_HASFFI +static int ffh_pairs(lua_State *L, MMS mm) +{ + TValue *o = lj_lib_checkany(L, 1); + cTValue *mo = lj_meta_lookup(L, o, mm); + if ((LJ_52 || tviscdata(o)) && !tvisnil(mo)) { + L->top = o+1; /* Only keep one argument. */ + copyTV(L, L->base-1-LJ_FR2, mo); /* Replace callable. */ + return FFH_TAILCALL; + } else { + if (!tvistab(o)) lj_err_argt(L, 1, LUA_TTABLE); + if (LJ_FR2) { copyTV(L, o-1, o); o--; } + setfuncV(L, o-1, funcV(lj_lib_upvalue(L, 1))); + if (mm == MM_pairs) setnilV(o+1); else setintV(o+1, 0); + return FFH_RES(3); + } +} +#else +#define ffh_pairs(L, mm) (lj_lib_checktab(L, 1), FFH_UNREACHABLE) +#endif + +LJLIB_PUSH(lastcl) +LJLIB_ASM(pairs) LJLIB_REC(xpairs 0) +{ + return ffh_pairs(L, MM_pairs); +} + +LJLIB_NOREGUV LJLIB_ASM(ipairs_aux) LJLIB_REC(.) +{ + lj_lib_checktab(L, 1); + lj_lib_checkint(L, 2); + return FFH_UNREACHABLE; +} + +LJLIB_PUSH(lastcl) +LJLIB_ASM(ipairs) LJLIB_REC(xpairs 1) +{ + return ffh_pairs(L, MM_ipairs); +} + +/* -- Base library: getters and setters ----------------------------------- */ + +LJLIB_ASM_(getmetatable) LJLIB_REC(.) +/* Recycle the lj_lib_checkany(L, 1) from assert. */ + +LJLIB_ASM(setmetatable) LJLIB_REC(.) +{ + GCtab *t = lj_lib_checktab(L, 1); + GCtab *mt = lj_lib_checktabornil(L, 2); + if (!tvisnil(lj_meta_lookup(L, L->base, MM_metatable))) + lj_err_caller(L, LJ_ERR_PROTMT); + setgcref(t->metatable, obj2gco(mt)); + if (mt) { lj_gc_objbarriert(L, t, mt); } + settabV(L, L->base-1-LJ_FR2, t); + return FFH_RES(1); +} + +LJLIB_CF(getfenv) LJLIB_REC(.) +{ + GCfunc *fn; + cTValue *o = L->base; + if (!(o < L->top && tvisfunc(o))) { + int level = lj_lib_optint(L, 1, 1); + o = lj_debug_frame(L, level, &level); + if (o == NULL) + lj_err_arg(L, 1, LJ_ERR_INVLVL); + if (LJ_FR2) o--; + } + fn = &gcval(o)->fn; + settabV(L, L->top++, isluafunc(fn) ? tabref(fn->l.env) : tabref(L->env)); + return 1; +} + +LJLIB_CF(setfenv) +{ + GCfunc *fn; + GCtab *t = lj_lib_checktab(L, 2); + cTValue *o = L->base; + if (!(o < L->top && tvisfunc(o))) { + int level = lj_lib_checkint(L, 1); + if (level == 0) { + /* NOBARRIER: A thread (i.e. L) is never black. */ + setgcref(L->env, obj2gco(t)); + return 0; + } + o = lj_debug_frame(L, level, &level); + if (o == NULL) + lj_err_arg(L, 1, LJ_ERR_INVLVL); + if (LJ_FR2) o--; + } + fn = &gcval(o)->fn; + if (!isluafunc(fn)) + lj_err_caller(L, LJ_ERR_SETFENV); + setgcref(fn->l.env, obj2gco(t)); + lj_gc_objbarrier(L, obj2gco(fn), t); + setfuncV(L, L->top++, fn); + return 1; +} + +LJLIB_ASM(rawget) LJLIB_REC(.) +{ + lj_lib_checktab(L, 1); + lj_lib_checkany(L, 2); + return FFH_UNREACHABLE; +} + +LJLIB_CF(rawset) LJLIB_REC(.) +{ + lj_lib_checktab(L, 1); + lj_lib_checkany(L, 2); + L->top = 1+lj_lib_checkany(L, 3); + lua_rawset(L, 1); + return 1; +} + +LJLIB_CF(rawequal) LJLIB_REC(.) +{ + cTValue *o1 = lj_lib_checkany(L, 1); + cTValue *o2 = lj_lib_checkany(L, 2); + setboolV(L->top-1, lj_obj_equal(o1, o2)); + return 1; +} + +#if LJ_52 +LJLIB_CF(rawlen) LJLIB_REC(.) +{ + cTValue *o = L->base; + int32_t len; + if (L->top > o && tvisstr(o)) + len = (int32_t)strV(o)->len; + else + len = (int32_t)lj_tab_len(lj_lib_checktab(L, 1)); + setintV(L->top-1, len); + return 1; +} +#endif + +LJLIB_CF(unpack) +{ + GCtab *t = lj_lib_checktab(L, 1); + int32_t n, i = lj_lib_optint(L, 2, 1); + int32_t e = (L->base+3-1 < L->top && !tvisnil(L->base+3-1)) ? + lj_lib_checkint(L, 3) : (int32_t)lj_tab_len(t); + if (i > e) return 0; + n = e - i + 1; + if (n <= 0 || !lua_checkstack(L, n)) + lj_err_caller(L, LJ_ERR_UNPACK); + do { + cTValue *tv = lj_tab_getint(t, i); + if (tv) { + copyTV(L, L->top++, tv); + } else { + setnilV(L->top++); + } + } while (i++ < e); + return n; +} + +LJLIB_CF(select) LJLIB_REC(.) +{ + int32_t n = (int32_t)(L->top - L->base); + if (n >= 1 && tvisstr(L->base) && *strVdata(L->base) == '#') { + setintV(L->top-1, n-1); + return 1; + } else { + int32_t i = lj_lib_checkint(L, 1); + if (i < 0) i = n + i; else if (i > n) i = n; + if (i < 1) + lj_err_arg(L, 1, LJ_ERR_IDXRNG); + return n - i; + } +} + +/* -- Base library: conversions ------------------------------------------- */ + +LJLIB_ASM(tonumber) LJLIB_REC(.) +{ + int32_t base = lj_lib_optint(L, 2, 10); + if (base == 10) { + TValue *o = lj_lib_checkany(L, 1); + if (lj_strscan_numberobj(o)) { + copyTV(L, L->base-1-LJ_FR2, o); + return FFH_RES(1); + } +#if LJ_HASFFI + if (tviscdata(o)) { + CTState *cts = ctype_cts(L); + CType *ct = lj_ctype_rawref(cts, cdataV(o)->ctypeid); + if (ctype_isenum(ct->info)) ct = ctype_child(cts, ct); + if (ctype_isnum(ct->info) || ctype_iscomplex(ct->info)) { + if (LJ_DUALNUM && ctype_isinteger_or_bool(ct->info) && + ct->size <= 4 && !(ct->size == 4 && (ct->info & CTF_UNSIGNED))) { + int32_t i; + lj_cconv_ct_tv(cts, ctype_get(cts, CTID_INT32), (uint8_t *)&i, o, 0); + setintV(L->base-1-LJ_FR2, i); + return FFH_RES(1); + } + lj_cconv_ct_tv(cts, ctype_get(cts, CTID_DOUBLE), + (uint8_t *)&(L->base-1-LJ_FR2)->n, o, 0); + return FFH_RES(1); + } + } +#endif + } else { + const char *p = strdata(lj_lib_checkstr(L, 1)); + char *ep; + unsigned int neg = 0; + unsigned long ul; + if (base < 2 || base > 36) + lj_err_arg(L, 2, LJ_ERR_BASERNG); + while (lj_char_isspace((unsigned char)(*p))) p++; + if (*p == '-') { p++; neg = 1; } else if (*p == '+') { p++; } + if (lj_char_isalnum((unsigned char)(*p))) { + ul = strtoul(p, &ep, base); + if (p != ep) { + while (lj_char_isspace((unsigned char)(*ep))) ep++; + if (*ep == '\0') { + if (LJ_DUALNUM && LJ_LIKELY(ul < 0x80000000u+neg)) { + if (neg) ul = -ul; + setintV(L->base-1-LJ_FR2, (int32_t)ul); + } else { + lua_Number n = (lua_Number)ul; + if (neg) n = -n; + setnumV(L->base-1-LJ_FR2, n); + } + return FFH_RES(1); + } + } + } + } + setnilV(L->base-1-LJ_FR2); + return FFH_RES(1); +} + +LJLIB_ASM(tostring) LJLIB_REC(.) +{ + TValue *o = lj_lib_checkany(L, 1); + cTValue *mo; + L->top = o+1; /* Only keep one argument. */ + if (!tvisnil(mo = lj_meta_lookup(L, o, MM_tostring))) { + copyTV(L, L->base-1-LJ_FR2, mo); /* Replace callable. */ + return FFH_TAILCALL; + } + lj_gc_check(L); + setstrV(L, L->base-1-LJ_FR2, lj_strfmt_obj(L, L->base)); + return FFH_RES(1); +} + +/* -- Base library: throw and catch errors -------------------------------- */ + +LJLIB_CF(error) +{ + int32_t level = lj_lib_optint(L, 2, 1); + lua_settop(L, 1); + if (lua_isstring(L, 1) && level > 0) { + luaL_where(L, level); + lua_pushvalue(L, 1); + lua_concat(L, 2); + } + return lua_error(L); +} + +LJLIB_ASM(pcall) LJLIB_REC(.) +{ + lj_lib_checkany(L, 1); + lj_lib_checkfunc(L, 2); /* For xpcall only. */ + return FFH_UNREACHABLE; +} +LJLIB_ASM_(xpcall) LJLIB_REC(.) + +/* -- Base library: load Lua code ----------------------------------------- */ + +static int load_aux(lua_State *L, int status, int envarg) +{ + if (status == LUA_OK) { + if (tvistab(L->base+envarg-1)) { + GCfunc *fn = funcV(L->top-1); + GCtab *t = tabV(L->base+envarg-1); + setgcref(fn->c.env, obj2gco(t)); + lj_gc_objbarrier(L, fn, t); + } + return 1; + } else { + setnilV(L->top-2); + return 2; + } +} + +LJLIB_CF(loadfile) +{ + GCstr *fname = lj_lib_optstr(L, 1); + GCstr *mode = lj_lib_optstr(L, 2); + int status; + lua_settop(L, 3); /* Ensure env arg exists. */ + status = luaL_loadfilex(L, fname ? strdata(fname) : NULL, + mode ? strdata(mode) : NULL); + return load_aux(L, status, 3); +} + +static const char *reader_func(lua_State *L, void *ud, size_t *size) +{ + UNUSED(ud); + luaL_checkstack(L, 2, "too many nested functions"); + copyTV(L, L->top++, L->base); + lua_call(L, 0, 1); /* Call user-supplied function. */ + L->top--; + if (tvisnil(L->top)) { + *size = 0; + return NULL; + } else if (tvisstr(L->top) || tvisnumber(L->top)) { + copyTV(L, L->base+4, L->top); /* Anchor string in reserved stack slot. */ + return lua_tolstring(L, 5, size); + } else { + lj_err_caller(L, LJ_ERR_RDRSTR); + return NULL; + } +} + +LJLIB_CF(load) +{ + GCstr *name = lj_lib_optstr(L, 2); + GCstr *mode = lj_lib_optstr(L, 3); + int status; + if (L->base < L->top && (tvisstr(L->base) || tvisnumber(L->base))) { + GCstr *s = lj_lib_checkstr(L, 1); + lua_settop(L, 4); /* Ensure env arg exists. */ + status = luaL_loadbufferx(L, strdata(s), s->len, strdata(name ? name : s), + mode ? strdata(mode) : NULL); + } else { + lj_lib_checkfunc(L, 1); + lua_settop(L, 5); /* Reserve a slot for the string from the reader. */ + status = lua_loadx(L, reader_func, NULL, name ? strdata(name) : "=(load)", + mode ? strdata(mode) : NULL); + } + return load_aux(L, status, 4); +} + +LJLIB_CF(loadstring) +{ + return lj_cf_load(L); +} + +LJLIB_CF(dofile) +{ + GCstr *fname = lj_lib_optstr(L, 1); + setnilV(L->top); + L->top = L->base+1; + if (luaL_loadfile(L, fname ? strdata(fname) : NULL) != LUA_OK) + lua_error(L); + lua_call(L, 0, LUA_MULTRET); + return (int)(L->top - L->base) - 1; +} + +/* -- Base library: GC control -------------------------------------------- */ + +LJLIB_CF(gcinfo) +{ + setintV(L->top++, (int32_t)(G(L)->gc.total >> 10)); + return 1; +} + +LJLIB_CF(collectgarbage) +{ + int opt = lj_lib_checkopt(L, 1, LUA_GCCOLLECT, /* ORDER LUA_GC* */ + "\4stop\7restart\7collect\5count\1\377\4step\10setpause\12setstepmul\1\377\11isrunning"); + int32_t data = lj_lib_optint(L, 2, 0); + if (opt == LUA_GCCOUNT) { + setnumV(L->top, (lua_Number)G(L)->gc.total/1024.0); + } else { + int res = lua_gc(L, opt, data); + if (opt == LUA_GCSTEP || opt == LUA_GCISRUNNING) + setboolV(L->top, res); + else + setintV(L->top, res); + } + L->top++; + return 1; +} + +/* -- Base library: miscellaneous functions ------------------------------- */ + +LJLIB_PUSH(top-2) /* Upvalue holds weak table. */ +LJLIB_CF(newproxy) +{ + lua_settop(L, 1); + lua_newuserdata(L, 0); + if (lua_toboolean(L, 1) == 0) { /* newproxy(): without metatable. */ + return 1; + } else if (lua_isboolean(L, 1)) { /* newproxy(true): with metatable. */ + lua_newtable(L); + lua_pushvalue(L, -1); + lua_pushboolean(L, 1); + lua_rawset(L, lua_upvalueindex(1)); /* Remember mt in weak table. */ + } else { /* newproxy(proxy): inherit metatable. */ + int validproxy = 0; + if (lua_getmetatable(L, 1)) { + lua_rawget(L, lua_upvalueindex(1)); + validproxy = lua_toboolean(L, -1); + lua_pop(L, 1); + } + if (!validproxy) + lj_err_arg(L, 1, LJ_ERR_NOPROXY); + lua_getmetatable(L, 1); + } + lua_setmetatable(L, 2); + return 1; +} + +LJLIB_PUSH("tostring") +LJLIB_CF(print) +{ + ptrdiff_t i, nargs = L->top - L->base; + cTValue *tv = lj_tab_getstr(tabref(L->env), strV(lj_lib_upvalue(L, 1))); + int shortcut; + if (tv && !tvisnil(tv)) { + copyTV(L, L->top++, tv); + } else { + setstrV(L, L->top++, strV(lj_lib_upvalue(L, 1))); + lua_gettable(L, LUA_GLOBALSINDEX); + tv = L->top-1; + } + shortcut = (tvisfunc(tv) && funcV(tv)->c.ffid == FF_tostring); + for (i = 0; i < nargs; i++) { + cTValue *o = &L->base[i]; + const char *str; + size_t size; + MSize len; + if (shortcut && (str = lj_strfmt_wstrnum(L, o, &len)) != NULL) { + size = len; + } else { + copyTV(L, L->top+1, o); + copyTV(L, L->top, L->top-1); + L->top += 2; + lua_call(L, 1, 1); + str = lua_tolstring(L, -1, &size); + if (!str) + lj_err_caller(L, LJ_ERR_PRTOSTR); + L->top--; + } + if (i) + putchar('\t'); + fwrite(str, 1, size, stdout); + } + putchar('\n'); + return 0; +} + +LJLIB_PUSH(top-3) +LJLIB_SET(_VERSION) + +#include "lj_libdef.h" + +/* -- Coroutine library --------------------------------------------------- */ + +#define LJLIB_MODULE_coroutine + +LJLIB_CF(coroutine_status) +{ + const char *s; + lua_State *co; + if (!(L->top > L->base && tvisthread(L->base))) + lj_err_arg(L, 1, LJ_ERR_NOCORO); + co = threadV(L->base); + if (co == L) s = "running"; + else if (co->status == LUA_YIELD) s = "suspended"; + else if (co->status != LUA_OK) s = "dead"; + else if (co->base > tvref(co->stack)+1+LJ_FR2) s = "normal"; + else if (co->top == co->base) s = "dead"; + else s = "suspended"; + lua_pushstring(L, s); + return 1; +} + +LJLIB_CF(coroutine_running) +{ +#if LJ_52 + int ismain = lua_pushthread(L); + setboolV(L->top++, ismain); + return 2; +#else + if (lua_pushthread(L)) + setnilV(L->top++); + return 1; +#endif +} + +LJLIB_CF(coroutine_isyieldable) +{ + setboolV(L->top++, cframe_canyield(L->cframe)); + return 1; +} + +LJLIB_CF(coroutine_create) +{ + lua_State *L1; + if (!(L->base < L->top && tvisfunc(L->base))) + lj_err_argt(L, 1, LUA_TFUNCTION); + L1 = lua_newthread(L); + setfuncV(L, L1->top++, funcV(L->base)); + return 1; +} + +LJLIB_ASM(coroutine_yield) +{ + lj_err_caller(L, LJ_ERR_CYIELD); + return FFH_UNREACHABLE; +} + +static int ffh_resume(lua_State *L, lua_State *co, int wrap) +{ + if (co->cframe != NULL || co->status > LUA_YIELD || + (co->status == LUA_OK && co->top == co->base)) { + ErrMsg em = co->cframe ? LJ_ERR_CORUN : LJ_ERR_CODEAD; + if (wrap) lj_err_caller(L, em); + setboolV(L->base-1-LJ_FR2, 0); + setstrV(L, L->base-LJ_FR2, lj_err_str(L, em)); + return FFH_RES(2); + } + lj_state_growstack(co, (MSize)(L->top - L->base)); + return FFH_RETRY; +} + +LJLIB_ASM(coroutine_resume) +{ + if (!(L->top > L->base && tvisthread(L->base))) + lj_err_arg(L, 1, LJ_ERR_NOCORO); + return ffh_resume(L, threadV(L->base), 0); +} + +LJLIB_NOREG LJLIB_ASM(coroutine_wrap_aux) +{ + return ffh_resume(L, threadV(lj_lib_upvalue(L, 1)), 1); +} + +/* Inline declarations. */ +LJ_ASMF void lj_ff_coroutine_wrap_aux(void); +#if !(LJ_TARGET_MIPS && defined(ljamalg_c)) +LJ_FUNCA_NORET void LJ_FASTCALL lj_ffh_coroutine_wrap_err(lua_State *L, + lua_State *co); +#endif + +/* Error handler, called from assembler VM. */ +void LJ_FASTCALL lj_ffh_coroutine_wrap_err(lua_State *L, lua_State *co) +{ + co->top--; copyTV(L, L->top, co->top); L->top++; + if (tvisstr(L->top-1)) + lj_err_callermsg(L, strVdata(L->top-1)); + else + lj_err_run(L); +} + +/* Forward declaration. */ +static void setpc_wrap_aux(lua_State *L, GCfunc *fn); + +LJLIB_CF(coroutine_wrap) +{ + GCfunc *fn; + lj_cf_coroutine_create(L); + fn = lj_lib_pushcc(L, lj_ffh_coroutine_wrap_aux, FF_coroutine_wrap_aux, 1); + setpc_wrap_aux(L, fn); + return 1; +} + +#include "lj_libdef.h" + +/* Fix the PC of wrap_aux. Really ugly workaround. */ +static void setpc_wrap_aux(lua_State *L, GCfunc *fn) +{ + setmref(fn->c.pc, &L2GG(L)->bcff[lj_lib_init_coroutine[1]+2]); +} + +/* ------------------------------------------------------------------------ */ + +static void newproxy_weaktable(lua_State *L) +{ + /* NOBARRIER: The table is new (marked white). */ + GCtab *t = lj_tab_new(L, 0, 1); + settabV(L, L->top++, t); + setgcref(t->metatable, obj2gco(t)); + setstrV(L, lj_tab_setstr(L, t, lj_str_newlit(L, "__mode")), + lj_str_newlit(L, "kv")); + t->nomm = (uint8_t)(~(1u<env); + settabV(L, lj_tab_setstr(L, env, lj_str_newlit(L, "_G")), env); + lua_pushliteral(L, LUA_VERSION); /* top-3. */ + newproxy_weaktable(L); /* top-2. */ + LJ_LIB_REG(L, "_G", base); + LJ_LIB_REG(L, LUA_COLIBNAME, coroutine); + return 2; +} + diff --git a/lib/LuaJIT/lib_base.o b/lib/LuaJIT/lib_base.o new file mode 100644 index 0000000000000000000000000000000000000000..9783586cdda9f5831089de4be64fd30321d62f08 GIT binary patch literal 19608 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 literal 0 HcmV?d00001 diff --git a/lib/LuaJIT/lib_base_dyn.o b/lib/LuaJIT/lib_base_dyn.o new file mode 100644 index 0000000000000000000000000000000000000000..7a3586b9419ff45c1668a61054ccf48d0c4c4b29 GIT binary patch literal 19584 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 literal 0 HcmV?d00001 diff --git a/lib/LuaJIT/lib_bit.c b/lib/LuaJIT/lib_bit.c new file mode 100644 index 0000000..c979a44 --- /dev/null +++ b/lib/LuaJIT/lib_bit.c @@ -0,0 +1,180 @@ +/* +** Bit manipulation library. +** Copyright (C) 2005-2017 Mike Pall. See Copyright Notice in luajit.h +*/ + +#define lib_bit_c +#define LUA_LIB + +#include "lua.h" +#include "lauxlib.h" +#include "lualib.h" + +#include "lj_obj.h" +#include "lj_err.h" +#include "lj_buf.h" +#include "lj_strscan.h" +#include "lj_strfmt.h" +#if LJ_HASFFI +#include "lj_ctype.h" +#include "lj_cdata.h" +#include "lj_cconv.h" +#include "lj_carith.h" +#endif +#include "lj_ff.h" +#include "lj_lib.h" + +/* ------------------------------------------------------------------------ */ + +#define LJLIB_MODULE_bit + +#if LJ_HASFFI +static int bit_result64(lua_State *L, CTypeID id, uint64_t x) +{ + GCcdata *cd = lj_cdata_new_(L, id, 8); + *(uint64_t *)cdataptr(cd) = x; + setcdataV(L, L->base-1-LJ_FR2, cd); + return FFH_RES(1); +} +#else +static int32_t bit_checkbit(lua_State *L, int narg) +{ + TValue *o = L->base + narg-1; + if (!(o < L->top && lj_strscan_numberobj(o))) + lj_err_argt(L, narg, LUA_TNUMBER); + if (LJ_LIKELY(tvisint(o))) { + return intV(o); + } else { + int32_t i = lj_num2bit(numV(o)); + if (LJ_DUALNUM) setintV(o, i); + return i; + } +} +#endif + +LJLIB_ASM(bit_tobit) LJLIB_REC(bit_tobit) +{ +#if LJ_HASFFI + CTypeID id = 0; + setintV(L->base-1-LJ_FR2, (int32_t)lj_carith_check64(L, 1, &id)); + return FFH_RES(1); +#else + lj_lib_checknumber(L, 1); + return FFH_RETRY; +#endif +} + +LJLIB_ASM(bit_bnot) LJLIB_REC(bit_unary IR_BNOT) +{ +#if LJ_HASFFI + CTypeID id = 0; + uint64_t x = lj_carith_check64(L, 1, &id); + return id ? bit_result64(L, id, ~x) : FFH_RETRY; +#else + lj_lib_checknumber(L, 1); + return FFH_RETRY; +#endif +} + +LJLIB_ASM(bit_bswap) LJLIB_REC(bit_unary IR_BSWAP) +{ +#if LJ_HASFFI + CTypeID id = 0; + uint64_t x = lj_carith_check64(L, 1, &id); + return id ? bit_result64(L, id, lj_bswap64(x)) : FFH_RETRY; +#else + lj_lib_checknumber(L, 1); + return FFH_RETRY; +#endif +} + +LJLIB_ASM(bit_lshift) LJLIB_REC(bit_shift IR_BSHL) +{ +#if LJ_HASFFI + CTypeID id = 0, id2 = 0; + uint64_t x = lj_carith_check64(L, 1, &id); + int32_t sh = (int32_t)lj_carith_check64(L, 2, &id2); + if (id) { + x = lj_carith_shift64(x, sh, curr_func(L)->c.ffid - (int)FF_bit_lshift); + return bit_result64(L, id, x); + } + if (id2) setintV(L->base+1, sh); + return FFH_RETRY; +#else + lj_lib_checknumber(L, 1); + bit_checkbit(L, 2); + return FFH_RETRY; +#endif +} +LJLIB_ASM_(bit_rshift) LJLIB_REC(bit_shift IR_BSHR) +LJLIB_ASM_(bit_arshift) LJLIB_REC(bit_shift IR_BSAR) +LJLIB_ASM_(bit_rol) LJLIB_REC(bit_shift IR_BROL) +LJLIB_ASM_(bit_ror) LJLIB_REC(bit_shift IR_BROR) + +LJLIB_ASM(bit_band) LJLIB_REC(bit_nary IR_BAND) +{ +#if LJ_HASFFI + CTypeID id = 0; + TValue *o = L->base, *top = L->top; + int i = 0; + do { lj_carith_check64(L, ++i, &id); } while (++o < top); + if (id) { + CTState *cts = ctype_cts(L); + CType *ct = ctype_get(cts, id); + int op = curr_func(L)->c.ffid - (int)FF_bit_bor; + uint64_t x, y = op >= 0 ? 0 : ~(uint64_t)0; + o = L->base; + do { + lj_cconv_ct_tv(cts, ct, (uint8_t *)&x, o, 0); + if (op < 0) y &= x; else if (op == 0) y |= x; else y ^= x; + } while (++o < top); + return bit_result64(L, id, y); + } + return FFH_RETRY; +#else + int i = 0; + do { lj_lib_checknumber(L, ++i); } while (L->base+i < L->top); + return FFH_RETRY; +#endif +} +LJLIB_ASM_(bit_bor) LJLIB_REC(bit_nary IR_BOR) +LJLIB_ASM_(bit_bxor) LJLIB_REC(bit_nary IR_BXOR) + +/* ------------------------------------------------------------------------ */ + +LJLIB_CF(bit_tohex) LJLIB_REC(.) +{ +#if LJ_HASFFI + CTypeID id = 0, id2 = 0; + uint64_t b = lj_carith_check64(L, 1, &id); + int32_t n = L->base+1>=L->top ? (id ? 16 : 8) : + (int32_t)lj_carith_check64(L, 2, &id2); +#else + uint32_t b = (uint32_t)bit_checkbit(L, 1); + int32_t n = L->base+1>=L->top ? 8 : bit_checkbit(L, 2); +#endif + SBuf *sb = lj_buf_tmp_(L); + SFormat sf = (STRFMT_UINT|STRFMT_T_HEX); + if (n < 0) { n = -n; sf |= STRFMT_F_UPPER; } + sf |= ((SFormat)((n+1)&255) << STRFMT_SH_PREC); +#if LJ_HASFFI + if (n < 16) b &= ((uint64_t)1 << 4*n)-1; +#else + if (n < 8) b &= (1u << 4*n)-1; +#endif + sb = lj_strfmt_putfxint(sb, sf, b); + setstrV(L, L->top-1, lj_buf_str(L, sb)); + lj_gc_check(L); + return 1; +} + +/* ------------------------------------------------------------------------ */ + +#include "lj_libdef.h" + +LUALIB_API int luaopen_bit(lua_State *L) +{ + LJ_LIB_REG(L, LUA_BITLIBNAME, bit); + return 1; +} + diff --git a/lib/LuaJIT/lib_bit.o b/lib/LuaJIT/lib_bit.o new file mode 100644 index 0000000000000000000000000000000000000000..f9e4cbbf7e2285daf5e0ce22c73d196e382ddcd0 GIT binary patch literal 4808 zcmbVQZ){sv6~DIA^fk$zU#0+No2U^Mq-a<%8fX~^Y{yQo!Go%4Hl$^9Y{#zCI&pAb znsmSx;ilv2&0BG$~>QcfxFRYQ-dvjzM`l*;+4=|w zA05bi`a)LxSq8(!`_7W#Sch&(HX|UkbSNd_yO-j(KLiCd$s^-Dyj+10LtD7CClfOe zDU(Y(k1GHxQr($0#I{qP6DPp!(=(zzv(Y-uZ(Z9O2{YFGu5bIVp#v8i9|WIW-~N-I zUut1Q{C98jTfeCG2m5cGx8(SmjQV1H-vAe{mQ!N3pNs1d4Hrp+I}?^Tf@$~{MhSN0X1S)yQ8EU3 zNIaf+EHRxpmJzq7r($oyo{e&+W@VjP2(}A@X4$4486lO%jxQ^2X2mJ|{BW~~muV-SfZ?rPO z^cCLNF}S#z8UMqn2f1TMjI8(%M8w6lthnWF@v%tX#v7-9EhRrwjf6mU_4hKLup$U* zM2aP3vM{q5@Me|8cObPDLnL=EC1pZy+y_~OEz#;--_Py^uUFh#T)5kZ6~A$1z^PnUt)r_#BziH8p=R$O$BLb{IjH{P9)F;iV&^*|O}>w619HB}>WAz)9f z1+7zw^;l153vlZ**1{4U)?<_Tpg4H&@?Av%1!(Z*Jy>_V`7w9+5)N{e@&toVW2Z20 zBYFqLM~BjO4W6b7i#0o4SYFL9O;(pH^JV*NT@KIX-LX-xRpF|q3i-vkMxjLP@Wwy!xLr*9D@2c2G`6mm~}bW3{ZI)^?)8xeqZz_&Cm;hq#17Z zCC$<2_od9Sv;7J4;l}<+bJm|8FdqgaVUB_XNPS+x$ir+{H@5dVk5htpiUKVEbs=9&0f3vHfkH>iGu}?{DLW+cV6=993g|B@N{T%pi_$xi|S9{=Bd*E;Oz~AkG-v<0%$n?4@6t|3pyoP*RE{mXm=%FTPiI|*Px1l)l0|c!Bm~y4>?1^G-A)a4R3Cw z5`sWp{G1C*9A7^qk9B~A@*jtb@P8u+ne{dx_o-U&KF>` z>+V>gS-n)vNA1!Y9PqikooCU)@-mCoYsjE@EE*#gMiiD$UBvS_ikuuy`N zABE0Sio$o%7nbdO@uX5HRdVHeet}M>yQaIjhd~LK=za6V0Yf@R>vdW9?b}J%H&7(r z2{TK*f_7E@@Jkh^R993lpH;XzE^RBFXHINssVn6z0v}Ii>%SM}gue{nIe%w*HsY z`p+prWU(YkpXv|IdwruyTzzuMpSZwdkHzG$~>QcfxFRYQ-dvjzM`l*;+4=|w zA05bi`a)LxSq8(!`_7W#Sch&(HX|UkbSNd_yO-j(KLiCd$s^-Dyj+10LtD7CClfOe zDU(Y(k1GHxQr($0#I{qP6DPp!(=(zzv(Y-uZ(Z9O2{YFGu5bIVp#v8i9|WIW-~N-I zUut1Q{C98jTfeCG2m5cGx8(SmjQV1H-vAe{mQ!N3pNs1d4Hrp+I}?^Tf@$~{MhSN0X1S)yQ8EU3 zNIaf+EHRxpmJzq7r($oyo{e&+W@VjP2(}A@X4$4486lO%jxQ^2X2mJ|{BW~~muV-SfZ?rPO z^cCLNF}S#z8UMqn2f1TMjI8(%M8w6lthnWF@v%tX#v7-9EhRrwjf6mU_4hKLup$U* zM2aP3vM{q5@Me|8cObPDLnL=EC1pZy+y_~OEz#;--_Py^uUFh#T)5kZ6~A$1z^PnUt)r_#BziH8p=R$O$BLb{IjH{P9)F;iV&^*|O}>w619HB}>WAz)9f z1+7zw^;l153vlZ**1{4U)?<_Tpg4H&@?Av%1!(Z*Jy>_V`7w9+5)N{e@&toVW2Z20 zBYFqLM~BjO4W6b7i#0o4SYFL9O;(pH^JV*NT@KIX-LX-xRpF|q3i-vkMxjLP@Wwy!xLr*9D@2c2G`6mm~}bW3{ZI)^?)8xeqZz_&Cm;hq#17Z zCC$<2_od9Sv;7J4;l}<+bJm|8FdqgaVUB_XNPS+x$ir+{H@5dVk5htpiUKVEbs=9&0f3vHfkH>iGu}?{DLW+cV6=993g|B@N{T%pi_$xi|S9{=Bd*E;Oz~AkG-v<0%$n?4@6t|3pyoP*RE{mXm=%FTPiI|*Px1l)l0|c!Bm~y4>?1^G-A)a4R3Cw z5`sWp{G1C*9A7^qk9B~A@*jtb@P8u+ne{dx_o-U&KF>` z>+V>gS-n)vNA1!Y9PqikooCU)@-mCoYsjE@EE*#gMiiD$UBvS_ikuuy`N zABE0Sio$o%7nbdO@uX5HRdVHeet}M>yQaIjhd~LK=za6V0Yf@R>vdW9?b}J%H&7(r z2{TK*f_7E@@Jkh^R993lpH;XzE^RBFXHINssVn6z0v}Ii>%SM}gue{nIe%w*HsY z`p+prWU(YkpXv|IdwruyTzzuMpSZwdkHztop++, registry(L)); + return 1; +} + +LJLIB_CF(debug_getmetatable) LJLIB_REC(.) +{ + lj_lib_checkany(L, 1); + if (!lua_getmetatable(L, 1)) { + setnilV(L->top-1); + } + return 1; +} + +LJLIB_CF(debug_setmetatable) +{ + lj_lib_checktabornil(L, 2); + L->top = L->base+2; + lua_setmetatable(L, 1); +#if !LJ_52 + setboolV(L->top-1, 1); +#endif + return 1; +} + +LJLIB_CF(debug_getfenv) +{ + lj_lib_checkany(L, 1); + lua_getfenv(L, 1); + return 1; +} + +LJLIB_CF(debug_setfenv) +{ + lj_lib_checktab(L, 2); + L->top = L->base+2; + if (!lua_setfenv(L, 1)) + lj_err_caller(L, LJ_ERR_SETFENV); + return 1; +} + +/* ------------------------------------------------------------------------ */ + +static void settabss(lua_State *L, const char *i, const char *v) +{ + lua_pushstring(L, v); + lua_setfield(L, -2, i); +} + +static void settabsi(lua_State *L, const char *i, int v) +{ + lua_pushinteger(L, v); + lua_setfield(L, -2, i); +} + +static void settabsb(lua_State *L, const char *i, int v) +{ + lua_pushboolean(L, v); + lua_setfield(L, -2, i); +} + +static lua_State *getthread(lua_State *L, int *arg) +{ + if (L->base < L->top && tvisthread(L->base)) { + *arg = 1; + return threadV(L->base); + } else { + *arg = 0; + return L; + } +} + +static void treatstackoption(lua_State *L, lua_State *L1, const char *fname) +{ + if (L == L1) { + lua_pushvalue(L, -2); + lua_remove(L, -3); + } + else + lua_xmove(L1, L, 1); + lua_setfield(L, -2, fname); +} + +LJLIB_CF(debug_getinfo) +{ + lj_Debug ar; + int arg, opt_f = 0, opt_L = 0; + lua_State *L1 = getthread(L, &arg); + const char *options = luaL_optstring(L, arg+2, "flnSu"); + if (lua_isnumber(L, arg+1)) { + if (!lua_getstack(L1, (int)lua_tointeger(L, arg+1), (lua_Debug *)&ar)) { + setnilV(L->top-1); + return 1; + } + } else if (L->base+arg < L->top && tvisfunc(L->base+arg)) { + options = lua_pushfstring(L, ">%s", options); + setfuncV(L1, L1->top++, funcV(L->base+arg)); + } else { + lj_err_arg(L, arg+1, LJ_ERR_NOFUNCL); + } + if (!lj_debug_getinfo(L1, options, &ar, 1)) + lj_err_arg(L, arg+2, LJ_ERR_INVOPT); + lua_createtable(L, 0, 16); /* Create result table. */ + for (; *options; options++) { + switch (*options) { + case 'S': + settabss(L, "source", ar.source); + settabss(L, "short_src", ar.short_src); + settabsi(L, "linedefined", ar.linedefined); + settabsi(L, "lastlinedefined", ar.lastlinedefined); + settabss(L, "what", ar.what); + break; + case 'l': + settabsi(L, "currentline", ar.currentline); + break; + case 'u': + settabsi(L, "nups", ar.nups); + settabsi(L, "nparams", ar.nparams); + settabsb(L, "isvararg", ar.isvararg); + break; + case 'n': + settabss(L, "name", ar.name); + settabss(L, "namewhat", ar.namewhat); + break; + case 'f': opt_f = 1; break; + case 'L': opt_L = 1; break; + default: break; + } + } + if (opt_L) treatstackoption(L, L1, "activelines"); + if (opt_f) treatstackoption(L, L1, "func"); + return 1; /* Return result table. */ +} + +LJLIB_CF(debug_getlocal) +{ + int arg; + lua_State *L1 = getthread(L, &arg); + lua_Debug ar; + const char *name; + int slot = lj_lib_checkint(L, arg+2); + if (tvisfunc(L->base+arg)) { + L->top = L->base+arg+1; + lua_pushstring(L, lua_getlocal(L, NULL, slot)); + return 1; + } + if (!lua_getstack(L1, lj_lib_checkint(L, arg+1), &ar)) + lj_err_arg(L, arg+1, LJ_ERR_LVLRNG); + name = lua_getlocal(L1, &ar, slot); + if (name) { + lua_xmove(L1, L, 1); + lua_pushstring(L, name); + lua_pushvalue(L, -2); + return 2; + } else { + setnilV(L->top-1); + return 1; + } +} + +LJLIB_CF(debug_setlocal) +{ + int arg; + lua_State *L1 = getthread(L, &arg); + lua_Debug ar; + TValue *tv; + if (!lua_getstack(L1, lj_lib_checkint(L, arg+1), &ar)) + lj_err_arg(L, arg+1, LJ_ERR_LVLRNG); + tv = lj_lib_checkany(L, arg+3); + copyTV(L1, L1->top++, tv); + lua_pushstring(L, lua_setlocal(L1, &ar, lj_lib_checkint(L, arg+2))); + return 1; +} + +static int debug_getupvalue(lua_State *L, int get) +{ + int32_t n = lj_lib_checkint(L, 2); + const char *name; + lj_lib_checkfunc(L, 1); + name = get ? lua_getupvalue(L, 1, n) : lua_setupvalue(L, 1, n); + if (name) { + lua_pushstring(L, name); + if (!get) return 1; + copyTV(L, L->top, L->top-2); + L->top++; + return 2; + } + return 0; +} + +LJLIB_CF(debug_getupvalue) +{ + return debug_getupvalue(L, 1); +} + +LJLIB_CF(debug_setupvalue) +{ + lj_lib_checkany(L, 3); + return debug_getupvalue(L, 0); +} + +LJLIB_CF(debug_upvalueid) +{ + GCfunc *fn = lj_lib_checkfunc(L, 1); + int32_t n = lj_lib_checkint(L, 2) - 1; + if ((uint32_t)n >= fn->l.nupvalues) + lj_err_arg(L, 2, LJ_ERR_IDXRNG); + setlightudV(L->top-1, isluafunc(fn) ? (void *)gcref(fn->l.uvptr[n]) : + (void *)&fn->c.upvalue[n]); + return 1; +} + +LJLIB_CF(debug_upvaluejoin) +{ + GCfunc *fn[2]; + GCRef *p[2]; + int i; + for (i = 0; i < 2; i++) { + int32_t n; + fn[i] = lj_lib_checkfunc(L, 2*i+1); + if (!isluafunc(fn[i])) + lj_err_arg(L, 2*i+1, LJ_ERR_NOLFUNC); + n = lj_lib_checkint(L, 2*i+2) - 1; + if ((uint32_t)n >= fn[i]->l.nupvalues) + lj_err_arg(L, 2*i+2, LJ_ERR_IDXRNG); + p[i] = &fn[i]->l.uvptr[n]; + } + setgcrefr(*p[0], *p[1]); + lj_gc_objbarrier(L, fn[0], gcref(*p[1])); + return 0; +} + +#if LJ_52 +LJLIB_CF(debug_getuservalue) +{ + TValue *o = L->base; + if (o < L->top && tvisudata(o)) + settabV(L, o, tabref(udataV(o)->env)); + else + setnilV(o); + L->top = o+1; + return 1; +} + +LJLIB_CF(debug_setuservalue) +{ + TValue *o = L->base; + if (!(o < L->top && tvisudata(o))) + lj_err_argt(L, 1, LUA_TUSERDATA); + if (!(o+1 < L->top && tvistab(o+1))) + lj_err_argt(L, 2, LUA_TTABLE); + L->top = o+2; + lua_setfenv(L, 1); + return 1; +} +#endif + +/* ------------------------------------------------------------------------ */ + +#define KEY_HOOK ((void *)0x3004) + +static void hookf(lua_State *L, lua_Debug *ar) +{ + static const char *const hooknames[] = + {"call", "return", "line", "count", "tail return"}; + lua_pushlightuserdata(L, KEY_HOOK); + lua_rawget(L, LUA_REGISTRYINDEX); + if (lua_isfunction(L, -1)) { + lua_pushstring(L, hooknames[(int)ar->event]); + if (ar->currentline >= 0) + lua_pushinteger(L, ar->currentline); + else lua_pushnil(L); + lua_call(L, 2, 0); + } +} + +static int makemask(const char *smask, int count) +{ + int mask = 0; + if (strchr(smask, 'c')) mask |= LUA_MASKCALL; + if (strchr(smask, 'r')) mask |= LUA_MASKRET; + if (strchr(smask, 'l')) mask |= LUA_MASKLINE; + if (count > 0) mask |= LUA_MASKCOUNT; + return mask; +} + +static char *unmakemask(int mask, char *smask) +{ + int i = 0; + if (mask & LUA_MASKCALL) smask[i++] = 'c'; + if (mask & LUA_MASKRET) smask[i++] = 'r'; + if (mask & LUA_MASKLINE) smask[i++] = 'l'; + smask[i] = '\0'; + return smask; +} + +LJLIB_CF(debug_sethook) +{ + int arg, mask, count; + lua_Hook func; + (void)getthread(L, &arg); + if (lua_isnoneornil(L, arg+1)) { + lua_settop(L, arg+1); + func = NULL; mask = 0; count = 0; /* turn off hooks */ + } else { + const char *smask = luaL_checkstring(L, arg+2); + luaL_checktype(L, arg+1, LUA_TFUNCTION); + count = luaL_optint(L, arg+3, 0); + func = hookf; mask = makemask(smask, count); + } + lua_pushlightuserdata(L, KEY_HOOK); + lua_pushvalue(L, arg+1); + lua_rawset(L, LUA_REGISTRYINDEX); + lua_sethook(L, func, mask, count); + return 0; +} + +LJLIB_CF(debug_gethook) +{ + char buff[5]; + int mask = lua_gethookmask(L); + lua_Hook hook = lua_gethook(L); + if (hook != NULL && hook != hookf) { /* external hook? */ + lua_pushliteral(L, "external hook"); + } else { + lua_pushlightuserdata(L, KEY_HOOK); + lua_rawget(L, LUA_REGISTRYINDEX); /* get hook */ + } + lua_pushstring(L, unmakemask(mask, buff)); + lua_pushinteger(L, lua_gethookcount(L)); + return 3; +} + +/* ------------------------------------------------------------------------ */ + +LJLIB_CF(debug_debug) +{ + for (;;) { + char buffer[250]; + fputs("lua_debug> ", stderr); + if (fgets(buffer, sizeof(buffer), stdin) == 0 || + strcmp(buffer, "cont\n") == 0) + return 0; + if (luaL_loadbuffer(L, buffer, strlen(buffer), "=(debug command)") || + lua_pcall(L, 0, 0, 0)) { + fputs(lua_tostring(L, -1), stderr); + fputs("\n", stderr); + } + lua_settop(L, 0); /* remove eventual returns */ + } +} + +/* ------------------------------------------------------------------------ */ + +#define LEVELS1 12 /* size of the first part of the stack */ +#define LEVELS2 10 /* size of the second part of the stack */ + +LJLIB_CF(debug_traceback) +{ + int arg; + lua_State *L1 = getthread(L, &arg); + const char *msg = lua_tostring(L, arg+1); + if (msg == NULL && L->top > L->base+arg) + L->top = L->base+arg+1; + else + luaL_traceback(L, L1, msg, lj_lib_optint(L, arg+2, (L == L1))); + return 1; +} + +/* ------------------------------------------------------------------------ */ + +#include "lj_libdef.h" + +LUALIB_API int luaopen_debug(lua_State *L) +{ + LJ_LIB_REG(L, LUA_DBLIBNAME, debug); + return 1; +} + diff --git a/lib/LuaJIT/lib_debug.o b/lib/LuaJIT/lib_debug.o new file mode 100644 index 0000000000000000000000000000000000000000..adeb1389e3c04d0ab8c5d3ff623e2334b72c0222 GIT binary patch literal 16248 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 literal 0 HcmV?d00001 diff --git a/lib/LuaJIT/lib_ffi.c b/lib/LuaJIT/lib_ffi.c new file mode 100644 index 0000000..8032411 --- /dev/null +++ b/lib/LuaJIT/lib_ffi.c @@ -0,0 +1,872 @@ +/* +** FFI library. +** Copyright (C) 2005-2017 Mike Pall. See Copyright Notice in luajit.h +*/ + +#define lib_ffi_c +#define LUA_LIB + +#include + +#include "lua.h" +#include "lauxlib.h" +#include "lualib.h" + +#include "lj_obj.h" + +#if LJ_HASFFI + +#include "lj_gc.h" +#include "lj_err.h" +#include "lj_str.h" +#include "lj_tab.h" +#include "lj_meta.h" +#include "lj_ctype.h" +#include "lj_cparse.h" +#include "lj_cdata.h" +#include "lj_cconv.h" +#include "lj_carith.h" +#include "lj_ccall.h" +#include "lj_ccallback.h" +#include "lj_clib.h" +#include "lj_strfmt.h" +#include "lj_ff.h" +#include "lj_lib.h" + +/* -- C type checks ------------------------------------------------------- */ + +/* Check first argument for a C type and returns its ID. */ +static CTypeID ffi_checkctype(lua_State *L, CTState *cts, TValue *param) +{ + TValue *o = L->base; + if (!(o < L->top)) { + err_argtype: + lj_err_argtype(L, 1, "C type"); + } + if (tvisstr(o)) { /* Parse an abstract C type declaration. */ + GCstr *s = strV(o); + CPState cp; + int errcode; + cp.L = L; + cp.cts = cts; + cp.srcname = strdata(s); + cp.p = strdata(s); + cp.param = param; + cp.mode = CPARSE_MODE_ABSTRACT|CPARSE_MODE_NOIMPLICIT; + errcode = lj_cparse(&cp); + if (errcode) lj_err_throw(L, errcode); /* Propagate errors. */ + return cp.val.id; + } else { + GCcdata *cd; + if (!tviscdata(o)) goto err_argtype; + if (param && param < L->top) lj_err_arg(L, 1, LJ_ERR_FFI_NUMPARAM); + cd = cdataV(o); + return cd->ctypeid == CTID_CTYPEID ? *(CTypeID *)cdataptr(cd) : cd->ctypeid; + } +} + +/* Check argument for C data and return it. */ +static GCcdata *ffi_checkcdata(lua_State *L, int narg) +{ + TValue *o = L->base + narg-1; + if (!(o < L->top && tviscdata(o))) + lj_err_argt(L, narg, LUA_TCDATA); + return cdataV(o); +} + +/* Convert argument to C pointer. */ +static void *ffi_checkptr(lua_State *L, int narg, CTypeID id) +{ + CTState *cts = ctype_cts(L); + TValue *o = L->base + narg-1; + void *p; + if (o >= L->top) + lj_err_arg(L, narg, LJ_ERR_NOVAL); + lj_cconv_ct_tv(cts, ctype_get(cts, id), (uint8_t *)&p, o, CCF_ARG(narg)); + return p; +} + +/* Convert argument to int32_t. */ +static int32_t ffi_checkint(lua_State *L, int narg) +{ + CTState *cts = ctype_cts(L); + TValue *o = L->base + narg-1; + int32_t i; + if (o >= L->top) + lj_err_arg(L, narg, LJ_ERR_NOVAL); + lj_cconv_ct_tv(cts, ctype_get(cts, CTID_INT32), (uint8_t *)&i, o, + CCF_ARG(narg)); + return i; +} + +/* -- C type metamethods -------------------------------------------------- */ + +#define LJLIB_MODULE_ffi_meta + +/* Handle ctype __index/__newindex metamethods. */ +static int ffi_index_meta(lua_State *L, CTState *cts, CType *ct, MMS mm) +{ + CTypeID id = ctype_typeid(cts, ct); + cTValue *tv = lj_ctype_meta(cts, id, mm); + TValue *base = L->base; + if (!tv) { + const char *s; + err_index: + s = strdata(lj_ctype_repr(L, id, NULL)); + if (tvisstr(L->base+1)) { + lj_err_callerv(L, LJ_ERR_FFI_BADMEMBER, s, strVdata(L->base+1)); + } else { + const char *key = tviscdata(L->base+1) ? + strdata(lj_ctype_repr(L, cdataV(L->base+1)->ctypeid, NULL)) : + lj_typename(L->base+1); + lj_err_callerv(L, LJ_ERR_FFI_BADIDXW, s, key); + } + } + if (!tvisfunc(tv)) { + if (mm == MM_index) { + cTValue *o = lj_meta_tget(L, tv, base+1); + if (o) { + if (tvisnil(o)) goto err_index; + copyTV(L, L->top-1, o); + return 1; + } + } else { + TValue *o = lj_meta_tset(L, tv, base+1); + if (o) { + copyTV(L, o, base+2); + return 0; + } + } + copyTV(L, base, L->top); + tv = L->top-1-LJ_FR2; + } + return lj_meta_tailcall(L, tv); +} + +LJLIB_CF(ffi_meta___index) LJLIB_REC(cdata_index 0) +{ + CTState *cts = ctype_cts(L); + CTInfo qual = 0; + CType *ct; + uint8_t *p; + TValue *o = L->base; + if (!(o+1 < L->top && tviscdata(o))) /* Also checks for presence of key. */ + lj_err_argt(L, 1, LUA_TCDATA); + ct = lj_cdata_index(cts, cdataV(o), o+1, &p, &qual); + if ((qual & 1)) + return ffi_index_meta(L, cts, ct, MM_index); + if (lj_cdata_get(cts, ct, L->top-1, p)) + lj_gc_check(L); + return 1; +} + +LJLIB_CF(ffi_meta___newindex) LJLIB_REC(cdata_index 1) +{ + CTState *cts = ctype_cts(L); + CTInfo qual = 0; + CType *ct; + uint8_t *p; + TValue *o = L->base; + if (!(o+2 < L->top && tviscdata(o))) /* Also checks for key and value. */ + lj_err_argt(L, 1, LUA_TCDATA); + ct = lj_cdata_index(cts, cdataV(o), o+1, &p, &qual); + if ((qual & 1)) { + if ((qual & CTF_CONST)) + lj_err_caller(L, LJ_ERR_FFI_WRCONST); + return ffi_index_meta(L, cts, ct, MM_newindex); + } + lj_cdata_set(cts, ct, p, o+2, qual); + return 0; +} + +/* Common handler for cdata arithmetic. */ +static int ffi_arith(lua_State *L) +{ + MMS mm = (MMS)(curr_func(L)->c.ffid - (int)FF_ffi_meta___eq + (int)MM_eq); + return lj_carith_op(L, mm); +} + +/* The following functions must be in contiguous ORDER MM. */ +LJLIB_CF(ffi_meta___eq) LJLIB_REC(cdata_arith MM_eq) +{ + return ffi_arith(L); +} + +LJLIB_CF(ffi_meta___len) LJLIB_REC(cdata_arith MM_len) +{ + return lj_carith_len(L); +} + +LJLIB_CF(ffi_meta___lt) LJLIB_REC(cdata_arith MM_lt) +{ + return ffi_arith(L); +} + +LJLIB_CF(ffi_meta___le) LJLIB_REC(cdata_arith MM_le) +{ + return ffi_arith(L); +} + +LJLIB_CF(ffi_meta___concat) LJLIB_REC(cdata_arith MM_concat) +{ + return ffi_arith(L); +} + +/* Forward declaration. */ +static int lj_cf_ffi_new(lua_State *L); + +LJLIB_CF(ffi_meta___call) LJLIB_REC(cdata_call) +{ + CTState *cts = ctype_cts(L); + GCcdata *cd = ffi_checkcdata(L, 1); + CTypeID id = cd->ctypeid; + CType *ct; + cTValue *tv; + MMS mm = MM_call; + if (cd->ctypeid == CTID_CTYPEID) { + id = *(CTypeID *)cdataptr(cd); + mm = MM_new; + } else { + int ret = lj_ccall_func(L, cd); + if (ret >= 0) + return ret; + } + /* Handle ctype __call/__new metamethod. */ + ct = ctype_raw(cts, id); + if (ctype_isptr(ct->info)) id = ctype_cid(ct->info); + tv = lj_ctype_meta(cts, id, mm); + if (tv) + return lj_meta_tailcall(L, tv); + else if (mm == MM_call) + lj_err_callerv(L, LJ_ERR_FFI_BADCALL, strdata(lj_ctype_repr(L, id, NULL))); + return lj_cf_ffi_new(L); +} + +LJLIB_CF(ffi_meta___add) LJLIB_REC(cdata_arith MM_add) +{ + return ffi_arith(L); +} + +LJLIB_CF(ffi_meta___sub) LJLIB_REC(cdata_arith MM_sub) +{ + return ffi_arith(L); +} + +LJLIB_CF(ffi_meta___mul) LJLIB_REC(cdata_arith MM_mul) +{ + return ffi_arith(L); +} + +LJLIB_CF(ffi_meta___div) LJLIB_REC(cdata_arith MM_div) +{ + return ffi_arith(L); +} + +LJLIB_CF(ffi_meta___mod) LJLIB_REC(cdata_arith MM_mod) +{ + return ffi_arith(L); +} + +LJLIB_CF(ffi_meta___pow) LJLIB_REC(cdata_arith MM_pow) +{ + return ffi_arith(L); +} + +LJLIB_CF(ffi_meta___unm) LJLIB_REC(cdata_arith MM_unm) +{ + return ffi_arith(L); +} +/* End of contiguous ORDER MM. */ + +LJLIB_CF(ffi_meta___tostring) +{ + GCcdata *cd = ffi_checkcdata(L, 1); + const char *msg = "cdata<%s>: %p"; + CTypeID id = cd->ctypeid; + void *p = cdataptr(cd); + if (id == CTID_CTYPEID) { + msg = "ctype<%s>"; + id = *(CTypeID *)p; + } else { + CTState *cts = ctype_cts(L); + CType *ct = ctype_raw(cts, id); + if (ctype_isref(ct->info)) { + p = *(void **)p; + ct = ctype_rawchild(cts, ct); + } + if (ctype_iscomplex(ct->info)) { + setstrV(L, L->top-1, lj_ctype_repr_complex(L, cdataptr(cd), ct->size)); + goto checkgc; + } else if (ct->size == 8 && ctype_isinteger(ct->info)) { + setstrV(L, L->top-1, lj_ctype_repr_int64(L, *(uint64_t *)cdataptr(cd), + (ct->info & CTF_UNSIGNED))); + goto checkgc; + } else if (ctype_isfunc(ct->info)) { + p = *(void **)p; + } else if (ctype_isenum(ct->info)) { + msg = "cdata<%s>: %d"; + p = (void *)(uintptr_t)*(uint32_t **)p; + } else { + if (ctype_isptr(ct->info)) { + p = cdata_getptr(p, ct->size); + ct = ctype_rawchild(cts, ct); + } + if (ctype_isstruct(ct->info) || ctype_isvector(ct->info)) { + /* Handle ctype __tostring metamethod. */ + cTValue *tv = lj_ctype_meta(cts, ctype_typeid(cts, ct), MM_tostring); + if (tv) + return lj_meta_tailcall(L, tv); + } + } + } + lj_strfmt_pushf(L, msg, strdata(lj_ctype_repr(L, id, NULL)), p); +checkgc: + lj_gc_check(L); + return 1; +} + +static int ffi_pairs(lua_State *L, MMS mm) +{ + CTState *cts = ctype_cts(L); + CTypeID id = ffi_checkcdata(L, 1)->ctypeid; + CType *ct = ctype_raw(cts, id); + cTValue *tv; + if (ctype_isptr(ct->info)) id = ctype_cid(ct->info); + tv = lj_ctype_meta(cts, id, mm); + if (!tv) + lj_err_callerv(L, LJ_ERR_FFI_BADMM, strdata(lj_ctype_repr(L, id, NULL)), + strdata(mmname_str(G(L), mm))); + return lj_meta_tailcall(L, tv); +} + +LJLIB_CF(ffi_meta___pairs) +{ + return ffi_pairs(L, MM_pairs); +} + +LJLIB_CF(ffi_meta___ipairs) +{ + return ffi_pairs(L, MM_ipairs); +} + +LJLIB_PUSH("ffi") LJLIB_SET(__metatable) + +#include "lj_libdef.h" + +/* -- C library metamethods ----------------------------------------------- */ + +#define LJLIB_MODULE_ffi_clib + +/* Index C library by a name. */ +static TValue *ffi_clib_index(lua_State *L) +{ + TValue *o = L->base; + CLibrary *cl; + if (!(o < L->top && tvisudata(o) && udataV(o)->udtype == UDTYPE_FFI_CLIB)) + lj_err_argt(L, 1, LUA_TUSERDATA); + cl = (CLibrary *)uddata(udataV(o)); + if (!(o+1 < L->top && tvisstr(o+1))) + lj_err_argt(L, 2, LUA_TSTRING); + return lj_clib_index(L, cl, strV(o+1)); +} + +LJLIB_CF(ffi_clib___index) LJLIB_REC(clib_index 1) +{ + TValue *tv = ffi_clib_index(L); + if (tviscdata(tv)) { + CTState *cts = ctype_cts(L); + GCcdata *cd = cdataV(tv); + CType *s = ctype_get(cts, cd->ctypeid); + if (ctype_isextern(s->info)) { + CTypeID sid = ctype_cid(s->info); + void *sp = *(void **)cdataptr(cd); + CType *ct = ctype_raw(cts, sid); + if (lj_cconv_tv_ct(cts, ct, sid, L->top-1, sp)) + lj_gc_check(L); + return 1; + } + } + copyTV(L, L->top-1, tv); + return 1; +} + +LJLIB_CF(ffi_clib___newindex) LJLIB_REC(clib_index 0) +{ + TValue *tv = ffi_clib_index(L); + TValue *o = L->base+2; + if (o < L->top && tviscdata(tv)) { + CTState *cts = ctype_cts(L); + GCcdata *cd = cdataV(tv); + CType *d = ctype_get(cts, cd->ctypeid); + if (ctype_isextern(d->info)) { + CTInfo qual = 0; + for (;;) { /* Skip attributes and collect qualifiers. */ + d = ctype_child(cts, d); + if (!ctype_isattrib(d->info)) break; + if (ctype_attrib(d->info) == CTA_QUAL) qual |= d->size; + } + if (!((d->info|qual) & CTF_CONST)) { + lj_cconv_ct_tv(cts, d, *(void **)cdataptr(cd), o, 0); + return 0; + } + } + } + lj_err_caller(L, LJ_ERR_FFI_WRCONST); + return 0; /* unreachable */ +} + +LJLIB_CF(ffi_clib___gc) +{ + TValue *o = L->base; + if (o < L->top && tvisudata(o) && udataV(o)->udtype == UDTYPE_FFI_CLIB) + lj_clib_unload((CLibrary *)uddata(udataV(o))); + return 0; +} + +#include "lj_libdef.h" + +/* -- Callback function metamethods --------------------------------------- */ + +#define LJLIB_MODULE_ffi_callback + +static int ffi_callback_set(lua_State *L, GCfunc *fn) +{ + GCcdata *cd = ffi_checkcdata(L, 1); + CTState *cts = ctype_cts(L); + CType *ct = ctype_raw(cts, cd->ctypeid); + if (ctype_isptr(ct->info) && (LJ_32 || ct->size == 8)) { + MSize slot = lj_ccallback_ptr2slot(cts, *(void **)cdataptr(cd)); + if (slot < cts->cb.sizeid && cts->cb.cbid[slot] != 0) { + GCtab *t = cts->miscmap; + TValue *tv = lj_tab_setint(L, t, (int32_t)slot); + if (fn) { + setfuncV(L, tv, fn); + lj_gc_anybarriert(L, t); + } else { + setnilV(tv); + cts->cb.cbid[slot] = 0; + cts->cb.topid = slot < cts->cb.topid ? slot : cts->cb.topid; + } + return 0; + } + } + lj_err_caller(L, LJ_ERR_FFI_BADCBACK); + return 0; +} + +LJLIB_CF(ffi_callback_free) +{ + return ffi_callback_set(L, NULL); +} + +LJLIB_CF(ffi_callback_set) +{ + GCfunc *fn = lj_lib_checkfunc(L, 2); + return ffi_callback_set(L, fn); +} + +LJLIB_PUSH(top-1) LJLIB_SET(__index) + +#include "lj_libdef.h" + +/* -- FFI library functions ----------------------------------------------- */ + +#define LJLIB_MODULE_ffi + +LJLIB_CF(ffi_cdef) +{ + GCstr *s = lj_lib_checkstr(L, 1); + CPState cp; + int errcode; + cp.L = L; + cp.cts = ctype_cts(L); + cp.srcname = strdata(s); + cp.p = strdata(s); + cp.param = L->base+1; + cp.mode = CPARSE_MODE_MULTI|CPARSE_MODE_DIRECT; + errcode = lj_cparse(&cp); + if (errcode) lj_err_throw(L, errcode); /* Propagate errors. */ + lj_gc_check(L); + return 0; +} + +LJLIB_CF(ffi_new) LJLIB_REC(.) +{ + CTState *cts = ctype_cts(L); + CTypeID id = ffi_checkctype(L, cts, NULL); + CType *ct = ctype_raw(cts, id); + CTSize sz; + CTInfo info = lj_ctype_info(cts, id, &sz); + TValue *o = L->base+1; + GCcdata *cd; + if ((info & CTF_VLA)) { + o++; + sz = lj_ctype_vlsize(cts, ct, (CTSize)ffi_checkint(L, 2)); + } + if (sz == CTSIZE_INVALID) + lj_err_arg(L, 1, LJ_ERR_FFI_INVSIZE); + cd = lj_cdata_newx(cts, id, sz, info); + setcdataV(L, o-1, cd); /* Anchor the uninitialized cdata. */ + lj_cconv_ct_init(cts, ct, sz, cdataptr(cd), + o, (MSize)(L->top - o)); /* Initialize cdata. */ + if (ctype_isstruct(ct->info)) { + /* Handle ctype __gc metamethod. Use the fast lookup here. */ + cTValue *tv = lj_tab_getinth(cts->miscmap, -(int32_t)id); + if (tv && tvistab(tv) && (tv = lj_meta_fast(L, tabV(tv), MM_gc))) { + GCtab *t = cts->finalizer; + if (gcref(t->metatable)) { + /* Add to finalizer table, if still enabled. */ + copyTV(L, lj_tab_set(L, t, o-1), tv); + lj_gc_anybarriert(L, t); + cd->marked |= LJ_GC_CDATA_FIN; + } + } + } + L->top = o; /* Only return the cdata itself. */ + lj_gc_check(L); + return 1; +} + +LJLIB_CF(ffi_cast) LJLIB_REC(ffi_new) +{ + CTState *cts = ctype_cts(L); + CTypeID id = ffi_checkctype(L, cts, NULL); + CType *d = ctype_raw(cts, id); + TValue *o = lj_lib_checkany(L, 2); + L->top = o+1; /* Make sure this is the last item on the stack. */ + if (!(ctype_isnum(d->info) || ctype_isptr(d->info) || ctype_isenum(d->info))) + lj_err_arg(L, 1, LJ_ERR_FFI_INVTYPE); + if (!(tviscdata(o) && cdataV(o)->ctypeid == id)) { + GCcdata *cd = lj_cdata_new(cts, id, d->size); + lj_cconv_ct_tv(cts, d, cdataptr(cd), o, CCF_CAST); + setcdataV(L, o, cd); + lj_gc_check(L); + } + return 1; +} + +LJLIB_CF(ffi_typeof) LJLIB_REC(.) +{ + CTState *cts = ctype_cts(L); + CTypeID id = ffi_checkctype(L, cts, L->base+1); + GCcdata *cd = lj_cdata_new(cts, CTID_CTYPEID, 4); + *(CTypeID *)cdataptr(cd) = id; + setcdataV(L, L->top-1, cd); + lj_gc_check(L); + return 1; +} + +/* Internal and unsupported API. */ +LJLIB_CF(ffi_typeinfo) +{ + CTState *cts = ctype_cts(L); + CTypeID id = (CTypeID)ffi_checkint(L, 1); + if (id > 0 && id < cts->top) { + CType *ct = ctype_get(cts, id); + GCtab *t; + lua_createtable(L, 0, 4); /* Increment hash size if fields are added. */ + t = tabV(L->top-1); + setintV(lj_tab_setstr(L, t, lj_str_newlit(L, "info")), (int32_t)ct->info); + if (ct->size != CTSIZE_INVALID) + setintV(lj_tab_setstr(L, t, lj_str_newlit(L, "size")), (int32_t)ct->size); + if (ct->sib) + setintV(lj_tab_setstr(L, t, lj_str_newlit(L, "sib")), (int32_t)ct->sib); + if (gcref(ct->name)) { + GCstr *s = gco2str(gcref(ct->name)); + setstrV(L, lj_tab_setstr(L, t, lj_str_newlit(L, "name")), s); + } + lj_gc_check(L); + return 1; + } + return 0; +} + +LJLIB_CF(ffi_istype) LJLIB_REC(.) +{ + CTState *cts = ctype_cts(L); + CTypeID id1 = ffi_checkctype(L, cts, NULL); + TValue *o = lj_lib_checkany(L, 2); + int b = 0; + if (tviscdata(o)) { + GCcdata *cd = cdataV(o); + CTypeID id2 = cd->ctypeid == CTID_CTYPEID ? *(CTypeID *)cdataptr(cd) : + cd->ctypeid; + CType *ct1 = lj_ctype_rawref(cts, id1); + CType *ct2 = lj_ctype_rawref(cts, id2); + if (ct1 == ct2) { + b = 1; + } else if (ctype_type(ct1->info) == ctype_type(ct2->info) && + ct1->size == ct2->size) { + if (ctype_ispointer(ct1->info)) + b = lj_cconv_compatptr(cts, ct1, ct2, CCF_IGNQUAL); + else if (ctype_isnum(ct1->info) || ctype_isvoid(ct1->info)) + b = (((ct1->info ^ ct2->info) & ~(CTF_QUAL|CTF_LONG)) == 0); + } else if (ctype_isstruct(ct1->info) && ctype_isptr(ct2->info) && + ct1 == ctype_rawchild(cts, ct2)) { + b = 1; + } + } + setboolV(L->top-1, b); + setboolV(&G(L)->tmptv2, b); /* Remember for trace recorder. */ + return 1; +} + +LJLIB_CF(ffi_sizeof) LJLIB_REC(ffi_xof FF_ffi_sizeof) +{ + CTState *cts = ctype_cts(L); + CTypeID id = ffi_checkctype(L, cts, NULL); + CTSize sz; + if (LJ_UNLIKELY(tviscdata(L->base) && cdataisv(cdataV(L->base)))) { + sz = cdatavlen(cdataV(L->base)); + } else { + CType *ct = lj_ctype_rawref(cts, id); + if (ctype_isvltype(ct->info)) + sz = lj_ctype_vlsize(cts, ct, (CTSize)ffi_checkint(L, 2)); + else + sz = ctype_hassize(ct->info) ? ct->size : CTSIZE_INVALID; + if (LJ_UNLIKELY(sz == CTSIZE_INVALID)) { + setnilV(L->top-1); + return 1; + } + } + setintV(L->top-1, (int32_t)sz); + return 1; +} + +LJLIB_CF(ffi_alignof) LJLIB_REC(ffi_xof FF_ffi_alignof) +{ + CTState *cts = ctype_cts(L); + CTypeID id = ffi_checkctype(L, cts, NULL); + CTSize sz = 0; + CTInfo info = lj_ctype_info(cts, id, &sz); + setintV(L->top-1, 1 << ctype_align(info)); + return 1; +} + +LJLIB_CF(ffi_offsetof) LJLIB_REC(ffi_xof FF_ffi_offsetof) +{ + CTState *cts = ctype_cts(L); + CTypeID id = ffi_checkctype(L, cts, NULL); + GCstr *name = lj_lib_checkstr(L, 2); + CType *ct = lj_ctype_rawref(cts, id); + CTSize ofs; + if (ctype_isstruct(ct->info) && ct->size != CTSIZE_INVALID) { + CType *fct = lj_ctype_getfield(cts, ct, name, &ofs); + if (fct) { + setintV(L->top-1, ofs); + if (ctype_isfield(fct->info)) { + return 1; + } else if (ctype_isbitfield(fct->info)) { + setintV(L->top++, ctype_bitpos(fct->info)); + setintV(L->top++, ctype_bitbsz(fct->info)); + return 3; + } + } + } + return 0; +} + +LJLIB_CF(ffi_errno) LJLIB_REC(.) +{ + int err = errno; + if (L->top > L->base) + errno = ffi_checkint(L, 1); + setintV(L->top++, err); + return 1; +} + +LJLIB_CF(ffi_string) LJLIB_REC(.) +{ + CTState *cts = ctype_cts(L); + TValue *o = lj_lib_checkany(L, 1); + const char *p; + size_t len; + if (o+1 < L->top && !tvisnil(o+1)) { + len = (size_t)ffi_checkint(L, 2); + lj_cconv_ct_tv(cts, ctype_get(cts, CTID_P_CVOID), (uint8_t *)&p, o, + CCF_ARG(1)); + } else { + lj_cconv_ct_tv(cts, ctype_get(cts, CTID_P_CCHAR), (uint8_t *)&p, o, + CCF_ARG(1)); + len = strlen(p); + } + L->top = o+1; /* Make sure this is the last item on the stack. */ + setstrV(L, o, lj_str_new(L, p, len)); + lj_gc_check(L); + return 1; +} + +LJLIB_CF(ffi_copy) LJLIB_REC(.) +{ + void *dp = ffi_checkptr(L, 1, CTID_P_VOID); + void *sp = ffi_checkptr(L, 2, CTID_P_CVOID); + TValue *o = L->base+1; + CTSize len; + if (tvisstr(o) && o+1 >= L->top) + len = strV(o)->len+1; /* Copy Lua string including trailing '\0'. */ + else + len = (CTSize)ffi_checkint(L, 3); + memcpy(dp, sp, len); + return 0; +} + +LJLIB_CF(ffi_fill) LJLIB_REC(.) +{ + void *dp = ffi_checkptr(L, 1, CTID_P_VOID); + CTSize len = (CTSize)ffi_checkint(L, 2); + int32_t fill = 0; + if (L->base+2 < L->top && !tvisnil(L->base+2)) fill = ffi_checkint(L, 3); + memset(dp, fill, len); + return 0; +} + +#define H_(le, be) LJ_ENDIAN_SELECT(0x##le, 0x##be) + +/* Test ABI string. */ +LJLIB_CF(ffi_abi) LJLIB_REC(.) +{ + GCstr *s = lj_lib_checkstr(L, 1); + int b = 0; + switch (s->hash) { +#if LJ_64 + case H_(849858eb,ad35fd06): b = 1; break; /* 64bit */ +#else + case H_(662d3c79,d0e22477): b = 1; break; /* 32bit */ +#endif +#if LJ_ARCH_HASFPU + case H_(e33ee463,e33ee463): b = 1; break; /* fpu */ +#endif +#if LJ_ABI_SOFTFP + case H_(61211a23,c2e8c81c): b = 1; break; /* softfp */ +#else + case H_(539417a8,8ce0812f): b = 1; break; /* hardfp */ +#endif +#if LJ_ABI_EABI + case H_(2182df8f,f2ed1152): b = 1; break; /* eabi */ +#endif +#if LJ_ABI_WIN + case H_(4ab624a8,4ab624a8): b = 1; break; /* win */ +#endif +#if LJ_TARGET_UWP + case H_(a40f0bcb,a40f0bcb): b = 1; break; /* uwp */ +#endif + case H_(3af93066,1f001464): b = 1; break; /* le/be */ +#if LJ_GC64 + case H_(9e89d2c9,13c83c92): b = 1; break; /* gc64 */ +#endif + default: + break; + } + setboolV(L->top-1, b); + setboolV(&G(L)->tmptv2, b); /* Remember for trace recorder. */ + return 1; +} + +#undef H_ + +LJLIB_PUSH(top-8) LJLIB_SET(!) /* Store reference to miscmap table. */ + +LJLIB_CF(ffi_metatype) +{ + CTState *cts = ctype_cts(L); + CTypeID id = ffi_checkctype(L, cts, NULL); + GCtab *mt = lj_lib_checktab(L, 2); + GCtab *t = cts->miscmap; + CType *ct = ctype_get(cts, id); /* Only allow raw types. */ + TValue *tv; + GCcdata *cd; + if (!(ctype_isstruct(ct->info) || ctype_iscomplex(ct->info) || + ctype_isvector(ct->info))) + lj_err_arg(L, 1, LJ_ERR_FFI_INVTYPE); + tv = lj_tab_setinth(L, t, -(int32_t)id); + if (!tvisnil(tv)) + lj_err_caller(L, LJ_ERR_PROTMT); + settabV(L, tv, mt); + lj_gc_anybarriert(L, t); + cd = lj_cdata_new(cts, CTID_CTYPEID, 4); + *(CTypeID *)cdataptr(cd) = id; + setcdataV(L, L->top-1, cd); + lj_gc_check(L); + return 1; +} + +LJLIB_PUSH(top-7) LJLIB_SET(!) /* Store reference to finalizer table. */ + +LJLIB_CF(ffi_gc) LJLIB_REC(.) +{ + GCcdata *cd = ffi_checkcdata(L, 1); + TValue *fin = lj_lib_checkany(L, 2); + CTState *cts = ctype_cts(L); + CType *ct = ctype_raw(cts, cd->ctypeid); + if (!(ctype_isptr(ct->info) || ctype_isstruct(ct->info) || + ctype_isrefarray(ct->info))) + lj_err_arg(L, 1, LJ_ERR_FFI_INVTYPE); + lj_cdata_setfin(L, cd, gcval(fin), itype(fin)); + L->top = L->base+1; /* Pass through the cdata object. */ + return 1; +} + +LJLIB_PUSH(top-5) LJLIB_SET(!) /* Store clib metatable in func environment. */ + +LJLIB_CF(ffi_load) +{ + GCstr *name = lj_lib_checkstr(L, 1); + int global = (L->base+1 < L->top && tvistruecond(L->base+1)); + lj_clib_load(L, tabref(curr_func(L)->c.env), name, global); + return 1; +} + +LJLIB_PUSH(top-4) LJLIB_SET(C) +LJLIB_PUSH(top-3) LJLIB_SET(os) +LJLIB_PUSH(top-2) LJLIB_SET(arch) + +#include "lj_libdef.h" + +/* ------------------------------------------------------------------------ */ + +/* Create special weak-keyed finalizer table. */ +static GCtab *ffi_finalizer(lua_State *L) +{ + /* NOBARRIER: The table is new (marked white). */ + GCtab *t = lj_tab_new(L, 0, 1); + settabV(L, L->top++, t); + setgcref(t->metatable, obj2gco(t)); + setstrV(L, lj_tab_setstr(L, t, lj_str_newlit(L, "__mode")), + lj_str_newlit(L, "k")); + t->nomm = (uint8_t)(~(1u<top-1); + lj_gc_anybarriert(L, t); + } +} + +LUALIB_API int luaopen_ffi(lua_State *L) +{ + CTState *cts = lj_ctype_init(L); + settabV(L, L->top++, (cts->miscmap = lj_tab_new(L, 0, 1))); + cts->finalizer = ffi_finalizer(L); + LJ_LIB_REG(L, NULL, ffi_meta); + /* NOBARRIER: basemt is a GC root. */ + setgcref(basemt_it(G(L), LJ_TCDATA), obj2gco(tabV(L->top-1))); + LJ_LIB_REG(L, NULL, ffi_clib); + LJ_LIB_REG(L, NULL, ffi_callback); + /* NOBARRIER: the key is new and lj_tab_newkey() handles the barrier. */ + settabV(L, lj_tab_setstr(L, cts->miscmap, &cts->g->strempty), tabV(L->top-1)); + L->top--; + lj_clib_default(L, tabV(L->top-1)); /* Create ffi.C default namespace. */ + lua_pushliteral(L, LJ_OS_NAME); + lua_pushliteral(L, LJ_ARCH_NAME); + LJ_LIB_REG(L, NULL, ffi); /* Note: no global "ffi" created! */ + ffi_register_module(L); + return 1; +} + +#endif diff --git a/lib/LuaJIT/lib_ffi.o b/lib/LuaJIT/lib_ffi.o new file mode 100644 index 0000000000000000000000000000000000000000..3c160df6693244aa8e71b16234af5d8c99856979 GIT binary patch literal 22984 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 Bnvq|`ZcE?x6{MC&RAAg<D{jAt5i?;-IT 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 Bnfunc; lib++) { + lua_pushcfunction(L, lib->func); + lua_pushstring(L, lib->name); + lua_call(L, 1, 0); + } + luaL_findtable(L, LUA_REGISTRYINDEX, "_PRELOAD", + sizeof(lj_lib_preload)/sizeof(lj_lib_preload[0])-1); + for (lib = lj_lib_preload; lib->func; lib++) { + lua_pushcfunction(L, lib->func); + lua_setfield(L, -2, lib->name); + } + lua_pop(L, 1); +} + diff --git a/lib/LuaJIT/lib_init.o b/lib/LuaJIT/lib_init.o new file mode 100644 index 0000000000000000000000000000000000000000..ef22e50f49646082b1669456380e11bb34deaf92 GIT binary patch literal 3496 zcmdUxO>7%g5XZ-HOAWM+DPJiCYE^(z1gjVU0)&v=)X5^OCQ4cfsY1)zUdP+6y_R<^ zg$fCzl#eP@>J4rj;7+ex$^ouLsHYwgy#xt~!hsVI=D+*Kep&Bc;KWGJGxK}%=DmHp zZ=T<)H*kfmfXuC53g-3;FgvryERP?AQaeSRj!QJKTNmsNheEsQIpq4}2NZ-ux4%I>JM2>1 zX^u9i>4tQk+y76N7Z+bC&o7@od!qcZQ85&VkRnirdMk(!=2`p{{AYNk9*Z_{zkb6DGlXpfoUy_BLk z_Wmqo@bwJd&fq}?e>a0~XYfxm_?H>{s|@~42EUQPZ)Nb?8T|JQjt?_!pAYeeo5qi1 zaPr)y<@VjsXpwi`g6H{mn~cWd0)8swjqHZy51c;t1!OHZR!-I$*2>b-di{)brgpMX zw-AE}t7!)g9BVKNx~-Py2czKxsB(nW+ z^1B+BH3AJZj{Ju5H#HvD>=?YSal}8b{Es#MxU!+HY8>&hFP~{#_5i4?IKD%)u^7QS zJwAP8@bQXuSFEAq*+%GWhGewu&?ci91Y`_-I0&y86?PF65w^IKjF#W)Iei|~_e00v z8~@oLr7lr#EO;oyZ*E4Jtxc%Gl6&{P6&vDa5=6_X(Y|H$j>xw1km*>8g z7++5KRrb^NZ-64p@}rsv`-d&XvqEJ5*4ReK0-z>8sBx&Jy2!dkpW{xk^|+tXU`i~` zDMI(03HEo?_$u4F$ohp{U?c7MSJhuSc}_e(>c5N^o;Ejf&&z_;i$Og`OOD4h$#=(y c=1~+_o=ogR!j0~*wDDcF{)=&%@o#ecKWoH$7ytkO literal 0 HcmV?d00001 diff --git a/lib/LuaJIT/lib_init_dyn.o b/lib/LuaJIT/lib_init_dyn.o new file mode 100644 index 0000000000000000000000000000000000000000..ef22e50f49646082b1669456380e11bb34deaf92 GIT binary patch literal 3496 zcmdUxO>7%g5XZ-HOAWM+DPJiCYE^(z1gjVU0)&v=)X5^OCQ4cfsY1)zUdP+6y_R<^ zg$fCzl#eP@>J4rj;7+ex$^ouLsHYwgy#xt~!hsVI=D+*Kep&Bc;KWGJGxK}%=DmHp zZ=T<)H*kfmfXuC53g-3;FgvryERP?AQaeSRj!QJKTNmsNheEsQIpq4}2NZ-ux4%I>JM2>1 zX^u9i>4tQk+y76N7Z+bC&o7@od!qcZQ85&VkRnirdMk(!=2`p{{AYNk9*Z_{zkb6DGlXpfoUy_BLk z_Wmqo@bwJd&fq}?e>a0~XYfxm_?H>{s|@~42EUQPZ)Nb?8T|JQjt?_!pAYeeo5qi1 zaPr)y<@VjsXpwi`g6H{mn~cWd0)8swjqHZy51c;t1!OHZR!-I$*2>b-di{)brgpMX zw-AE}t7!)g9BVKNx~-Py2czKxsB(nW+ z^1B+BH3AJZj{Ju5H#HvD>=?YSal}8b{Es#MxU!+HY8>&hFP~{#_5i4?IKD%)u^7QS zJwAP8@bQXuSFEAq*+%GWhGewu&?ci91Y`_-I0&y86?PF65w^IKjF#W)Iei|~_e00v z8~@oLr7lr#EO;oyZ*E4Jtxc%Gl6&{P6&vDa5=6_X(Y|H$j>xw1km*>8g z7++5KRrb^NZ-64p@}rsv`-d&XvqEJ5*4ReK0-z>8sBx&Jy2!dkpW{xk^|+tXU`i~` zDMI(03HEo?_$u4F$ohp{U?c7MSJhuSc}_e(>c5N^o;Ejf&&z_;i$Og`OOD4h$#=(y c=1~+_o=ogR!j0~*wDDcF{)=&%@o#ecKWoH$7ytkO literal 0 HcmV?d00001 diff --git a/lib/LuaJIT/lib_io.c b/lib/LuaJIT/lib_io.c new file mode 100644 index 0000000..73fd932 --- /dev/null +++ b/lib/LuaJIT/lib_io.c @@ -0,0 +1,539 @@ +/* +** I/O library. +** Copyright (C) 2005-2017 Mike Pall. See Copyright Notice in luajit.h +** +** Major portions taken verbatim or adapted from the Lua interpreter. +** Copyright (C) 1994-2011 Lua.org, PUC-Rio. See Copyright Notice in lua.h +*/ + +#include +#include + +#define lib_io_c +#define LUA_LIB + +#include "lua.h" +#include "lauxlib.h" +#include "lualib.h" + +#include "lj_obj.h" +#include "lj_gc.h" +#include "lj_err.h" +#include "lj_buf.h" +#include "lj_str.h" +#include "lj_state.h" +#include "lj_strfmt.h" +#include "lj_ff.h" +#include "lj_lib.h" + +/* Userdata payload for I/O file. */ +typedef struct IOFileUD { + FILE *fp; /* File handle. */ + uint32_t type; /* File type. */ +} IOFileUD; + +#define IOFILE_TYPE_FILE 0 /* Regular file. */ +#define IOFILE_TYPE_PIPE 1 /* Pipe. */ +#define IOFILE_TYPE_STDF 2 /* Standard file handle. */ +#define IOFILE_TYPE_MASK 3 + +#define IOFILE_FLAG_CLOSE 4 /* Close after io.lines() iterator. */ + +#define IOSTDF_UD(L, id) (&gcref(G(L)->gcroot[(id)])->ud) +#define IOSTDF_IOF(L, id) ((IOFileUD *)uddata(IOSTDF_UD(L, (id)))) + +/* -- Open/close helpers -------------------------------------------------- */ + +static IOFileUD *io_tofilep(lua_State *L) +{ + if (!(L->base < L->top && tvisudata(L->base) && + udataV(L->base)->udtype == UDTYPE_IO_FILE)) + lj_err_argtype(L, 1, "FILE*"); + return (IOFileUD *)uddata(udataV(L->base)); +} + +static IOFileUD *io_tofile(lua_State *L) +{ + IOFileUD *iof = io_tofilep(L); + if (iof->fp == NULL) + lj_err_caller(L, LJ_ERR_IOCLFL); + return iof; +} + +static FILE *io_stdfile(lua_State *L, ptrdiff_t id) +{ + IOFileUD *iof = IOSTDF_IOF(L, id); + if (iof->fp == NULL) + lj_err_caller(L, LJ_ERR_IOSTDCL); + return iof->fp; +} + +static IOFileUD *io_file_new(lua_State *L) +{ + IOFileUD *iof = (IOFileUD *)lua_newuserdata(L, sizeof(IOFileUD)); + GCudata *ud = udataV(L->top-1); + ud->udtype = UDTYPE_IO_FILE; + /* NOBARRIER: The GCudata is new (marked white). */ + setgcrefr(ud->metatable, curr_func(L)->c.env); + iof->fp = NULL; + iof->type = IOFILE_TYPE_FILE; + return iof; +} + +static IOFileUD *io_file_open(lua_State *L, const char *mode) +{ + const char *fname = strdata(lj_lib_checkstr(L, 1)); + IOFileUD *iof = io_file_new(L); + iof->fp = fopen(fname, mode); + if (iof->fp == NULL) + luaL_argerror(L, 1, lj_strfmt_pushf(L, "%s: %s", fname, strerror(errno))); + return iof; +} + +static int io_file_close(lua_State *L, IOFileUD *iof) +{ + int ok; + if ((iof->type & IOFILE_TYPE_MASK) == IOFILE_TYPE_FILE) { + ok = (fclose(iof->fp) == 0); + } else if ((iof->type & IOFILE_TYPE_MASK) == IOFILE_TYPE_PIPE) { + int stat = -1; +#if LJ_TARGET_POSIX + stat = pclose(iof->fp); +#elif LJ_TARGET_WINDOWS && !LJ_TARGET_XBOXONE && !LJ_TARGET_UWP + stat = _pclose(iof->fp); +#else + lua_assert(0); + return 0; +#endif +#if LJ_52 + iof->fp = NULL; + return luaL_execresult(L, stat); +#else + ok = (stat != -1); +#endif + } else { + lua_assert((iof->type & IOFILE_TYPE_MASK) == IOFILE_TYPE_STDF); + setnilV(L->top++); + lua_pushliteral(L, "cannot close standard file"); + return 2; + } + iof->fp = NULL; + return luaL_fileresult(L, ok, NULL); +} + +/* -- Read/write helpers -------------------------------------------------- */ + +static int io_file_readnum(lua_State *L, FILE *fp) +{ + lua_Number d; + if (fscanf(fp, LUA_NUMBER_SCAN, &d) == 1) { + if (LJ_DUALNUM) { + int32_t i = lj_num2int(d); + if (d == (lua_Number)i && !tvismzero((cTValue *)&d)) { + setintV(L->top++, i); + return 1; + } + } + setnumV(L->top++, d); + return 1; + } else { + setnilV(L->top++); + return 0; + } +} + +static int io_file_readline(lua_State *L, FILE *fp, MSize chop) +{ + MSize m = LUAL_BUFFERSIZE, n = 0, ok = 0; + char *buf; + for (;;) { + buf = lj_buf_tmp(L, m); + if (fgets(buf+n, m-n, fp) == NULL) break; + n += (MSize)strlen(buf+n); + ok |= n; + if (n && buf[n-1] == '\n') { n -= chop; break; } + if (n >= m - 64) m += m; + } + setstrV(L, L->top++, lj_str_new(L, buf, (size_t)n)); + lj_gc_check(L); + return (int)ok; +} + +static void io_file_readall(lua_State *L, FILE *fp) +{ + MSize m, n; + for (m = LUAL_BUFFERSIZE, n = 0; ; m += m) { + char *buf = lj_buf_tmp(L, m); + n += (MSize)fread(buf+n, 1, m-n, fp); + if (n != m) { + setstrV(L, L->top++, lj_str_new(L, buf, (size_t)n)); + lj_gc_check(L); + return; + } + } +} + +static int io_file_readlen(lua_State *L, FILE *fp, MSize m) +{ + if (m) { + char *buf = lj_buf_tmp(L, m); + MSize n = (MSize)fread(buf, 1, m, fp); + setstrV(L, L->top++, lj_str_new(L, buf, (size_t)n)); + lj_gc_check(L); + return (n > 0 || m == 0); + } else { + int c = getc(fp); + ungetc(c, fp); + setstrV(L, L->top++, &G(L)->strempty); + return (c != EOF); + } +} + +static int io_file_read(lua_State *L, FILE *fp, int start) +{ + int ok, n, nargs = (int)(L->top - L->base) - start; + clearerr(fp); + if (nargs == 0) { + ok = io_file_readline(L, fp, 1); + n = start+1; /* Return 1 result. */ + } else { + /* The results plus the buffers go on top of the args. */ + luaL_checkstack(L, nargs+LUA_MINSTACK, "too many arguments"); + ok = 1; + for (n = start; nargs-- && ok; n++) { + if (tvisstr(L->base+n)) { + const char *p = strVdata(L->base+n); + if (p[0] == '*') p++; + if (p[0] == 'n') + ok = io_file_readnum(L, fp); + else if ((p[0] & ~0x20) == 'L') + ok = io_file_readline(L, fp, (p[0] == 'l')); + else if (p[0] == 'a') + io_file_readall(L, fp); + else + lj_err_arg(L, n+1, LJ_ERR_INVFMT); + } else if (tvisnumber(L->base+n)) { + ok = io_file_readlen(L, fp, (MSize)lj_lib_checkint(L, n+1)); + } else { + lj_err_arg(L, n+1, LJ_ERR_INVOPT); + } + } + } + if (ferror(fp)) + return luaL_fileresult(L, 0, NULL); + if (!ok) + setnilV(L->top-1); /* Replace last result with nil. */ + return n - start; +} + +static int io_file_write(lua_State *L, FILE *fp, int start) +{ + cTValue *tv; + int status = 1; + for (tv = L->base+start; tv < L->top; tv++) { + MSize len; + const char *p = lj_strfmt_wstrnum(L, tv, &len); + if (!p) + lj_err_argt(L, (int)(tv - L->base) + 1, LUA_TSTRING); + status = status && (fwrite(p, 1, len, fp) == len); + } + if (LJ_52 && status) { + L->top = L->base+1; + if (start == 0) + setudataV(L, L->base, IOSTDF_UD(L, GCROOT_IO_OUTPUT)); + return 1; + } + return luaL_fileresult(L, status, NULL); +} + +static int io_file_iter(lua_State *L) +{ + GCfunc *fn = curr_func(L); + IOFileUD *iof = uddata(udataV(&fn->c.upvalue[0])); + int n = fn->c.nupvalues - 1; + if (iof->fp == NULL) + lj_err_caller(L, LJ_ERR_IOCLFL); + L->top = L->base; + if (n) { /* Copy upvalues with options to stack. */ + if (n > LUAI_MAXCSTACK) + lj_err_caller(L, LJ_ERR_STKOV); + lj_state_checkstack(L, (MSize)n); + memcpy(L->top, &fn->c.upvalue[1], n*sizeof(TValue)); + L->top += n; + } + n = io_file_read(L, iof->fp, 0); + if (ferror(iof->fp)) + lj_err_callermsg(L, strVdata(L->top-2)); + if (tvisnil(L->base) && (iof->type & IOFILE_FLAG_CLOSE)) { + io_file_close(L, iof); /* Return values are ignored. */ + return 0; + } + return n; +} + +static int io_file_lines(lua_State *L) +{ + int n = (int)(L->top - L->base); + if (n > LJ_MAX_UPVAL) + lj_err_caller(L, LJ_ERR_UNPACK); + lua_pushcclosure(L, io_file_iter, n); + return 1; +} + +/* -- I/O file methods ---------------------------------------------------- */ + +#define LJLIB_MODULE_io_method + +LJLIB_CF(io_method_close) +{ + IOFileUD *iof = L->base < L->top ? io_tofile(L) : + IOSTDF_IOF(L, GCROOT_IO_OUTPUT); + return io_file_close(L, iof); +} + +LJLIB_CF(io_method_read) +{ + return io_file_read(L, io_tofile(L)->fp, 1); +} + +LJLIB_CF(io_method_write) LJLIB_REC(io_write 0) +{ + return io_file_write(L, io_tofile(L)->fp, 1); +} + +LJLIB_CF(io_method_flush) LJLIB_REC(io_flush 0) +{ + return luaL_fileresult(L, fflush(io_tofile(L)->fp) == 0, NULL); +} + +LJLIB_CF(io_method_seek) +{ + FILE *fp = io_tofile(L)->fp; + int opt = lj_lib_checkopt(L, 2, 1, "\3set\3cur\3end"); + int64_t ofs = 0; + cTValue *o; + int res; + if (opt == 0) opt = SEEK_SET; + else if (opt == 1) opt = SEEK_CUR; + else if (opt == 2) opt = SEEK_END; + o = L->base+2; + if (o < L->top) { + if (tvisint(o)) + ofs = (int64_t)intV(o); + else if (tvisnum(o)) + ofs = (int64_t)numV(o); + else if (!tvisnil(o)) + lj_err_argt(L, 3, LUA_TNUMBER); + } +#if LJ_TARGET_POSIX + res = fseeko(fp, ofs, opt); +#elif _MSC_VER >= 1400 + res = _fseeki64(fp, ofs, opt); +#elif defined(__MINGW32__) + res = fseeko64(fp, ofs, opt); +#else + res = fseek(fp, (long)ofs, opt); +#endif + if (res) + return luaL_fileresult(L, 0, NULL); +#if LJ_TARGET_POSIX + ofs = ftello(fp); +#elif _MSC_VER >= 1400 + ofs = _ftelli64(fp); +#elif defined(__MINGW32__) + ofs = ftello64(fp); +#else + ofs = (int64_t)ftell(fp); +#endif + setint64V(L->top-1, ofs); + return 1; +} + +LJLIB_CF(io_method_setvbuf) +{ + FILE *fp = io_tofile(L)->fp; + int opt = lj_lib_checkopt(L, 2, -1, "\4full\4line\2no"); + size_t sz = (size_t)lj_lib_optint(L, 3, LUAL_BUFFERSIZE); + if (opt == 0) opt = _IOFBF; + else if (opt == 1) opt = _IOLBF; + else if (opt == 2) opt = _IONBF; + return luaL_fileresult(L, setvbuf(fp, NULL, opt, sz) == 0, NULL); +} + +LJLIB_CF(io_method_lines) +{ + io_tofile(L); + return io_file_lines(L); +} + +LJLIB_CF(io_method___gc) +{ + IOFileUD *iof = io_tofilep(L); + if (iof->fp != NULL && (iof->type & IOFILE_TYPE_MASK) != IOFILE_TYPE_STDF) + io_file_close(L, iof); + return 0; +} + +LJLIB_CF(io_method___tostring) +{ + IOFileUD *iof = io_tofilep(L); + if (iof->fp != NULL) + lua_pushfstring(L, "file (%p)", iof->fp); + else + lua_pushliteral(L, "file (closed)"); + return 1; +} + +LJLIB_PUSH(top-1) LJLIB_SET(__index) + +#include "lj_libdef.h" + +/* -- I/O library functions ----------------------------------------------- */ + +#define LJLIB_MODULE_io + +LJLIB_PUSH(top-2) LJLIB_SET(!) /* Set environment. */ + +LJLIB_CF(io_open) +{ + const char *fname = strdata(lj_lib_checkstr(L, 1)); + GCstr *s = lj_lib_optstr(L, 2); + const char *mode = s ? strdata(s) : "r"; + IOFileUD *iof = io_file_new(L); + iof->fp = fopen(fname, mode); + return iof->fp != NULL ? 1 : luaL_fileresult(L, 0, fname); +} + +LJLIB_CF(io_popen) +{ +#if LJ_TARGET_POSIX || (LJ_TARGET_WINDOWS && !LJ_TARGET_XBOXONE && !LJ_TARGET_UWP) + const char *fname = strdata(lj_lib_checkstr(L, 1)); + GCstr *s = lj_lib_optstr(L, 2); + const char *mode = s ? strdata(s) : "r"; + IOFileUD *iof = io_file_new(L); + iof->type = IOFILE_TYPE_PIPE; +#if LJ_TARGET_POSIX + fflush(NULL); + iof->fp = popen(fname, mode); +#else + iof->fp = _popen(fname, mode); +#endif + return iof->fp != NULL ? 1 : luaL_fileresult(L, 0, fname); +#else + return luaL_error(L, LUA_QL("popen") " not supported"); +#endif +} + +LJLIB_CF(io_tmpfile) +{ + IOFileUD *iof = io_file_new(L); +#if LJ_TARGET_PS3 || LJ_TARGET_PS4 || LJ_TARGET_PSVITA + iof->fp = NULL; errno = ENOSYS; +#else + iof->fp = tmpfile(); +#endif + return iof->fp != NULL ? 1 : luaL_fileresult(L, 0, NULL); +} + +LJLIB_CF(io_close) +{ + return lj_cf_io_method_close(L); +} + +LJLIB_CF(io_read) +{ + return io_file_read(L, io_stdfile(L, GCROOT_IO_INPUT), 0); +} + +LJLIB_CF(io_write) LJLIB_REC(io_write GCROOT_IO_OUTPUT) +{ + return io_file_write(L, io_stdfile(L, GCROOT_IO_OUTPUT), 0); +} + +LJLIB_CF(io_flush) LJLIB_REC(io_flush GCROOT_IO_OUTPUT) +{ + return luaL_fileresult(L, fflush(io_stdfile(L, GCROOT_IO_OUTPUT)) == 0, NULL); +} + +static int io_std_getset(lua_State *L, ptrdiff_t id, const char *mode) +{ + if (L->base < L->top && !tvisnil(L->base)) { + if (tvisudata(L->base)) { + io_tofile(L); + L->top = L->base+1; + } else { + io_file_open(L, mode); + } + /* NOBARRIER: The standard I/O handles are GC roots. */ + setgcref(G(L)->gcroot[id], gcV(L->top-1)); + } else { + setudataV(L, L->top++, IOSTDF_UD(L, id)); + } + return 1; +} + +LJLIB_CF(io_input) +{ + return io_std_getset(L, GCROOT_IO_INPUT, "r"); +} + +LJLIB_CF(io_output) +{ + return io_std_getset(L, GCROOT_IO_OUTPUT, "w"); +} + +LJLIB_CF(io_lines) +{ + if (L->base == L->top) setnilV(L->top++); + if (!tvisnil(L->base)) { /* io.lines(fname) */ + IOFileUD *iof = io_file_open(L, "r"); + iof->type = IOFILE_TYPE_FILE|IOFILE_FLAG_CLOSE; + L->top--; + setudataV(L, L->base, udataV(L->top)); + } else { /* io.lines() iterates over stdin. */ + setudataV(L, L->base, IOSTDF_UD(L, GCROOT_IO_INPUT)); + } + return io_file_lines(L); +} + +LJLIB_CF(io_type) +{ + cTValue *o = lj_lib_checkany(L, 1); + if (!(tvisudata(o) && udataV(o)->udtype == UDTYPE_IO_FILE)) + setnilV(L->top++); + else if (((IOFileUD *)uddata(udataV(o)))->fp != NULL) + lua_pushliteral(L, "file"); + else + lua_pushliteral(L, "closed file"); + return 1; +} + +#include "lj_libdef.h" + +/* ------------------------------------------------------------------------ */ + +static GCobj *io_std_new(lua_State *L, FILE *fp, const char *name) +{ + IOFileUD *iof = (IOFileUD *)lua_newuserdata(L, sizeof(IOFileUD)); + GCudata *ud = udataV(L->top-1); + ud->udtype = UDTYPE_IO_FILE; + /* NOBARRIER: The GCudata is new (marked white). */ + setgcref(ud->metatable, gcV(L->top-3)); + iof->fp = fp; + iof->type = IOFILE_TYPE_STDF; + lua_setfield(L, -2, name); + return obj2gco(ud); +} + +LUALIB_API int luaopen_io(lua_State *L) +{ + LJ_LIB_REG(L, NULL, io_method); + copyTV(L, L->top, L->top-1); L->top++; + lua_setfield(L, LUA_REGISTRYINDEX, LUA_FILEHANDLE); + LJ_LIB_REG(L, LUA_IOLIBNAME, io); + setgcref(G(L)->gcroot[GCROOT_IO_INPUT], io_std_new(L, stdin, "stdin")); + setgcref(G(L)->gcroot[GCROOT_IO_OUTPUT], io_std_new(L, stdout, "stdout")); + io_std_new(L, stderr, "stderr"); + return 1; +} + diff --git a/lib/LuaJIT/lib_io.o b/lib/LuaJIT/lib_io.o new file mode 100644 index 0000000000000000000000000000000000000000..bb39dc22b7e1d8345dc0ae2a6e7547fe831f4329 GIT binary patch literal 15360 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 literal 0 HcmV?d00001 diff --git a/lib/LuaJIT/lib_io_dyn.o b/lib/LuaJIT/lib_io_dyn.o new file mode 100644 index 0000000000000000000000000000000000000000..b04bc8c45154dd4e791fcd19eb04be5fa2628894 GIT binary patch literal 15360 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 == L->top || tvisnil(L->base)) { /* jit.on/off/flush([nil]) */ + mode |= LUAJIT_MODE_ENGINE; + } else { + /* jit.on/off/flush(func|proto, nil|true|false) */ + if (tvisfunc(L->base) || tvisproto(L->base)) + idx = 1; + else if (!tvistrue(L->base)) /* jit.on/off/flush(true, nil|true|false) */ + goto err; + if (L->base+1 < L->top && tvisbool(L->base+1)) + mode |= boolV(L->base+1) ? LUAJIT_MODE_ALLFUNC : LUAJIT_MODE_ALLSUBFUNC; + else + mode |= LUAJIT_MODE_FUNC; + } + if (luaJIT_setmode(L, idx, mode) != 1) { + if ((mode & LUAJIT_MODE_MASK) == LUAJIT_MODE_ENGINE) + lj_err_caller(L, LJ_ERR_NOJIT); + err: + lj_err_argt(L, 1, LUA_TFUNCTION); + } + return 0; +} + +LJLIB_CF(jit_on) +{ + return setjitmode(L, LUAJIT_MODE_ON); +} + +LJLIB_CF(jit_off) +{ + return setjitmode(L, LUAJIT_MODE_OFF); +} + +LJLIB_CF(jit_flush) +{ +#if LJ_HASJIT + if (L->base < L->top && tvisnumber(L->base)) { + int traceno = lj_lib_checkint(L, 1); + luaJIT_setmode(L, traceno, LUAJIT_MODE_FLUSH|LUAJIT_MODE_TRACE); + return 0; + } +#endif + return setjitmode(L, LUAJIT_MODE_FLUSH); +} + +#if LJ_HASJIT +/* Push a string for every flag bit that is set. */ +static void flagbits_to_strings(lua_State *L, uint32_t flags, uint32_t base, + const char *str) +{ + for (; *str; base <<= 1, str += 1+*str) + if (flags & base) + setstrV(L, L->top++, lj_str_new(L, str+1, *(uint8_t *)str)); +} +#endif + +LJLIB_CF(jit_status) +{ +#if LJ_HASJIT + jit_State *J = L2J(L); + L->top = L->base; + setboolV(L->top++, (J->flags & JIT_F_ON) ? 1 : 0); + flagbits_to_strings(L, J->flags, JIT_F_CPU_FIRST, JIT_F_CPUSTRING); + flagbits_to_strings(L, J->flags, JIT_F_OPT_FIRST, JIT_F_OPTSTRING); + return (int)(L->top - L->base); +#else + setboolV(L->top++, 0); + return 1; +#endif +} + +LJLIB_CF(jit_attach) +{ +#ifdef LUAJIT_DISABLE_VMEVENT + luaL_error(L, "vmevent API disabled"); +#else + GCfunc *fn = lj_lib_checkfunc(L, 1); + GCstr *s = lj_lib_optstr(L, 2); + luaL_findtable(L, LUA_REGISTRYINDEX, LJ_VMEVENTS_REGKEY, LJ_VMEVENTS_HSIZE); + if (s) { /* Attach to given event. */ + const uint8_t *p = (const uint8_t *)strdata(s); + uint32_t h = s->len; + while (*p) h = h ^ (lj_rol(h, 6) + *p++); + lua_pushvalue(L, 1); + lua_rawseti(L, -2, VMEVENT_HASHIDX(h)); + G(L)->vmevmask = VMEVENT_NOCACHE; /* Invalidate cache. */ + } else { /* Detach if no event given. */ + setnilV(L->top++); + while (lua_next(L, -2)) { + L->top--; + if (tvisfunc(L->top) && funcV(L->top) == fn) { + setnilV(lj_tab_set(L, tabV(L->top-2), L->top-1)); + } + } + } +#endif + return 0; +} + +LJLIB_PUSH(top-5) LJLIB_SET(os) +LJLIB_PUSH(top-4) LJLIB_SET(arch) +LJLIB_PUSH(top-3) LJLIB_SET(version_num) +LJLIB_PUSH(top-2) LJLIB_SET(version) + +#include "lj_libdef.h" + +/* -- jit.util.* functions ------------------------------------------------ */ + +#define LJLIB_MODULE_jit_util + +/* -- Reflection API for Lua functions ------------------------------------ */ + +/* Return prototype of first argument (Lua function or prototype object) */ +static GCproto *check_Lproto(lua_State *L, int nolua) +{ + TValue *o = L->base; + if (L->top > o) { + if (tvisproto(o)) { + return protoV(o); + } else if (tvisfunc(o)) { + if (isluafunc(funcV(o))) + return funcproto(funcV(o)); + else if (nolua) + return NULL; + } + } + lj_err_argt(L, 1, LUA_TFUNCTION); + return NULL; /* unreachable */ +} + +static void setintfield(lua_State *L, GCtab *t, const char *name, int32_t val) +{ + setintV(lj_tab_setstr(L, t, lj_str_newz(L, name)), val); +} + +/* local info = jit.util.funcinfo(func [,pc]) */ +LJLIB_CF(jit_util_funcinfo) +{ + GCproto *pt = check_Lproto(L, 1); + if (pt) { + BCPos pc = (BCPos)lj_lib_optint(L, 2, 0); + GCtab *t; + lua_createtable(L, 0, 16); /* Increment hash size if fields are added. */ + t = tabV(L->top-1); + setintfield(L, t, "linedefined", pt->firstline); + setintfield(L, t, "lastlinedefined", pt->firstline + pt->numline); + setintfield(L, t, "stackslots", pt->framesize); + setintfield(L, t, "params", pt->numparams); + setintfield(L, t, "bytecodes", (int32_t)pt->sizebc); + setintfield(L, t, "gcconsts", (int32_t)pt->sizekgc); + setintfield(L, t, "nconsts", (int32_t)pt->sizekn); + setintfield(L, t, "upvalues", (int32_t)pt->sizeuv); + if (pc < pt->sizebc) + setintfield(L, t, "currentline", lj_debug_line(pt, pc)); + lua_pushboolean(L, (pt->flags & PROTO_VARARG)); + lua_setfield(L, -2, "isvararg"); + lua_pushboolean(L, (pt->flags & PROTO_CHILD)); + lua_setfield(L, -2, "children"); + setstrV(L, L->top++, proto_chunkname(pt)); + lua_setfield(L, -2, "source"); + lj_debug_pushloc(L, pt, pc); + lua_setfield(L, -2, "loc"); + setprotoV(L, lj_tab_setstr(L, t, lj_str_newlit(L, "proto")), pt); + } else { + GCfunc *fn = funcV(L->base); + GCtab *t; + lua_createtable(L, 0, 4); /* Increment hash size if fields are added. */ + t = tabV(L->top-1); + if (!iscfunc(fn)) + setintfield(L, t, "ffid", fn->c.ffid); + setintptrV(lj_tab_setstr(L, t, lj_str_newlit(L, "addr")), + (intptr_t)(void *)fn->c.f); + setintfield(L, t, "upvalues", fn->c.nupvalues); + } + return 1; +} + +/* local ins, m = jit.util.funcbc(func, pc) */ +LJLIB_CF(jit_util_funcbc) +{ + GCproto *pt = check_Lproto(L, 0); + BCPos pc = (BCPos)lj_lib_checkint(L, 2); + if (pc < pt->sizebc) { + BCIns ins = proto_bc(pt)[pc]; + BCOp op = bc_op(ins); + lua_assert(op < BC__MAX); + setintV(L->top, ins); + setintV(L->top+1, lj_bc_mode[op]); + L->top += 2; + return 2; + } + return 0; +} + +/* local k = jit.util.funck(func, idx) */ +LJLIB_CF(jit_util_funck) +{ + GCproto *pt = check_Lproto(L, 0); + ptrdiff_t idx = (ptrdiff_t)lj_lib_checkint(L, 2); + if (idx >= 0) { + if (idx < (ptrdiff_t)pt->sizekn) { + copyTV(L, L->top-1, proto_knumtv(pt, idx)); + return 1; + } + } else { + if (~idx < (ptrdiff_t)pt->sizekgc) { + GCobj *gc = proto_kgc(pt, idx); + setgcV(L, L->top-1, gc, ~gc->gch.gct); + return 1; + } + } + return 0; +} + +/* local name = jit.util.funcuvname(func, idx) */ +LJLIB_CF(jit_util_funcuvname) +{ + GCproto *pt = check_Lproto(L, 0); + uint32_t idx = (uint32_t)lj_lib_checkint(L, 2); + if (idx < pt->sizeuv) { + setstrV(L, L->top-1, lj_str_newz(L, lj_debug_uvname(pt, idx))); + return 1; + } + return 0; +} + +/* -- Reflection API for traces ------------------------------------------- */ + +#if LJ_HASJIT + +/* Check trace argument. Must not throw for non-existent trace numbers. */ +static GCtrace *jit_checktrace(lua_State *L) +{ + TraceNo tr = (TraceNo)lj_lib_checkint(L, 1); + jit_State *J = L2J(L); + if (tr > 0 && tr < J->sizetrace) + return traceref(J, tr); + return NULL; +} + +/* Names of link types. ORDER LJ_TRLINK */ +static const char *const jit_trlinkname[] = { + "none", "root", "loop", "tail-recursion", "up-recursion", "down-recursion", + "interpreter", "return", "stitch" +}; + +/* local info = jit.util.traceinfo(tr) */ +LJLIB_CF(jit_util_traceinfo) +{ + GCtrace *T = jit_checktrace(L); + if (T) { + GCtab *t; + lua_createtable(L, 0, 8); /* Increment hash size if fields are added. */ + t = tabV(L->top-1); + setintfield(L, t, "nins", (int32_t)T->nins - REF_BIAS - 1); + setintfield(L, t, "nk", REF_BIAS - (int32_t)T->nk); + setintfield(L, t, "link", T->link); + setintfield(L, t, "nexit", T->nsnap); + setstrV(L, L->top++, lj_str_newz(L, jit_trlinkname[T->linktype])); + lua_setfield(L, -2, "linktype"); + /* There are many more fields. Add them only when needed. */ + return 1; + } + return 0; +} + +/* local m, ot, op1, op2, prev = jit.util.traceir(tr, idx) */ +LJLIB_CF(jit_util_traceir) +{ + GCtrace *T = jit_checktrace(L); + IRRef ref = (IRRef)lj_lib_checkint(L, 2) + REF_BIAS; + if (T && ref >= REF_BIAS && ref < T->nins) { + IRIns *ir = &T->ir[ref]; + int32_t m = lj_ir_mode[ir->o]; + setintV(L->top-2, m); + setintV(L->top-1, ir->ot); + setintV(L->top++, (int32_t)ir->op1 - (irm_op1(m)==IRMref ? REF_BIAS : 0)); + setintV(L->top++, (int32_t)ir->op2 - (irm_op2(m)==IRMref ? REF_BIAS : 0)); + setintV(L->top++, ir->prev); + return 5; + } + return 0; +} + +/* local k, t [, slot] = jit.util.tracek(tr, idx) */ +LJLIB_CF(jit_util_tracek) +{ + GCtrace *T = jit_checktrace(L); + IRRef ref = (IRRef)lj_lib_checkint(L, 2) + REF_BIAS; + if (T && ref >= T->nk && ref < REF_BIAS) { + IRIns *ir = &T->ir[ref]; + int32_t slot = -1; + if (ir->o == IR_KSLOT) { + slot = ir->op2; + ir = &T->ir[ir->op1]; + } +#if LJ_HASFFI + if (ir->o == IR_KINT64 && !ctype_ctsG(G(L))) { + ptrdiff_t oldtop = savestack(L, L->top); + luaopen_ffi(L); /* Load FFI library on-demand. */ + L->top = restorestack(L, oldtop); + } +#endif + lj_ir_kvalue(L, L->top-2, ir); + setintV(L->top-1, (int32_t)irt_type(ir->t)); + if (slot == -1) + return 2; + setintV(L->top++, slot); + return 3; + } + return 0; +} + +/* local snap = jit.util.tracesnap(tr, sn) */ +LJLIB_CF(jit_util_tracesnap) +{ + GCtrace *T = jit_checktrace(L); + SnapNo sn = (SnapNo)lj_lib_checkint(L, 2); + if (T && sn < T->nsnap) { + SnapShot *snap = &T->snap[sn]; + SnapEntry *map = &T->snapmap[snap->mapofs]; + MSize n, nent = snap->nent; + GCtab *t; + lua_createtable(L, nent+2, 0); + t = tabV(L->top-1); + setintV(lj_tab_setint(L, t, 0), (int32_t)snap->ref - REF_BIAS); + setintV(lj_tab_setint(L, t, 1), (int32_t)snap->nslots); + for (n = 0; n < nent; n++) + setintV(lj_tab_setint(L, t, (int32_t)(n+2)), (int32_t)map[n]); + setintV(lj_tab_setint(L, t, (int32_t)(nent+2)), (int32_t)SNAP(255, 0, 0)); + return 1; + } + return 0; +} + +/* local mcode, addr, loop = jit.util.tracemc(tr) */ +LJLIB_CF(jit_util_tracemc) +{ + GCtrace *T = jit_checktrace(L); + if (T && T->mcode != NULL) { + setstrV(L, L->top-1, lj_str_new(L, (const char *)T->mcode, T->szmcode)); + setintptrV(L->top++, (intptr_t)(void *)T->mcode); + setintV(L->top++, T->mcloop); + return 3; + } + return 0; +} + +/* local addr = jit.util.traceexitstub([tr,] exitno) */ +LJLIB_CF(jit_util_traceexitstub) +{ +#ifdef EXITSTUBS_PER_GROUP + ExitNo exitno = (ExitNo)lj_lib_checkint(L, 1); + jit_State *J = L2J(L); + if (exitno < EXITSTUBS_PER_GROUP*LJ_MAX_EXITSTUBGR) { + setintptrV(L->top-1, (intptr_t)(void *)exitstub_addr(J, exitno)); + return 1; + } +#else + if (L->top > L->base+1) { /* Don't throw for one-argument variant. */ + GCtrace *T = jit_checktrace(L); + ExitNo exitno = (ExitNo)lj_lib_checkint(L, 2); + ExitNo maxexit = T->root ? T->nsnap+1 : T->nsnap; + if (T && T->mcode != NULL && exitno < maxexit) { + setintptrV(L->top-1, (intptr_t)(void *)exitstub_trace_addr(T, exitno)); + return 1; + } + } +#endif + return 0; +} + +/* local addr = jit.util.ircalladdr(idx) */ +LJLIB_CF(jit_util_ircalladdr) +{ + uint32_t idx = (uint32_t)lj_lib_checkint(L, 1); + if (idx < IRCALL__MAX) { + setintptrV(L->top-1, (intptr_t)(void *)lj_ir_callinfo[idx].func); + return 1; + } + return 0; +} + +#endif + +#include "lj_libdef.h" + +static int luaopen_jit_util(lua_State *L) +{ + LJ_LIB_REG(L, NULL, jit_util); + return 1; +} + +/* -- jit.opt module ------------------------------------------------------ */ + +#if LJ_HASJIT + +#define LJLIB_MODULE_jit_opt + +/* Parse optimization level. */ +static int jitopt_level(jit_State *J, const char *str) +{ + if (str[0] >= '0' && str[0] <= '9' && str[1] == '\0') { + uint32_t flags; + if (str[0] == '0') flags = JIT_F_OPT_0; + else if (str[0] == '1') flags = JIT_F_OPT_1; + else if (str[0] == '2') flags = JIT_F_OPT_2; + else flags = JIT_F_OPT_3; + J->flags = (J->flags & ~JIT_F_OPT_MASK) | flags; + return 1; /* Ok. */ + } + return 0; /* No match. */ +} + +/* Parse optimization flag. */ +static int jitopt_flag(jit_State *J, const char *str) +{ + const char *lst = JIT_F_OPTSTRING; + uint32_t opt; + int set = 1; + if (str[0] == '+') { + str++; + } else if (str[0] == '-') { + str++; + set = 0; + } else if (str[0] == 'n' && str[1] == 'o') { + str += str[2] == '-' ? 3 : 2; + set = 0; + } + for (opt = JIT_F_OPT_FIRST; ; opt <<= 1) { + size_t len = *(const uint8_t *)lst; + if (len == 0) + break; + if (strncmp(str, lst+1, len) == 0 && str[len] == '\0') { + if (set) J->flags |= opt; else J->flags &= ~opt; + return 1; /* Ok. */ + } + lst += 1+len; + } + return 0; /* No match. */ +} + +/* Parse optimization parameter. */ +static int jitopt_param(jit_State *J, const char *str) +{ + const char *lst = JIT_P_STRING; + int i; + for (i = 0; i < JIT_P__MAX; i++) { + size_t len = *(const uint8_t *)lst; + lua_assert(len != 0); + if (strncmp(str, lst+1, len) == 0 && str[len] == '=') { + int32_t n = 0; + const char *p = &str[len+1]; + while (*p >= '0' && *p <= '9') + n = n*10 + (*p++ - '0'); + if (*p) return 0; /* Malformed number. */ + J->param[i] = n; + if (i == JIT_P_hotloop) + lj_dispatch_init_hotcount(J2G(J)); + return 1; /* Ok. */ + } + lst += 1+len; + } + return 0; /* No match. */ +} + +/* jit.opt.start(flags...) */ +LJLIB_CF(jit_opt_start) +{ + jit_State *J = L2J(L); + int nargs = (int)(L->top - L->base); + if (nargs == 0) { + J->flags = (J->flags & ~JIT_F_OPT_MASK) | JIT_F_OPT_DEFAULT; + } else { + int i; + for (i = 1; i <= nargs; i++) { + const char *str = strdata(lj_lib_checkstr(L, i)); + if (!jitopt_level(J, str) && + !jitopt_flag(J, str) && + !jitopt_param(J, str)) + lj_err_callerv(L, LJ_ERR_JITOPT, str); + } + } + return 0; +} + +#include "lj_libdef.h" + +#endif + +/* -- jit.profile module -------------------------------------------------- */ + +#if LJ_HASPROFILE + +#define LJLIB_MODULE_jit_profile + +/* Not loaded by default, use: local profile = require("jit.profile") */ + +static const char KEY_PROFILE_THREAD = 't'; +static const char KEY_PROFILE_FUNC = 'f'; + +static void jit_profile_callback(lua_State *L2, lua_State *L, int samples, + int vmstate) +{ + TValue key; + cTValue *tv; + setlightudV(&key, (void *)&KEY_PROFILE_FUNC); + tv = lj_tab_get(L, tabV(registry(L)), &key); + if (tvisfunc(tv)) { + char vmst = (char)vmstate; + int status; + setfuncV(L2, L2->top++, funcV(tv)); + setthreadV(L2, L2->top++, L); + setintV(L2->top++, samples); + setstrV(L2, L2->top++, lj_str_new(L2, &vmst, 1)); + status = lua_pcall(L2, 3, 0, 0); /* callback(thread, samples, vmstate) */ + if (status) { + if (G(L2)->panic) G(L2)->panic(L2); + exit(EXIT_FAILURE); + } + lj_trace_abort(G(L2)); + } +} + +/* profile.start(mode, cb) */ +LJLIB_CF(jit_profile_start) +{ + GCtab *registry = tabV(registry(L)); + GCstr *mode = lj_lib_optstr(L, 1); + GCfunc *func = lj_lib_checkfunc(L, 2); + lua_State *L2 = lua_newthread(L); /* Thread that runs profiler callback. */ + TValue key; + /* Anchor thread and function in registry. */ + setlightudV(&key, (void *)&KEY_PROFILE_THREAD); + setthreadV(L, lj_tab_set(L, registry, &key), L2); + setlightudV(&key, (void *)&KEY_PROFILE_FUNC); + setfuncV(L, lj_tab_set(L, registry, &key), func); + lj_gc_anybarriert(L, registry); + luaJIT_profile_start(L, mode ? strdata(mode) : "", + (luaJIT_profile_callback)jit_profile_callback, L2); + return 0; +} + +/* profile.stop() */ +LJLIB_CF(jit_profile_stop) +{ + GCtab *registry; + TValue key; + luaJIT_profile_stop(L); + registry = tabV(registry(L)); + setlightudV(&key, (void *)&KEY_PROFILE_THREAD); + setnilV(lj_tab_set(L, registry, &key)); + setlightudV(&key, (void *)&KEY_PROFILE_FUNC); + setnilV(lj_tab_set(L, registry, &key)); + lj_gc_anybarriert(L, registry); + return 0; +} + +/* dump = profile.dumpstack([thread,] fmt, depth) */ +LJLIB_CF(jit_profile_dumpstack) +{ + lua_State *L2 = L; + int arg = 0; + size_t len; + int depth; + GCstr *fmt; + const char *p; + if (L->top > L->base && tvisthread(L->base)) { + L2 = threadV(L->base); + arg = 1; + } + fmt = lj_lib_checkstr(L, arg+1); + depth = lj_lib_checkint(L, arg+2); + p = luaJIT_profile_dumpstack(L2, strdata(fmt), depth, &len); + lua_pushlstring(L, p, len); + return 1; +} + +#include "lj_libdef.h" + +static int luaopen_jit_profile(lua_State *L) +{ + LJ_LIB_REG(L, NULL, jit_profile); + return 1; +} + +#endif + +/* -- JIT compiler initialization ----------------------------------------- */ + +#if LJ_HASJIT +/* Default values for JIT parameters. */ +static const int32_t jit_param_default[JIT_P__MAX+1] = { +#define JIT_PARAMINIT(len, name, value) (value), +JIT_PARAMDEF(JIT_PARAMINIT) +#undef JIT_PARAMINIT + 0 +}; +#endif + +#if LJ_TARGET_ARM && LJ_TARGET_LINUX +#include +#endif + +/* Arch-dependent CPU detection. */ +static uint32_t jit_cpudetect(lua_State *L) +{ + uint32_t flags = 0; +#if LJ_TARGET_X86ORX64 + uint32_t vendor[4]; + uint32_t features[4]; + if (lj_vm_cpuid(0, vendor) && lj_vm_cpuid(1, features)) { +#if !LJ_HASJIT +#define JIT_F_SSE2 2 +#endif + flags |= ((features[3] >> 26)&1) * JIT_F_SSE2; +#if LJ_HASJIT + flags |= ((features[2] >> 0)&1) * JIT_F_SSE3; + flags |= ((features[2] >> 19)&1) * JIT_F_SSE4_1; + if (vendor[2] == 0x6c65746e) { /* Intel. */ + if ((features[0] & 0x0fff0ff0) == 0x000106c0) /* Atom. */ + flags |= JIT_F_LEA_AGU; + } else if (vendor[2] == 0x444d4163) { /* AMD. */ + uint32_t fam = (features[0] & 0x0ff00f00); + if (fam >= 0x00000f00) /* K8, K10. */ + flags |= JIT_F_PREFER_IMUL; + } + if (vendor[0] >= 7) { + uint32_t xfeatures[4]; + lj_vm_cpuid(7, xfeatures); + flags |= ((xfeatures[1] >> 8)&1) * JIT_F_BMI2; + } +#endif + } + /* Check for required instruction set support on x86 (unnecessary on x64). */ +#if LJ_TARGET_X86 + if (!(flags & JIT_F_SSE2)) + luaL_error(L, "CPU with SSE2 required"); +#endif +#elif LJ_TARGET_ARM +#if LJ_HASJIT + int ver = LJ_ARCH_VERSION; /* Compile-time ARM CPU detection. */ +#if LJ_TARGET_LINUX + if (ver < 70) { /* Runtime ARM CPU detection. */ + struct utsname ut; + uname(&ut); + if (strncmp(ut.machine, "armv", 4) == 0) { + if (ut.machine[4] >= '7') + ver = 70; + else if (ut.machine[4] == '6') + ver = 60; + } + } +#endif + flags |= ver >= 70 ? JIT_F_ARMV7 : + ver >= 61 ? JIT_F_ARMV6T2_ : + ver >= 60 ? JIT_F_ARMV6_ : 0; + flags |= LJ_ARCH_HASFPU == 0 ? 0 : ver >= 70 ? JIT_F_VFPV3 : JIT_F_VFPV2; +#endif +#elif LJ_TARGET_ARM64 + /* No optional CPU features to detect (for now). */ +#elif LJ_TARGET_PPC +#if LJ_HASJIT +#if LJ_ARCH_SQRT + flags |= JIT_F_SQRT; +#endif +#if LJ_ARCH_ROUND + flags |= JIT_F_ROUND; +#endif +#endif +#elif LJ_TARGET_MIPS +#if LJ_HASJIT + /* Compile-time MIPS CPU detection. */ +#if LJ_ARCH_VERSION >= 20 + flags |= JIT_F_MIPSXXR2; +#endif + /* Runtime MIPS CPU detection. */ +#if defined(__GNUC__) + if (!(flags & JIT_F_MIPSXXR2)) { + int x; +#ifdef __mips16 + x = 0; /* Runtime detection is difficult. Ensure optimal -march flags. */ +#else + /* On MIPS32R1 rotr is treated as srl. rotr r2,r2,1 -> srl r2,r2,1. */ + __asm__("li $2, 1\n\t.long 0x00221042\n\tmove %0, $2" : "=r"(x) : : "$2"); +#endif + if (x) flags |= JIT_F_MIPSXXR2; /* Either 0x80000000 (R2) or 0 (R1). */ + } +#endif +#endif +#else +#error "Missing CPU detection for this architecture" +#endif + UNUSED(L); + return flags; +} + +/* Initialize JIT compiler. */ +static void jit_init(lua_State *L) +{ + uint32_t flags = jit_cpudetect(L); +#if LJ_HASJIT + jit_State *J = L2J(L); + J->flags = flags | JIT_F_ON | JIT_F_OPT_DEFAULT; + memcpy(J->param, jit_param_default, sizeof(J->param)); + lj_dispatch_update(G(L)); +#else + UNUSED(flags); +#endif +} + +LUALIB_API int luaopen_jit(lua_State *L) +{ + jit_init(L); + lua_pushliteral(L, LJ_OS_NAME); + lua_pushliteral(L, LJ_ARCH_NAME); + lua_pushinteger(L, LUAJIT_VERSION_NUM); + lua_pushliteral(L, LUAJIT_VERSION); + LJ_LIB_REG(L, LUA_JITLIBNAME, jit); +#if LJ_HASPROFILE + lj_lib_prereg(L, LUA_JITLIBNAME ".profile", luaopen_jit_profile, + tabref(L->env)); +#endif +#ifndef LUAJIT_DISABLE_JITUTIL + lj_lib_prereg(L, LUA_JITLIBNAME ".util", luaopen_jit_util, tabref(L->env)); +#endif +#if LJ_HASJIT + LJ_LIB_REG(L, "jit.opt", jit_opt); +#endif + L->top -= 2; + return 1; +} + diff --git a/lib/LuaJIT/lib_jit.o b/lib/LuaJIT/lib_jit.o new file mode 100644 index 0000000000000000000000000000000000000000..3332e2a75e64b2991c5e98de4e0e1b15fc924510 GIT binary patch literal 19776 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 + +#define lib_math_c +#define LUA_LIB + +#include "lua.h" +#include "lauxlib.h" +#include "lualib.h" + +#include "lj_obj.h" +#include "lj_lib.h" +#include "lj_vm.h" + +/* ------------------------------------------------------------------------ */ + +#define LJLIB_MODULE_math + +LJLIB_ASM(math_abs) LJLIB_REC(.) +{ + lj_lib_checknumber(L, 1); + return FFH_RETRY; +} +LJLIB_ASM_(math_floor) LJLIB_REC(math_round IRFPM_FLOOR) +LJLIB_ASM_(math_ceil) LJLIB_REC(math_round IRFPM_CEIL) + +LJLIB_ASM(math_sqrt) LJLIB_REC(math_unary IRFPM_SQRT) +{ + lj_lib_checknum(L, 1); + return FFH_RETRY; +} +LJLIB_ASM_(math_log10) LJLIB_REC(math_unary IRFPM_LOG10) +LJLIB_ASM_(math_exp) LJLIB_REC(math_unary IRFPM_EXP) +LJLIB_ASM_(math_sin) LJLIB_REC(math_unary IRFPM_SIN) +LJLIB_ASM_(math_cos) LJLIB_REC(math_unary IRFPM_COS) +LJLIB_ASM_(math_tan) LJLIB_REC(math_unary IRFPM_TAN) +LJLIB_ASM_(math_asin) LJLIB_REC(math_atrig FF_math_asin) +LJLIB_ASM_(math_acos) LJLIB_REC(math_atrig FF_math_acos) +LJLIB_ASM_(math_atan) LJLIB_REC(math_atrig FF_math_atan) +LJLIB_ASM_(math_sinh) LJLIB_REC(math_htrig IRCALL_sinh) +LJLIB_ASM_(math_cosh) LJLIB_REC(math_htrig IRCALL_cosh) +LJLIB_ASM_(math_tanh) LJLIB_REC(math_htrig IRCALL_tanh) +LJLIB_ASM_(math_frexp) +LJLIB_ASM_(math_modf) LJLIB_REC(.) + +LJLIB_ASM(math_log) LJLIB_REC(math_log) +{ + double x = lj_lib_checknum(L, 1); + if (L->base+1 < L->top) { + double y = lj_lib_checknum(L, 2); +#ifdef LUAJIT_NO_LOG2 + x = log(x); y = 1.0 / log(y); +#else + x = lj_vm_log2(x); y = 1.0 / lj_vm_log2(y); +#endif + setnumV(L->base-1-LJ_FR2, x*y); /* Do NOT join the expression to x / y. */ + return FFH_RES(1); + } + return FFH_RETRY; +} + +LJLIB_LUA(math_deg) /* function(x) return x * 57.29577951308232 end */ +LJLIB_LUA(math_rad) /* function(x) return x * 0.017453292519943295 end */ + +LJLIB_ASM(math_atan2) LJLIB_REC(.) +{ + lj_lib_checknum(L, 1); + lj_lib_checknum(L, 2); + return FFH_RETRY; +} +LJLIB_ASM_(math_pow) LJLIB_REC(.) +LJLIB_ASM_(math_fmod) + +LJLIB_ASM(math_ldexp) LJLIB_REC(.) +{ + lj_lib_checknum(L, 1); +#if LJ_DUALNUM && !LJ_TARGET_X86ORX64 + lj_lib_checkint(L, 2); +#else + lj_lib_checknum(L, 2); +#endif + return FFH_RETRY; +} + +LJLIB_ASM(math_min) LJLIB_REC(math_minmax IR_MIN) +{ + int i = 0; + do { lj_lib_checknumber(L, ++i); } while (L->base+i < L->top); + return FFH_RETRY; +} +LJLIB_ASM_(math_max) LJLIB_REC(math_minmax IR_MAX) + +LJLIB_PUSH(3.14159265358979323846) LJLIB_SET(pi) +LJLIB_PUSH(1e310) LJLIB_SET(huge) + +/* ------------------------------------------------------------------------ */ + +/* This implements a Tausworthe PRNG with period 2^223. Based on: +** Tables of maximally-equidistributed combined LFSR generators, +** Pierre L'Ecuyer, 1991, table 3, 1st entry. +** Full-period ME-CF generator with L=64, J=4, k=223, N1=49. +*/ + +/* PRNG state. */ +struct RandomState { + uint64_t gen[4]; /* State of the 4 LFSR generators. */ + int valid; /* State is valid. */ +}; + +/* Union needed for bit-pattern conversion between uint64_t and double. */ +typedef union { uint64_t u64; double d; } U64double; + +/* Update generator i and compute a running xor of all states. */ +#define TW223_GEN(i, k, q, s) \ + z = rs->gen[i]; \ + z = (((z<> (k-s)) ^ ((z&((uint64_t)(int64_t)-1 << (64-k)))<gen[i] = z; + +/* PRNG step function. Returns a double in the range 1.0 <= d < 2.0. */ +LJ_NOINLINE uint64_t LJ_FASTCALL lj_math_random_step(RandomState *rs) +{ + uint64_t z, r = 0; + TW223_GEN(0, 63, 31, 18) + TW223_GEN(1, 58, 19, 28) + TW223_GEN(2, 55, 24, 7) + TW223_GEN(3, 47, 21, 8) + return (r & U64x(000fffff,ffffffff)) | U64x(3ff00000,00000000); +} + +/* PRNG initialization function. */ +static void random_init(RandomState *rs, double d) +{ + uint32_t r = 0x11090601; /* 64-k[i] as four 8 bit constants. */ + int i; + for (i = 0; i < 4; i++) { + U64double u; + uint32_t m = 1u << (r&255); + r >>= 8; + u.d = d = d * 3.14159265358979323846 + 2.7182818284590452354; + if (u.u64 < m) u.u64 += m; /* Ensure k[i] MSB of gen[i] are non-zero. */ + rs->gen[i] = u.u64; + } + rs->valid = 1; + for (i = 0; i < 10; i++) + lj_math_random_step(rs); +} + +/* PRNG extract function. */ +LJLIB_PUSH(top-2) /* Upvalue holds userdata with RandomState. */ +LJLIB_CF(math_random) LJLIB_REC(.) +{ + int n = (int)(L->top - L->base); + RandomState *rs = (RandomState *)(uddata(udataV(lj_lib_upvalue(L, 1)))); + U64double u; + double d; + if (LJ_UNLIKELY(!rs->valid)) random_init(rs, 0.0); + u.u64 = lj_math_random_step(rs); + d = u.d - 1.0; + if (n > 0) { +#if LJ_DUALNUM + int isint = 1; + double r1; + lj_lib_checknumber(L, 1); + if (tvisint(L->base)) { + r1 = (lua_Number)intV(L->base); + } else { + isint = 0; + r1 = numV(L->base); + } +#else + double r1 = lj_lib_checknum(L, 1); +#endif + if (n == 1) { + d = lj_vm_floor(d*r1) + 1.0; /* d is an int in range [1, r1] */ + } else { +#if LJ_DUALNUM + double r2; + lj_lib_checknumber(L, 2); + if (tvisint(L->base+1)) { + r2 = (lua_Number)intV(L->base+1); + } else { + isint = 0; + r2 = numV(L->base+1); + } +#else + double r2 = lj_lib_checknum(L, 2); +#endif + d = lj_vm_floor(d*(r2-r1+1.0)) + r1; /* d is an int in range [r1, r2] */ + } +#if LJ_DUALNUM + if (isint) { + setintV(L->top-1, lj_num2int(d)); + return 1; + } +#endif + } /* else: d is a double in range [0, 1] */ + setnumV(L->top++, d); + return 1; +} + +/* PRNG seed function. */ +LJLIB_PUSH(top-2) /* Upvalue holds userdata with RandomState. */ +LJLIB_CF(math_randomseed) +{ + RandomState *rs = (RandomState *)(uddata(udataV(lj_lib_upvalue(L, 1)))); + random_init(rs, lj_lib_checknum(L, 1)); + return 0; +} + +/* ------------------------------------------------------------------------ */ + +#include "lj_libdef.h" + +LUALIB_API int luaopen_math(lua_State *L) +{ + RandomState *rs; + rs = (RandomState *)lua_newuserdata(L, sizeof(RandomState)); + rs->valid = 0; /* Use lazy initialization to save some time on startup. */ + LJ_LIB_REG(L, LUA_MATHLIBNAME, math); + return 1; +} + diff --git a/lib/LuaJIT/lib_math.o b/lib/LuaJIT/lib_math.o new file mode 100644 index 0000000000000000000000000000000000000000..4a3e53fe1b7abef88f60e8670a3f746c48b1fa6c GIT binary patch literal 5296 zcmcIoZETxY6~4B!)=4}2ZONFYZHgVjZzq$<2ZJ0=8x2N zmN03QVW-S^mVyBBZ{pAR!7l{+(G3)wW?i=;jYvai6KG=vp_Bq$%ZIWxZFtUo?{#k8 z+ClV+;i^7tDGK+4m%wVrpUoQ%{C>4GB#~&@}^)-wu>2TjMr|C27k*d ze{p;JB8)Xk4qm&;E6a{m(&F_YgEzX~;GZ1w75~61FS&!wM@}rK0^Yu=@G`bYv%!g_ zUc0*Jge-($^ThJPmA6ux%HBRk6A2TkLN1pzl~L*M1{4h$Lf9AzHhHD#=7VA1?42+% zBDLlt%icZ{GruinyzbrA)i3m~dySp_5F`EWly~2f!jAR3g$}TDEx*vcla>)WOHp6< zKKcrfE?9qY3CiYLYpRr5?|+J)*YD=_BW}J(Zr3+9Hh67?Fa5rc2hm;H&7Zo&*xCkP zTHQ}b?IOSr@Y;4B1ozH;eD5{B^yfV!)sGlLW87_N`7D*~so<+n8L(6U!pXQ{soZ6$ zxH0|_{?t1N;1(f5#m8SnFd#4Edsq026dq&m9^&0Adiocz{<~;|*KS1{la4+WD`2u zgO=(gQXi_UI%fVU>Nm#gBZjDX-CV1u{Z?rz=Y80Ps|CbMyGYv~W23xAd&5U|yk79} z2hYQjFWq$2O+RnU`ReAu`KGt}Q%oqFA-+7y`ejk*`M;jNpVz}a-Z+XNM}4TW8%6e= zr6{qn7v%PP8w1gPw{TFo=|Bb`iTzXll-O_BcbW>^)2^|Uj^6=al&03+qy2dNl`Chk z|8-bIL?f3oSSYp_&R@!VwYfTbYh&Z`A;K2M7WcuKcD{H+nDK@v(*@H{lkvvKsT1Lf zrJPvK#mi|x4;XvG@yXJBDqAQNtBGVL8!4SCmglpDlfi*Xa&Ed(%H%7FLa9=Y=c{oD z)i|VToJ2_JDx`E3QhGjBgnl(wm`a6V{+??pd6GGBa2!q_(ia7L?VHd3(Dl1t{^T27 z@41TcDck7Z|MA`DdS70=ukHwUd$*B!X>HpWhdq~BU8Skz4{NfIijfBGw z`S*=HJocb}Fc1t3$jN(U?|9-1%rR#;?%lcFy@2fW1HVs>eS-Rs$2jARczhMtu*aYB z_`)6|~bk$33*eIVl(nk&m;a1JZrad@BNF?0MM}_D9KQWlu4$N&dssiRs$w+a2I)`w}wd!e2Xb z!IPbK0&r@6MD2i4EeSW#6Ny@(58y}m1bQM>CsXhaHccQ%w zpIRLG*~B#%_@|*q4ZKXFMs@&@%mkHMa$x%OM-$BxE zGjFJaAC}Eb#^`MbBv|$n66mdR(z zG?++P^KD+44s`fLY$yaWD32mIL%_-h^TOB#MZcoomTI^fufg6Nv~Abv{! zA&J}9)hq84>h9L`_h~r23(C(aiIbl_8vbn!*Yoz4hU@xQH2->>eezt`<2>8}ACow_ z)BQZE>C>IA;-Be&KiR?0k2L+gnxE%2{oNY=GY#LR;je1=JsSR+=Kn()4ptDAjn+18 ziA>#x3Rmxnx;qrUAoZzsL<%>B#F$_93ZIZNz4Js0wfQ27oMNY+ zA}$$)n^K;#;S*Axv*9WaYJJL&l7DW~SNXBt2P+n`QTO$#O<&#F>o#1ii|%zIHLuD; z#D=T;odrr?&6VSm=*vZ`Pb;pN%*F%dlGY0Tx7H7HTC)9% z!30M1rrg(-bo<*zcLberQz^*)Y5D2!G$k11!H?!q`(Ewm2PAHrhTTI>+mI|zYviNg z$mf4+AA~LWJb<50YJU5dh?-yZCX9%e%WS#u%lP&0G8*)^G%V(G=*Zq4zh6#lT57G< z-{LHW&SAU!8Fdv){|R6;Kiz#gP00CGoz$4JSA7lxWUuZOr8g(-?S4s@ zNcE3vycm$px9o|RxUy5abufh4^S>bNkJ}7X+{*q8n0EPFk@<5=dlKoZSJ|t)w40lJ yuWq9j{|S(V^@69fVz;L+W_#@cu>tw1`*~aZHWB|bY;XStO$hp))wNo?{l5Vc?UsuG literal 0 HcmV?d00001 diff --git a/lib/LuaJIT/lib_math_dyn.o b/lib/LuaJIT/lib_math_dyn.o new file mode 100644 index 0000000000000000000000000000000000000000..4a3e53fe1b7abef88f60e8670a3f746c48b1fa6c GIT binary patch literal 5296 zcmcIoZETxY6~4B!)=4}2ZONFYZHgVjZzq$<2ZJ0=8x2N zmN03QVW-S^mVyBBZ{pAR!7l{+(G3)wW?i=;jYvai6KG=vp_Bq$%ZIWxZFtUo?{#k8 z+ClV+;i^7tDGK+4m%wVrpUoQ%{C>4GB#~&@}^)-wu>2TjMr|C27k*d ze{p;JB8)Xk4qm&;E6a{m(&F_YgEzX~;GZ1w75~61FS&!wM@}rK0^Yu=@G`bYv%!g_ zUc0*Jge-($^ThJPmA6ux%HBRk6A2TkLN1pzl~L*M1{4h$Lf9AzHhHD#=7VA1?42+% zBDLlt%icZ{GruinyzbrA)i3m~dySp_5F`EWly~2f!jAR3g$}TDEx*vcla>)WOHp6< zKKcrfE?9qY3CiYLYpRr5?|+J)*YD=_BW}J(Zr3+9Hh67?Fa5rc2hm;H&7Zo&*xCkP zTHQ}b?IOSr@Y;4B1ozH;eD5{B^yfV!)sGlLW87_N`7D*~so<+n8L(6U!pXQ{soZ6$ zxH0|_{?t1N;1(f5#m8SnFd#4Edsq026dq&m9^&0Adiocz{<~;|*KS1{la4+WD`2u zgO=(gQXi_UI%fVU>Nm#gBZjDX-CV1u{Z?rz=Y80Ps|CbMyGYv~W23xAd&5U|yk79} z2hYQjFWq$2O+RnU`ReAu`KGt}Q%oqFA-+7y`ejk*`M;jNpVz}a-Z+XNM}4TW8%6e= zr6{qn7v%PP8w1gPw{TFo=|Bb`iTzXll-O_BcbW>^)2^|Uj^6=al&03+qy2dNl`Chk z|8-bIL?f3oSSYp_&R@!VwYfTbYh&Z`A;K2M7WcuKcD{H+nDK@v(*@H{lkvvKsT1Lf zrJPvK#mi|x4;XvG@yXJBDqAQNtBGVL8!4SCmglpDlfi*Xa&Ed(%H%7FLa9=Y=c{oD z)i|VToJ2_JDx`E3QhGjBgnl(wm`a6V{+??pd6GGBa2!q_(ia7L?VHd3(Dl1t{^T27 z@41TcDck7Z|MA`DdS70=ukHwUd$*B!X>HpWhdq~BU8Skz4{NfIijfBGw z`S*=HJocb}Fc1t3$jN(U?|9-1%rR#;?%lcFy@2fW1HVs>eS-Rs$2jARczhMtu*aYB z_`)6|~bk$33*eIVl(nk&m;a1JZrad@BNF?0MM}_D9KQWlu4$N&dssiRs$w+a2I)`w}wd!e2Xb z!IPbK0&r@6MD2i4EeSW#6Ny@(58y}m1bQM>CsXhaHccQ%w zpIRLG*~B#%_@|*q4ZKXFMs@&@%mkHMa$x%OM-$BxE zGjFJaAC}Eb#^`MbBv|$n66mdR(z zG?++P^KD+44s`fLY$yaWD32mIL%_-h^TOB#MZcoomTI^fufg6Nv~Abv{! zA&J}9)hq84>h9L`_h~r23(C(aiIbl_8vbn!*Yoz4hU@xQH2->>eezt`<2>8}ACow_ z)BQZE>C>IA;-Be&KiR?0k2L+gnxE%2{oNY=GY#LR;je1=JsSR+=Kn()4ptDAjn+18 ziA>#x3Rmxnx;qrUAoZzsL<%>B#F$_93ZIZNz4Js0wfQ27oMNY+ zA}$$)n^K;#;S*Axv*9WaYJJL&l7DW~SNXBt2P+n`QTO$#O<&#F>o#1ii|%zIHLuD; z#D=T;odrr?&6VSm=*vZ`Pb;pN%*F%dlGY0Tx7H7HTC)9% z!30M1rrg(-bo<*zcLberQz^*)Y5D2!G$k11!H?!q`(Ewm2PAHrhTTI>+mI|zYviNg z$mf4+AA~LWJb<50YJU5dh?-yZCX9%e%WS#u%lP&0G8*)^G%V(G=*Zq4zh6#lT57G< z-{LHW&SAU!8Fdv){|R6;Kiz#gP00CGoz$4JSA7lxWUuZOr8g(-?S4s@ zNcE3vycm$px9o|RxUy5abufh4^S>bNkJ}7X+{*q8n0EPFk@<5=dlKoZSJ|t)w40lJ yuWq9j{|S(V^@69fVz;L+W_#@cu>tw1`*~aZHWB|bY;XStO$hp))wNo?{l5Vc?UsuG literal 0 HcmV?d00001 diff --git a/lib/LuaJIT/lib_os.c b/lib/LuaJIT/lib_os.c new file mode 100644 index 0000000..ffbc3fd --- /dev/null +++ b/lib/LuaJIT/lib_os.c @@ -0,0 +1,292 @@ +/* +** OS library. +** Copyright (C) 2005-2017 Mike Pall. See Copyright Notice in luajit.h +** +** Major portions taken verbatim or adapted from the Lua interpreter. +** Copyright (C) 1994-2008 Lua.org, PUC-Rio. See Copyright Notice in lua.h +*/ + +#include +#include + +#define lib_os_c +#define LUA_LIB + +#include "lua.h" +#include "lauxlib.h" +#include "lualib.h" + +#include "lj_obj.h" +#include "lj_gc.h" +#include "lj_err.h" +#include "lj_buf.h" +#include "lj_str.h" +#include "lj_lib.h" + +#if LJ_TARGET_POSIX +#include +#else +#include +#endif + +#if !LJ_TARGET_PSVITA +#include +#endif + +/* ------------------------------------------------------------------------ */ + +#define LJLIB_MODULE_os + +LJLIB_CF(os_execute) +{ +#if LJ_NO_SYSTEM +#if LJ_52 + errno = ENOSYS; + return luaL_fileresult(L, 0, NULL); +#else + lua_pushinteger(L, -1); + return 1; +#endif +#else + const char *cmd = luaL_optstring(L, 1, NULL); + int stat = system(cmd); +#if LJ_52 + if (cmd) + return luaL_execresult(L, stat); + setboolV(L->top++, 1); +#else + setintV(L->top++, stat); +#endif + return 1; +#endif +} + +LJLIB_CF(os_remove) +{ + const char *filename = luaL_checkstring(L, 1); + return luaL_fileresult(L, remove(filename) == 0, filename); +} + +LJLIB_CF(os_rename) +{ + const char *fromname = luaL_checkstring(L, 1); + const char *toname = luaL_checkstring(L, 2); + return luaL_fileresult(L, rename(fromname, toname) == 0, fromname); +} + +LJLIB_CF(os_tmpname) +{ +#if LJ_TARGET_PS3 || LJ_TARGET_PS4 || LJ_TARGET_PSVITA + lj_err_caller(L, LJ_ERR_OSUNIQF); + return 0; +#else +#if LJ_TARGET_POSIX + char buf[15+1]; + int fp; + strcpy(buf, "/tmp/lua_XXXXXX"); + fp = mkstemp(buf); + if (fp != -1) + close(fp); + else + lj_err_caller(L, LJ_ERR_OSUNIQF); +#else + char buf[L_tmpnam]; + if (tmpnam(buf) == NULL) + lj_err_caller(L, LJ_ERR_OSUNIQF); +#endif + lua_pushstring(L, buf); + return 1; +#endif +} + +LJLIB_CF(os_getenv) +{ +#if LJ_TARGET_CONSOLE + lua_pushnil(L); +#else + lua_pushstring(L, getenv(luaL_checkstring(L, 1))); /* if NULL push nil */ +#endif + return 1; +} + +LJLIB_CF(os_exit) +{ + int status; + if (L->base < L->top && tvisbool(L->base)) + status = boolV(L->base) ? EXIT_SUCCESS : EXIT_FAILURE; + else + status = lj_lib_optint(L, 1, EXIT_SUCCESS); + if (L->base+1 < L->top && tvistruecond(L->base+1)) + lua_close(L); + exit(status); + return 0; /* Unreachable. */ +} + +LJLIB_CF(os_clock) +{ + setnumV(L->top++, ((lua_Number)clock())*(1.0/(lua_Number)CLOCKS_PER_SEC)); + return 1; +} + +/* ------------------------------------------------------------------------ */ + +static void setfield(lua_State *L, const char *key, int value) +{ + lua_pushinteger(L, value); + lua_setfield(L, -2, key); +} + +static void setboolfield(lua_State *L, const char *key, int value) +{ + if (value < 0) /* undefined? */ + return; /* does not set field */ + lua_pushboolean(L, value); + lua_setfield(L, -2, key); +} + +static int getboolfield(lua_State *L, const char *key) +{ + int res; + lua_getfield(L, -1, key); + res = lua_isnil(L, -1) ? -1 : lua_toboolean(L, -1); + lua_pop(L, 1); + return res; +} + +static int getfield(lua_State *L, const char *key, int d) +{ + int res; + lua_getfield(L, -1, key); + if (lua_isnumber(L, -1)) { + res = (int)lua_tointeger(L, -1); + } else { + if (d < 0) + lj_err_callerv(L, LJ_ERR_OSDATEF, key); + res = d; + } + lua_pop(L, 1); + return res; +} + +LJLIB_CF(os_date) +{ + const char *s = luaL_optstring(L, 1, "%c"); + time_t t = luaL_opt(L, (time_t)luaL_checknumber, 2, time(NULL)); + struct tm *stm; +#if LJ_TARGET_POSIX + struct tm rtm; +#endif + if (*s == '!') { /* UTC? */ + s++; /* Skip '!' */ +#if LJ_TARGET_POSIX + stm = gmtime_r(&t, &rtm); +#else + stm = gmtime(&t); +#endif + } else { +#if LJ_TARGET_POSIX + stm = localtime_r(&t, &rtm); +#else + stm = localtime(&t); +#endif + } + if (stm == NULL) { /* Invalid date? */ + setnilV(L->top++); + } else if (strcmp(s, "*t") == 0) { + lua_createtable(L, 0, 9); /* 9 = number of fields */ + setfield(L, "sec", stm->tm_sec); + setfield(L, "min", stm->tm_min); + setfield(L, "hour", stm->tm_hour); + setfield(L, "day", stm->tm_mday); + setfield(L, "month", stm->tm_mon+1); + setfield(L, "year", stm->tm_year+1900); + setfield(L, "wday", stm->tm_wday+1); + setfield(L, "yday", stm->tm_yday+1); + setboolfield(L, "isdst", stm->tm_isdst); + } else if (*s) { + SBuf *sb = &G(L)->tmpbuf; + MSize sz = 0, retry = 4; + const char *q; + for (q = s; *q; q++) + sz += (*q == '%') ? 30 : 1; /* Overflow doesn't matter. */ + setsbufL(sb, L); + while (retry--) { /* Limit growth for invalid format or empty result. */ + char *buf = lj_buf_need(sb, sz); + size_t len = strftime(buf, sbufsz(sb), s, stm); + if (len) { + setstrV(L, L->top++, lj_str_new(L, buf, len)); + lj_gc_check(L); + break; + } + sz += (sz|1); + } + } else { + setstrV(L, L->top++, &G(L)->strempty); + } + return 1; +} + +LJLIB_CF(os_time) +{ + time_t t; + if (lua_isnoneornil(L, 1)) { /* called without args? */ + t = time(NULL); /* get current time */ + } else { + struct tm ts; + luaL_checktype(L, 1, LUA_TTABLE); + lua_settop(L, 1); /* make sure table is at the top */ + ts.tm_sec = getfield(L, "sec", 0); + ts.tm_min = getfield(L, "min", 0); + ts.tm_hour = getfield(L, "hour", 12); + ts.tm_mday = getfield(L, "day", -1); + ts.tm_mon = getfield(L, "month", -1) - 1; + ts.tm_year = getfield(L, "year", -1) - 1900; + ts.tm_isdst = getboolfield(L, "isdst"); + t = mktime(&ts); + } + if (t == (time_t)(-1)) + lua_pushnil(L); + else + lua_pushnumber(L, (lua_Number)t); + return 1; +} + +LJLIB_CF(os_difftime) +{ + lua_pushnumber(L, difftime((time_t)(luaL_checknumber(L, 1)), + (time_t)(luaL_optnumber(L, 2, (lua_Number)0)))); + return 1; +} + +/* ------------------------------------------------------------------------ */ + +LJLIB_CF(os_setlocale) +{ +#if LJ_TARGET_PSVITA + lua_pushliteral(L, "C"); +#else + GCstr *s = lj_lib_optstr(L, 1); + const char *str = s ? strdata(s) : NULL; + int opt = lj_lib_checkopt(L, 2, 6, + "\5ctype\7numeric\4time\7collate\10monetary\1\377\3all"); + if (opt == 0) opt = LC_CTYPE; + else if (opt == 1) opt = LC_NUMERIC; + else if (opt == 2) opt = LC_TIME; + else if (opt == 3) opt = LC_COLLATE; + else if (opt == 4) opt = LC_MONETARY; + else if (opt == 6) opt = LC_ALL; + lua_pushstring(L, setlocale(opt, str)); +#endif + return 1; +} + +/* ------------------------------------------------------------------------ */ + +#include "lj_libdef.h" + +LUALIB_API int luaopen_os(lua_State *L) +{ + LJ_LIB_REG(L, LUA_OSLIBNAME, os); + return 1; +} + diff --git a/lib/LuaJIT/lib_os.o b/lib/LuaJIT/lib_os.o new file mode 100644 index 0000000000000000000000000000000000000000..e7d3393dd5babb5332a41dabe9b4bc93d7bd8b63 GIT binary patch literal 10304 zcmb`M4R9O96@XWkV=F)C6bMD-&q{E}4@9v7gqT3kIkJ5QB?N4f0HKO4pJNeO@qT3Q2p688oTR{nXwL<P0j{fIo-F4vG*9z;MBsxO z59R}S3qt%u)_sZU{XfS@pbxOdL@S5rCph7gqoeA^&=D()mb>b`YP*%QxBxx8yLbda zf{*h$F34thnn}(fK6J)3n5aLudcHDrH?(U7qc)%wcGdd1MvM9Ky5UpGP&+i`RxeeC z_JJSD3+~8Bzh`Jn8M+U6OJV(H&HNqQhzG8QR?UQG+Kc!&l+Tx27n-jq5y;w?T&nA! z@TnAQ7$oLarHfoCKzzl|0Km$C! z@~@zwe6qIE)jtk_>Jj0@r<(d1H8$SOu(C5Ce`rw zuz94>uN5{x5ZnES!F!7-Syqp;-b9M^r28|ht7DKsyV1i2^^OcnWV^BkTNr2Cv0)HT=`5eP!^xbu}xeUYhJtdmcZiX_3cXn^}$fEP7FLxl&**G zVXhrM*A;WBJ)`j8sRe$eXnPRL2Cr{VMT^(J+a2)+DjL21Cap>lO{BpS)&#KqA zyJCYkFzEF)c|B|~pf29Zn1LnODPZ?K?x;7gyRvVV*AECRwaoTI^Qh1bVF|YK6=|?y z`SP@Ai(|?CQPH1fZ-6oE`zmsfrFi0g^zgpu15Ya`@4+_Ow=M%7SaVTlTl1cW4_=UU zc;dR67{~DX!7ez<<8!OfTRly$v#5uDbqT$C)Z=s7&i%R(syWe?#*+ICG0sDbx40L& zEYDQ0r)lvmT~ysPrn5$|4C z#ouRnS2cO-RPSO?X^=5*o{j5Y32y`xj1!A^eR1v`R;CNOS;^ijo6hdCI|#f#JRyNNRe1Qa_R0LmW1Q4IwGQ@&9Q2=X;CmhTGY%Y|x8?jiguG1LRyp|j zD+m2%2mQY|jC;y~w>#LGbkINUz-Pjo%FS!Q!Tx2a4{dhY4WJxf?{VNyIq;_)_=^twhy#BOIUIKRlLLR-fqww}Vt8YO#RGA% zGzjmZa`ss=(HTo;gK?I8C>HOA%UJPSxP;_)a*6OS=~5NHu1huRle<(GpW-E8eWKGk zez}+G;)}in@FTrc!w-0@q3_&hSxKZ4226nkqigH%v;t2z;ORy@-Grx=c)A%+tMF8h zrx4c;h4?k^6ym)?H%iOtJ|k;nWG&vK$F~7uvF7Mq4Qe!YS5s4KV_U3EZHP9;7=F+9 z^=EsaMzw7}{Z=|0YlbRuQwThZi0 zF+G!s!3HL&X9lE~FeE5&RamYpgseDnQn=Q;EoI>h9Ath$nOHYMXw*e-$AJa{M-y{F?|tkgtG4@?RqaL4E@qlD~-%1Uc?=B>xy82y*<@ zQu0s3dk6*jN;o91BOGHQc_ZQYu9f_L!7-k=H?WM%)#HMrTUQhPy@b~g z{v*Nzg#V22`Gg-Qd;#H;gkMAWRTuye^rsdM>Hl?tV?pnuRfN+xv=FWlJDUinap)qv zh3M}joZi<@5Pk>I{|4cA68@^-I4|6z$ax(j`Vqq4C7jM{#tcvb!DjkgT0}TL>!kf3 z5WbG^>ME#!pq=$_Nc~F)zl-q81-IwPBBH;6=-)s%wQ~!xL-VSc=ucD)Ub}knj*L@MOvw-MRJ1Yst-%Mm)-A3%x5`G8Kr*F-g^~Z1`AYpzkarwKPX5(8#9diIh+OcYk1;hoqEnMeB+L3&V(CZPU zW4epR@6tnAcGom+^VW#$`PHqMsZ$C!CezNG{i_$EGjW7k{rok@_-jKecfg zXO9?1>dSp73uXs<;jpjxJ=qa7^c@BZ!p^RP1v|5_h0CO|g6g3l?9^x# z_GwlXO6Br%|80NVo1jhnW}za-$2}7j>6Y{(42|Xb|3WC*?aF?# z#CHf@i?oC*O9CQp_->#QpcS~@3oRDzdhe%U6x-I{p;iaqI_5MxBDfg!^cF`C*Y7w zbmc|GELtmF+jaLr6Egeyc|?D|twGLPTKpDVl#Bo9d`n}M7!c>{g+un2@h>+v_6*Fy xGu)S=!(6yt3WvQvhRL2gfY_FKcrLw1t=jZhq1|%*Us+)F_@V45N->=x{TI)PonHU| literal 0 HcmV?d00001 diff --git a/lib/LuaJIT/lib_os_dyn.o b/lib/LuaJIT/lib_os_dyn.o new file mode 100644 index 0000000000000000000000000000000000000000..e7d3393dd5babb5332a41dabe9b4bc93d7bd8b63 GIT binary patch literal 10304 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 literal 0 HcmV?d00001 diff --git a/lib/LuaJIT/lib_package.c b/lib/LuaJIT/lib_package.c new file mode 100644 index 0000000..bedd6d7 --- /dev/null +++ b/lib/LuaJIT/lib_package.c @@ -0,0 +1,623 @@ +/* +** Package library. +** Copyright (C) 2005-2017 Mike Pall. See Copyright Notice in luajit.h +** +** Major portions taken verbatim or adapted from the Lua interpreter. +** Copyright (C) 1994-2012 Lua.org, PUC-Rio. See Copyright Notice in lua.h +*/ + +#define lib_package_c +#define LUA_LIB + +#include "lua.h" +#include "lauxlib.h" +#include "lualib.h" + +#include "lj_obj.h" +#include "lj_err.h" +#include "lj_lib.h" + +/* ------------------------------------------------------------------------ */ + +/* Error codes for ll_loadfunc. */ +#define PACKAGE_ERR_LIB 1 +#define PACKAGE_ERR_FUNC 2 +#define PACKAGE_ERR_LOAD 3 + +/* Redefined in platform specific part. */ +#define PACKAGE_LIB_FAIL "open" +#define setprogdir(L) ((void)0) + +/* Symbol name prefixes. */ +#define SYMPREFIX_CF "luaopen_%s" +#define SYMPREFIX_BC "luaJIT_BC_%s" + +#if LJ_TARGET_DLOPEN + +#include + +static void ll_unloadlib(void *lib) +{ + dlclose(lib); +} + +static void *ll_load(lua_State *L, const char *path, int gl) +{ + void *lib = dlopen(path, RTLD_NOW | (gl ? RTLD_GLOBAL : RTLD_LOCAL)); + if (lib == NULL) lua_pushstring(L, dlerror()); + return lib; +} + +static lua_CFunction ll_sym(lua_State *L, void *lib, const char *sym) +{ + lua_CFunction f = (lua_CFunction)dlsym(lib, sym); + if (f == NULL) lua_pushstring(L, dlerror()); + return f; +} + +static const char *ll_bcsym(void *lib, const char *sym) +{ +#if defined(RTLD_DEFAULT) + if (lib == NULL) lib = RTLD_DEFAULT; +#elif LJ_TARGET_OSX || LJ_TARGET_BSD + if (lib == NULL) lib = (void *)(intptr_t)-2; +#endif + return (const char *)dlsym(lib, sym); +} + +#elif LJ_TARGET_WINDOWS + +#define WIN32_LEAN_AND_MEAN +#include + +#ifndef GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS +#define GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS 4 +#define GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT 2 +BOOL WINAPI GetModuleHandleExA(DWORD, LPCSTR, HMODULE*); +#endif + +#if LJ_TARGET_UWP +void *LJ_WIN_LOADLIBA(const char *path) +{ + DWORD err = GetLastError(); + wchar_t wpath[256]; + HANDLE lib = NULL; + if (MultiByteToWideChar(CP_ACP, 0, path, -1, wpath, 256) > 0) { + lib = LoadPackagedLibrary(wpath, 0); + } + SetLastError(err); + return lib; +} +#endif + +#undef setprogdir + +static void setprogdir(lua_State *L) +{ + char buff[MAX_PATH + 1]; + char *lb; + DWORD nsize = sizeof(buff); + DWORD n = GetModuleFileNameA(NULL, buff, nsize); + if (n == 0 || n == nsize || (lb = strrchr(buff, '\\')) == NULL) { + luaL_error(L, "unable to get ModuleFileName"); + } else { + *lb = '\0'; + luaL_gsub(L, lua_tostring(L, -1), LUA_EXECDIR, buff); + lua_remove(L, -2); /* remove original string */ + } +} + +static void pusherror(lua_State *L) +{ + DWORD error = GetLastError(); +#if LJ_TARGET_XBOXONE + wchar_t wbuffer[128]; + char buffer[128*2]; + if (FormatMessageW(FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_SYSTEM, + NULL, error, 0, wbuffer, sizeof(wbuffer)/sizeof(wchar_t), NULL) && + WideCharToMultiByte(CP_ACP, 0, wbuffer, 128, buffer, 128*2, NULL, NULL)) +#else + char buffer[128]; + if (FormatMessageA(FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_SYSTEM, + NULL, error, 0, buffer, sizeof(buffer), NULL)) +#endif + lua_pushstring(L, buffer); + else + lua_pushfstring(L, "system error %d\n", error); +} + +static void ll_unloadlib(void *lib) +{ + FreeLibrary((HINSTANCE)lib); +} + +static void *ll_load(lua_State *L, const char *path, int gl) +{ + HINSTANCE lib = LJ_WIN_LOADLIBA(path); + if (lib == NULL) pusherror(L); + UNUSED(gl); + return lib; +} + +static lua_CFunction ll_sym(lua_State *L, void *lib, const char *sym) +{ + lua_CFunction f = (lua_CFunction)GetProcAddress((HINSTANCE)lib, sym); + if (f == NULL) pusherror(L); + return f; +} + +#if LJ_TARGET_UWP +EXTERN_C IMAGE_DOS_HEADER __ImageBase; +#endif + +static const char *ll_bcsym(void *lib, const char *sym) +{ + if (lib) { + return (const char *)GetProcAddress((HINSTANCE)lib, sym); + } else { +#if LJ_TARGET_UWP + return (const char *)GetProcAddress((HINSTANCE)&__ImageBase, sym); +#else + HINSTANCE h = GetModuleHandleA(NULL); + const char *p = (const char *)GetProcAddress(h, sym); + if (p == NULL && GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS|GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, + (const char *)ll_bcsym, &h)) + p = (const char *)GetProcAddress(h, sym); + return p; +#endif + } +} + +#else + +#undef PACKAGE_LIB_FAIL +#define PACKAGE_LIB_FAIL "absent" + +#define DLMSG "dynamic libraries not enabled; no support for target OS" + +static void ll_unloadlib(void *lib) +{ + UNUSED(lib); +} + +static void *ll_load(lua_State *L, const char *path, int gl) +{ + UNUSED(path); UNUSED(gl); + lua_pushliteral(L, DLMSG); + return NULL; +} + +static lua_CFunction ll_sym(lua_State *L, void *lib, const char *sym) +{ + UNUSED(lib); UNUSED(sym); + lua_pushliteral(L, DLMSG); + return NULL; +} + +static const char *ll_bcsym(void *lib, const char *sym) +{ + UNUSED(lib); UNUSED(sym); + return NULL; +} + +#endif + +/* ------------------------------------------------------------------------ */ + +static void **ll_register(lua_State *L, const char *path) +{ + void **plib; + lua_pushfstring(L, "LOADLIB: %s", path); + lua_gettable(L, LUA_REGISTRYINDEX); /* check library in registry? */ + if (!lua_isnil(L, -1)) { /* is there an entry? */ + plib = (void **)lua_touserdata(L, -1); + } else { /* no entry yet; create one */ + lua_pop(L, 1); + plib = (void **)lua_newuserdata(L, sizeof(void *)); + *plib = NULL; + luaL_setmetatable(L, "_LOADLIB"); + lua_pushfstring(L, "LOADLIB: %s", path); + lua_pushvalue(L, -2); + lua_settable(L, LUA_REGISTRYINDEX); + } + return plib; +} + +static const char *mksymname(lua_State *L, const char *modname, + const char *prefix) +{ + const char *funcname; + const char *mark = strchr(modname, *LUA_IGMARK); + if (mark) modname = mark + 1; + funcname = luaL_gsub(L, modname, ".", "_"); + funcname = lua_pushfstring(L, prefix, funcname); + lua_remove(L, -2); /* remove 'gsub' result */ + return funcname; +} + +static int ll_loadfunc(lua_State *L, const char *path, const char *name, int r) +{ + void **reg = ll_register(L, path); + if (*reg == NULL) *reg = ll_load(L, path, (*name == '*')); + if (*reg == NULL) { + return PACKAGE_ERR_LIB; /* Unable to load library. */ + } else if (*name == '*') { /* Only load library into global namespace. */ + lua_pushboolean(L, 1); + return 0; + } else { + const char *sym = r ? name : mksymname(L, name, SYMPREFIX_CF); + lua_CFunction f = ll_sym(L, *reg, sym); + if (f) { + lua_pushcfunction(L, f); + return 0; + } + if (!r) { + const char *bcdata = ll_bcsym(*reg, mksymname(L, name, SYMPREFIX_BC)); + lua_pop(L, 1); + if (bcdata) { + if (luaL_loadbuffer(L, bcdata, LJ_MAX_BUF, name) != 0) + return PACKAGE_ERR_LOAD; + return 0; + } + } + return PACKAGE_ERR_FUNC; /* Unable to find function. */ + } +} + +static int lj_cf_package_loadlib(lua_State *L) +{ + const char *path = luaL_checkstring(L, 1); + const char *init = luaL_checkstring(L, 2); + int st = ll_loadfunc(L, path, init, 1); + if (st == 0) { /* no errors? */ + return 1; /* return the loaded function */ + } else { /* error; error message is on stack top */ + lua_pushnil(L); + lua_insert(L, -2); + lua_pushstring(L, (st == PACKAGE_ERR_LIB) ? PACKAGE_LIB_FAIL : "init"); + return 3; /* return nil, error message, and where */ + } +} + +static int lj_cf_package_unloadlib(lua_State *L) +{ + void **lib = (void **)luaL_checkudata(L, 1, "_LOADLIB"); + if (*lib) ll_unloadlib(*lib); + *lib = NULL; /* mark library as closed */ + return 0; +} + +/* ------------------------------------------------------------------------ */ + +static int readable(const char *filename) +{ + FILE *f = fopen(filename, "r"); /* try to open file */ + if (f == NULL) return 0; /* open failed */ + fclose(f); + return 1; +} + +static const char *pushnexttemplate(lua_State *L, const char *path) +{ + const char *l; + while (*path == *LUA_PATHSEP) path++; /* skip separators */ + if (*path == '\0') return NULL; /* no more templates */ + l = strchr(path, *LUA_PATHSEP); /* find next separator */ + if (l == NULL) l = path + strlen(path); + lua_pushlstring(L, path, (size_t)(l - path)); /* template */ + return l; +} + +static const char *searchpath (lua_State *L, const char *name, + const char *path, const char *sep, + const char *dirsep) +{ + luaL_Buffer msg; /* to build error message */ + luaL_buffinit(L, &msg); + if (*sep != '\0') /* non-empty separator? */ + name = luaL_gsub(L, name, sep, dirsep); /* replace it by 'dirsep' */ + while ((path = pushnexttemplate(L, path)) != NULL) { + const char *filename = luaL_gsub(L, lua_tostring(L, -1), + LUA_PATH_MARK, name); + lua_remove(L, -2); /* remove path template */ + if (readable(filename)) /* does file exist and is readable? */ + return filename; /* return that file name */ + lua_pushfstring(L, "\n\tno file " LUA_QS, filename); + lua_remove(L, -2); /* remove file name */ + luaL_addvalue(&msg); /* concatenate error msg. entry */ + } + luaL_pushresult(&msg); /* create error message */ + return NULL; /* not found */ +} + +static int lj_cf_package_searchpath(lua_State *L) +{ + const char *f = searchpath(L, luaL_checkstring(L, 1), + luaL_checkstring(L, 2), + luaL_optstring(L, 3, "."), + luaL_optstring(L, 4, LUA_DIRSEP)); + if (f != NULL) { + return 1; + } else { /* error message is on top of the stack */ + lua_pushnil(L); + lua_insert(L, -2); + return 2; /* return nil + error message */ + } +} + +static const char *findfile(lua_State *L, const char *name, + const char *pname) +{ + const char *path; + lua_getfield(L, LUA_ENVIRONINDEX, pname); + path = lua_tostring(L, -1); + if (path == NULL) + luaL_error(L, LUA_QL("package.%s") " must be a string", pname); + return searchpath(L, name, path, ".", LUA_DIRSEP); +} + +static void loaderror(lua_State *L, const char *filename) +{ + luaL_error(L, "error loading module " LUA_QS " from file " LUA_QS ":\n\t%s", + lua_tostring(L, 1), filename, lua_tostring(L, -1)); +} + +static int lj_cf_package_loader_lua(lua_State *L) +{ + const char *filename; + const char *name = luaL_checkstring(L, 1); + filename = findfile(L, name, "path"); + if (filename == NULL) return 1; /* library not found in this path */ + if (luaL_loadfile(L, filename) != 0) + loaderror(L, filename); + return 1; /* library loaded successfully */ +} + +static int lj_cf_package_loader_c(lua_State *L) +{ + const char *name = luaL_checkstring(L, 1); + const char *filename = findfile(L, name, "cpath"); + if (filename == NULL) return 1; /* library not found in this path */ + if (ll_loadfunc(L, filename, name, 0) != 0) + loaderror(L, filename); + return 1; /* library loaded successfully */ +} + +static int lj_cf_package_loader_croot(lua_State *L) +{ + const char *filename; + const char *name = luaL_checkstring(L, 1); + const char *p = strchr(name, '.'); + int st; + if (p == NULL) return 0; /* is root */ + lua_pushlstring(L, name, (size_t)(p - name)); + filename = findfile(L, lua_tostring(L, -1), "cpath"); + if (filename == NULL) return 1; /* root not found */ + if ((st = ll_loadfunc(L, filename, name, 0)) != 0) { + if (st != PACKAGE_ERR_FUNC) loaderror(L, filename); /* real error */ + lua_pushfstring(L, "\n\tno module " LUA_QS " in file " LUA_QS, + name, filename); + return 1; /* function not found */ + } + return 1; +} + +static int lj_cf_package_loader_preload(lua_State *L) +{ + const char *name = luaL_checkstring(L, 1); + lua_getfield(L, LUA_ENVIRONINDEX, "preload"); + if (!lua_istable(L, -1)) + luaL_error(L, LUA_QL("package.preload") " must be a table"); + lua_getfield(L, -1, name); + if (lua_isnil(L, -1)) { /* Not found? */ + const char *bcname = mksymname(L, name, SYMPREFIX_BC); + const char *bcdata = ll_bcsym(NULL, bcname); + if (bcdata == NULL || luaL_loadbuffer(L, bcdata, LJ_MAX_BUF, name) != 0) + lua_pushfstring(L, "\n\tno field package.preload['%s']", name); + } + return 1; +} + +/* ------------------------------------------------------------------------ */ + +#define sentinel ((void *)0x4004) + +static int lj_cf_package_require(lua_State *L) +{ + const char *name = luaL_checkstring(L, 1); + int i; + lua_settop(L, 1); /* _LOADED table will be at index 2 */ + lua_getfield(L, LUA_REGISTRYINDEX, "_LOADED"); + lua_getfield(L, 2, name); + if (lua_toboolean(L, -1)) { /* is it there? */ + if (lua_touserdata(L, -1) == sentinel) /* check loops */ + luaL_error(L, "loop or previous error loading module " LUA_QS, name); + return 1; /* package is already loaded */ + } + /* else must load it; iterate over available loaders */ + lua_getfield(L, LUA_ENVIRONINDEX, "loaders"); + if (!lua_istable(L, -1)) + luaL_error(L, LUA_QL("package.loaders") " must be a table"); + lua_pushliteral(L, ""); /* error message accumulator */ + for (i = 1; ; i++) { + lua_rawgeti(L, -2, i); /* get a loader */ + if (lua_isnil(L, -1)) + luaL_error(L, "module " LUA_QS " not found:%s", + name, lua_tostring(L, -2)); + lua_pushstring(L, name); + lua_call(L, 1, 1); /* call it */ + if (lua_isfunction(L, -1)) /* did it find module? */ + break; /* module loaded successfully */ + else if (lua_isstring(L, -1)) /* loader returned error message? */ + lua_concat(L, 2); /* accumulate it */ + else + lua_pop(L, 1); + } + lua_pushlightuserdata(L, sentinel); + lua_setfield(L, 2, name); /* _LOADED[name] = sentinel */ + lua_pushstring(L, name); /* pass name as argument to module */ + lua_call(L, 1, 1); /* run loaded module */ + if (!lua_isnil(L, -1)) /* non-nil return? */ + lua_setfield(L, 2, name); /* _LOADED[name] = returned value */ + lua_getfield(L, 2, name); + if (lua_touserdata(L, -1) == sentinel) { /* module did not set a value? */ + lua_pushboolean(L, 1); /* use true as result */ + lua_pushvalue(L, -1); /* extra copy to be returned */ + lua_setfield(L, 2, name); /* _LOADED[name] = true */ + } + lj_lib_checkfpu(L); + return 1; +} + +/* ------------------------------------------------------------------------ */ + +static void setfenv(lua_State *L) +{ + lua_Debug ar; + if (lua_getstack(L, 1, &ar) == 0 || + lua_getinfo(L, "f", &ar) == 0 || /* get calling function */ + lua_iscfunction(L, -1)) + luaL_error(L, LUA_QL("module") " not called from a Lua function"); + lua_pushvalue(L, -2); + lua_setfenv(L, -2); + lua_pop(L, 1); +} + +static void dooptions(lua_State *L, int n) +{ + int i; + for (i = 2; i <= n; i++) { + lua_pushvalue(L, i); /* get option (a function) */ + lua_pushvalue(L, -2); /* module */ + lua_call(L, 1, 0); + } +} + +static void modinit(lua_State *L, const char *modname) +{ + const char *dot; + lua_pushvalue(L, -1); + lua_setfield(L, -2, "_M"); /* module._M = module */ + lua_pushstring(L, modname); + lua_setfield(L, -2, "_NAME"); + dot = strrchr(modname, '.'); /* look for last dot in module name */ + if (dot == NULL) dot = modname; else dot++; + /* set _PACKAGE as package name (full module name minus last part) */ + lua_pushlstring(L, modname, (size_t)(dot - modname)); + lua_setfield(L, -2, "_PACKAGE"); +} + +static int lj_cf_package_module(lua_State *L) +{ + const char *modname = luaL_checkstring(L, 1); + int lastarg = (int)(L->top - L->base); + luaL_pushmodule(L, modname, 1); + lua_getfield(L, -1, "_NAME"); + if (!lua_isnil(L, -1)) { /* Module already initialized? */ + lua_pop(L, 1); + } else { + lua_pop(L, 1); + modinit(L, modname); + } + lua_pushvalue(L, -1); + setfenv(L); + dooptions(L, lastarg); + return LJ_52; +} + +static int lj_cf_package_seeall(lua_State *L) +{ + luaL_checktype(L, 1, LUA_TTABLE); + if (!lua_getmetatable(L, 1)) { + lua_createtable(L, 0, 1); /* create new metatable */ + lua_pushvalue(L, -1); + lua_setmetatable(L, 1); + } + lua_pushvalue(L, LUA_GLOBALSINDEX); + lua_setfield(L, -2, "__index"); /* mt.__index = _G */ + return 0; +} + +/* ------------------------------------------------------------------------ */ + +#define AUXMARK "\1" + +static void setpath(lua_State *L, const char *fieldname, const char *envname, + const char *def, int noenv) +{ +#if LJ_TARGET_CONSOLE + const char *path = NULL; + UNUSED(envname); +#else + const char *path = getenv(envname); +#endif + if (path == NULL || noenv) { + lua_pushstring(L, def); + } else { + path = luaL_gsub(L, path, LUA_PATHSEP LUA_PATHSEP, + LUA_PATHSEP AUXMARK LUA_PATHSEP); + luaL_gsub(L, path, AUXMARK, def); + lua_remove(L, -2); + } + setprogdir(L); + lua_setfield(L, -2, fieldname); +} + +static const luaL_Reg package_lib[] = { + { "loadlib", lj_cf_package_loadlib }, + { "searchpath", lj_cf_package_searchpath }, + { "seeall", lj_cf_package_seeall }, + { NULL, NULL } +}; + +static const luaL_Reg package_global[] = { + { "module", lj_cf_package_module }, + { "require", lj_cf_package_require }, + { NULL, NULL } +}; + +static const lua_CFunction package_loaders[] = +{ + lj_cf_package_loader_preload, + lj_cf_package_loader_lua, + lj_cf_package_loader_c, + lj_cf_package_loader_croot, + NULL +}; + +LUALIB_API int luaopen_package(lua_State *L) +{ + int i; + int noenv; + luaL_newmetatable(L, "_LOADLIB"); + lj_lib_pushcf(L, lj_cf_package_unloadlib, 1); + lua_setfield(L, -2, "__gc"); + luaL_register(L, LUA_LOADLIBNAME, package_lib); + lua_copy(L, -1, LUA_ENVIRONINDEX); + lua_createtable(L, sizeof(package_loaders)/sizeof(package_loaders[0])-1, 0); + for (i = 0; package_loaders[i] != NULL; i++) { + lj_lib_pushcf(L, package_loaders[i], 1); + lua_rawseti(L, -2, i+1); + } +#if LJ_52 + lua_pushvalue(L, -1); + lua_setfield(L, -3, "searchers"); +#endif + lua_setfield(L, -2, "loaders"); + lua_getfield(L, LUA_REGISTRYINDEX, "LUA_NOENV"); + noenv = lua_toboolean(L, -1); + lua_pop(L, 1); + setpath(L, "path", LUA_PATH, LUA_PATH_DEFAULT, noenv); + setpath(L, "cpath", LUA_CPATH, LUA_CPATH_DEFAULT, noenv); + lua_pushliteral(L, LUA_PATH_CONFIG); + lua_setfield(L, -2, "config"); + luaL_findtable(L, LUA_REGISTRYINDEX, "_LOADED", 16); + lua_setfield(L, -2, "loaded"); + luaL_findtable(L, LUA_REGISTRYINDEX, "_PRELOAD", 4); + lua_setfield(L, -2, "preload"); + lua_pushvalue(L, LUA_GLOBALSINDEX); + luaL_register(L, NULL, package_global); + lua_pop(L, 1); + return 1; +} + diff --git a/lib/LuaJIT/lib_package.o b/lib/LuaJIT/lib_package.o new file mode 100644 index 0000000000000000000000000000000000000000..3c2db56081dc63058c49535e8d62f597c64ce4f4 GIT binary patch literal 18456 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 Fe*>@{Xi)$F literal 0 HcmV?d00001 diff --git a/lib/LuaJIT/lib_package_dyn.o b/lib/LuaJIT/lib_package_dyn.o new file mode 100644 index 0000000000000000000000000000000000000000..3c2db56081dc63058c49535e8d62f597c64ce4f4 GIT binary patch literal 18456 zcmdU$dw7)9oyXry07KB3cr6uM1`85F0+WCu@sh~}COBM#fLaK{B$efpH#dQ&}Ub4U6IlnXWotM1r zKD&SId7jC<@8^8a@BGg1+}=4exw9@j*HchnnN(n1W##S+wXCtVx$7!*S!I=2J}cM| zc)8v_-akZXS;+os>HC}+vX2E`?q@q8d#@t(_QA0IyA>gO59NjWX0fUONh!J#2CsWl!VbQ8AU*7xj{$?P5Q z^*%-}^&itELfXKo!k&{OeZ38&47@Ckl8Jxi#wcyLZyHy8y}|n^V!8e&qsG4W{?A)=oUto)C7Hh_@hSF>*he>=*R1YrBeEO@xT;LQ?r9L6kL@D_^@R~Ym`#K7OPh=Zy zaGoFPrsu1IJLcpP_SoT!Ku>h6xTk~U4At4QfR}z&E#!hK>A#jA`7_?$h>3r zb*@ji7Ol-j4zu6uFEasBgY{6TZ*f8R;1zGQ|TVOvMT+Y>g%GEwk)xIhNt<-N>_4d1A`-CbR?7m{6 z#h&33P$#B z57`tVwc3+e$CwP;@2HYt`!n@ESV7K6KN`t549ClQB?CV`Znk%NLmG^NE zk7E0nulD1dqrX&@6e{)LBofp5tG6bzSFie#YS~h0h9@_#cLE8u4c_(ixD~zZYo+<6 zWKh$Sgr1X+^QyR@_e0;tcc>Ngl(O3z8oE<&qss8^LK3WK?4_rbV56^Y557|2SvF#+?QrmR5;+CRi@|2ffeom2yW@FCl~QXV%{}udR7ZV51EnE+ z@nH#?iziVB5*OP&)rpk~m1?)&*)4GqyYe_l}e+E{C=X4B@~ z%?;=|=}C_wX9CwxLnk|G>9H?;W_GPIbJ<9!kvjHRgk1Jz zA0KF4-`l!9f8;&#`Or{+YLHP$4dZB=A;!_>m=fEi;%^pLSx53rn|Kb}zU(ffdba2b z9nQ0Ik<`^>&UBdM^^11iWAD#8PDRY)8n?uZ?`1N;zuL{Kpbh~|-RvqszF$HH`ahzF zYTdbXLxXt8Oj)iuFu)Ig-F&H@nF747X4M?gQw<;aJM|pLpD(igsXts0kcV#5kGt5T z+|b|rhoK>RuNpJGuV>ES?I_zGteD+Y7598m5YnqWWt$=3tJ6!DVd%!ed1hvcgF)MJOpPQ@VAghI5 z^)c#t-IR_Ysp$vxAy2IbY6`zU{xe=;22@Whpr)S1LrfkFWjq+?=v}$58_gq;`&hKF zA_g0zf5Lr}NMNZ=H22{wa6lf)xf>T#JhVN%;FyIUndxmAkGuK9VLy*Z<5)jODYti$ z{sEd^xq6f_`?Y-A^g*zG%gi@Iy(coSk|7iD?10Pm=P|WcYKJHSTyge>)wT|P&_1Zl zs3#CpShn9E(t7>J@^7VlD!jXx+99pYt3u2vwyT6iIfwbfwY!)jGpsnfzC)K%>u`;b zS_AsgmaOEYEA+-gla}nqlF3BUA5TPE+B@6)9f_7qJm#M~DK**OnoM-~TifM!PVwkT zDbmLii7r2t=t{=cwI?zuzpDWfQ<~frZC)E~i#8UFI}>Stb2J{0wP+8b{%|JhZ_RWzr`r>qIeX*)n{=~RJi0SM-pArCe&d0ZSi!Ap zlxnTA=~tJ^}}iLO{@gcx1UuWyJ{*YHhqS2Vpw*Mh#acjiWxCH-P4X(ZC#*%G_OVrv!$%&IJ? zv??p<&@2*ZYbKEzPF8qXFtTt_-NNPSYB4Qa>Y_$nO)su2zPflq@w8&AInmkL-like zLakp?$1Y2ttYqw_OnWk>vMK&nDi%#PuThSqVlj%c87BXK-{;lT%<-4bTe$3E|K&WE zr6uRcd%>+stb$ug3dWs&TG4je=1YlRAbE57z1mx{)pLz^?54t6ufJ!+D(~2Z-jZ5x z5oLws#j?A2J0WeKr`|huYhlpq-!!7uTiP?S);pu{iGpHp>0GZr=p9RvYHv~97|(tA zb#{}+3!uGYQ3|2PMhyNT;+MR*gKmLxj~X@W2<`1Y^o~E zHYly*KUD8F&wTILzQQ`Mf9nV;uxaEx?~K*t^tE0;1xH7qo}6Ax^377VLgKg0GcQ|q zA(y2}dq#O~rs5P+sJL(n$AMU_GFj&>n$K~#gkEom&0Tb@m%9TrIi`9);lr6>Mai^5|9@iE0mTdBP<`sL3vTb-t^0#t6wLxsEF}seeQV|+= z^7)B#?EYFM8 zQuY_wvJ?f!yzsQ?9?VXQ(3l(YXNB(1e-K+A%2?g%q1ecLEc|r=w1_JkK5@|z)_)ES zms@BigCn!hl8R5^{A6-!Dzwx|PF{wO?mS>$ttp}?v_|D=`R1GSVm;3dPHJ7D_v!Rv zepvchtr5g(g})>&sWC-tA~l%xRpOGG=foF^{voMa%~@jC3HP5y7nIa|r1xfev7ObU znOc^bQ}liZy_oy?v5%6PJM{h+^kTkUxSAWp{z-TjPdZA*&*$ix^@oM4@lEWo@YSc& z1tm4E>HP$~SpTtbHExM{rM-Uj2y0nt%o0CO_)g(!JQANyyugyi(tRntI9)BgTisb! zmAqdg+}xXegtH65j>y$pp(Q~;sPxiU;NK5YqJ?;TZ@2rSJ_7{-W?dbMRM$^L)be zn(&7a()D5CI~@F7;rku@sNy57oeutq@PiJ1QtZ6w;9m>>HwPb~)~Qiy9%ELq@YfO2 zb&2qI9Q-`t>c9c=E)aec7++o}{38c175-liUMBnt2cM}pEuN}+3xCjs|I~%=cHz&t@Yh}VQ5SyFg%{G&JX}AX?ZU^o@C#jdIq|b- z@$d5L#Z!Iupv!6(eFJk^Q=F;EMc?JZ*Sqju7jDt9O~jn=HOomD9cfze8zarFCMS|T z=*gq{NXqJ1n_Az|8SRK!t#tUrN1j$Z9^peZK6Z3i(r0av=42v~cF7tZn`5qg$HW|; z3g&0X`BOe*HJ?-E(@dw~saRSaUghV*sg^`aHI7bW&2d_Ujhei+c%ms9x5~mbGx$${ z|5WgwS^Vcx{xg&Rl=Gj<_)kD-0!luc!R3l7K|mD^sImc7IG{=glrW&nD}A{tU#@JG ztFq;)Y`LyYStwT)%2layWuaV^t87DqEq-R;aQwRjHZE z!c0|mrYbvAm7O`uip&cyst$%Di{{QWJaiK(uporsx6jQC(~l0DUpcBqMiC~eHRTrvfPzPt-<1Sj@cZg0Y_QM=*?uI zJuAz35aEM!#oJRrm96%+HR(($mTZZpX{eDIatOW1%i|L07T>Rn#xu&duyRi34qGB` zQfWGuRc6_>&elY>idg45i+rJDRhp9|ZcjC55A%r&4oBKj85-9l;JRAD`(&&mu}&&( z%CyoHr9+R~zZq{4AJJtkw?)nqJ{iR;*rcluk0mN?D%Y<Eur8Pt`6NZyD9OEpZ(bGS0zQVMuhPVKk|udUKMl z@~O>G&yBnusE%l+<&WJ?#@gCbY2BnqbE0d#@{UF#g+h(QaD>-R?KtlNXURrD+n2R~ zUi3=?l_t7yK5yg1TgbQQW%MzIl-SN9dKqp$Uou||{5HDg#CE<-FQc~^Qer)SCN}&5 zhLoKAZ#7nY30)iglfv2lc;I`4JMH`$IDh6gc77+E?f8M4&uwg{0r;nM&589B=w}@a4cufulc{0q3(YW4|8w^}v?{M>}hP-vIjcz_Gu!0LO8n&oy+vECf4` zg8sX}p9a1H`18QAU2g%$b{zqZ?HU4(aDMv^y-a`gfgbJu2snTFY4nc(Uj85KVLW( zn+SX==rNwNfn)p^0LSsu2ps+I0KOXP{ho04XA z1a@8kj_o=E9PONR;iqxnDe)VQ=Lx{kei?AIKT9~rXEMD^KURYt$HM~PIKLXe&P1>i z1wGp71l|n#t-x_TyjM8;$!FKb&xb&d_ICkq0sVWxd9N{cJ`&FMF9yz^yErlDGhm}1 zBbirz_Hv%6uif$;eKWph_{J%&z+nEM@2Iw(vRlw1o5b!wISqZ!Y z_&VU7!25u=0^bcB*Q*1-F`n-T=XRBWoezQI_!)H?i72t2zw0vbDG|x@IG*VL znZP$wo~gG~IL8gof676R?YbQF)nI2X=y82s3LMX!nt$945? z;B}zC5BOZ*4+6)1@KNCNK>wIwPOE%;OvU| zz1%~<*`Jl7-vONU=J$Jk;mo8-ii929_;2bpdJ~6(4!!w(;|QtW=*{mN7dyE5edAUK z_j4mC9d>ZjU*fq-0 zjV%st`fI0yoA0l!GWx!UzMi1>bW*>sF<11TB+;^T?3T1uroKQaYf7c8vSfm9SRN>& zZwv93zA98@lz^@%lm5SjzI?OFniCxzu}-o?|9%lGqo3VR(_fA00tp5%jzznE4KN+Ezi#^Y?D8GV|qyHf6`H*@!#MoDl}aA#&*B z{|C<21qSq6_OCyCr7srI+i>xJs7mLX`ONX>DQo;U@oyu>_0OOertWj}oirnxW&Aep zU6f<4vm&~>0-F{e;m{b{d-r-;`Z}LI8)xV zf4I82WS0oc`oWKVy!JZF^D%+LMIx59dXjpxelSwMo4?NXpGg_RmET;WOWbV=%Dc1t Fe*>@{Xi)$F literal 0 HcmV?d00001 diff --git a/lib/LuaJIT/lib_string.c b/lib/LuaJIT/lib_string.c new file mode 100644 index 0000000..76b0730 --- /dev/null +++ b/lib/LuaJIT/lib_string.c @@ -0,0 +1,748 @@ +/* +** String library. +** Copyright (C) 2005-2017 Mike Pall. See Copyright Notice in luajit.h +** +** Major portions taken verbatim or adapted from the Lua interpreter. +** Copyright (C) 1994-2008 Lua.org, PUC-Rio. See Copyright Notice in lua.h +*/ + +#define lib_string_c +#define LUA_LIB + +#include "lua.h" +#include "lauxlib.h" +#include "lualib.h" + +#include "lj_obj.h" +#include "lj_gc.h" +#include "lj_err.h" +#include "lj_buf.h" +#include "lj_str.h" +#include "lj_tab.h" +#include "lj_meta.h" +#include "lj_state.h" +#include "lj_ff.h" +#include "lj_bcdump.h" +#include "lj_char.h" +#include "lj_strfmt.h" +#include "lj_lib.h" + +/* ------------------------------------------------------------------------ */ + +#define LJLIB_MODULE_string + +LJLIB_LUA(string_len) /* + function(s) + CHECK_str(s) + return #s + end +*/ + +LJLIB_ASM(string_byte) LJLIB_REC(string_range 0) +{ + GCstr *s = lj_lib_checkstr(L, 1); + int32_t len = (int32_t)s->len; + int32_t start = lj_lib_optint(L, 2, 1); + int32_t stop = lj_lib_optint(L, 3, start); + int32_t n, i; + const unsigned char *p; + if (stop < 0) stop += len+1; + if (start < 0) start += len+1; + if (start <= 0) start = 1; + if (stop > len) stop = len; + if (start > stop) return FFH_RES(0); /* Empty interval: return no results. */ + start--; + n = stop - start; + if ((uint32_t)n > LUAI_MAXCSTACK) + lj_err_caller(L, LJ_ERR_STRSLC); + lj_state_checkstack(L, (MSize)n); + p = (const unsigned char *)strdata(s) + start; + for (i = 0; i < n; i++) + setintV(L->base + i-1-LJ_FR2, p[i]); + return FFH_RES(n); +} + +LJLIB_ASM(string_char) LJLIB_REC(.) +{ + int i, nargs = (int)(L->top - L->base); + char *buf = lj_buf_tmp(L, (MSize)nargs); + for (i = 1; i <= nargs; i++) { + int32_t k = lj_lib_checkint(L, i); + if (!checku8(k)) + lj_err_arg(L, i, LJ_ERR_BADVAL); + buf[i-1] = (char)k; + } + setstrV(L, L->base-1-LJ_FR2, lj_str_new(L, buf, (size_t)nargs)); + return FFH_RES(1); +} + +LJLIB_ASM(string_sub) LJLIB_REC(string_range 1) +{ + lj_lib_checkstr(L, 1); + lj_lib_checkint(L, 2); + setintV(L->base+2, lj_lib_optint(L, 3, -1)); + return FFH_RETRY; +} + +LJLIB_CF(string_rep) LJLIB_REC(.) +{ + GCstr *s = lj_lib_checkstr(L, 1); + int32_t rep = lj_lib_checkint(L, 2); + GCstr *sep = lj_lib_optstr(L, 3); + SBuf *sb = lj_buf_tmp_(L); + if (sep && rep > 1) { + GCstr *s2 = lj_buf_cat2str(L, sep, s); + lj_buf_reset(sb); + lj_buf_putstr(sb, s); + s = s2; + rep--; + } + sb = lj_buf_putstr_rep(sb, s, rep); + setstrV(L, L->top-1, lj_buf_str(L, sb)); + lj_gc_check(L); + return 1; +} + +LJLIB_ASM(string_reverse) LJLIB_REC(string_op IRCALL_lj_buf_putstr_reverse) +{ + lj_lib_checkstr(L, 1); + return FFH_RETRY; +} +LJLIB_ASM_(string_lower) LJLIB_REC(string_op IRCALL_lj_buf_putstr_lower) +LJLIB_ASM_(string_upper) LJLIB_REC(string_op IRCALL_lj_buf_putstr_upper) + +/* ------------------------------------------------------------------------ */ + +static int writer_buf(lua_State *L, const void *p, size_t size, void *sb) +{ + lj_buf_putmem((SBuf *)sb, p, (MSize)size); + UNUSED(L); + return 0; +} + +LJLIB_CF(string_dump) +{ + GCfunc *fn = lj_lib_checkfunc(L, 1); + int strip = L->base+1 < L->top && tvistruecond(L->base+1); + SBuf *sb = lj_buf_tmp_(L); /* Assumes lj_bcwrite() doesn't use tmpbuf. */ + L->top = L->base+1; + if (!isluafunc(fn) || lj_bcwrite(L, funcproto(fn), writer_buf, sb, strip)) + lj_err_caller(L, LJ_ERR_STRDUMP); + setstrV(L, L->top-1, lj_buf_str(L, sb)); + lj_gc_check(L); + return 1; +} + +/* ------------------------------------------------------------------------ */ + +/* macro to `unsign' a character */ +#define uchar(c) ((unsigned char)(c)) + +#define CAP_UNFINISHED (-1) +#define CAP_POSITION (-2) + +typedef struct MatchState { + const char *src_init; /* init of source string */ + const char *src_end; /* end (`\0') of source string */ + lua_State *L; + int level; /* total number of captures (finished or unfinished) */ + int depth; + struct { + const char *init; + ptrdiff_t len; + } capture[LUA_MAXCAPTURES]; +} MatchState; + +#define L_ESC '%' + +static int check_capture(MatchState *ms, int l) +{ + l -= '1'; + if (l < 0 || l >= ms->level || ms->capture[l].len == CAP_UNFINISHED) + lj_err_caller(ms->L, LJ_ERR_STRCAPI); + return l; +} + +static int capture_to_close(MatchState *ms) +{ + int level = ms->level; + for (level--; level>=0; level--) + if (ms->capture[level].len == CAP_UNFINISHED) return level; + lj_err_caller(ms->L, LJ_ERR_STRPATC); + return 0; /* unreachable */ +} + +static const char *classend(MatchState *ms, const char *p) +{ + switch (*p++) { + case L_ESC: + if (*p == '\0') + lj_err_caller(ms->L, LJ_ERR_STRPATE); + return p+1; + case '[': + if (*p == '^') p++; + do { /* look for a `]' */ + if (*p == '\0') + lj_err_caller(ms->L, LJ_ERR_STRPATM); + if (*(p++) == L_ESC && *p != '\0') + p++; /* skip escapes (e.g. `%]') */ + } while (*p != ']'); + return p+1; + default: + return p; + } +} + +static const unsigned char match_class_map[32] = { + 0,LJ_CHAR_ALPHA,0,LJ_CHAR_CNTRL,LJ_CHAR_DIGIT,0,0,LJ_CHAR_GRAPH,0,0,0,0, + LJ_CHAR_LOWER,0,0,0,LJ_CHAR_PUNCT,0,0,LJ_CHAR_SPACE,0, + LJ_CHAR_UPPER,0,LJ_CHAR_ALNUM,LJ_CHAR_XDIGIT,0,0,0,0,0,0,0 +}; + +static int match_class(int c, int cl) +{ + if ((cl & 0xc0) == 0x40) { + int t = match_class_map[(cl&0x1f)]; + if (t) { + t = lj_char_isa(c, t); + return (cl & 0x20) ? t : !t; + } + if (cl == 'z') return c == 0; + if (cl == 'Z') return c != 0; + } + return (cl == c); +} + +static int matchbracketclass(int c, const char *p, const char *ec) +{ + int sig = 1; + if (*(p+1) == '^') { + sig = 0; + p++; /* skip the `^' */ + } + while (++p < ec) { + if (*p == L_ESC) { + p++; + if (match_class(c, uchar(*p))) + return sig; + } + else if ((*(p+1) == '-') && (p+2 < ec)) { + p+=2; + if (uchar(*(p-2)) <= c && c <= uchar(*p)) + return sig; + } + else if (uchar(*p) == c) return sig; + } + return !sig; +} + +static int singlematch(int c, const char *p, const char *ep) +{ + switch (*p) { + case '.': return 1; /* matches any char */ + case L_ESC: return match_class(c, uchar(*(p+1))); + case '[': return matchbracketclass(c, p, ep-1); + default: return (uchar(*p) == c); + } +} + +static const char *match(MatchState *ms, const char *s, const char *p); + +static const char *matchbalance(MatchState *ms, const char *s, const char *p) +{ + if (*p == 0 || *(p+1) == 0) + lj_err_caller(ms->L, LJ_ERR_STRPATU); + if (*s != *p) { + return NULL; + } else { + int b = *p; + int e = *(p+1); + int cont = 1; + while (++s < ms->src_end) { + if (*s == e) { + if (--cont == 0) return s+1; + } else if (*s == b) { + cont++; + } + } + } + return NULL; /* string ends out of balance */ +} + +static const char *max_expand(MatchState *ms, const char *s, + const char *p, const char *ep) +{ + ptrdiff_t i = 0; /* counts maximum expand for item */ + while ((s+i)src_end && singlematch(uchar(*(s+i)), p, ep)) + i++; + /* keeps trying to match with the maximum repetitions */ + while (i>=0) { + const char *res = match(ms, (s+i), ep+1); + if (res) return res; + i--; /* else didn't match; reduce 1 repetition to try again */ + } + return NULL; +} + +static const char *min_expand(MatchState *ms, const char *s, + const char *p, const char *ep) +{ + for (;;) { + const char *res = match(ms, s, ep+1); + if (res != NULL) + return res; + else if (ssrc_end && singlematch(uchar(*s), p, ep)) + s++; /* try with one more repetition */ + else + return NULL; + } +} + +static const char *start_capture(MatchState *ms, const char *s, + const char *p, int what) +{ + const char *res; + int level = ms->level; + if (level >= LUA_MAXCAPTURES) lj_err_caller(ms->L, LJ_ERR_STRCAPN); + ms->capture[level].init = s; + ms->capture[level].len = what; + ms->level = level+1; + if ((res=match(ms, s, p)) == NULL) /* match failed? */ + ms->level--; /* undo capture */ + return res; +} + +static const char *end_capture(MatchState *ms, const char *s, + const char *p) +{ + int l = capture_to_close(ms); + const char *res; + ms->capture[l].len = s - ms->capture[l].init; /* close capture */ + if ((res = match(ms, s, p)) == NULL) /* match failed? */ + ms->capture[l].len = CAP_UNFINISHED; /* undo capture */ + return res; +} + +static const char *match_capture(MatchState *ms, const char *s, int l) +{ + size_t len; + l = check_capture(ms, l); + len = (size_t)ms->capture[l].len; + if ((size_t)(ms->src_end-s) >= len && + memcmp(ms->capture[l].init, s, len) == 0) + return s+len; + else + return NULL; +} + +static const char *match(MatchState *ms, const char *s, const char *p) +{ + if (++ms->depth > LJ_MAX_XLEVEL) + lj_err_caller(ms->L, LJ_ERR_STRPATX); + init: /* using goto's to optimize tail recursion */ + switch (*p) { + case '(': /* start capture */ + if (*(p+1) == ')') /* position capture? */ + s = start_capture(ms, s, p+2, CAP_POSITION); + else + s = start_capture(ms, s, p+1, CAP_UNFINISHED); + break; + case ')': /* end capture */ + s = end_capture(ms, s, p+1); + break; + case L_ESC: + switch (*(p+1)) { + case 'b': /* balanced string? */ + s = matchbalance(ms, s, p+2); + if (s == NULL) break; + p+=4; + goto init; /* else s = match(ms, s, p+4); */ + case 'f': { /* frontier? */ + const char *ep; char previous; + p += 2; + if (*p != '[') + lj_err_caller(ms->L, LJ_ERR_STRPATB); + ep = classend(ms, p); /* points to what is next */ + previous = (s == ms->src_init) ? '\0' : *(s-1); + if (matchbracketclass(uchar(previous), p, ep-1) || + !matchbracketclass(uchar(*s), p, ep-1)) { s = NULL; break; } + p=ep; + goto init; /* else s = match(ms, s, ep); */ + } + default: + if (lj_char_isdigit(uchar(*(p+1)))) { /* capture results (%0-%9)? */ + s = match_capture(ms, s, uchar(*(p+1))); + if (s == NULL) break; + p+=2; + goto init; /* else s = match(ms, s, p+2) */ + } + goto dflt; /* case default */ + } + break; + case '\0': /* end of pattern */ + break; /* match succeeded */ + case '$': + /* is the `$' the last char in pattern? */ + if (*(p+1) != '\0') goto dflt; + if (s != ms->src_end) s = NULL; /* check end of string */ + break; + default: dflt: { /* it is a pattern item */ + const char *ep = classend(ms, p); /* points to what is next */ + int m = ssrc_end && singlematch(uchar(*s), p, ep); + switch (*ep) { + case '?': { /* optional */ + const char *res; + if (m && ((res=match(ms, s+1, ep+1)) != NULL)) { + s = res; + break; + } + p=ep+1; + goto init; /* else s = match(ms, s, ep+1); */ + } + case '*': /* 0 or more repetitions */ + s = max_expand(ms, s, p, ep); + break; + case '+': /* 1 or more repetitions */ + s = (m ? max_expand(ms, s+1, p, ep) : NULL); + break; + case '-': /* 0 or more repetitions (minimum) */ + s = min_expand(ms, s, p, ep); + break; + default: + if (m) { s++; p=ep; goto init; } /* else s = match(ms, s+1, ep); */ + s = NULL; + break; + } + break; + } + } + ms->depth--; + return s; +} + +static void push_onecapture(MatchState *ms, int i, const char *s, const char *e) +{ + if (i >= ms->level) { + if (i == 0) /* ms->level == 0, too */ + lua_pushlstring(ms->L, s, (size_t)(e - s)); /* add whole match */ + else + lj_err_caller(ms->L, LJ_ERR_STRCAPI); + } else { + ptrdiff_t l = ms->capture[i].len; + if (l == CAP_UNFINISHED) lj_err_caller(ms->L, LJ_ERR_STRCAPU); + if (l == CAP_POSITION) + lua_pushinteger(ms->L, ms->capture[i].init - ms->src_init + 1); + else + lua_pushlstring(ms->L, ms->capture[i].init, (size_t)l); + } +} + +static int push_captures(MatchState *ms, const char *s, const char *e) +{ + int i; + int nlevels = (ms->level == 0 && s) ? 1 : ms->level; + luaL_checkstack(ms->L, nlevels, "too many captures"); + for (i = 0; i < nlevels; i++) + push_onecapture(ms, i, s, e); + return nlevels; /* number of strings pushed */ +} + +static int str_find_aux(lua_State *L, int find) +{ + GCstr *s = lj_lib_checkstr(L, 1); + GCstr *p = lj_lib_checkstr(L, 2); + int32_t start = lj_lib_optint(L, 3, 1); + MSize st; + if (start < 0) start += (int32_t)s->len; else start--; + if (start < 0) start = 0; + st = (MSize)start; + if (st > s->len) { +#if LJ_52 + setnilV(L->top-1); + return 1; +#else + st = s->len; +#endif + } + if (find && ((L->base+3 < L->top && tvistruecond(L->base+3)) || + !lj_str_haspattern(p))) { /* Search for fixed string. */ + const char *q = lj_str_find(strdata(s)+st, strdata(p), s->len-st, p->len); + if (q) { + setintV(L->top-2, (int32_t)(q-strdata(s)) + 1); + setintV(L->top-1, (int32_t)(q-strdata(s)) + (int32_t)p->len); + return 2; + } + } else { /* Search for pattern. */ + MatchState ms; + const char *pstr = strdata(p); + const char *sstr = strdata(s) + st; + int anchor = 0; + if (*pstr == '^') { pstr++; anchor = 1; } + ms.L = L; + ms.src_init = strdata(s); + ms.src_end = strdata(s) + s->len; + do { /* Loop through string and try to match the pattern. */ + const char *q; + ms.level = ms.depth = 0; + q = match(&ms, sstr, pstr); + if (q) { + if (find) { + setintV(L->top++, (int32_t)(sstr-(strdata(s)-1))); + setintV(L->top++, (int32_t)(q-strdata(s))); + return push_captures(&ms, NULL, NULL) + 2; + } else { + return push_captures(&ms, sstr, q); + } + } + } while (sstr++ < ms.src_end && !anchor); + } + setnilV(L->top-1); /* Not found. */ + return 1; +} + +LJLIB_CF(string_find) LJLIB_REC(.) +{ + return str_find_aux(L, 1); +} + +LJLIB_CF(string_match) +{ + return str_find_aux(L, 0); +} + +LJLIB_NOREG LJLIB_CF(string_gmatch_aux) +{ + const char *p = strVdata(lj_lib_upvalue(L, 2)); + GCstr *str = strV(lj_lib_upvalue(L, 1)); + const char *s = strdata(str); + TValue *tvpos = lj_lib_upvalue(L, 3); + const char *src = s + tvpos->u32.lo; + MatchState ms; + ms.L = L; + ms.src_init = s; + ms.src_end = s + str->len; + for (; src <= ms.src_end; src++) { + const char *e; + ms.level = ms.depth = 0; + if ((e = match(&ms, src, p)) != NULL) { + int32_t pos = (int32_t)(e - s); + if (e == src) pos++; /* Ensure progress for empty match. */ + tvpos->u32.lo = (uint32_t)pos; + return push_captures(&ms, src, e); + } + } + return 0; /* not found */ +} + +LJLIB_CF(string_gmatch) +{ + lj_lib_checkstr(L, 1); + lj_lib_checkstr(L, 2); + L->top = L->base+3; + (L->top-1)->u64 = 0; + lj_lib_pushcc(L, lj_cf_string_gmatch_aux, FF_string_gmatch_aux, 3); + return 1; +} + +static void add_s(MatchState *ms, luaL_Buffer *b, const char *s, const char *e) +{ + size_t l, i; + const char *news = lua_tolstring(ms->L, 3, &l); + for (i = 0; i < l; i++) { + if (news[i] != L_ESC) { + luaL_addchar(b, news[i]); + } else { + i++; /* skip ESC */ + if (!lj_char_isdigit(uchar(news[i]))) { + luaL_addchar(b, news[i]); + } else if (news[i] == '0') { + luaL_addlstring(b, s, (size_t)(e - s)); + } else { + push_onecapture(ms, news[i] - '1', s, e); + luaL_addvalue(b); /* add capture to accumulated result */ + } + } + } +} + +static void add_value(MatchState *ms, luaL_Buffer *b, + const char *s, const char *e) +{ + lua_State *L = ms->L; + switch (lua_type(L, 3)) { + case LUA_TNUMBER: + case LUA_TSTRING: { + add_s(ms, b, s, e); + return; + } + case LUA_TFUNCTION: { + int n; + lua_pushvalue(L, 3); + n = push_captures(ms, s, e); + lua_call(L, n, 1); + break; + } + case LUA_TTABLE: { + push_onecapture(ms, 0, s, e); + lua_gettable(L, 3); + break; + } + } + if (!lua_toboolean(L, -1)) { /* nil or false? */ + lua_pop(L, 1); + lua_pushlstring(L, s, (size_t)(e - s)); /* keep original text */ + } else if (!lua_isstring(L, -1)) { + lj_err_callerv(L, LJ_ERR_STRGSRV, luaL_typename(L, -1)); + } + luaL_addvalue(b); /* add result to accumulator */ +} + +LJLIB_CF(string_gsub) +{ + size_t srcl; + const char *src = luaL_checklstring(L, 1, &srcl); + const char *p = luaL_checkstring(L, 2); + int tr = lua_type(L, 3); + int max_s = luaL_optint(L, 4, (int)(srcl+1)); + int anchor = (*p == '^') ? (p++, 1) : 0; + int n = 0; + MatchState ms; + luaL_Buffer b; + if (!(tr == LUA_TNUMBER || tr == LUA_TSTRING || + tr == LUA_TFUNCTION || tr == LUA_TTABLE)) + lj_err_arg(L, 3, LJ_ERR_NOSFT); + luaL_buffinit(L, &b); + ms.L = L; + ms.src_init = src; + ms.src_end = src+srcl; + while (n < max_s) { + const char *e; + ms.level = ms.depth = 0; + e = match(&ms, src, p); + if (e) { + n++; + add_value(&ms, &b, src, e); + } + if (e && e>src) /* non empty match? */ + src = e; /* skip it */ + else if (src < ms.src_end) + luaL_addchar(&b, *src++); + else + break; + if (anchor) + break; + } + luaL_addlstring(&b, src, (size_t)(ms.src_end-src)); + luaL_pushresult(&b); + lua_pushinteger(L, n); /* number of substitutions */ + return 2; +} + +/* ------------------------------------------------------------------------ */ + +/* Emulate tostring() inline. */ +static GCstr *string_fmt_tostring(lua_State *L, int arg, int retry) +{ + TValue *o = L->base+arg-1; + cTValue *mo; + lua_assert(o < L->top); /* Caller already checks for existence. */ + if (LJ_LIKELY(tvisstr(o))) + return strV(o); + if (retry != 2 && !tvisnil(mo = lj_meta_lookup(L, o, MM_tostring))) { + copyTV(L, L->top++, mo); + copyTV(L, L->top++, o); + lua_call(L, 1, 1); + copyTV(L, L->base+arg-1, --L->top); + return NULL; /* Buffer may be overwritten, retry. */ + } + return lj_strfmt_obj(L, o); +} + +LJLIB_CF(string_format) LJLIB_REC(.) +{ + int arg, top = (int)(L->top - L->base); + GCstr *fmt; + SBuf *sb; + FormatState fs; + SFormat sf; + int retry = 0; +again: + arg = 1; + sb = lj_buf_tmp_(L); + fmt = lj_lib_checkstr(L, arg); + lj_strfmt_init(&fs, strdata(fmt), fmt->len); + while ((sf = lj_strfmt_parse(&fs)) != STRFMT_EOF) { + if (sf == STRFMT_LIT) { + lj_buf_putmem(sb, fs.str, fs.len); + } else if (sf == STRFMT_ERR) { + lj_err_callerv(L, LJ_ERR_STRFMT, strdata(lj_str_new(L, fs.str, fs.len))); + } else { + if (++arg > top) + luaL_argerror(L, arg, lj_obj_typename[0]); + switch (STRFMT_TYPE(sf)) { + case STRFMT_INT: + if (tvisint(L->base+arg-1)) { + int32_t k = intV(L->base+arg-1); + if (sf == STRFMT_INT) + lj_strfmt_putint(sb, k); /* Shortcut for plain %d. */ + else + lj_strfmt_putfxint(sb, sf, k); + } else { + lj_strfmt_putfnum_int(sb, sf, lj_lib_checknum(L, arg)); + } + break; + case STRFMT_UINT: + if (tvisint(L->base+arg-1)) + lj_strfmt_putfxint(sb, sf, intV(L->base+arg-1)); + else + lj_strfmt_putfnum_uint(sb, sf, lj_lib_checknum(L, arg)); + break; + case STRFMT_NUM: + lj_strfmt_putfnum(sb, sf, lj_lib_checknum(L, arg)); + break; + case STRFMT_STR: { + GCstr *str = string_fmt_tostring(L, arg, retry); + if (str == NULL) + retry = 1; + else if ((sf & STRFMT_T_QUOTED)) + lj_strfmt_putquoted(sb, str); /* No formatting. */ + else + lj_strfmt_putfstr(sb, sf, str); + break; + } + case STRFMT_CHAR: + lj_strfmt_putfchar(sb, sf, lj_lib_checkint(L, arg)); + break; + case STRFMT_PTR: /* No formatting. */ + lj_strfmt_putptr(sb, lj_obj_ptr(L->base+arg-1)); + break; + default: + lua_assert(0); + break; + } + } + } + if (retry++ == 1) goto again; + setstrV(L, L->top-1, lj_buf_str(L, sb)); + lj_gc_check(L); + return 1; +} + +/* ------------------------------------------------------------------------ */ + +#include "lj_libdef.h" + +LUALIB_API int luaopen_string(lua_State *L) +{ + GCtab *mt; + global_State *g; + LJ_LIB_REG(L, LUA_STRLIBNAME, string); + mt = lj_tab_new(L, 0, 1); + /* NOBARRIER: basemt is a GC root. */ + g = G(L); + setgcref(basemt_it(g, LJ_TSTR), obj2gco(mt)); + settabV(L, lj_tab_setstr(L, mt, mmname_str(g, MM_index)), tabV(L->top-1)); + mt->nomm = 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 literal 0 HcmV?d00001 diff --git a/lib/LuaJIT/lib_string_dyn.o b/lib/LuaJIT/lib_string_dyn.o new file mode 100644 index 0000000000000000000000000000000000000000..38fe18914e07587e85b7b563416a065547aa20fb GIT binary patch literal 16344 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 literal 0 HcmV?d00001 diff --git a/lib/LuaJIT/lib_table.c b/lib/LuaJIT/lib_table.c new file mode 100644 index 0000000..0450f1f --- /dev/null +++ b/lib/LuaJIT/lib_table.c @@ -0,0 +1,327 @@ +/* +** Table library. +** Copyright (C) 2005-2017 Mike Pall. See Copyright Notice in luajit.h +** +** Major portions taken verbatim or adapted from the Lua interpreter. +** Copyright (C) 1994-2008 Lua.org, PUC-Rio. See Copyright Notice in lua.h +*/ + +#define lib_table_c +#define LUA_LIB + +#include "lua.h" +#include "lauxlib.h" +#include "lualib.h" + +#include "lj_obj.h" +#include "lj_gc.h" +#include "lj_err.h" +#include "lj_buf.h" +#include "lj_tab.h" +#include "lj_ff.h" +#include "lj_lib.h" + +/* ------------------------------------------------------------------------ */ + +#define LJLIB_MODULE_table + +LJLIB_LUA(table_foreachi) /* + function(t, f) + CHECK_tab(t) + CHECK_func(f) + for i=1,#t do + local r = f(i, t[i]) + if r ~= nil then return r end + end + end +*/ + +LJLIB_LUA(table_foreach) /* + function(t, f) + CHECK_tab(t) + CHECK_func(f) + for k, v in PAIRS(t) do + local r = f(k, v) + if r ~= nil then return r end + end + end +*/ + +LJLIB_LUA(table_getn) /* + function(t) + CHECK_tab(t) + return #t + end +*/ + +LJLIB_CF(table_maxn) +{ + GCtab *t = lj_lib_checktab(L, 1); + TValue *array = tvref(t->array); + Node *node; + lua_Number m = 0; + ptrdiff_t i; + for (i = (ptrdiff_t)t->asize - 1; i >= 0; i--) + if (!tvisnil(&array[i])) { + m = (lua_Number)(int32_t)i; + break; + } + node = noderef(t->node); + for (i = (ptrdiff_t)t->hmask; i >= 0; i--) + if (!tvisnil(&node[i].val) && tvisnumber(&node[i].key)) { + lua_Number n = numberVnum(&node[i].key); + if (n > m) m = n; + } + setnumV(L->top-1, m); + return 1; +} + +LJLIB_CF(table_insert) LJLIB_REC(.) +{ + GCtab *t = lj_lib_checktab(L, 1); + int32_t n, i = (int32_t)lj_tab_len(t) + 1; + int nargs = (int)((char *)L->top - (char *)L->base); + if (nargs != 2*sizeof(TValue)) { + if (nargs != 3*sizeof(TValue)) + lj_err_caller(L, LJ_ERR_TABINS); + /* NOBARRIER: This just moves existing elements around. */ + for (n = lj_lib_checkint(L, 2); i > n; i--) { + /* The set may invalidate the get pointer, so need to do it first! */ + TValue *dst = lj_tab_setint(L, t, i); + cTValue *src = lj_tab_getint(t, i-1); + if (src) { + copyTV(L, dst, src); + } else { + setnilV(dst); + } + } + i = n; + } + { + TValue *dst = lj_tab_setint(L, t, i); + copyTV(L, dst, L->top-1); /* Set new value. */ + lj_gc_barriert(L, t, dst); + } + return 0; +} + +LJLIB_LUA(table_remove) /* + function(t, pos) + CHECK_tab(t) + local len = #t + if pos == nil then + if len ~= 0 then + local old = t[len] + t[len] = nil + return old + end + else + CHECK_int(pos) + if pos >= 1 and pos <= len then + local old = t[pos] + for i=pos+1,len do + t[i-1] = t[i] + end + t[len] = nil + return old + end + end + end +*/ + +LJLIB_LUA(table_move) /* + function(a1, f, e, t, a2) + CHECK_tab(a1) + CHECK_int(f) + CHECK_int(e) + CHECK_int(t) + if a2 == nil then a2 = a1 end + CHECK_tab(a2) + if e >= f then + local d = t - f + if t > e or t <= f or a2 ~= a1 then + for i=f,e do a2[i+d] = a1[i] end + else + for i=e,f,-1 do a2[i+d] = a1[i] end + end + end + return a2 + end +*/ + +LJLIB_CF(table_concat) LJLIB_REC(.) +{ + GCtab *t = lj_lib_checktab(L, 1); + GCstr *sep = lj_lib_optstr(L, 2); + int32_t i = lj_lib_optint(L, 3, 1); + int32_t e = (L->base+3 < L->top && !tvisnil(L->base+3)) ? + lj_lib_checkint(L, 4) : (int32_t)lj_tab_len(t); + SBuf *sb = lj_buf_tmp_(L); + SBuf *sbx = lj_buf_puttab(sb, t, sep, i, e); + if (LJ_UNLIKELY(!sbx)) { /* Error: bad element type. */ + int32_t idx = (int32_t)(intptr_t)sbufP(sb); + cTValue *o = lj_tab_getint(t, idx); + lj_err_callerv(L, LJ_ERR_TABCAT, + lj_obj_itypename[o ? itypemap(o) : ~LJ_TNIL], idx); + } + setstrV(L, L->top-1, lj_buf_str(L, sbx)); + lj_gc_check(L); + return 1; +} + +/* ------------------------------------------------------------------------ */ + +static void set2(lua_State *L, int i, int j) +{ + lua_rawseti(L, 1, i); + lua_rawseti(L, 1, j); +} + +static int sort_comp(lua_State *L, int a, int b) +{ + if (!lua_isnil(L, 2)) { /* function? */ + int res; + lua_pushvalue(L, 2); + lua_pushvalue(L, a-1); /* -1 to compensate function */ + lua_pushvalue(L, b-2); /* -2 to compensate function and `a' */ + lua_call(L, 2, 1); + res = lua_toboolean(L, -1); + lua_pop(L, 1); + return res; + } else { /* a < b? */ + return lua_lessthan(L, a, b); + } +} + +static void auxsort(lua_State *L, int l, int u) +{ + while (l < u) { /* for tail recursion */ + int i, j; + /* sort elements a[l], a[(l+u)/2] and a[u] */ + lua_rawgeti(L, 1, l); + lua_rawgeti(L, 1, u); + if (sort_comp(L, -1, -2)) /* a[u] < a[l]? */ + set2(L, l, u); /* swap a[l] - a[u] */ + else + lua_pop(L, 2); + if (u-l == 1) break; /* only 2 elements */ + i = (l+u)/2; + lua_rawgeti(L, 1, i); + lua_rawgeti(L, 1, l); + if (sort_comp(L, -2, -1)) { /* a[i]= P */ + while (lua_rawgeti(L, 1, ++i), sort_comp(L, -1, -2)) { + if (i>=u) lj_err_caller(L, LJ_ERR_TABSORT); + lua_pop(L, 1); /* remove a[i] */ + } + /* repeat --j until a[j] <= P */ + while (lua_rawgeti(L, 1, --j), sort_comp(L, -3, -1)) { + if (j<=l) lj_err_caller(L, LJ_ERR_TABSORT); + lua_pop(L, 1); /* remove a[j] */ + } + if (jbase+1)) + lj_lib_checkfunc(L, 2); + auxsort(L, 1, n); + return 0; +} + +#if LJ_52 +LJLIB_PUSH("n") +LJLIB_CF(table_pack) +{ + TValue *array, *base = L->base; + MSize i, n = (uint32_t)(L->top - base); + GCtab *t = lj_tab_new(L, n ? n+1 : 0, 1); + /* NOBARRIER: The table is new (marked white). */ + setintV(lj_tab_setstr(L, t, strV(lj_lib_upvalue(L, 1))), (int32_t)n); + for (array = tvref(t->array) + 1, i = 0; i < n; i++) + copyTV(L, &array[i], &base[i]); + settabV(L, base, t); + L->top = base+1; + lj_gc_check(L); + return 1; +} +#endif + +LJLIB_NOREG LJLIB_CF(table_new) LJLIB_REC(.) +{ + int32_t a = lj_lib_checkint(L, 1); + int32_t h = lj_lib_checkint(L, 2); + lua_createtable(L, a, h); + return 1; +} + +LJLIB_NOREG LJLIB_CF(table_clear) LJLIB_REC(.) +{ + lj_tab_clear(lj_lib_checktab(L, 1)); + return 0; +} + +static int luaopen_table_new(lua_State *L) +{ + return lj_lib_postreg(L, lj_cf_table_new, FF_table_new, "new"); +} + +static int luaopen_table_clear(lua_State *L) +{ + return lj_lib_postreg(L, lj_cf_table_clear, FF_table_clear, "clear"); +} + +/* ------------------------------------------------------------------------ */ + +#include "lj_libdef.h" + +LUALIB_API int luaopen_table(lua_State *L) +{ + LJ_LIB_REG(L, LUA_TABLIBNAME, table); +#if LJ_52 + lua_getglobal(L, "unpack"); + lua_setfield(L, -2, "unpack"); +#endif + lj_lib_prereg(L, LUA_TABLIBNAME ".new", luaopen_table_new, tabV(L->top-1)); + lj_lib_prereg(L, LUA_TABLIBNAME ".clear", luaopen_table_clear, tabV(L->top-1)); + return 1; +} + diff --git a/lib/LuaJIT/lib_table.o b/lib/LuaJIT/lib_table.o new file mode 100644 index 0000000000000000000000000000000000000000..0f8bac89fdc34bbdf1dab229a78a1c184805df05 GIT binary patch literal 8808 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 i`Z%pG6{S>kqD;eJ5RsTPEDf8$6 literal 0 HcmV?d00001 diff --git a/lib/LuaJIT/lib_table_dyn.o b/lib/LuaJIT/lib_table_dyn.o new file mode 100644 index 0000000000000000000000000000000000000000..0f8bac89fdc34bbdf1dab229a78a1c184805df05 GIT binary patch literal 8808 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 i`Z%pG6{S>kqD;eJ5RsTPEDf8$6 literal 0 HcmV?d00001 diff --git a/lib/LuaJIT/libluajit.a b/lib/LuaJIT/libluajit.a new file mode 100644 index 0000000000000000000000000000000000000000..c82e81cd8914d68135d55224133655172bcd90a9 GIT binary patch literal 866936 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%0A(gM*_8YigGup&dD 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�JxMMs8RD+ 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

p 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

i_}`Ad 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 literal 0 HcmV?d00001 diff --git a/lib/LuaJIT/lj_crecord_dyn.o b/lib/LuaJIT/lj_crecord_dyn.o new file mode 100644 index 0000000000000000000000000000000000000000..8e826204bceebf7594d2f4e65759fdcbef5bcfe3 GIT binary patch literal 38208 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

i_}`Ad z#56J`&NHk;@=xX&h&L+3OvW>bH?5s{l5?i$`a_hqH+Wa{<8e;O?5Mqs9IUXz2+ou6 znI}X$z<70nK&3ZoR40&I`DnR;2@8W%=gou0?eYxx2H7b_(NSKaV8UGqRb(vz zP8aD3^rXlj>s?;?#sidh&ki)B9P5$nh5UR4dA#dkXB=9-3WZZxnfVJ6V;D~;=^D4_ zcgQw;#FXL+=YNNSpwG9JUV!tJte zMf8%LuX!ltrl`!h&{R=&rsv7MaeEn0_PNO&E%;M2wM4X%CwlZ+ZieT%oM(2P$InEpa#Iu*^bQ?!iYQDBTHxs}FFRL`$@h_V61cr(+F|*ZJ337jN=u_U{QLgI)ex z5Yr=UX}%q`-yCCI{0^qe6i~!v_Uqw+;JUdS*8|_O)|b;RU&cVsx$&@wBtB(J>6du| zPonk?c@#(Z8?h7J-EbAPpzaIMHUqkJjnB2v%=tLfj+7OLPOdOV6=7Dfr-4^{csbhS z1mHY;gW)`UC9&f5x%mm3u-PZU_#R3(t7l&-Os5csUi8LWs|qoRAg-?H8du|}DK>=p zb!Rh$fr}$ZYicTJiTgRdMS%L?|8QUq&Isd5+~bXhv2ZJ6#fdaJTs9&3p_W*P3mtjM zAdacg;Y_)EPg4n(lMZ?f?cf8W10M37f1xsIF_IQe7ztM~h>K%Cq2D{Oj^S)6nGh!B zs56dVjlfYsd0t2S7EG$VP~Er32_d%c$Y@N6zd_L`{JfM0?X=PcoffQ@fo4Y&(^;PRFG@ zk!~0KU_GZsxpCQir)z%V%$4B`!Dx)#Y&ffUo5KuJg2X-B4XB&~9SoW2IEWh?PG|Rrs$&R> zw!gsH?f5x(vo1F>wZtz-XsFv2jDK3E27@b%h@${9h$U+J2Q1nYCPks06*BC4Pnrn3e0yxFe`%Xu0)e#F>J1 z{I5X*T~e5i-znjKPj{@PcHY*do$i|jpK?35f&yx$A5Dlr*Fq^!y+`Sff()6U+>sBV z{Pfbl@;LMOT}tpux6t0PEjQ$lm*5jgp7$;AyIKKd`IW(nD~7BF9nKtzlRC&g;Y@)% z*avf>=JGjM{9aaT{uFr3cPTlLUYsks*`h2I zVm{wyA0^PGbZY&=vB8Hq{O%^t$-y}MWFb#@5Cx^()kt=-Lp-FenYzw(oHP|7WUlJ# z*K_i;d;4)ln9sF(+W!issYzu`$J$Uk_7PbIX54ga9d2T0n`(KL?HfK@-V!IET_pj(AKiiEVZ9@b>pPDD3lW${iFddUy2#{r1UqHUN`D9hrGxm`rc=FyLnNmd?UjS{D96y z*EMaM9uQj$w$;p4(JL=BBh`twPo!DgzZ?)15_*8ykbjExuBIQPtMPI02IF%3Sh_Q3 z1Vv*n>|#$;-3Esj?=O0ceyB}}2c=4QX4@lt6eV;D;yx#o?p%%X*naKK9h;YX1PS5xME2_34ePNb{Hk zg~8D~YO&fntQtzWozKB(;G}{&fs7PGSMT2jnTFslfY9 zI8eqZ3$g0JK2G2x9K-A2uRo7pk1OIT?De^5Swv>9xbL^p?@3bJFGa!2L-f+A-QG$o z78!D5xC~JYi1k!G~jm|RuDwIjDt(n>!f01CMSmrWkz0>T!wiUPeQr3kEB$}15gyh(MqLo$R! zLdZx>_u~gTD)gdP>3ye9QN(^{f>S6QA!ZT0_!KD^Oux$Y#Zz$U>#2}+MAPowkDh4X z9dIL61ahzJF|GaLclFi`I7?l4^w7`vC67N+U#<|&Xuyla+!c-AXiaXwb955PH3^q2 zmtnt5qG3fOy6tpDz$;n6%oNbVE^ziX;uL}o@hdH4C^Ch2Igg0eWqwa}bN?uMmW}JJ z-FpbRLv$^=fpm-?4tr&f+q9N`o~F6^K$^Lv5oMl5d6}1XZIWbx-{3cg>DSRdrX2*) zogsS%|IiF_^h$=4jv9$4n46^2vR~q@=Y#kKIk@%7;YO~TKj9f*19oZ_*^vw^^7j&8_gL6ksotk*l^Ph!Xf7yUj~F8PxBI{s6(hYyDP z;&*qv&*cAq{~14a>~|`MjK6%!(8|%ntA~$(HfYq*$G`MSpMP<9Zk#n?!?81N||Gc3h73sy;5UPElGJ z{w*dteCCTU#_pEa8|C**d^gMM2!)xD;od;FwBnwEF{PEMV7PS1-Mx&`5z7jV(ot4# zqjX|mNuSbDK=jffpcqqHff5kWEH3!m-{X@A$wT#oXz<`eKe?2@9-ZnLU%ER`U&c?gErw zM!ceOIbS+FXR}(Pyezd_tfza zC5nHmrEG6^)p1TX9v4!!#y~-zo<#_gT@t?#**!kjm*P*Zt+{c=ea7Q#d}*`4U~JEV z8?K#CmHf`9KDs%pJLcC-cN4GdQ47SZ3dL8tyl^f0*&yFCzO-^mX+=ahA?i!oDy0wk zF433Q^(hVM#TVl?Eua0Bo}qpBP+!Wif^o5oY|<(WGzp7VJO{|9C z!W-+P8XwI8L2Q4~XCeK`(L%rRENQs-$2^cwisuFTjY|6D$^?GI$_7UDPnFLP5K$MN zf$wLTqJE-HNZPBq(ar~HxU>wFny>|$kDipYOC>GFD|8()if=9rk>8>N>h$@5{#;3Z z3w`PFThlyl>oV}34)PKjOZY7P*YrdwN#jqo>_J1+Me;JSyM>U00CYQb6G|RX!dp@#*rO;}2X{OMJJ) z-8ds6@rhETjq>?*60ekV3dbA3Zj^Yv98Vk{0J~Y@bU2qjJfDk8v`P}od-}Q+|49Bu zI&6*)&)N7+;UBl_D}>7V7T>?cKf+f?oaa(t&q%!TYq-FN?Hu22_($|*U&jSLJfDe6 z$PpcG7nCD)l+|+ zod^FmaH^Ll7SzY_3BHXy^w;FUXGuQ0FGOK{I4;3AjT_=$Gmc>&#}~L-!OA{t&gsz)y|){Al4ACjm>ZH&1krB;rX>QXT)X{+?jMe zr(xQU=gf=IWvpRF?W}3D8X6S*f{}DnOMIq*Y*xcO`LGtX%S(Q^0OGmj=EZNC78fO{ zs(CXS8s;yW7Mm~Yp(^WR4byU*Z=4mYn=@fH`eAJ&Y4#?KXZVz- z8T=Gov~b!@@wu@%5OfYVs1{9Lz)}g?h8c?*X4U!7p>=iBxCcQxbJk)iADeN*G>Ac$ zqU%MUBNkkX9WY!Shf#Zg(hnyIA=sBX^8X$xmGpnr(bf_XFQr_Go-vx^Wj8s@}i zyU5%*H{_%eRY0zJGj8IBNHJlIlD@8P&a`>67Abe=z78s^s^LP?Js&ci#|lJ%KxB(tMaeUgO8ITGx1UB<9Tp*El2be z;=A(C3gHGHGNUu`&&6Z3Z-WovCHUv!&q>@{Z$`n@c9mkSOCK-&R}`Gq*)IP}6kKiB z<9YBc3a;w^Q4$&-Z@up-xJo~V@}oV_#}r(pKcwKQoLAGru?Ien<-wm*a8+;bD7ec1 zWFGtyt>=0tD!9sjfr6|2@5_U4&4Z7j4*Jq=Kvb zUsQ0F{%r+U+Z!Mve7ybgT?JSD*lh}~*4wP$bm{8h?L7EbNeFzr^**fNltXd({87Ox z75pUySL?lij0qnv|7#SSin{#c3a;AWP--MTUOrP4T;+3%f~)eZRB*a<>)ov2D*b19 z@KWjwd`KSEKPMGj<@0nNe5-=1^6XP^mCrD$3?HgjZLgu=syy`yu8xZ)1y}XlqTpwt z4p%?>6?X8djsJGsm6mrN=AkcwO7TzrQoVO*C@Cu&xZ+ZKZk_Chxk7xp+(%-1y>bPrAaFx#k3a;|`gMzE{FDkf7 zzemAU`q1#6+f}aMDt${H{6hs->-|E(RX&y9>bc%(1y|{(D7ebs%7gzl557^s)$y`d z!Bzfm&_*&oWVfo{JEh>2c;?#G@2YWwkJrx6IX{QfPe~k;`YUf~)#(&x7w#aJAkO3a;|`+J(7#Rr=fV;M)~k)x+QN z;Gxku{wn=V3QjJ>ZSU&}uF7*z!BsmM^qm}kmEO#Q->BdNm3nVha8*AKD7ec1mWy)r zs`1H{7w2%5ep?=V!zDR-)t(1^H;1e3{jP$m{QKAB=vBO4!Bss!so*O83kt64;X?&i z=}+dt4c*5d?>Mbf@Jdj){k~Gc)%n*qCdWsucWEBnR&Z7RClp-evp)}BG&WbSYA3pa zt9<6?!ABZ7J}Uk4Jos7#SNr{a1y}hD3ir(abp@{kiL2+M3a;`weO!)@s<#&uoLs5P z=OqOvSL))u$LIJ|D)_e*T&?$E1y|d1y}vj>k9rA zMg9*JT%`|3a`LG34GONd*MC`#Ud2yWaJ9W76;JLA5`#41s{BQ9zF`L>T`#Jt9*_rxGK+CSLFDgqttt`f~)+0ng=hI z`_Dn}Q2DRTgKtxCRi9rdxXP#M%AA}kK2E_^KJ^N&(uXJY%;!o4SNYtc;3}WvdGG;~ zbNtCQy8XK(5B`9HtNeeX;Ho^U6bpXdJyiDRNJ@_!G=f^$x@-hMZ(Sr|?e4g~+l@fp2 zgX6d^e^z_&?@9c54}Q7C+dTLP$$x_fA1U!I9(1up=IPwxmDd!Cik| z>A~eKziUC)uD_iwAKWUxU3|B^U*W;sbIV>ktfYeYl=#KB%f~&p9P{Arx#czw?w(Jk z-@&Ah%ZE3?a6i?9yXTYJJh*#4Iat7KvV7e0$(0`5J*WD-2Y1hrmIS-jFbDqO`b*;Bet{+x-aQB?&KRmd5&NEclm5+O#^I{L~p69&I zgHM!k_HqyIp67hpgS+QBU-jVbdCs!lUFC7lbH+Tld!F-25AL4ZJmJCJ^O?Tk3zpo3 z1913`HHh!o?p4FAxif~xW-X5ShSSMR-|!n2Ebt9yv|+wLYUkf{(=42H96oP;Y}RnR zv3TBs*o@jAOR8D3r`0vkA-n(mrxGk^2uL+$8P>?F-(Q`D_@b=qEuYaL)2Z>`kN;Qz zPYx2_3jIefEx_qp|Z zDwM+{OIMAnp4vZkxg?Q_rS{X%@V5U_U{u8j{8K;sriwf1X0wl6aoq3mc;a4XiEwdB zmRB*l_R?M_F>m{Q*NBoMT!F=HR(^TwBQ^Ed{(B^a>=?uqdfQLol!t`NO}&r5e0Ab0``26k{{ZcXgbx4! literal 0 HcmV?d00001 diff --git a/lib/LuaJIT/lj_ctype.c b/lib/LuaJIT/lj_ctype.c new file mode 100644 index 0000000..0ea89c7 --- /dev/null +++ b/lib/LuaJIT/lj_ctype.c @@ -0,0 +1,637 @@ +/* +** C type management. +** Copyright (C) 2005-2017 Mike Pall. See Copyright Notice in luajit.h +*/ + +#include "lj_obj.h" + +#if LJ_HASFFI + +#include "lj_gc.h" +#include "lj_err.h" +#include "lj_str.h" +#include "lj_tab.h" +#include "lj_strfmt.h" +#include "lj_ctype.h" +#include "lj_ccallback.h" +#include "lj_buf.h" + +/* -- C type definitions -------------------------------------------------- */ + +/* Predefined typedefs. */ +#define CTTDDEF(_) \ + /* Vararg handling. */ \ + _("va_list", P_VOID) \ + _("__builtin_va_list", P_VOID) \ + _("__gnuc_va_list", P_VOID) \ + /* From stddef.h. */ \ + _("ptrdiff_t", INT_PSZ) \ + _("size_t", UINT_PSZ) \ + _("wchar_t", WCHAR) \ + /* Subset of stdint.h. */ \ + _("int8_t", INT8) \ + _("int16_t", INT16) \ + _("int32_t", INT32) \ + _("int64_t", INT64) \ + _("uint8_t", UINT8) \ + _("uint16_t", UINT16) \ + _("uint32_t", UINT32) \ + _("uint64_t", UINT64) \ + _("intptr_t", INT_PSZ) \ + _("uintptr_t", UINT_PSZ) \ + /* From POSIX. */ \ + _("ssize_t", INT_PSZ) \ + /* End of typedef list. */ + +/* Keywords (only the ones we actually care for). */ +#define CTKWDEF(_) \ + /* Type specifiers. */ \ + _("void", -1, CTOK_VOID) \ + _("_Bool", 0, CTOK_BOOL) \ + _("bool", 1, CTOK_BOOL) \ + _("char", 1, CTOK_CHAR) \ + _("int", 4, CTOK_INT) \ + _("__int8", 1, CTOK_INT) \ + _("__int16", 2, CTOK_INT) \ + _("__int32", 4, CTOK_INT) \ + _("__int64", 8, CTOK_INT) \ + _("float", 4, CTOK_FP) \ + _("double", 8, CTOK_FP) \ + _("long", 0, CTOK_LONG) \ + _("short", 0, CTOK_SHORT) \ + _("_Complex", 0, CTOK_COMPLEX) \ + _("complex", 0, CTOK_COMPLEX) \ + _("__complex", 0, CTOK_COMPLEX) \ + _("__complex__", 0, CTOK_COMPLEX) \ + _("signed", 0, CTOK_SIGNED) \ + _("__signed", 0, CTOK_SIGNED) \ + _("__signed__", 0, CTOK_SIGNED) \ + _("unsigned", 0, CTOK_UNSIGNED) \ + /* Type qualifiers. */ \ + _("const", 0, CTOK_CONST) \ + _("__const", 0, CTOK_CONST) \ + _("__const__", 0, CTOK_CONST) \ + _("volatile", 0, CTOK_VOLATILE) \ + _("__volatile", 0, CTOK_VOLATILE) \ + _("__volatile__", 0, CTOK_VOLATILE) \ + _("restrict", 0, CTOK_RESTRICT) \ + _("__restrict", 0, CTOK_RESTRICT) \ + _("__restrict__", 0, CTOK_RESTRICT) \ + _("inline", 0, CTOK_INLINE) \ + _("__inline", 0, CTOK_INLINE) \ + _("__inline__", 0, CTOK_INLINE) \ + /* Storage class specifiers. */ \ + _("typedef", 0, CTOK_TYPEDEF) \ + _("extern", 0, CTOK_EXTERN) \ + _("static", 0, CTOK_STATIC) \ + _("auto", 0, CTOK_AUTO) \ + _("register", 0, CTOK_REGISTER) \ + /* GCC Attributes. */ \ + _("__extension__", 0, CTOK_EXTENSION) \ + _("__attribute", 0, CTOK_ATTRIBUTE) \ + _("__attribute__", 0, CTOK_ATTRIBUTE) \ + _("asm", 0, CTOK_ASM) \ + _("__asm", 0, CTOK_ASM) \ + _("__asm__", 0, CTOK_ASM) \ + /* MSVC Attributes. */ \ + _("__declspec", 0, CTOK_DECLSPEC) \ + _("__cdecl", CTCC_CDECL, CTOK_CCDECL) \ + _("__thiscall", CTCC_THISCALL, CTOK_CCDECL) \ + _("__fastcall", CTCC_FASTCALL, CTOK_CCDECL) \ + _("__stdcall", CTCC_STDCALL, CTOK_CCDECL) \ + _("__ptr32", 4, CTOK_PTRSZ) \ + _("__ptr64", 8, CTOK_PTRSZ) \ + /* Other type specifiers. */ \ + _("struct", 0, CTOK_STRUCT) \ + _("union", 0, CTOK_UNION) \ + _("enum", 0, CTOK_ENUM) \ + /* Operators. */ \ + _("sizeof", 0, CTOK_SIZEOF) \ + _("__alignof", 0, CTOK_ALIGNOF) \ + _("__alignof__", 0, CTOK_ALIGNOF) \ + /* End of keyword list. */ + +/* Type info for predefined types. Size merged in. */ +static CTInfo lj_ctype_typeinfo[] = { +#define CTTYINFODEF(id, sz, ct, info) CTINFO((ct),(((sz)&0x3fu)<<10)+(info)), +#define CTTDINFODEF(name, id) CTINFO(CT_TYPEDEF, CTID_##id), +#define CTKWINFODEF(name, sz, kw) CTINFO(CT_KW,(((sz)&0x3fu)<<10)+(kw)), +CTTYDEF(CTTYINFODEF) +CTTDDEF(CTTDINFODEF) +CTKWDEF(CTKWINFODEF) +#undef CTTYINFODEF +#undef CTTDINFODEF +#undef CTKWINFODEF + 0 +}; + +/* Predefined type names collected in a single string. */ +static const char * const lj_ctype_typenames = +#define CTTDNAMEDEF(name, id) name "\0" +#define CTKWNAMEDEF(name, sz, cds) name "\0" +CTTDDEF(CTTDNAMEDEF) +CTKWDEF(CTKWNAMEDEF) +#undef CTTDNAMEDEF +#undef CTKWNAMEDEF +; + +#define CTTYPEINFO_NUM (sizeof(lj_ctype_typeinfo)/sizeof(CTInfo)-1) +#ifdef LUAJIT_CTYPE_CHECK_ANCHOR +#define CTTYPETAB_MIN CTTYPEINFO_NUM +#else +#define CTTYPETAB_MIN 128 +#endif + +/* -- C type interning ---------------------------------------------------- */ + +#define ct_hashtype(info, size) (hashrot(info, size) & CTHASH_MASK) +#define ct_hashname(name) \ + (hashrot(u32ptr(name), u32ptr(name) + HASH_BIAS) & CTHASH_MASK) + +/* Create new type element. */ +CTypeID lj_ctype_new(CTState *cts, CType **ctp) +{ + CTypeID id = cts->top; + CType *ct; + lua_assert(cts->L); + if (LJ_UNLIKELY(id >= cts->sizetab)) { + if (id >= CTID_MAX) lj_err_msg(cts->L, LJ_ERR_TABOV); +#ifdef LUAJIT_CTYPE_CHECK_ANCHOR + ct = lj_mem_newvec(cts->L, id+1, CType); + memcpy(ct, cts->tab, id*sizeof(CType)); + memset(cts->tab, 0, id*sizeof(CType)); + lj_mem_freevec(cts->g, cts->tab, cts->sizetab, CType); + cts->tab = ct; + cts->sizetab = id+1; +#else + lj_mem_growvec(cts->L, cts->tab, cts->sizetab, CTID_MAX, CType); +#endif + } + cts->top = id+1; + *ctp = ct = &cts->tab[id]; + ct->info = 0; + ct->size = 0; + ct->sib = 0; + ct->next = 0; + setgcrefnull(ct->name); + return id; +} + +/* Intern a type element. */ +CTypeID lj_ctype_intern(CTState *cts, CTInfo info, CTSize size) +{ + uint32_t h = ct_hashtype(info, size); + CTypeID id = cts->hash[h]; + lua_assert(cts->L); + while (id) { + CType *ct = ctype_get(cts, id); + if (ct->info == info && ct->size == size) + return id; + id = ct->next; + } + id = cts->top; + if (LJ_UNLIKELY(id >= cts->sizetab)) { + if (id >= CTID_MAX) lj_err_msg(cts->L, LJ_ERR_TABOV); + lj_mem_growvec(cts->L, cts->tab, cts->sizetab, CTID_MAX, CType); + } + cts->top = id+1; + cts->tab[id].info = info; + cts->tab[id].size = size; + cts->tab[id].sib = 0; + cts->tab[id].next = cts->hash[h]; + setgcrefnull(cts->tab[id].name); + cts->hash[h] = (CTypeID1)id; + return id; +} + +/* Add type element to hash table. */ +static void ctype_addtype(CTState *cts, CType *ct, CTypeID id) +{ + uint32_t h = ct_hashtype(ct->info, ct->size); + ct->next = cts->hash[h]; + cts->hash[h] = (CTypeID1)id; +} + +/* Add named element to hash table. */ +void lj_ctype_addname(CTState *cts, CType *ct, CTypeID id) +{ + uint32_t h = ct_hashname(gcref(ct->name)); + ct->next = cts->hash[h]; + cts->hash[h] = (CTypeID1)id; +} + +/* Get a C type by name, matching the type mask. */ +CTypeID lj_ctype_getname(CTState *cts, CType **ctp, GCstr *name, uint32_t tmask) +{ + CTypeID id = cts->hash[ct_hashname(name)]; + while (id) { + CType *ct = ctype_get(cts, id); + if (gcref(ct->name) == obj2gco(name) && + ((tmask >> ctype_type(ct->info)) & 1)) { + *ctp = ct; + return id; + } + id = ct->next; + } + *ctp = &cts->tab[0]; /* Simplify caller logic. ctype_get() would assert. */ + return 0; +} + +/* Get a struct/union/enum/function field by name. */ +CType *lj_ctype_getfieldq(CTState *cts, CType *ct, GCstr *name, CTSize *ofs, + CTInfo *qual) +{ + while (ct->sib) { + ct = ctype_get(cts, ct->sib); + if (gcref(ct->name) == obj2gco(name)) { + *ofs = ct->size; + return ct; + } + if (ctype_isxattrib(ct->info, CTA_SUBTYPE)) { + CType *fct, *cct = ctype_child(cts, ct); + CTInfo q = 0; + while (ctype_isattrib(cct->info)) { + if (ctype_attrib(cct->info) == CTA_QUAL) q |= cct->size; + cct = ctype_child(cts, cct); + } + fct = lj_ctype_getfieldq(cts, cct, name, ofs, qual); + if (fct) { + if (qual) *qual |= q; + *ofs += ct->size; + return fct; + } + } + } + return NULL; /* Not found. */ +} + +/* -- C type information -------------------------------------------------- */ + +/* Follow references and get raw type for a C type ID. */ +CType *lj_ctype_rawref(CTState *cts, CTypeID id) +{ + CType *ct = ctype_get(cts, id); + while (ctype_isattrib(ct->info) || ctype_isref(ct->info)) + ct = ctype_child(cts, ct); + return ct; +} + +/* Get size for a C type ID. Does NOT support VLA/VLS. */ +CTSize lj_ctype_size(CTState *cts, CTypeID id) +{ + CType *ct = ctype_raw(cts, id); + return ctype_hassize(ct->info) ? ct->size : CTSIZE_INVALID; +} + +/* Get size for a variable-length C type. Does NOT support other C types. */ +CTSize lj_ctype_vlsize(CTState *cts, CType *ct, CTSize nelem) +{ + uint64_t xsz = 0; + if (ctype_isstruct(ct->info)) { + CTypeID arrid = 0, fid = ct->sib; + xsz = ct->size; /* Add the struct size. */ + while (fid) { + CType *ctf = ctype_get(cts, fid); + if (ctype_type(ctf->info) == CT_FIELD) + arrid = ctype_cid(ctf->info); /* Remember last field of VLS. */ + fid = ctf->sib; + } + ct = ctype_raw(cts, arrid); + } + lua_assert(ctype_isvlarray(ct->info)); /* Must be a VLA. */ + ct = ctype_rawchild(cts, ct); /* Get array element. */ + lua_assert(ctype_hassize(ct->info)); + /* Calculate actual size of VLA and check for overflow. */ + xsz += (uint64_t)ct->size * nelem; + return xsz < 0x80000000u ? (CTSize)xsz : CTSIZE_INVALID; +} + +/* Get type, qualifiers, size and alignment for a C type ID. */ +CTInfo lj_ctype_info(CTState *cts, CTypeID id, CTSize *szp) +{ + CTInfo qual = 0; + CType *ct = ctype_get(cts, id); + for (;;) { + CTInfo info = ct->info; + if (ctype_isenum(info)) { + /* Follow child. Need to look at its attributes, too. */ + } else if (ctype_isattrib(info)) { + if (ctype_isxattrib(info, CTA_QUAL)) + qual |= ct->size; + else if (ctype_isxattrib(info, CTA_ALIGN) && !(qual & CTFP_ALIGNED)) + qual |= CTFP_ALIGNED + CTALIGN(ct->size); + } else { + if (!(qual & CTFP_ALIGNED)) qual |= (info & CTF_ALIGN); + qual |= (info & ~(CTF_ALIGN|CTMASK_CID)); + lua_assert(ctype_hassize(info) || ctype_isfunc(info)); + *szp = ctype_isfunc(info) ? CTSIZE_INVALID : ct->size; + break; + } + ct = ctype_get(cts, ctype_cid(info)); + } + return qual; +} + +/* Get ctype metamethod. */ +cTValue *lj_ctype_meta(CTState *cts, CTypeID id, MMS mm) +{ + CType *ct = ctype_get(cts, id); + cTValue *tv; + while (ctype_isattrib(ct->info) || ctype_isref(ct->info)) { + id = ctype_cid(ct->info); + ct = ctype_get(cts, id); + } + if (ctype_isptr(ct->info) && + ctype_isfunc(ctype_get(cts, ctype_cid(ct->info))->info)) + tv = lj_tab_getstr(cts->miscmap, &cts->g->strempty); + else + tv = lj_tab_getinth(cts->miscmap, -(int32_t)id); + if (tv && tvistab(tv) && + (tv = lj_tab_getstr(tabV(tv), mmname_str(cts->g, mm))) && !tvisnil(tv)) + return tv; + return NULL; +} + +/* -- C type representation ----------------------------------------------- */ + +/* Fixed max. length of a C type representation. */ +#define CTREPR_MAX 512 + +typedef struct CTRepr { + char *pb, *pe; + CTState *cts; + lua_State *L; + int needsp; + int ok; + char buf[CTREPR_MAX]; +} CTRepr; + +/* Prepend string. */ +static void ctype_prepstr(CTRepr *ctr, const char *str, MSize len) +{ + char *p = ctr->pb; + if (ctr->buf + len+1 > p) { ctr->ok = 0; return; } + if (ctr->needsp) *--p = ' '; + ctr->needsp = 1; + p -= len; + while (len-- > 0) p[len] = str[len]; + ctr->pb = p; +} + +#define ctype_preplit(ctr, str) ctype_prepstr((ctr), "" str, sizeof(str)-1) + +/* Prepend char. */ +static void ctype_prepc(CTRepr *ctr, int c) +{ + if (ctr->buf >= ctr->pb) { ctr->ok = 0; return; } + *--ctr->pb = c; +} + +/* Prepend number. */ +static void ctype_prepnum(CTRepr *ctr, uint32_t n) +{ + char *p = ctr->pb; + if (ctr->buf + 10+1 > p) { ctr->ok = 0; return; } + do { *--p = (char)('0' + n % 10); } while (n /= 10); + ctr->pb = p; + ctr->needsp = 0; +} + +/* Append char. */ +static void ctype_appc(CTRepr *ctr, int c) +{ + if (ctr->pe >= ctr->buf + CTREPR_MAX) { ctr->ok = 0; return; } + *ctr->pe++ = c; +} + +/* Append number. */ +static void ctype_appnum(CTRepr *ctr, uint32_t n) +{ + char buf[10]; + char *p = buf+sizeof(buf); + char *q = ctr->pe; + if (q > ctr->buf + CTREPR_MAX - 10) { ctr->ok = 0; return; } + do { *--p = (char)('0' + n % 10); } while (n /= 10); + do { *q++ = *p++; } while (p < buf+sizeof(buf)); + ctr->pe = q; +} + +/* Prepend qualifiers. */ +static void ctype_prepqual(CTRepr *ctr, CTInfo info) +{ + if ((info & CTF_VOLATILE)) ctype_preplit(ctr, "volatile"); + if ((info & CTF_CONST)) ctype_preplit(ctr, "const"); +} + +/* Prepend named type. */ +static void ctype_preptype(CTRepr *ctr, CType *ct, CTInfo qual, const char *t) +{ + if (gcref(ct->name)) { + GCstr *str = gco2str(gcref(ct->name)); + ctype_prepstr(ctr, strdata(str), str->len); + } else { + if (ctr->needsp) ctype_prepc(ctr, ' '); + ctype_prepnum(ctr, ctype_typeid(ctr->cts, ct)); + ctr->needsp = 1; + } + ctype_prepstr(ctr, t, (MSize)strlen(t)); + ctype_prepqual(ctr, qual); +} + +static void ctype_repr(CTRepr *ctr, CTypeID id) +{ + CType *ct = ctype_get(ctr->cts, id); + CTInfo qual = 0; + int ptrto = 0; + for (;;) { + CTInfo info = ct->info; + CTSize size = ct->size; + switch (ctype_type(info)) { + case CT_NUM: + if ((info & CTF_BOOL)) { + ctype_preplit(ctr, "bool"); + } else if ((info & CTF_FP)) { + if (size == sizeof(double)) ctype_preplit(ctr, "double"); + else if (size == sizeof(float)) ctype_preplit(ctr, "float"); + else ctype_preplit(ctr, "long double"); + } else if (size == 1) { + if (!((info ^ CTF_UCHAR) & CTF_UNSIGNED)) ctype_preplit(ctr, "char"); + else if (CTF_UCHAR) ctype_preplit(ctr, "signed char"); + else ctype_preplit(ctr, "unsigned char"); + } else if (size < 8) { + if (size == 4) ctype_preplit(ctr, "int"); + else ctype_preplit(ctr, "short"); + if ((info & CTF_UNSIGNED)) ctype_preplit(ctr, "unsigned"); + } else { + ctype_preplit(ctr, "_t"); + ctype_prepnum(ctr, size*8); + ctype_preplit(ctr, "int"); + if ((info & CTF_UNSIGNED)) ctype_prepc(ctr, 'u'); + } + ctype_prepqual(ctr, (qual|info)); + return; + case CT_VOID: + ctype_preplit(ctr, "void"); + ctype_prepqual(ctr, (qual|info)); + return; + case CT_STRUCT: + ctype_preptype(ctr, ct, qual, (info & CTF_UNION) ? "union" : "struct"); + return; + case CT_ENUM: + if (id == CTID_CTYPEID) { + ctype_preplit(ctr, "ctype"); + return; + } + ctype_preptype(ctr, ct, qual, "enum"); + return; + case CT_ATTRIB: + if (ctype_attrib(info) == CTA_QUAL) qual |= size; + break; + case CT_PTR: + if ((info & CTF_REF)) { + ctype_prepc(ctr, '&'); + } else { + ctype_prepqual(ctr, (qual|info)); + if (LJ_64 && size == 4) ctype_preplit(ctr, "__ptr32"); + ctype_prepc(ctr, '*'); + } + qual = 0; + ptrto = 1; + ctr->needsp = 1; + break; + case CT_ARRAY: + if (ctype_isrefarray(info)) { + ctr->needsp = 1; + if (ptrto) { ptrto = 0; ctype_prepc(ctr, '('); ctype_appc(ctr, ')'); } + ctype_appc(ctr, '['); + if (size != CTSIZE_INVALID) { + CTSize csize = ctype_child(ctr->cts, ct)->size; + ctype_appnum(ctr, csize ? size/csize : 0); + } else if ((info & CTF_VLA)) { + ctype_appc(ctr, '?'); + } + ctype_appc(ctr, ']'); + } else if ((info & CTF_COMPLEX)) { + if (size == 2*sizeof(float)) ctype_preplit(ctr, "float"); + ctype_preplit(ctr, "complex"); + return; + } else { + ctype_preplit(ctr, ")))"); + ctype_prepnum(ctr, size); + ctype_preplit(ctr, "__attribute__((vector_size("); + } + break; + case CT_FUNC: + ctr->needsp = 1; + if (ptrto) { ptrto = 0; ctype_prepc(ctr, '('); ctype_appc(ctr, ')'); } + ctype_appc(ctr, '('); + ctype_appc(ctr, ')'); + break; + default: + lua_assert(0); + break; + } + ct = ctype_get(ctr->cts, ctype_cid(info)); + } +} + +/* Return a printable representation of a C type. */ +GCstr *lj_ctype_repr(lua_State *L, CTypeID id, GCstr *name) +{ + global_State *g = G(L); + CTRepr ctr; + ctr.pb = ctr.pe = &ctr.buf[CTREPR_MAX/2]; + ctr.cts = ctype_ctsG(g); + ctr.L = L; + ctr.ok = 1; + ctr.needsp = 0; + if (name) ctype_prepstr(&ctr, strdata(name), name->len); + ctype_repr(&ctr, id); + if (LJ_UNLIKELY(!ctr.ok)) return lj_str_newlit(L, "?"); + return lj_str_new(L, ctr.pb, ctr.pe - ctr.pb); +} + +/* Convert int64_t/uint64_t to string with 'LL' or 'ULL' suffix. */ +GCstr *lj_ctype_repr_int64(lua_State *L, uint64_t n, int isunsigned) +{ + char buf[1+20+3]; + char *p = buf+sizeof(buf); + int sign = 0; + *--p = 'L'; *--p = 'L'; + if (isunsigned) { + *--p = 'U'; + } else if ((int64_t)n < 0) { + n = (uint64_t)-(int64_t)n; + sign = 1; + } + do { *--p = (char)('0' + n % 10); } while (n /= 10); + if (sign) *--p = '-'; + return lj_str_new(L, p, (size_t)(buf+sizeof(buf)-p)); +} + +/* Convert complex to string with 'i' or 'I' suffix. */ +GCstr *lj_ctype_repr_complex(lua_State *L, void *sp, CTSize size) +{ + SBuf *sb = lj_buf_tmp_(L); + TValue re, im; + if (size == 2*sizeof(double)) { + re.n = *(double *)sp; im.n = ((double *)sp)[1]; + } else { + re.n = (double)*(float *)sp; im.n = (double)((float *)sp)[1]; + } + lj_strfmt_putfnum(sb, STRFMT_G14, re.n); + if (!(im.u32.hi & 0x80000000u) || im.n != im.n) lj_buf_putchar(sb, '+'); + lj_strfmt_putfnum(sb, STRFMT_G14, im.n); + lj_buf_putchar(sb, sbufP(sb)[-1] >= 'a' ? 'I' : 'i'); + return lj_buf_str(L, sb); +} + +/* -- C type state -------------------------------------------------------- */ + +/* Initialize C type table and state. */ +CTState *lj_ctype_init(lua_State *L) +{ + CTState *cts = lj_mem_newt(L, sizeof(CTState), CTState); + CType *ct = lj_mem_newvec(L, CTTYPETAB_MIN, CType); + const char *name = lj_ctype_typenames; + CTypeID id; + memset(cts, 0, sizeof(CTState)); + cts->tab = ct; + cts->sizetab = CTTYPETAB_MIN; + cts->top = CTTYPEINFO_NUM; + cts->L = NULL; + cts->g = G(L); + for (id = 0; id < CTTYPEINFO_NUM; id++, ct++) { + CTInfo info = lj_ctype_typeinfo[id]; + ct->size = (CTSize)((int32_t)(info << 16) >> 26); + ct->info = info & 0xffff03ffu; + ct->sib = 0; + if (ctype_type(info) == CT_KW || ctype_istypedef(info)) { + size_t len = strlen(name); + GCstr *str = lj_str_new(L, name, len); + ctype_setname(ct, str); + name += len+1; + lj_ctype_addname(cts, ct, id); + } else { + setgcrefnull(ct->name); + ct->next = 0; + if (!ctype_isenum(info)) ctype_addtype(cts, ct, id); + } + } + setmref(G(L)->ctype_state, cts); + return cts; +} + +/* Free C type table and state. */ +void lj_ctype_freestate(global_State *g) +{ + CTState *cts = ctype_ctsG(g); + if (cts) { + lj_ccallback_mcode_free(cts); + lj_mem_freevec(g, cts->tab, cts->sizetab, CType); + lj_mem_freevec(g, cts->cb.cbid, cts->cb.sizeid, CTypeID1); + lj_mem_freet(g, cts); + } +} + +#endif diff --git a/lib/LuaJIT/lj_ctype.h b/lib/LuaJIT/lj_ctype.h new file mode 100644 index 0000000..0c220a8 --- /dev/null +++ b/lib/LuaJIT/lj_ctype.h @@ -0,0 +1,461 @@ +/* +** C type management. +** Copyright (C) 2005-2017 Mike Pall. See Copyright Notice in luajit.h +*/ + +#ifndef _LJ_CTYPE_H +#define _LJ_CTYPE_H + +#include "lj_obj.h" +#include "lj_gc.h" + +#if LJ_HASFFI + +/* -- C type definitions -------------------------------------------------- */ + +/* C type numbers. Highest 4 bits of C type info. ORDER CT. */ +enum { + /* Externally visible types. */ + CT_NUM, /* Integer or floating-point numbers. */ + CT_STRUCT, /* Struct or union. */ + CT_PTR, /* Pointer or reference. */ + CT_ARRAY, /* Array or complex type. */ + CT_MAYCONVERT = CT_ARRAY, + CT_VOID, /* Void type. */ + CT_ENUM, /* Enumeration. */ + CT_HASSIZE = CT_ENUM, /* Last type where ct->size holds the actual size. */ + CT_FUNC, /* Function. */ + CT_TYPEDEF, /* Typedef. */ + CT_ATTRIB, /* Miscellaneous attributes. */ + /* Internal element types. */ + CT_FIELD, /* Struct/union field or function parameter. */ + CT_BITFIELD, /* Struct/union bitfield. */ + CT_CONSTVAL, /* Constant value. */ + CT_EXTERN, /* External reference. */ + CT_KW /* Keyword. */ +}; + +LJ_STATIC_ASSERT(((int)CT_PTR & (int)CT_ARRAY) == CT_PTR); +LJ_STATIC_ASSERT(((int)CT_STRUCT & (int)CT_ARRAY) == CT_STRUCT); + +/* +** ---------- info ------------ +** |type flags... A cid | size | sib | next | name | +** +----------------------------+--------+-------+-------+-------+-- +** |NUM BFcvUL.. A | size | | type | | +** |STRUCT ..cvU..V A | size | field | name? | name? | +** |PTR ..cvR... A cid | size | | type | | +** |ARRAY VCcv...V A cid | size | | type | | +** |VOID ..cv.... A | size | | type | | +** |ENUM A cid | size | const | name? | name? | +** |FUNC ....VS.. cc cid | nargs | field | name? | name? | +** |TYPEDEF cid | | | name | name | +** |ATTRIB attrnum cid | attr | sib? | type? | | +** |FIELD cid | offset | field | | name? | +** |BITFIELD B.cvU csz bsz pos | offset | field | | name? | +** |CONSTVAL c cid | value | const | name | name | +** |EXTERN cid | | sib? | name | name | +** |KW tok | size | | name | name | +** +----------------------------+--------+-------+-------+-------+-- +** ^^ ^^--- bits used for C type conversion dispatch +*/ + +/* C type info flags. TFFArrrr */ +#define CTF_BOOL 0x08000000u /* Boolean: NUM, BITFIELD. */ +#define CTF_FP 0x04000000u /* Floating-point: NUM. */ +#define CTF_CONST 0x02000000u /* Const qualifier. */ +#define CTF_VOLATILE 0x01000000u /* Volatile qualifier. */ +#define CTF_UNSIGNED 0x00800000u /* Unsigned: NUM, BITFIELD. */ +#define CTF_LONG 0x00400000u /* Long: NUM. */ +#define CTF_VLA 0x00100000u /* Variable-length: ARRAY, STRUCT. */ +#define CTF_REF 0x00800000u /* Reference: PTR. */ +#define CTF_VECTOR 0x08000000u /* Vector: ARRAY. */ +#define CTF_COMPLEX 0x04000000u /* Complex: ARRAY. */ +#define CTF_UNION 0x00800000u /* Union: STRUCT. */ +#define CTF_VARARG 0x00800000u /* Vararg: FUNC. */ +#define CTF_SSEREGPARM 0x00400000u /* SSE register parameters: FUNC. */ + +#define CTF_QUAL (CTF_CONST|CTF_VOLATILE) +#define CTF_ALIGN (CTMASK_ALIGN< 0 ? CTF_UNSIGNED : 0) + +/* Flags used in parser. .F.Ammvf cp->attr */ +#define CTFP_ALIGNED 0x00000001u /* cp->attr + ALIGN */ +#define CTFP_PACKED 0x00000002u /* cp->attr */ +/* ...C...f cp->fattr */ +#define CTFP_CCONV 0x00000001u /* cp->fattr + CCONV/[SSE]REGPARM */ + +/* C type info bitfields. */ +#define CTMASK_CID 0x0000ffffu /* Max. 65536 type IDs. */ +#define CTMASK_NUM 0xf0000000u /* Max. 16 type numbers. */ +#define CTSHIFT_NUM 28 +#define CTMASK_ALIGN 15 /* Max. alignment is 2^15. */ +#define CTSHIFT_ALIGN 16 +#define CTMASK_ATTRIB 255 /* Max. 256 attributes. */ +#define CTSHIFT_ATTRIB 16 +#define CTMASK_CCONV 3 /* Max. 4 calling conventions. */ +#define CTSHIFT_CCONV 16 +#define CTMASK_REGPARM 3 /* Max. 0-3 regparms. */ +#define CTSHIFT_REGPARM 18 +/* Bitfields only used in parser. */ +#define CTMASK_VSIZEP 15 /* Max. vector size is 2^15. */ +#define CTSHIFT_VSIZEP 4 +#define CTMASK_MSIZEP 255 /* Max. type size (via mode) is 128. */ +#define CTSHIFT_MSIZEP 8 + +/* Info bits for BITFIELD. Max. size of bitfield is 64 bits. */ +#define CTBSZ_MAX 32 /* Max. size of bitfield is 32 bit. */ +#define CTBSZ_FIELD 127 /* Temp. marker for regular field. */ +#define CTMASK_BITPOS 127 +#define CTMASK_BITBSZ 127 +#define CTMASK_BITCSZ 127 +#define CTSHIFT_BITPOS 0 +#define CTSHIFT_BITBSZ 8 +#define CTSHIFT_BITCSZ 16 + +#define CTF_INSERT(info, field, val) \ + info = (info & ~(CTMASK_##field<> CTSHIFT_NUM) +#define ctype_cid(info) ((CTypeID)((info) & CTMASK_CID)) +#define ctype_align(info) (((info) >> CTSHIFT_ALIGN) & CTMASK_ALIGN) +#define ctype_attrib(info) (((info) >> CTSHIFT_ATTRIB) & CTMASK_ATTRIB) +#define ctype_bitpos(info) (((info) >> CTSHIFT_BITPOS) & CTMASK_BITPOS) +#define ctype_bitbsz(info) (((info) >> CTSHIFT_BITBSZ) & CTMASK_BITBSZ) +#define ctype_bitcsz(info) (((info) >> CTSHIFT_BITCSZ) & CTMASK_BITCSZ) +#define ctype_vsizeP(info) (((info) >> CTSHIFT_VSIZEP) & CTMASK_VSIZEP) +#define ctype_msizeP(info) (((info) >> CTSHIFT_MSIZEP) & CTMASK_MSIZEP) +#define ctype_cconv(info) (((info) >> CTSHIFT_CCONV) & CTMASK_CCONV) + +/* Simple type checks. */ +#define ctype_isnum(info) (ctype_type((info)) == CT_NUM) +#define ctype_isvoid(info) (ctype_type((info)) == CT_VOID) +#define ctype_isptr(info) (ctype_type((info)) == CT_PTR) +#define ctype_isarray(info) (ctype_type((info)) == CT_ARRAY) +#define ctype_isstruct(info) (ctype_type((info)) == CT_STRUCT) +#define ctype_isfunc(info) (ctype_type((info)) == CT_FUNC) +#define ctype_isenum(info) (ctype_type((info)) == CT_ENUM) +#define ctype_istypedef(info) (ctype_type((info)) == CT_TYPEDEF) +#define ctype_isattrib(info) (ctype_type((info)) == CT_ATTRIB) +#define ctype_isfield(info) (ctype_type((info)) == CT_FIELD) +#define ctype_isbitfield(info) (ctype_type((info)) == CT_BITFIELD) +#define ctype_isconstval(info) (ctype_type((info)) == CT_CONSTVAL) +#define ctype_isextern(info) (ctype_type((info)) == CT_EXTERN) +#define ctype_hassize(info) (ctype_type((info)) <= CT_HASSIZE) + +/* Combined type and flag checks. */ +#define ctype_isinteger(info) \ + (((info) & (CTMASK_NUM|CTF_BOOL|CTF_FP)) == CTINFO(CT_NUM, 0)) +#define ctype_isinteger_or_bool(info) \ + (((info) & (CTMASK_NUM|CTF_FP)) == CTINFO(CT_NUM, 0)) +#define ctype_isbool(info) \ + (((info) & (CTMASK_NUM|CTF_BOOL)) == CTINFO(CT_NUM, CTF_BOOL)) +#define ctype_isfp(info) \ + (((info) & (CTMASK_NUM|CTF_FP)) == CTINFO(CT_NUM, CTF_FP)) + +#define ctype_ispointer(info) \ + ((ctype_type(info) >> 1) == (CT_PTR >> 1)) /* Pointer or array. */ +#define ctype_isref(info) \ + (((info) & (CTMASK_NUM|CTF_REF)) == CTINFO(CT_PTR, CTF_REF)) + +#define ctype_isrefarray(info) \ + (((info) & (CTMASK_NUM|CTF_VECTOR|CTF_COMPLEX)) == CTINFO(CT_ARRAY, 0)) +#define ctype_isvector(info) \ + (((info) & (CTMASK_NUM|CTF_VECTOR)) == CTINFO(CT_ARRAY, CTF_VECTOR)) +#define ctype_iscomplex(info) \ + (((info) & (CTMASK_NUM|CTF_COMPLEX)) == CTINFO(CT_ARRAY, CTF_COMPLEX)) + +#define ctype_isvltype(info) \ + (((info) & ((CTMASK_NUM|CTF_VLA) - (2u<") _(STRING, "") \ + _(INTEGER, "") _(EOF, "") \ + _(OROR, "||") _(ANDAND, "&&") _(EQ, "==") _(NE, "!=") \ + _(LE, "<=") _(GE, ">=") _(SHL, "<<") _(SHR, ">>") _(DEREF, "->") + +/* Simple declaration specifiers. */ +#define CDSDEF(_) \ + _(VOID) _(BOOL) _(CHAR) _(INT) _(FP) \ + _(LONG) _(LONGLONG) _(SHORT) _(COMPLEX) _(SIGNED) _(UNSIGNED) \ + _(CONST) _(VOLATILE) _(RESTRICT) _(INLINE) \ + _(TYPEDEF) _(EXTERN) _(STATIC) _(AUTO) _(REGISTER) + +/* C keywords. */ +#define CKWDEF(_) \ + CDSDEF(_) _(EXTENSION) _(ASM) _(ATTRIBUTE) \ + _(DECLSPEC) _(CCDECL) _(PTRSZ) \ + _(STRUCT) _(UNION) _(ENUM) \ + _(SIZEOF) _(ALIGNOF) + +/* C token numbers. */ +enum { + CTOK_OFS = 255, +#define CTOKNUM(name, sym) CTOK_##name, +#define CKWNUM(name) CTOK_##name, +CTOKDEF(CTOKNUM) +CKWDEF(CKWNUM) +#undef CTOKNUM +#undef CKWNUM + CTOK_FIRSTDECL = CTOK_VOID, + CTOK_FIRSTSCL = CTOK_TYPEDEF, + CTOK_LASTDECLFLAG = CTOK_REGISTER, + CTOK_LASTDECL = CTOK_ENUM +}; + +/* Declaration specifier flags. */ +enum { +#define CDSFLAG(name) CDF_##name = (1u << (CTOK_##name - CTOK_FIRSTDECL)), +CDSDEF(CDSFLAG) +#undef CDSFLAG + CDF__END +}; + +#define CDF_SCL (CDF_TYPEDEF|CDF_EXTERN|CDF_STATIC|CDF_AUTO|CDF_REGISTER) + +/* -- C type management --------------------------------------------------- */ + +#define ctype_ctsG(g) (mref((g)->ctype_state, CTState)) + +/* Get C type state. */ +static LJ_AINLINE CTState *ctype_cts(lua_State *L) +{ + CTState *cts = ctype_ctsG(G(L)); + cts->L = L; /* Save L for errors and allocations. */ + return cts; +} + +/* Save and restore state of C type table. */ +#define LJ_CTYPE_SAVE(cts) CTState savects_ = *(cts) +#define LJ_CTYPE_RESTORE(cts) \ + ((cts)->top = savects_.top, \ + memcpy((cts)->hash, savects_.hash, sizeof(savects_.hash))) + +/* Check C type ID for validity when assertions are enabled. */ +static LJ_AINLINE CTypeID ctype_check(CTState *cts, CTypeID id) +{ + lua_assert(id > 0 && id < cts->top); UNUSED(cts); + return id; +} + +/* Get C type for C type ID. */ +static LJ_AINLINE CType *ctype_get(CTState *cts, CTypeID id) +{ + return &cts->tab[ctype_check(cts, id)]; +} + +/* Get C type ID for a C type. */ +#define ctype_typeid(cts, ct) ((CTypeID)((ct) - (cts)->tab)) + +/* Get child C type. */ +static LJ_AINLINE CType *ctype_child(CTState *cts, CType *ct) +{ + lua_assert(!(ctype_isvoid(ct->info) || ctype_isstruct(ct->info) || + ctype_isbitfield(ct->info))); /* These don't have children. */ + return ctype_get(cts, ctype_cid(ct->info)); +} + +/* Get raw type for a C type ID. */ +static LJ_AINLINE CType *ctype_raw(CTState *cts, CTypeID id) +{ + CType *ct = ctype_get(cts, id); + while (ctype_isattrib(ct->info)) ct = ctype_child(cts, ct); + return ct; +} + +/* Get raw type of the child of a C type. */ +static LJ_AINLINE CType *ctype_rawchild(CTState *cts, CType *ct) +{ + do { ct = ctype_child(cts, ct); } while (ctype_isattrib(ct->info)); + return ct; +} + +/* Set the name of a C type table element. */ +static LJ_AINLINE void ctype_setname(CType *ct, GCstr *s) +{ + /* NOBARRIER: mark string as fixed -- the C type table is never collected. */ + fixstring(s); + setgcref(ct->name, obj2gco(s)); +} + +LJ_FUNC CTypeID lj_ctype_new(CTState *cts, CType **ctp); +LJ_FUNC CTypeID lj_ctype_intern(CTState *cts, CTInfo info, CTSize size); +LJ_FUNC void lj_ctype_addname(CTState *cts, CType *ct, CTypeID id); +LJ_FUNC CTypeID lj_ctype_getname(CTState *cts, CType **ctp, GCstr *name, + uint32_t tmask); +LJ_FUNC CType *lj_ctype_getfieldq(CTState *cts, CType *ct, GCstr *name, + CTSize *ofs, CTInfo *qual); +#define lj_ctype_getfield(cts, ct, name, ofs) \ + lj_ctype_getfieldq((cts), (ct), (name), (ofs), NULL) +LJ_FUNC CType *lj_ctype_rawref(CTState *cts, CTypeID id); +LJ_FUNC CTSize lj_ctype_size(CTState *cts, CTypeID id); +LJ_FUNC CTSize lj_ctype_vlsize(CTState *cts, CType *ct, CTSize nelem); +LJ_FUNC CTInfo lj_ctype_info(CTState *cts, CTypeID id, CTSize *szp); +LJ_FUNC cTValue *lj_ctype_meta(CTState *cts, CTypeID id, MMS mm); +LJ_FUNC GCstr *lj_ctype_repr(lua_State *L, CTypeID id, GCstr *name); +LJ_FUNC GCstr *lj_ctype_repr_int64(lua_State *L, uint64_t n, int isunsigned); +LJ_FUNC GCstr *lj_ctype_repr_complex(lua_State *L, void *sp, CTSize size); +LJ_FUNC CTState *lj_ctype_init(lua_State *L); +LJ_FUNC void lj_ctype_freestate(global_State *g); + +#endif + +#endif diff --git a/lib/LuaJIT/lj_ctype.o b/lib/LuaJIT/lj_ctype.o new file mode 100644 index 0000000000000000000000000000000000000000..9453cf7fea63b9a32c5e0f2e33d91ee3943f7907 GIT binary patch literal 13016 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 + /* Traverse frames backwards. */ + for (nextframe = frame = L->base-1; frame > bot; ) { + if (frame_gc(frame) == obj2gco(L)) + level++; /* Skip dummy frames. See lj_err_optype_call(). */ + if (level-- == 0) { + *size = (int)(nextframe - frame); + return frame; /* Level found. */ + } + nextframe = frame; + if (frame_islua(frame)) { + frame = frame_prevl(frame); + } else { + if (frame_isvarg(frame)) + level++; /* Skip vararg pseudo-frame. */ + frame = frame_prevd(frame); + } + } + *size = level; + return NULL; /* Level not found. */ +} + +/* Invalid bytecode position. */ +#define NO_BCPOS (~(BCPos)0) + +/* Return bytecode position for function/frame or NO_BCPOS. */ +static BCPos debug_framepc(lua_State *L, GCfunc *fn, cTValue *nextframe) +{ + const BCIns *ins; + GCproto *pt; + BCPos pos; + lua_assert(fn->c.gct == ~LJ_TFUNC || fn->c.gct == ~LJ_TTHREAD); + if (!isluafunc(fn)) { /* Cannot derive a PC for non-Lua functions. */ + return NO_BCPOS; + } else if (nextframe == NULL) { /* Lua function on top. */ + void *cf = cframe_raw(L->cframe); + if (cf == NULL || (char *)cframe_pc(cf) == (char *)cframe_L(cf)) + return NO_BCPOS; + ins = cframe_pc(cf); /* Only happens during error/hook handling. */ + } else { + if (frame_islua(nextframe)) { + ins = frame_pc(nextframe); + } else if (frame_iscont(nextframe)) { + ins = frame_contpc(nextframe); + } else { + /* Lua function below errfunc/gc/hook: find cframe to get the PC. */ + void *cf = cframe_raw(L->cframe); + TValue *f = L->base-1; + for (;;) { + if (cf == NULL) + return NO_BCPOS; + while (cframe_nres(cf) < 0) { + if (f >= restorestack(L, -cframe_nres(cf))) + break; + cf = cframe_raw(cframe_prev(cf)); + if (cf == NULL) + return NO_BCPOS; + } + if (f < nextframe) + break; + if (frame_islua(f)) { + f = frame_prevl(f); + } else { + if (frame_isc(f) || (frame_iscont(f) && frame_iscont_fficb(f))) + cf = cframe_raw(cframe_prev(cf)); + f = frame_prevd(f); + } + } + ins = cframe_pc(cf); + } + } + pt = funcproto(fn); + pos = proto_bcpos(pt, ins) - 1; +#if LJ_HASJIT + if (pos > pt->sizebc) { /* Undo the effects of lj_trace_exit for JLOOP. */ + GCtrace *T = (GCtrace *)((char *)(ins-1) - offsetof(GCtrace, startins)); + lua_assert(bc_isret(bc_op(ins[-1]))); + pos = proto_bcpos(pt, mref(T->startpc, const BCIns)); + } +#endif + return pos; +} + +/* -- Line numbers -------------------------------------------------------- */ + +/* Get line number for a bytecode position. */ +BCLine LJ_FASTCALL lj_debug_line(GCproto *pt, BCPos pc) +{ + const void *lineinfo = proto_lineinfo(pt); + if (pc <= pt->sizebc && lineinfo) { + BCLine first = pt->firstline; + if (pc == pt->sizebc) return first + pt->numline; + if (pc-- == 0) return first; + if (pt->numline < 256) + return first + (BCLine)((const uint8_t *)lineinfo)[pc]; + else if (pt->numline < 65536) + return first + (BCLine)((const uint16_t *)lineinfo)[pc]; + else + return first + (BCLine)((const uint32_t *)lineinfo)[pc]; + } + return 0; +} + +/* Get line number for function/frame. */ +static BCLine debug_frameline(lua_State *L, GCfunc *fn, cTValue *nextframe) +{ + BCPos pc = debug_framepc(L, fn, nextframe); + if (pc != NO_BCPOS) { + GCproto *pt = funcproto(fn); + lua_assert(pc <= pt->sizebc); + return lj_debug_line(pt, pc); + } + return -1; +} + +/* -- Variable names ------------------------------------------------------ */ + +/* Get name of a local variable from slot number and PC. */ +static const char *debug_varname(const GCproto *pt, BCPos pc, BCReg slot) +{ + const char *p = (const char *)proto_varinfo(pt); + if (p) { + BCPos lastpc = 0; + for (;;) { + const char *name = p; + uint32_t vn = *(const uint8_t *)p; + BCPos startpc, endpc; + if (vn < VARNAME__MAX) { + if (vn == VARNAME_END) break; /* End of varinfo. */ + } else { + do { p++; } while (*(const uint8_t *)p); /* Skip over variable name. */ + } + p++; + lastpc = startpc = lastpc + lj_buf_ruleb128(&p); + if (startpc > pc) break; + endpc = startpc + lj_buf_ruleb128(&p); + if (pc < endpc && slot-- == 0) { + if (vn < VARNAME__MAX) { +#define VARNAMESTR(name, str) str "\0" + name = VARNAMEDEF(VARNAMESTR); +#undef VARNAMESTR + if (--vn) while (*name++ || --vn) ; + } + return name; + } + } + } + return NULL; +} + +/* Get name of local variable from 1-based slot number and function/frame. */ +static TValue *debug_localname(lua_State *L, const lua_Debug *ar, + const char **name, BCReg slot1) +{ + uint32_t offset = (uint32_t)ar->i_ci & 0xffff; + uint32_t size = (uint32_t)ar->i_ci >> 16; + TValue *frame = tvref(L->stack) + offset; + TValue *nextframe = size ? frame + size : NULL; + GCfunc *fn = frame_func(frame); + BCPos pc = debug_framepc(L, fn, nextframe); + if (!nextframe) nextframe = L->top+LJ_FR2; + if ((int)slot1 < 0) { /* Negative slot number is for varargs. */ + if (pc != NO_BCPOS) { + GCproto *pt = funcproto(fn); + if ((pt->flags & PROTO_VARARG)) { + slot1 = pt->numparams + (BCReg)(-(int)slot1); + if (frame_isvarg(frame)) { /* Vararg frame has been set up? (pc!=0) */ + nextframe = frame; + frame = frame_prevd(frame); + } + if (frame + slot1+LJ_FR2 < nextframe) { + *name = "(*vararg)"; + return frame+slot1; + } + } + } + return NULL; + } + if (pc != NO_BCPOS && + (*name = debug_varname(funcproto(fn), pc, slot1-1)) != NULL) + ; + else if (slot1 > 0 && frame + slot1+LJ_FR2 < nextframe) + *name = "(*temporary)"; + return frame+slot1; +} + +/* Get name of upvalue. */ +const char *lj_debug_uvname(GCproto *pt, uint32_t idx) +{ + const uint8_t *p = proto_uvinfo(pt); + lua_assert(idx < pt->sizeuv); + if (!p) return ""; + if (idx) while (*p++ || --idx) ; + return (const char *)p; +} + +/* Get name and value of upvalue. */ +const char *lj_debug_uvnamev(cTValue *o, uint32_t idx, TValue **tvp) +{ + if (tvisfunc(o)) { + GCfunc *fn = funcV(o); + if (isluafunc(fn)) { + GCproto *pt = funcproto(fn); + if (idx < pt->sizeuv) { + *tvp = uvval(&gcref(fn->l.uvptr[idx])->uv); + return lj_debug_uvname(pt, idx); + } + } else { + if (idx < fn->c.nupvalues) { + *tvp = &fn->c.upvalue[idx]; + return ""; + } + } + } + return NULL; +} + +/* Deduce name of an object from slot number and PC. */ +const char *lj_debug_slotname(GCproto *pt, const BCIns *ip, BCReg slot, + const char **name) +{ + const char *lname; +restart: + lname = debug_varname(pt, proto_bcpos(pt, ip), slot); + if (lname != NULL) { *name = lname; return "local"; } + while (--ip > proto_bc(pt)) { + BCIns ins = *ip; + BCOp op = bc_op(ins); + BCReg ra = bc_a(ins); + if (bcmode_a(op) == BCMbase) { + if (slot >= ra && (op != BC_KNIL || slot <= bc_d(ins))) + return NULL; + } else if (bcmode_a(op) == BCMdst && ra == slot) { + switch (bc_op(ins)) { + case BC_MOV: + if (ra == slot) { slot = bc_d(ins); goto restart; } + break; + case BC_GGET: + *name = strdata(gco2str(proto_kgc(pt, ~(ptrdiff_t)bc_d(ins)))); + return "global"; + case BC_TGETS: + *name = strdata(gco2str(proto_kgc(pt, ~(ptrdiff_t)bc_c(ins)))); + if (ip > proto_bc(pt)) { + BCIns insp = ip[-1]; + if (bc_op(insp) == BC_MOV && bc_a(insp) == ra+1+LJ_FR2 && + bc_d(insp) == bc_b(ins)) + return "method"; + } + return "field"; + case BC_UGET: + *name = lj_debug_uvname(pt, bc_d(ins)); + return "upvalue"; + default: + return NULL; + } + } + } + return NULL; +} + +/* Deduce function name from caller of a frame. */ +const char *lj_debug_funcname(lua_State *L, cTValue *frame, const char **name) +{ + cTValue *pframe; + GCfunc *fn; + BCPos pc; + if (frame <= tvref(L->stack)+LJ_FR2) + return NULL; + if (frame_isvarg(frame)) + frame = frame_prevd(frame); + pframe = frame_prev(frame); + fn = frame_func(pframe); + pc = debug_framepc(L, fn, frame); + if (pc != NO_BCPOS) { + GCproto *pt = funcproto(fn); + const BCIns *ip = &proto_bc(pt)[check_exp(pc < pt->sizebc, pc)]; + MMS mm = bcmode_mm(bc_op(*ip)); + if (mm == MM_call) { + BCReg slot = bc_a(*ip); + if (bc_op(*ip) == BC_ITERC) slot -= 3; + return lj_debug_slotname(pt, ip, slot, name); + } else if (mm != MM__MAX) { + *name = strdata(mmname_str(G(L), mm)); + return "metamethod"; + } + } + return NULL; +} + +/* -- Source code locations ----------------------------------------------- */ + +/* Generate shortened source name. */ +void lj_debug_shortname(char *out, GCstr *str, BCLine line) +{ + const char *src = strdata(str); + if (*src == '=') { + strncpy(out, src+1, LUA_IDSIZE); /* Remove first char. */ + out[LUA_IDSIZE-1] = '\0'; /* Ensures null termination. */ + } else if (*src == '@') { /* Output "source", or "...source". */ + size_t len = str->len-1; + src++; /* Skip the `@' */ + if (len >= LUA_IDSIZE) { + src += len-(LUA_IDSIZE-4); /* Get last part of file name. */ + *out++ = '.'; *out++ = '.'; *out++ = '.'; + } + strcpy(out, src); + } else { /* Output [string "string"] or [builtin:name]. */ + size_t len; /* Length, up to first control char. */ + for (len = 0; len < LUA_IDSIZE-12; len++) + if (((const unsigned char *)src)[len] < ' ') break; + strcpy(out, line == ~(BCLine)0 ? "[builtin:" : "[string \""); out += 9; + if (src[len] != '\0') { /* Must truncate? */ + if (len > LUA_IDSIZE-15) len = LUA_IDSIZE-15; + strncpy(out, src, len); out += len; + strcpy(out, "..."); out += 3; + } else { + strcpy(out, src); out += len; + } + strcpy(out, line == ~(BCLine)0 ? "]" : "\"]"); + } +} + +/* Add current location of a frame to error message. */ +void lj_debug_addloc(lua_State *L, const char *msg, + cTValue *frame, cTValue *nextframe) +{ + if (frame) { + GCfunc *fn = frame_func(frame); + if (isluafunc(fn)) { + BCLine line = debug_frameline(L, fn, nextframe); + if (line >= 0) { + GCproto *pt = funcproto(fn); + char buf[LUA_IDSIZE]; + lj_debug_shortname(buf, proto_chunkname(pt), pt->firstline); + lj_strfmt_pushf(L, "%s:%d: %s", buf, line, msg); + return; + } + } + } + lj_strfmt_pushf(L, "%s", msg); +} + +/* Push location string for a bytecode position to Lua stack. */ +void lj_debug_pushloc(lua_State *L, GCproto *pt, BCPos pc) +{ + GCstr *name = proto_chunkname(pt); + const char *s = strdata(name); + MSize i, len = name->len; + BCLine line = lj_debug_line(pt, pc); + if (pt->firstline == ~(BCLine)0) { + lj_strfmt_pushf(L, "builtin:%s", s); + } else if (*s == '@') { + s++; len--; + for (i = len; i > 0; i--) + if (s[i] == '/' || s[i] == '\\') { + s += i+1; + break; + } + lj_strfmt_pushf(L, "%s:%d", s, line); + } else if (len > 40) { + lj_strfmt_pushf(L, "%p:%d", pt, line); + } else if (*s == '=') { + lj_strfmt_pushf(L, "%s:%d", s+1, line); + } else { + lj_strfmt_pushf(L, "\"%s\":%d", s, line); + } +} + +/* -- Public debug API ---------------------------------------------------- */ + +/* lua_getupvalue() and lua_setupvalue() are in lj_api.c. */ + +LUA_API const char *lua_getlocal(lua_State *L, const lua_Debug *ar, int n) +{ + const char *name = NULL; + if (ar) { + TValue *o = debug_localname(L, ar, &name, (BCReg)n); + if (name) { + copyTV(L, L->top, o); + incr_top(L); + } + } else if (tvisfunc(L->top-1) && isluafunc(funcV(L->top-1))) { + name = debug_varname(funcproto(funcV(L->top-1)), 0, (BCReg)n-1); + } + return name; +} + +LUA_API const char *lua_setlocal(lua_State *L, const lua_Debug *ar, int n) +{ + const char *name = NULL; + TValue *o = debug_localname(L, ar, &name, (BCReg)n); + if (name) + copyTV(L, o, L->top-1); + L->top--; + return name; +} + +int lj_debug_getinfo(lua_State *L, const char *what, lj_Debug *ar, int ext) +{ + int opt_f = 0, opt_L = 0; + TValue *frame = NULL; + TValue *nextframe = NULL; + GCfunc *fn; + if (*what == '>') { + TValue *func = L->top - 1; + api_check(L, tvisfunc(func)); + fn = funcV(func); + L->top--; + what++; + } else { + uint32_t offset = (uint32_t)ar->i_ci & 0xffff; + uint32_t size = (uint32_t)ar->i_ci >> 16; + lua_assert(offset != 0); + frame = tvref(L->stack) + offset; + if (size) nextframe = frame + size; + lua_assert(frame <= tvref(L->maxstack) && + (!nextframe || nextframe <= tvref(L->maxstack))); + fn = frame_func(frame); + lua_assert(fn->c.gct == ~LJ_TFUNC); + } + for (; *what; what++) { + if (*what == 'S') { + if (isluafunc(fn)) { + GCproto *pt = funcproto(fn); + BCLine firstline = pt->firstline; + GCstr *name = proto_chunkname(pt); + ar->source = strdata(name); + lj_debug_shortname(ar->short_src, name, pt->firstline); + ar->linedefined = (int)firstline; + ar->lastlinedefined = (int)(firstline + pt->numline); + ar->what = (firstline || !pt->numline) ? "Lua" : "main"; + } else { + ar->source = "=[C]"; + ar->short_src[0] = '['; + ar->short_src[1] = 'C'; + ar->short_src[2] = ']'; + ar->short_src[3] = '\0'; + ar->linedefined = -1; + ar->lastlinedefined = -1; + ar->what = "C"; + } + } else if (*what == 'l') { + ar->currentline = frame ? debug_frameline(L, fn, nextframe) : -1; + } else if (*what == 'u') { + ar->nups = fn->c.nupvalues; + if (ext) { + if (isluafunc(fn)) { + GCproto *pt = funcproto(fn); + ar->nparams = pt->numparams; + ar->isvararg = !!(pt->flags & PROTO_VARARG); + } else { + ar->nparams = 0; + ar->isvararg = 1; + } + } + } else if (*what == 'n') { + ar->namewhat = frame ? lj_debug_funcname(L, frame, &ar->name) : NULL; + if (ar->namewhat == NULL) { + ar->namewhat = ""; + ar->name = NULL; + } + } else if (*what == 'f') { + opt_f = 1; + } else if (*what == 'L') { + opt_L = 1; + } else { + return 0; /* Bad option. */ + } + } + if (opt_f) { + setfuncV(L, L->top, fn); + incr_top(L); + } + if (opt_L) { + if (isluafunc(fn)) { + GCtab *t = lj_tab_new(L, 0, 0); + GCproto *pt = funcproto(fn); + const void *lineinfo = proto_lineinfo(pt); + if (lineinfo) { + BCLine first = pt->firstline; + int sz = pt->numline < 256 ? 1 : pt->numline < 65536 ? 2 : 4; + MSize i, szl = pt->sizebc-1; + for (i = 0; i < szl; i++) { + BCLine line = first + + (sz == 1 ? (BCLine)((const uint8_t *)lineinfo)[i] : + sz == 2 ? (BCLine)((const uint16_t *)lineinfo)[i] : + (BCLine)((const uint32_t *)lineinfo)[i]); + setboolV(lj_tab_setint(L, t, line), 1); + } + } + settabV(L, L->top, t); + } else { + setnilV(L->top); + } + incr_top(L); + } + return 1; /* Ok. */ +} + +LUA_API int lua_getinfo(lua_State *L, const char *what, lua_Debug *ar) +{ + return lj_debug_getinfo(L, what, (lj_Debug *)ar, 0); +} + +LUA_API int lua_getstack(lua_State *L, int level, lua_Debug *ar) +{ + int size; + cTValue *frame = lj_debug_frame(L, level, &size); + if (frame) { + ar->i_ci = (size << 16) + (int)(frame - tvref(L->stack)); + return 1; + } else { + ar->i_ci = level - size; + return 0; + } +} + +#if LJ_HASPROFILE +/* Put the chunkname into a buffer. */ +static int debug_putchunkname(SBuf *sb, GCproto *pt, int pathstrip) +{ + GCstr *name = proto_chunkname(pt); + const char *p = strdata(name); + if (pt->firstline == ~(BCLine)0) { + lj_buf_putmem(sb, "[builtin:", 9); + lj_buf_putstr(sb, name); + lj_buf_putb(sb, ']'); + return 0; + } + if (*p == '=' || *p == '@') { + MSize len = name->len-1; + p++; + if (pathstrip) { + int i; + for (i = len-1; i >= 0; i--) + if (p[i] == '/' || p[i] == '\\') { + len -= i+1; + p = p+i+1; + break; + } + } + lj_buf_putmem(sb, p, len); + } else { + lj_buf_putmem(sb, "[string]", 8); + } + return 1; +} + +/* Put a compact stack dump into a buffer. */ +void lj_debug_dumpstack(lua_State *L, SBuf *sb, const char *fmt, int depth) +{ + int level = 0, dir = 1, pathstrip = 1; + MSize lastlen = 0; + if (depth < 0) { level = ~depth; depth = dir = -1; } /* Reverse frames. */ + while (level != depth) { /* Loop through all frame. */ + int size; + cTValue *frame = lj_debug_frame(L, level, &size); + if (frame) { + cTValue *nextframe = size ? frame+size : NULL; + GCfunc *fn = frame_func(frame); + const uint8_t *p = (const uint8_t *)fmt; + int c; + while ((c = *p++)) { + switch (c) { + case 'p': /* Preserve full path. */ + pathstrip = 0; + break; + case 'F': case 'f': { /* Dump function name. */ + const char *name; + const char *what = lj_debug_funcname(L, frame, &name); + if (what) { + if (c == 'F' && isluafunc(fn)) { /* Dump module:name for 'F'. */ + GCproto *pt = funcproto(fn); + if (pt->firstline != ~(BCLine)0) { /* Not a bytecode builtin. */ + debug_putchunkname(sb, pt, pathstrip); + lj_buf_putb(sb, ':'); + } + } + lj_buf_putmem(sb, name, (MSize)strlen(name)); + break; + } /* else: can't derive a name, dump module:line. */ + } + /* fallthrough */ + case 'l': /* Dump module:line. */ + if (isluafunc(fn)) { + GCproto *pt = funcproto(fn); + if (debug_putchunkname(sb, pt, pathstrip)) { + /* Regular Lua function. */ + BCLine line = c == 'l' ? debug_frameline(L, fn, nextframe) : + pt->firstline; + lj_buf_putb(sb, ':'); + lj_strfmt_putint(sb, line >= 0 ? line : pt->firstline); + } + } else if (isffunc(fn)) { /* Dump numbered builtins. */ + lj_buf_putmem(sb, "[builtin#", 9); + lj_strfmt_putint(sb, fn->c.ffid); + lj_buf_putb(sb, ']'); + } else { /* Dump C function address. */ + lj_buf_putb(sb, '@'); + lj_strfmt_putptr(sb, fn->c.f); + } + break; + case 'Z': /* Zap trailing separator. */ + lastlen = sbuflen(sb); + break; + default: + lj_buf_putb(sb, c); + break; + } + } + } else if (dir == 1) { + break; + } else { + level -= size; /* Reverse frame order: quickly skip missing level. */ + } + level += dir; + } + if (lastlen) + setsbufP(sb, sbufB(sb) + lastlen); /* Zap trailing separator. */ +} +#endif + +/* Number of frames for the leading and trailing part of a traceback. */ +#define TRACEBACK_LEVELS1 12 +#define TRACEBACK_LEVELS2 10 + +LUALIB_API void luaL_traceback (lua_State *L, lua_State *L1, const char *msg, + int level) +{ + int top = (int)(L->top - L->base); + int lim = TRACEBACK_LEVELS1; + lua_Debug ar; + if (msg) lua_pushfstring(L, "%s\n", msg); + lua_pushliteral(L, "stack traceback:"); + while (lua_getstack(L1, level++, &ar)) { + GCfunc *fn; + if (level > lim) { + if (!lua_getstack(L1, level + TRACEBACK_LEVELS2, &ar)) { + level--; + } else { + lua_pushliteral(L, "\n\t..."); + lua_getstack(L1, -10, &ar); + level = ar.i_ci - TRACEBACK_LEVELS2; + } + lim = 2147483647; + continue; + } + lua_getinfo(L1, "Snlf", &ar); + fn = funcV(L1->top-1); L1->top--; + if (isffunc(fn) && !*ar.namewhat) + lua_pushfstring(L, "\n\t[builtin#%d]:", fn->c.ffid); + else + lua_pushfstring(L, "\n\t%s:", ar.short_src); + if (ar.currentline > 0) + lua_pushfstring(L, "%d:", ar.currentline); + if (*ar.namewhat) { + lua_pushfstring(L, " in function " LUA_QS, ar.name); + } else { + if (*ar.what == 'm') { + lua_pushliteral(L, " in main chunk"); + } else if (*ar.what == 'C') { + lua_pushfstring(L, " at %p", fn->c.f); + } else { + lua_pushfstring(L, " in function <%s:%d>", + ar.short_src, ar.linedefined); + } + } + if ((int)(L->top - L->base) - top >= 15) + lua_concat(L, (int)(L->top - L->base) - top); + } + lua_concat(L, (int)(L->top - L->base) - top); +} + diff --git a/lib/LuaJIT/lj_debug.h b/lib/LuaJIT/lj_debug.h new file mode 100644 index 0000000..5917c00 --- /dev/null +++ b/lib/LuaJIT/lj_debug.h @@ -0,0 +1,65 @@ +/* +** Debugging and introspection. +** Copyright (C) 2005-2017 Mike Pall. See Copyright Notice in luajit.h +*/ + +#ifndef _LJ_DEBUG_H +#define _LJ_DEBUG_H + +#include "lj_obj.h" + +typedef struct lj_Debug { + /* Common fields. Must be in the same order as in lua.h. */ + int event; + const char *name; + const char *namewhat; + const char *what; + const char *source; + int currentline; + int nups; + int linedefined; + int lastlinedefined; + char short_src[LUA_IDSIZE]; + int i_ci; + /* Extended fields. Only valid if lj_debug_getinfo() is called with ext = 1.*/ + int nparams; + int isvararg; +} lj_Debug; + +LJ_FUNC cTValue *lj_debug_frame(lua_State *L, int level, int *size); +LJ_FUNC BCLine LJ_FASTCALL lj_debug_line(GCproto *pt, BCPos pc); +LJ_FUNC const char *lj_debug_uvname(GCproto *pt, uint32_t idx); +LJ_FUNC const char *lj_debug_uvnamev(cTValue *o, uint32_t idx, TValue **tvp); +LJ_FUNC const char *lj_debug_slotname(GCproto *pt, const BCIns *pc, + BCReg slot, const char **name); +LJ_FUNC const char *lj_debug_funcname(lua_State *L, cTValue *frame, + const char **name); +LJ_FUNC void lj_debug_shortname(char *out, GCstr *str, BCLine line); +LJ_FUNC void lj_debug_addloc(lua_State *L, const char *msg, + cTValue *frame, cTValue *nextframe); +LJ_FUNC void lj_debug_pushloc(lua_State *L, GCproto *pt, BCPos pc); +LJ_FUNC int lj_debug_getinfo(lua_State *L, const char *what, lj_Debug *ar, + int ext); +#if LJ_HASPROFILE +LJ_FUNC void lj_debug_dumpstack(lua_State *L, SBuf *sb, const char *fmt, + int depth); +#endif + +/* Fixed internal variable names. */ +#define VARNAMEDEF(_) \ + _(FOR_IDX, "(for index)") \ + _(FOR_STOP, "(for limit)") \ + _(FOR_STEP, "(for step)") \ + _(FOR_GEN, "(for generator)") \ + _(FOR_STATE, "(for state)") \ + _(FOR_CTL, "(for control)") + +enum { + VARNAME_END, +#define VARNAMEENUM(name, str) VARNAME_##name, + VARNAMEDEF(VARNAMEENUM) +#undef VARNAMEENUM + VARNAME__MAX +}; + +#endif diff --git a/lib/LuaJIT/lj_debug.o b/lib/LuaJIT/lj_debug.o new file mode 100644 index 0000000000000000000000000000000000000000..15d7a0d899d8076df43fd0c91b267e7ae8170b60 GIT binary patch literal 14336 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 literal 0 HcmV?d00001 diff --git a/lib/LuaJIT/lj_debug_dyn.o b/lib/LuaJIT/lj_debug_dyn.o new file mode 100644 index 0000000000000000000000000000000000000000..04e28dae4157a1fe398c9ddff1e014c43fc28581 GIT binary patch literal 14336 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 literal 0 HcmV?d00001 diff --git a/lib/LuaJIT/lj_def.h b/lib/LuaJIT/lj_def.h new file mode 100644 index 0000000..e67bb24 --- /dev/null +++ b/lib/LuaJIT/lj_def.h @@ -0,0 +1,361 @@ +/* +** LuaJIT common internal definitions. +** Copyright (C) 2005-2017 Mike Pall. See Copyright Notice in luajit.h +*/ + +#ifndef _LJ_DEF_H +#define _LJ_DEF_H + +#include "lua.h" + +#if defined(_MSC_VER) +/* MSVC is stuck in the last century and doesn't have C99's stdint.h. */ +typedef __int8 int8_t; +typedef __int16 int16_t; +typedef __int32 int32_t; +typedef __int64 int64_t; +typedef unsigned __int8 uint8_t; +typedef unsigned __int16 uint16_t; +typedef unsigned __int32 uint32_t; +typedef unsigned __int64 uint64_t; +#ifdef _WIN64 +typedef __int64 intptr_t; +typedef unsigned __int64 uintptr_t; +#else +typedef __int32 intptr_t; +typedef unsigned __int32 uintptr_t; +#endif +#elif defined(__symbian__) +/* Cough. */ +typedef signed char int8_t; +typedef short int int16_t; +typedef int int32_t; +typedef long long int64_t; +typedef unsigned char uint8_t; +typedef unsigned short int uint16_t; +typedef unsigned int uint32_t; +typedef unsigned long long uint64_t; +typedef int intptr_t; +typedef unsigned int uintptr_t; +#else +#include +#endif + +/* Needed everywhere. */ +#include +#include + +/* Various VM limits. */ +#define LJ_MAX_MEM32 0x7fffff00 /* Max. 32 bit memory allocation. */ +#define LJ_MAX_MEM64 ((uint64_t)1<<47) /* Max. 64 bit memory allocation. */ +/* Max. total memory allocation. */ +#define LJ_MAX_MEM (LJ_GC64 ? LJ_MAX_MEM64 : LJ_MAX_MEM32) +#define LJ_MAX_ALLOC LJ_MAX_MEM /* Max. individual allocation length. */ +#define LJ_MAX_STR LJ_MAX_MEM32 /* Max. string length. */ +#define LJ_MAX_BUF LJ_MAX_MEM32 /* Max. buffer length. */ +#define LJ_MAX_UDATA LJ_MAX_MEM32 /* Max. userdata length. */ + +#define LJ_MAX_STRTAB (1<<26) /* Max. string table size. */ +#define LJ_MAX_HBITS 26 /* Max. hash bits. */ +#define LJ_MAX_ABITS 28 /* Max. bits of array key. */ +#define LJ_MAX_ASIZE ((1<<(LJ_MAX_ABITS-1))+1) /* Max. array part size. */ +#define LJ_MAX_COLOSIZE 16 /* Max. elems for colocated array. */ + +#define LJ_MAX_LINE LJ_MAX_MEM32 /* Max. source code line number. */ +#define LJ_MAX_XLEVEL 200 /* Max. syntactic nesting level. */ +#define LJ_MAX_BCINS (1<<26) /* Max. # of bytecode instructions. */ +#define LJ_MAX_SLOTS 250 /* Max. # of slots in a Lua func. */ +#define LJ_MAX_LOCVAR 200 /* Max. # of local variables. */ +#define LJ_MAX_UPVAL 60 /* Max. # of upvalues. */ + +#define LJ_MAX_IDXCHAIN 100 /* __index/__newindex chain limit. */ +#define LJ_STACK_EXTRA (5+2*LJ_FR2) /* Extra stack space (metamethods). */ + +#define LJ_NUM_CBPAGE 1 /* Number of FFI callback pages. */ + +/* Minimum table/buffer sizes. */ +#define LJ_MIN_GLOBAL 6 /* Min. global table size (hbits). */ +#define LJ_MIN_REGISTRY 2 /* Min. registry size (hbits). */ +#define LJ_MIN_STRTAB 256 /* Min. string table size (pow2). */ +#define LJ_MIN_SBUF 32 /* Min. string buffer length. */ +#define LJ_MIN_VECSZ 8 /* Min. size for growable vectors. */ +#define LJ_MIN_IRSZ 32 /* Min. size for growable IR. */ + +/* JIT compiler limits. */ +#define LJ_MAX_JSLOTS 250 /* Max. # of stack slots for a trace. */ +#define LJ_MAX_PHI 64 /* Max. # of PHIs for a loop. */ +#define LJ_MAX_EXITSTUBGR 16 /* Max. # of exit stub groups. */ + +/* Various macros. */ +#ifndef UNUSED +#define UNUSED(x) ((void)(x)) /* to avoid warnings */ +#endif + +#define U64x(hi, lo) (((uint64_t)0x##hi << 32) + (uint64_t)0x##lo) +#define i32ptr(p) ((int32_t)(intptr_t)(void *)(p)) +#define u32ptr(p) ((uint32_t)(intptr_t)(void *)(p)) +#define i64ptr(p) ((int64_t)(intptr_t)(void *)(p)) +#define u64ptr(p) ((uint64_t)(intptr_t)(void *)(p)) +#define igcptr(p) (LJ_GC64 ? i64ptr(p) : i32ptr(p)) + +#define checki8(x) ((x) == (int32_t)(int8_t)(x)) +#define checku8(x) ((x) == (int32_t)(uint8_t)(x)) +#define checki16(x) ((x) == (int32_t)(int16_t)(x)) +#define checku16(x) ((x) == (int32_t)(uint16_t)(x)) +#define checki32(x) ((x) == (int32_t)(x)) +#define checku32(x) ((x) == (uint32_t)(x)) +#define checkptr32(x) ((uintptr_t)(x) == (uint32_t)(uintptr_t)(x)) +#define checkptr47(x) (((uint64_t)(uintptr_t)(x) >> 47) == 0) +#define checkptrGC(x) (LJ_GC64 ? checkptr47((x)) : LJ_64 ? checkptr32((x)) :1) + +/* Every half-decent C compiler transforms this into a rotate instruction. */ +#define lj_rol(x, n) (((x)<<(n)) | ((x)>>(-(int)(n)&(8*sizeof(x)-1)))) +#define lj_ror(x, n) (((x)<<(-(int)(n)&(8*sizeof(x)-1))) | ((x)>>(n))) + +/* A really naive Bloom filter. But sufficient for our needs. */ +typedef uintptr_t BloomFilter; +#define BLOOM_MASK (8*sizeof(BloomFilter) - 1) +#define bloombit(x) ((uintptr_t)1 << ((x) & BLOOM_MASK)) +#define bloomset(b, x) ((b) |= bloombit((x))) +#define bloomtest(b, x) ((b) & bloombit((x))) + +#if defined(__GNUC__) || defined(__psp2__) + +#define LJ_NORET __attribute__((noreturn)) +#define LJ_ALIGN(n) __attribute__((aligned(n))) +#define LJ_INLINE inline +#define LJ_AINLINE inline __attribute__((always_inline)) +#define LJ_NOINLINE __attribute__((noinline)) + +#if defined(__ELF__) || defined(__MACH__) || defined(__psp2__) +#if !((defined(__sun__) && defined(__svr4__)) || defined(__CELLOS_LV2__)) +#define LJ_NOAPI extern __attribute__((visibility("hidden"))) +#endif +#endif + +/* Note: it's only beneficial to use fastcall on x86 and then only for up to +** two non-FP args. The amalgamated compile covers all LJ_FUNC cases. Only +** indirect calls and related tail-called C functions are marked as fastcall. +*/ +#if defined(__i386__) +#define LJ_FASTCALL __attribute__((fastcall)) +#endif + +#define LJ_LIKELY(x) __builtin_expect(!!(x), 1) +#define LJ_UNLIKELY(x) __builtin_expect(!!(x), 0) + +#define lj_ffs(x) ((uint32_t)__builtin_ctz(x)) +/* Don't ask ... */ +#if defined(__INTEL_COMPILER) && (defined(__i386__) || defined(__x86_64__)) +static LJ_AINLINE uint32_t lj_fls(uint32_t x) +{ + uint32_t r; __asm__("bsrl %1, %0" : "=r" (r) : "rm" (x) : "cc"); return r; +} +#else +#define lj_fls(x) ((uint32_t)(__builtin_clz(x)^31)) +#endif + +#if defined(__arm__) +static LJ_AINLINE uint32_t lj_bswap(uint32_t x) +{ +#if defined(__psp2__) + return __builtin_rev(x); +#else + uint32_t r; +#if __ARM_ARCH_6__ || __ARM_ARCH_6J__ || __ARM_ARCH_6T2__ || __ARM_ARCH_6Z__ ||\ + __ARM_ARCH_6ZK__ || __ARM_ARCH_7__ || __ARM_ARCH_7A__ || __ARM_ARCH_7R__ + __asm__("rev %0, %1" : "=r" (r) : "r" (x)); + return r; +#else +#ifdef __thumb__ + r = x ^ lj_ror(x, 16); +#else + __asm__("eor %0, %1, %1, ror #16" : "=r" (r) : "r" (x)); +#endif + return ((r & 0xff00ffffu) >> 8) ^ lj_ror(x, 8); +#endif +#endif +} + +static LJ_AINLINE uint64_t lj_bswap64(uint64_t x) +{ + return ((uint64_t)lj_bswap((uint32_t)x)<<32) | lj_bswap((uint32_t)(x>>32)); +} +#elif (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3) +static LJ_AINLINE uint32_t lj_bswap(uint32_t x) +{ + return (uint32_t)__builtin_bswap32((int32_t)x); +} + +static LJ_AINLINE uint64_t lj_bswap64(uint64_t x) +{ + return (uint64_t)__builtin_bswap64((int64_t)x); +} +#elif defined(__i386__) || defined(__x86_64__) +static LJ_AINLINE uint32_t lj_bswap(uint32_t x) +{ + uint32_t r; __asm__("bswap %0" : "=r" (r) : "0" (x)); return r; +} + +#if defined(__i386__) +static LJ_AINLINE uint64_t lj_bswap64(uint64_t x) +{ + return ((uint64_t)lj_bswap((uint32_t)x)<<32) | lj_bswap((uint32_t)(x>>32)); +} +#else +static LJ_AINLINE uint64_t lj_bswap64(uint64_t x) +{ + uint64_t r; __asm__("bswap %0" : "=r" (r) : "0" (x)); return r; +} +#endif +#else +static LJ_AINLINE uint32_t lj_bswap(uint32_t x) +{ + return (x << 24) | ((x & 0xff00) << 8) | ((x >> 8) & 0xff00) | (x >> 24); +} + +static LJ_AINLINE uint64_t lj_bswap64(uint64_t x) +{ + return (uint64_t)lj_bswap((uint32_t)(x >> 32)) | + ((uint64_t)lj_bswap((uint32_t)x) << 32); +} +#endif + +typedef union __attribute__((packed)) Unaligned16 { + uint16_t u; + uint8_t b[2]; +} Unaligned16; + +typedef union __attribute__((packed)) Unaligned32 { + uint32_t u; + uint8_t b[4]; +} Unaligned32; + +/* Unaligned load of uint16_t. */ +static LJ_AINLINE uint16_t lj_getu16(const void *p) +{ + return ((const Unaligned16 *)p)->u; +} + +/* Unaligned load of uint32_t. */ +static LJ_AINLINE uint32_t lj_getu32(const void *p) +{ + return ((const Unaligned32 *)p)->u; +} + +#elif defined(_MSC_VER) + +#define LJ_NORET __declspec(noreturn) +#define LJ_ALIGN(n) __declspec(align(n)) +#define LJ_INLINE __inline +#define LJ_AINLINE __forceinline +#define LJ_NOINLINE __declspec(noinline) +#if defined(_M_IX86) +#define LJ_FASTCALL __fastcall +#endif + +#ifdef _M_PPC +unsigned int _CountLeadingZeros(long); +#pragma intrinsic(_CountLeadingZeros) +static LJ_AINLINE uint32_t lj_fls(uint32_t x) +{ + return _CountLeadingZeros(x) ^ 31; +} +#else +unsigned char _BitScanForward(uint32_t *, unsigned long); +unsigned char _BitScanReverse(uint32_t *, unsigned long); +#pragma intrinsic(_BitScanForward) +#pragma intrinsic(_BitScanReverse) + +static LJ_AINLINE uint32_t lj_ffs(uint32_t x) +{ + uint32_t r; _BitScanForward(&r, x); return r; +} + +static LJ_AINLINE uint32_t lj_fls(uint32_t x) +{ + uint32_t r; _BitScanReverse(&r, x); return r; +} +#endif + +unsigned long _byteswap_ulong(unsigned long); +uint64_t _byteswap_uint64(uint64_t); +#define lj_bswap(x) (_byteswap_ulong((x))) +#define lj_bswap64(x) (_byteswap_uint64((x))) + +#if defined(_M_PPC) && defined(LUAJIT_NO_UNALIGNED) +/* +** Replacement for unaligned loads on Xbox 360. Disabled by default since it's +** usually more costly than the occasional stall when crossing a cache-line. +*/ +static LJ_AINLINE uint16_t lj_getu16(const void *v) +{ + const uint8_t *p = (const uint8_t *)v; + return (uint16_t)((p[0]<<8) | p[1]); +} +static LJ_AINLINE uint32_t lj_getu32(const void *v) +{ + const uint8_t *p = (const uint8_t *)v; + return (uint32_t)((p[0]<<24) | (p[1]<<16) | (p[2]<<8) | p[3]); +} +#else +/* Unaligned loads are generally ok on x86/x64. */ +#define lj_getu16(p) (*(uint16_t *)(p)) +#define lj_getu32(p) (*(uint32_t *)(p)) +#endif + +#else +#error "missing defines for your compiler" +#endif + +/* Optional defines. */ +#ifndef LJ_FASTCALL +#define LJ_FASTCALL +#endif +#ifndef LJ_NORET +#define LJ_NORET +#endif +#ifndef LJ_NOAPI +#define LJ_NOAPI extern +#endif +#ifndef LJ_LIKELY +#define LJ_LIKELY(x) (x) +#define LJ_UNLIKELY(x) (x) +#endif + +/* Attributes for internal functions. */ +#define LJ_DATA LJ_NOAPI +#define LJ_DATADEF +#define LJ_ASMF LJ_NOAPI +#define LJ_FUNCA LJ_NOAPI +#if defined(ljamalg_c) +#define LJ_FUNC static +#else +#define LJ_FUNC LJ_NOAPI +#endif +#define LJ_FUNC_NORET LJ_FUNC LJ_NORET +#define LJ_FUNCA_NORET LJ_FUNCA LJ_NORET +#define LJ_ASMF_NORET LJ_ASMF LJ_NORET + +/* Runtime assertions. */ +#ifdef lua_assert +#define check_exp(c, e) (lua_assert(c), (e)) +#define api_check(l, e) lua_assert(e) +#else +#define lua_assert(c) ((void)0) +#define check_exp(c, e) (e) +#define api_check luai_apicheck +#endif + +/* Static assertions. */ +#define LJ_ASSERT_NAME2(name, line) name ## line +#define LJ_ASSERT_NAME(line) LJ_ASSERT_NAME2(lj_assert_, line) +#ifdef __COUNTER__ +#define LJ_STATIC_ASSERT(cond) \ + extern void LJ_ASSERT_NAME(__COUNTER__)(int STATIC_ASSERTION_FAILED[(cond)?1:-1]) +#else +#define LJ_STATIC_ASSERT(cond) \ + extern void LJ_ASSERT_NAME(__LINE__)(int STATIC_ASSERTION_FAILED[(cond)?1:-1]) +#endif + +#endif diff --git a/lib/LuaJIT/lj_dispatch.c b/lib/LuaJIT/lj_dispatch.c new file mode 100644 index 0000000..5d6795f --- /dev/null +++ b/lib/LuaJIT/lj_dispatch.c @@ -0,0 +1,557 @@ +/* +** Instruction dispatch handling. +** Copyright (C) 2005-2017 Mike Pall. See Copyright Notice in luajit.h +*/ + +#define lj_dispatch_c +#define LUA_CORE + +#include "lj_obj.h" +#include "lj_err.h" +#include "lj_buf.h" +#include "lj_func.h" +#include "lj_str.h" +#include "lj_tab.h" +#include "lj_meta.h" +#include "lj_debug.h" +#include "lj_state.h" +#include "lj_frame.h" +#include "lj_bc.h" +#include "lj_ff.h" +#include "lj_strfmt.h" +#if LJ_HASJIT +#include "lj_jit.h" +#endif +#if LJ_HASFFI +#include "lj_ccallback.h" +#endif +#include "lj_trace.h" +#include "lj_dispatch.h" +#if LJ_HASPROFILE +#include "lj_profile.h" +#endif +#include "lj_vm.h" +#include "luajit.h" + +/* Bump GG_NUM_ASMFF in lj_dispatch.h as needed. Ugly. */ +LJ_STATIC_ASSERT(GG_NUM_ASMFF == FF_NUM_ASMFUNC); + +/* -- Dispatch table management ------------------------------------------- */ + +#if LJ_TARGET_MIPS +#include +LJ_FUNCA_NORET void LJ_FASTCALL lj_ffh_coroutine_wrap_err(lua_State *L, + lua_State *co); +#if !LJ_HASJIT +#define lj_dispatch_stitch lj_dispatch_ins +#endif +#if !LJ_HASPROFILE +#define lj_dispatch_profile lj_dispatch_ins +#endif + +#define GOTFUNC(name) (ASMFunction)name, +static const ASMFunction dispatch_got[] = { + GOTDEF(GOTFUNC) +}; +#undef GOTFUNC +#endif + +/* Initialize instruction dispatch table and hot counters. */ +void lj_dispatch_init(GG_State *GG) +{ + uint32_t i; + ASMFunction *disp = GG->dispatch; + for (i = 0; i < GG_LEN_SDISP; i++) + disp[GG_LEN_DDISP+i] = disp[i] = makeasmfunc(lj_bc_ofs[i]); + for (i = GG_LEN_SDISP; i < GG_LEN_DDISP; i++) + disp[i] = makeasmfunc(lj_bc_ofs[i]); + /* The JIT engine is off by default. luaopen_jit() turns it on. */ + disp[BC_FORL] = disp[BC_IFORL]; + disp[BC_ITERL] = disp[BC_IITERL]; + disp[BC_LOOP] = disp[BC_ILOOP]; + disp[BC_FUNCF] = disp[BC_IFUNCF]; + disp[BC_FUNCV] = disp[BC_IFUNCV]; + GG->g.bc_cfunc_ext = GG->g.bc_cfunc_int = BCINS_AD(BC_FUNCC, LUA_MINSTACK, 0); + for (i = 0; i < GG_NUM_ASMFF; i++) + GG->bcff[i] = BCINS_AD(BC__MAX+i, 0, 0); +#if LJ_TARGET_MIPS + memcpy(GG->got, dispatch_got, LJ_GOT__MAX*sizeof(ASMFunction *)); +#endif +} + +#if LJ_HASJIT +/* Initialize hotcount table. */ +void lj_dispatch_init_hotcount(global_State *g) +{ + int32_t hotloop = G2J(g)->param[JIT_P_hotloop]; + HotCount start = (HotCount)(hotloop*HOTCOUNT_LOOP - 1); + HotCount *hotcount = G2GG(g)->hotcount; + uint32_t i; + for (i = 0; i < HOTCOUNT_SIZE; i++) + hotcount[i] = start; +} +#endif + +/* Internal dispatch mode bits. */ +#define DISPMODE_CALL 0x01 /* Override call dispatch. */ +#define DISPMODE_RET 0x02 /* Override return dispatch. */ +#define DISPMODE_INS 0x04 /* Override instruction dispatch. */ +#define DISPMODE_JIT 0x10 /* JIT compiler on. */ +#define DISPMODE_REC 0x20 /* Recording active. */ +#define DISPMODE_PROF 0x40 /* Profiling active. */ + +/* Update dispatch table depending on various flags. */ +void lj_dispatch_update(global_State *g) +{ + uint8_t oldmode = g->dispatchmode; + uint8_t mode = 0; +#if LJ_HASJIT + mode |= (G2J(g)->flags & JIT_F_ON) ? DISPMODE_JIT : 0; + mode |= G2J(g)->state != LJ_TRACE_IDLE ? + (DISPMODE_REC|DISPMODE_INS|DISPMODE_CALL) : 0; +#endif +#if LJ_HASPROFILE + mode |= (g->hookmask & HOOK_PROFILE) ? (DISPMODE_PROF|DISPMODE_INS) : 0; +#endif + mode |= (g->hookmask & (LUA_MASKLINE|LUA_MASKCOUNT)) ? DISPMODE_INS : 0; + mode |= (g->hookmask & LUA_MASKCALL) ? DISPMODE_CALL : 0; + mode |= (g->hookmask & LUA_MASKRET) ? DISPMODE_RET : 0; + if (oldmode != mode) { /* Mode changed? */ + ASMFunction *disp = G2GG(g)->dispatch; + ASMFunction f_forl, f_iterl, f_loop, f_funcf, f_funcv; + g->dispatchmode = mode; + + /* Hotcount if JIT is on, but not while recording. */ + if ((mode & (DISPMODE_JIT|DISPMODE_REC)) == DISPMODE_JIT) { + f_forl = makeasmfunc(lj_bc_ofs[BC_FORL]); + f_iterl = makeasmfunc(lj_bc_ofs[BC_ITERL]); + f_loop = makeasmfunc(lj_bc_ofs[BC_LOOP]); + f_funcf = makeasmfunc(lj_bc_ofs[BC_FUNCF]); + f_funcv = makeasmfunc(lj_bc_ofs[BC_FUNCV]); + } else { /* Otherwise use the non-hotcounting instructions. */ + f_forl = disp[GG_LEN_DDISP+BC_IFORL]; + f_iterl = disp[GG_LEN_DDISP+BC_IITERL]; + f_loop = disp[GG_LEN_DDISP+BC_ILOOP]; + f_funcf = makeasmfunc(lj_bc_ofs[BC_IFUNCF]); + f_funcv = makeasmfunc(lj_bc_ofs[BC_IFUNCV]); + } + /* Init static counting instruction dispatch first (may be copied below). */ + disp[GG_LEN_DDISP+BC_FORL] = f_forl; + disp[GG_LEN_DDISP+BC_ITERL] = f_iterl; + disp[GG_LEN_DDISP+BC_LOOP] = f_loop; + + /* Set dynamic instruction dispatch. */ + if ((oldmode ^ mode) & (DISPMODE_PROF|DISPMODE_REC|DISPMODE_INS)) { + /* Need to update the whole table. */ + if (!(mode & DISPMODE_INS)) { /* No ins dispatch? */ + /* Copy static dispatch table to dynamic dispatch table. */ + memcpy(&disp[0], &disp[GG_LEN_DDISP], GG_LEN_SDISP*sizeof(ASMFunction)); + /* Overwrite with dynamic return dispatch. */ + if ((mode & DISPMODE_RET)) { + disp[BC_RETM] = lj_vm_rethook; + disp[BC_RET] = lj_vm_rethook; + disp[BC_RET0] = lj_vm_rethook; + disp[BC_RET1] = lj_vm_rethook; + } + } else { + /* The recording dispatch also checks for hooks. */ + ASMFunction f = (mode & DISPMODE_PROF) ? lj_vm_profhook : + (mode & DISPMODE_REC) ? lj_vm_record : lj_vm_inshook; + uint32_t i; + for (i = 0; i < GG_LEN_SDISP; i++) + disp[i] = f; + } + } else if (!(mode & DISPMODE_INS)) { + /* Otherwise set dynamic counting ins. */ + disp[BC_FORL] = f_forl; + disp[BC_ITERL] = f_iterl; + disp[BC_LOOP] = f_loop; + /* Set dynamic return dispatch. */ + if ((mode & DISPMODE_RET)) { + disp[BC_RETM] = lj_vm_rethook; + disp[BC_RET] = lj_vm_rethook; + disp[BC_RET0] = lj_vm_rethook; + disp[BC_RET1] = lj_vm_rethook; + } else { + disp[BC_RETM] = disp[GG_LEN_DDISP+BC_RETM]; + disp[BC_RET] = disp[GG_LEN_DDISP+BC_RET]; + disp[BC_RET0] = disp[GG_LEN_DDISP+BC_RET0]; + disp[BC_RET1] = disp[GG_LEN_DDISP+BC_RET1]; + } + } + + /* Set dynamic call dispatch. */ + if ((oldmode ^ mode) & DISPMODE_CALL) { /* Update the whole table? */ + uint32_t i; + if ((mode & DISPMODE_CALL) == 0) { /* No call hooks? */ + for (i = GG_LEN_SDISP; i < GG_LEN_DDISP; i++) + disp[i] = makeasmfunc(lj_bc_ofs[i]); + } else { + for (i = GG_LEN_SDISP; i < GG_LEN_DDISP; i++) + disp[i] = lj_vm_callhook; + } + } + if (!(mode & DISPMODE_CALL)) { /* Overwrite dynamic counting ins. */ + disp[BC_FUNCF] = f_funcf; + disp[BC_FUNCV] = f_funcv; + } + +#if LJ_HASJIT + /* Reset hotcounts for JIT off to on transition. */ + if ((mode & DISPMODE_JIT) && !(oldmode & DISPMODE_JIT)) + lj_dispatch_init_hotcount(g); +#endif + } +} + +/* -- JIT mode setting ---------------------------------------------------- */ + +#if LJ_HASJIT +/* Set JIT mode for a single prototype. */ +static void setptmode(global_State *g, GCproto *pt, int mode) +{ + if ((mode & LUAJIT_MODE_ON)) { /* (Re-)enable JIT compilation. */ + pt->flags &= ~PROTO_NOJIT; + lj_trace_reenableproto(pt); /* Unpatch all ILOOP etc. bytecodes. */ + } else { /* Flush and/or disable JIT compilation. */ + if (!(mode & LUAJIT_MODE_FLUSH)) + pt->flags |= PROTO_NOJIT; + lj_trace_flushproto(g, pt); /* Flush all traces of prototype. */ + } +} + +/* Recursively set the JIT mode for all children of a prototype. */ +static void setptmode_all(global_State *g, GCproto *pt, int mode) +{ + ptrdiff_t i; + if (!(pt->flags & PROTO_CHILD)) return; + for (i = -(ptrdiff_t)pt->sizekgc; i < 0; i++) { + GCobj *o = proto_kgc(pt, i); + if (o->gch.gct == ~LJ_TPROTO) { + setptmode(g, gco2pt(o), mode); + setptmode_all(g, gco2pt(o), mode); + } + } +} +#endif + +/* Public API function: control the JIT engine. */ +int luaJIT_setmode(lua_State *L, int idx, int mode) +{ + global_State *g = G(L); + int mm = mode & LUAJIT_MODE_MASK; + lj_trace_abort(g); /* Abort recording on any state change. */ + /* Avoid pulling the rug from under our own feet. */ + if ((g->hookmask & HOOK_GC)) + lj_err_caller(L, LJ_ERR_NOGCMM); + switch (mm) { +#if LJ_HASJIT + case LUAJIT_MODE_ENGINE: + if ((mode & LUAJIT_MODE_FLUSH)) { + lj_trace_flushall(L); + } else { + if (!(mode & LUAJIT_MODE_ON)) + G2J(g)->flags &= ~(uint32_t)JIT_F_ON; +#if LJ_TARGET_X86ORX64 + else if ((G2J(g)->flags & JIT_F_SSE2)) + G2J(g)->flags |= (uint32_t)JIT_F_ON; + else + return 0; /* Don't turn on JIT compiler without SSE2 support. */ +#else + else + G2J(g)->flags |= (uint32_t)JIT_F_ON; +#endif + lj_dispatch_update(g); + } + break; + case LUAJIT_MODE_FUNC: + case LUAJIT_MODE_ALLFUNC: + case LUAJIT_MODE_ALLSUBFUNC: { + cTValue *tv = idx == 0 ? frame_prev(L->base-1)-LJ_FR2 : + idx > 0 ? L->base + (idx-1) : L->top + idx; + GCproto *pt; + if ((idx == 0 || tvisfunc(tv)) && isluafunc(&gcval(tv)->fn)) + pt = funcproto(&gcval(tv)->fn); /* Cannot use funcV() for frame slot. */ + else if (tvisproto(tv)) + pt = protoV(tv); + else + return 0; /* Failed. */ + if (mm != LUAJIT_MODE_ALLSUBFUNC) + setptmode(g, pt, mode); + if (mm != LUAJIT_MODE_FUNC) + setptmode_all(g, pt, mode); + break; + } + case LUAJIT_MODE_TRACE: + if (!(mode & LUAJIT_MODE_FLUSH)) + return 0; /* Failed. */ + lj_trace_flush(G2J(g), idx); + break; +#else + case LUAJIT_MODE_ENGINE: + case LUAJIT_MODE_FUNC: + case LUAJIT_MODE_ALLFUNC: + case LUAJIT_MODE_ALLSUBFUNC: + UNUSED(idx); + if ((mode & LUAJIT_MODE_ON)) + return 0; /* Failed. */ + break; +#endif + case LUAJIT_MODE_WRAPCFUNC: + if ((mode & LUAJIT_MODE_ON)) { + if (idx != 0) { + cTValue *tv = idx > 0 ? L->base + (idx-1) : L->top + idx; + if (tvislightud(tv)) + g->wrapf = (lua_CFunction)lightudV(tv); + else + return 0; /* Failed. */ + } else { + return 0; /* Failed. */ + } + g->bc_cfunc_ext = BCINS_AD(BC_FUNCCW, 0, 0); + } else { + g->bc_cfunc_ext = BCINS_AD(BC_FUNCC, 0, 0); + } + break; + default: + return 0; /* Failed. */ + } + return 1; /* OK. */ +} + +/* Enforce (dynamic) linker error for version mismatches. See luajit.c. */ +LUA_API void LUAJIT_VERSION_SYM(void) +{ +} + +/* -- Hooks --------------------------------------------------------------- */ + +/* This function can be called asynchronously (e.g. during a signal). */ +LUA_API int lua_sethook(lua_State *L, lua_Hook func, int mask, int count) +{ + global_State *g = G(L); + mask &= HOOK_EVENTMASK; + if (func == NULL || mask == 0) { mask = 0; func = NULL; } /* Consistency. */ + g->hookf = func; + g->hookcount = g->hookcstart = (int32_t)count; + g->hookmask = (uint8_t)((g->hookmask & ~HOOK_EVENTMASK) | mask); + lj_trace_abort(g); /* Abort recording on any hook change. */ + lj_dispatch_update(g); + return 1; +} + +LUA_API lua_Hook lua_gethook(lua_State *L) +{ + return G(L)->hookf; +} + +LUA_API int lua_gethookmask(lua_State *L) +{ + return G(L)->hookmask & HOOK_EVENTMASK; +} + +LUA_API int lua_gethookcount(lua_State *L) +{ + return (int)G(L)->hookcstart; +} + +/* Call a hook. */ +static void callhook(lua_State *L, int event, BCLine line) +{ + global_State *g = G(L); + lua_Hook hookf = g->hookf; + if (hookf && !hook_active(g)) { + lua_Debug ar; + lj_trace_abort(g); /* Abort recording on any hook call. */ + ar.event = event; + ar.currentline = line; + /* Top frame, nextframe = NULL. */ + ar.i_ci = (int)((L->base-1) - tvref(L->stack)); + lj_state_checkstack(L, 1+LUA_MINSTACK); +#if LJ_HASPROFILE && !LJ_PROFILE_SIGPROF + lj_profile_hook_enter(g); +#else + hook_enter(g); +#endif + hookf(L, &ar); + lua_assert(hook_active(g)); + setgcref(g->cur_L, obj2gco(L)); +#if LJ_HASPROFILE && !LJ_PROFILE_SIGPROF + lj_profile_hook_leave(g); +#else + hook_leave(g); +#endif + } +} + +/* -- Dispatch callbacks -------------------------------------------------- */ + +/* Calculate number of used stack slots in the current frame. */ +static BCReg cur_topslot(GCproto *pt, const BCIns *pc, uint32_t nres) +{ + BCIns ins = pc[-1]; + if (bc_op(ins) == BC_UCLO) + ins = pc[bc_j(ins)]; + switch (bc_op(ins)) { + case BC_CALLM: case BC_CALLMT: return bc_a(ins) + bc_c(ins) + nres-1+1+LJ_FR2; + case BC_RETM: return bc_a(ins) + bc_d(ins) + nres-1; + case BC_TSETM: return bc_a(ins) + nres-1; + default: return pt->framesize; + } +} + +/* Instruction dispatch. Used by instr/line/return hooks or when recording. */ +void LJ_FASTCALL lj_dispatch_ins(lua_State *L, const BCIns *pc) +{ + ERRNO_SAVE + GCfunc *fn = curr_func(L); + GCproto *pt = funcproto(fn); + void *cf = cframe_raw(L->cframe); + const BCIns *oldpc = cframe_pc(cf); + global_State *g = G(L); + BCReg slots; + setcframe_pc(cf, pc); + slots = cur_topslot(pt, pc, cframe_multres_n(cf)); + L->top = L->base + slots; /* Fix top. */ +#if LJ_HASJIT + { + jit_State *J = G2J(g); + if (J->state != LJ_TRACE_IDLE) { +#ifdef LUA_USE_ASSERT + ptrdiff_t delta = L->top - L->base; +#endif + J->L = L; + lj_trace_ins(J, pc-1); /* The interpreter bytecode PC is offset by 1. */ + lua_assert(L->top - L->base == delta); + } + } +#endif + if ((g->hookmask & LUA_MASKCOUNT) && g->hookcount == 0) { + g->hookcount = g->hookcstart; + callhook(L, LUA_HOOKCOUNT, -1); + L->top = L->base + slots; /* Fix top again. */ + } + if ((g->hookmask & LUA_MASKLINE)) { + BCPos npc = proto_bcpos(pt, pc) - 1; + BCPos opc = proto_bcpos(pt, oldpc) - 1; + BCLine line = lj_debug_line(pt, npc); + if (pc <= oldpc || opc >= pt->sizebc || line != lj_debug_line(pt, opc)) { + callhook(L, LUA_HOOKLINE, line); + L->top = L->base + slots; /* Fix top again. */ + } + } + if ((g->hookmask & LUA_MASKRET) && bc_isret(bc_op(pc[-1]))) + callhook(L, LUA_HOOKRET, -1); + ERRNO_RESTORE +} + +/* Initialize call. Ensure stack space and return # of missing parameters. */ +static int call_init(lua_State *L, GCfunc *fn) +{ + if (isluafunc(fn)) { + GCproto *pt = funcproto(fn); + int numparams = pt->numparams; + int gotparams = (int)(L->top - L->base); + int need = pt->framesize; + if ((pt->flags & PROTO_VARARG)) need += 1+gotparams; + lj_state_checkstack(L, (MSize)need); + numparams -= gotparams; + return numparams >= 0 ? numparams : 0; + } else { + lj_state_checkstack(L, LUA_MINSTACK); + return 0; + } +} + +/* Call dispatch. Used by call hooks, hot calls or when recording. */ +ASMFunction LJ_FASTCALL lj_dispatch_call(lua_State *L, const BCIns *pc) +{ + ERRNO_SAVE + GCfunc *fn = curr_func(L); + BCOp op; + global_State *g = G(L); +#if LJ_HASJIT + jit_State *J = G2J(g); +#endif + int missing = call_init(L, fn); +#if LJ_HASJIT + J->L = L; + if ((uintptr_t)pc & 1) { /* Marker for hot call. */ +#ifdef LUA_USE_ASSERT + ptrdiff_t delta = L->top - L->base; +#endif + pc = (const BCIns *)((uintptr_t)pc & ~(uintptr_t)1); + lj_trace_hot(J, pc); + lua_assert(L->top - L->base == delta); + goto out; + } else if (J->state != LJ_TRACE_IDLE && + !(g->hookmask & (HOOK_GC|HOOK_VMEVENT))) { +#ifdef LUA_USE_ASSERT + ptrdiff_t delta = L->top - L->base; +#endif + /* Record the FUNC* bytecodes, too. */ + lj_trace_ins(J, pc-1); /* The interpreter bytecode PC is offset by 1. */ + lua_assert(L->top - L->base == delta); + } +#endif + if ((g->hookmask & LUA_MASKCALL)) { + int i; + for (i = 0; i < missing; i++) /* Add missing parameters. */ + setnilV(L->top++); + callhook(L, LUA_HOOKCALL, -1); + /* Preserve modifications of missing parameters by lua_setlocal(). */ + while (missing-- > 0 && tvisnil(L->top - 1)) + L->top--; + } +#if LJ_HASJIT +out: +#endif + op = bc_op(pc[-1]); /* Get FUNC* op. */ +#if LJ_HASJIT + /* Use the non-hotcounting variants if JIT is off or while recording. */ + if ((!(J->flags & JIT_F_ON) || J->state != LJ_TRACE_IDLE) && + (op == BC_FUNCF || op == BC_FUNCV)) + op = (BCOp)((int)op+(int)BC_IFUNCF-(int)BC_FUNCF); +#endif + ERRNO_RESTORE + return makeasmfunc(lj_bc_ofs[op]); /* Return static dispatch target. */ +} + +#if LJ_HASJIT +/* Stitch a new trace. */ +void LJ_FASTCALL lj_dispatch_stitch(jit_State *J, const BCIns *pc) +{ + ERRNO_SAVE + lua_State *L = J->L; + void *cf = cframe_raw(L->cframe); + const BCIns *oldpc = cframe_pc(cf); + setcframe_pc(cf, pc); + /* Before dispatch, have to bias PC by 1. */ + L->top = L->base + cur_topslot(curr_proto(L), pc+1, cframe_multres_n(cf)); + lj_trace_stitch(J, pc-1); /* Point to the CALL instruction. */ + setcframe_pc(cf, oldpc); + ERRNO_RESTORE +} +#endif + +#if LJ_HASPROFILE +/* Profile dispatch. */ +void LJ_FASTCALL lj_dispatch_profile(lua_State *L, const BCIns *pc) +{ + ERRNO_SAVE + GCfunc *fn = curr_func(L); + GCproto *pt = funcproto(fn); + void *cf = cframe_raw(L->cframe); + const BCIns *oldpc = cframe_pc(cf); + global_State *g; + setcframe_pc(cf, pc); + L->top = L->base + cur_topslot(pt, pc, cframe_multres_n(cf)); + lj_profile_interpreter(L); + setcframe_pc(cf, oldpc); + g = G(L); + setgcref(g->cur_L, obj2gco(L)); + setvmstate(g, INTERP); + ERRNO_RESTORE +} +#endif + diff --git a/lib/LuaJIT/lj_dispatch.h b/lib/LuaJIT/lj_dispatch.h new file mode 100644 index 0000000..5bda51a --- /dev/null +++ b/lib/LuaJIT/lj_dispatch.h @@ -0,0 +1,156 @@ +/* +** Instruction dispatch handling. +** Copyright (C) 2005-2017 Mike Pall. See Copyright Notice in luajit.h +*/ + +#ifndef _LJ_DISPATCH_H +#define _LJ_DISPATCH_H + +#include "lj_obj.h" +#include "lj_bc.h" +#if LJ_HASJIT +#include "lj_jit.h" +#endif + +#if LJ_TARGET_MIPS +/* Need our own global offset table for the dreaded MIPS calling conventions. */ + +#ifndef _LJ_VM_H +LJ_ASMF int32_t LJ_FASTCALL lj_vm_modi(int32_t a, int32_t b); +#endif + +#if LJ_SOFTFP +#ifndef _LJ_IRCALL_H +extern double __adddf3(double a, double b); +extern double __subdf3(double a, double b); +extern double __muldf3(double a, double b); +extern double __divdf3(double a, double b); +#endif +#define SFGOTDEF(_) _(sqrt) _(__adddf3) _(__subdf3) _(__muldf3) _(__divdf3) +#else +#define SFGOTDEF(_) +#endif +#if LJ_HASJIT +#define JITGOTDEF(_) _(lj_trace_exit) _(lj_trace_hot) +#else +#define JITGOTDEF(_) +#endif +#if LJ_HASFFI +#define FFIGOTDEF(_) \ + _(lj_meta_equal_cd) _(lj_ccallback_enter) _(lj_ccallback_leave) +#else +#define FFIGOTDEF(_) +#endif +#define GOTDEF(_) \ + _(floor) _(ceil) _(trunc) _(log) _(log10) _(exp) _(sin) _(cos) _(tan) \ + _(asin) _(acos) _(atan) _(sinh) _(cosh) _(tanh) _(frexp) _(modf) _(atan2) \ + _(pow) _(fmod) _(ldexp) _(lj_vm_modi) \ + _(lj_dispatch_call) _(lj_dispatch_ins) _(lj_dispatch_stitch) \ + _(lj_dispatch_profile) _(lj_err_throw) \ + _(lj_ffh_coroutine_wrap_err) _(lj_func_closeuv) _(lj_func_newL_gc) \ + _(lj_gc_barrieruv) _(lj_gc_step) _(lj_gc_step_fixtop) _(lj_meta_arith) \ + _(lj_meta_call) _(lj_meta_cat) _(lj_meta_comp) _(lj_meta_equal) \ + _(lj_meta_for) _(lj_meta_istype) _(lj_meta_len) _(lj_meta_tget) \ + _(lj_meta_tset) _(lj_state_growstack) _(lj_strfmt_number) \ + _(lj_str_new) _(lj_tab_dup) _(lj_tab_get) _(lj_tab_getinth) _(lj_tab_len) \ + _(lj_tab_new) _(lj_tab_newkey) _(lj_tab_next) _(lj_tab_reasize) \ + _(lj_tab_setinth) _(lj_buf_putstr_reverse) _(lj_buf_putstr_lower) \ + _(lj_buf_putstr_upper) _(lj_buf_tostr) \ + JITGOTDEF(_) FFIGOTDEF(_) SFGOTDEF(_) + +enum { +#define GOTENUM(name) LJ_GOT_##name, +GOTDEF(GOTENUM) +#undef GOTENUM + LJ_GOT__MAX +}; +#endif + +/* Type of hot counter. Must match the code in the assembler VM. */ +/* 16 bits are sufficient. Only 0.0015% overhead with maximum slot penalty. */ +typedef uint16_t HotCount; + +/* Number of hot counter hash table entries (must be a power of two). */ +#define HOTCOUNT_SIZE 64 +#define HOTCOUNT_PCMASK ((HOTCOUNT_SIZE-1)*sizeof(HotCount)) + +/* Hotcount decrements. */ +#define HOTCOUNT_LOOP 2 +#define HOTCOUNT_CALL 1 + +/* This solves a circular dependency problem -- bump as needed. Sigh. */ +#define GG_NUM_ASMFF 57 + +#define GG_LEN_DDISP (BC__MAX + GG_NUM_ASMFF) +#define GG_LEN_SDISP BC_FUNCF +#define GG_LEN_DISP (GG_LEN_DDISP + GG_LEN_SDISP) + +/* Global state, main thread and extra fields are allocated together. */ +typedef struct GG_State { + lua_State L; /* Main thread. */ + global_State g; /* Global state. */ +#if LJ_TARGET_MIPS + ASMFunction got[LJ_GOT__MAX]; /* Global offset table. */ +#endif +#if LJ_HASJIT + jit_State J; /* JIT state. */ + HotCount hotcount[HOTCOUNT_SIZE]; /* Hot counters. */ +#endif + ASMFunction dispatch[GG_LEN_DISP]; /* Instruction dispatch tables. */ + BCIns bcff[GG_NUM_ASMFF]; /* Bytecode for ASM fast functions. */ +} GG_State; + +#define GG_OFS(field) ((int)offsetof(GG_State, field)) +#define G2GG(gl) ((GG_State *)((char *)(gl) - GG_OFS(g))) +#define J2GG(j) ((GG_State *)((char *)(j) - GG_OFS(J))) +#define L2GG(L) (G2GG(G(L))) +#define J2G(J) (&J2GG(J)->g) +#define G2J(gl) (&G2GG(gl)->J) +#define L2J(L) (&L2GG(L)->J) +#define GG_G2J (GG_OFS(J) - GG_OFS(g)) +#define GG_G2DISP (GG_OFS(dispatch) - GG_OFS(g)) +#define GG_DISP2G (GG_OFS(g) - GG_OFS(dispatch)) +#define GG_DISP2J (GG_OFS(J) - GG_OFS(dispatch)) +#define GG_DISP2HOT (GG_OFS(hotcount) - GG_OFS(dispatch)) +#define GG_DISP2STATIC (GG_LEN_DDISP*(int)sizeof(ASMFunction)) + +#define hotcount_get(gg, pc) \ + (gg)->hotcount[(u32ptr(pc)>>2) & (HOTCOUNT_SIZE-1)] +#define hotcount_set(gg, pc, val) \ + (hotcount_get((gg), (pc)) = (HotCount)(val)) + +/* Dispatch table management. */ +LJ_FUNC void lj_dispatch_init(GG_State *GG); +#if LJ_HASJIT +LJ_FUNC void lj_dispatch_init_hotcount(global_State *g); +#endif +LJ_FUNC void lj_dispatch_update(global_State *g); + +/* Instruction dispatch callback for hooks or when recording. */ +LJ_FUNCA void LJ_FASTCALL lj_dispatch_ins(lua_State *L, const BCIns *pc); +LJ_FUNCA ASMFunction LJ_FASTCALL lj_dispatch_call(lua_State *L, const BCIns*pc); +#if LJ_HASJIT +LJ_FUNCA void LJ_FASTCALL lj_dispatch_stitch(jit_State *J, const BCIns *pc); +#endif +#if LJ_HASPROFILE +LJ_FUNCA void LJ_FASTCALL lj_dispatch_profile(lua_State *L, const BCIns *pc); +#endif + +#if LJ_HASFFI && !defined(_BUILDVM_H) +/* Save/restore errno and GetLastError() around hooks, exits and recording. */ +#include +#if LJ_TARGET_WINDOWS +#define WIN32_LEAN_AND_MEAN +#include +#define ERRNO_SAVE int olderr = errno; DWORD oldwerr = GetLastError(); +#define ERRNO_RESTORE errno = olderr; SetLastError(oldwerr); +#else +#define ERRNO_SAVE int olderr = errno; +#define ERRNO_RESTORE errno = olderr; +#endif +#else +#define ERRNO_SAVE +#define ERRNO_RESTORE +#endif + +#endif diff --git a/lib/LuaJIT/lj_dispatch.o b/lib/LuaJIT/lj_dispatch.o new file mode 100644 index 0000000000000000000000000000000000000000..a4670adff8ac4d76ecc754398d956ab1856b01c8 GIT binary patch literal 8328 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 M2>4%$=3<@yXYq^Y_y7O^ literal 0 HcmV?d00001 diff --git a/lib/LuaJIT/lj_dispatch_dyn.o b/lib/LuaJIT/lj_dispatch_dyn.o new file mode 100644 index 0000000000000000000000000000000000000000..a4670adff8ac4d76ecc754398d956ab1856b01c8 GIT binary patch literal 8328 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 M2>4%$=3<@yXYq^Y_y7O^ literal 0 HcmV?d00001 diff --git a/lib/LuaJIT/lj_emit_arm.h b/lib/LuaJIT/lj_emit_arm.h new file mode 100644 index 0000000..dee8bdc --- /dev/null +++ b/lib/LuaJIT/lj_emit_arm.h @@ -0,0 +1,357 @@ +/* +** ARM instruction emitter. +** Copyright (C) 2005-2017 Mike Pall. See Copyright Notice in luajit.h +*/ + +/* -- Constant encoding --------------------------------------------------- */ + +static uint8_t emit_invai[16] = { + /* AND */ (ARMI_AND^ARMI_BIC) >> 21, + /* EOR */ 0, + /* SUB */ (ARMI_SUB^ARMI_ADD) >> 21, + /* RSB */ 0, + /* ADD */ (ARMI_ADD^ARMI_SUB) >> 21, + /* ADC */ (ARMI_ADC^ARMI_SBC) >> 21, + /* SBC */ (ARMI_SBC^ARMI_ADC) >> 21, + /* RSC */ 0, + /* TST */ 0, + /* TEQ */ 0, + /* CMP */ (ARMI_CMP^ARMI_CMN) >> 21, + /* CMN */ (ARMI_CMN^ARMI_CMP) >> 21, + /* ORR */ 0, + /* MOV */ (ARMI_MOV^ARMI_MVN) >> 21, + /* BIC */ (ARMI_BIC^ARMI_AND) >> 21, + /* MVN */ (ARMI_MVN^ARMI_MOV) >> 21 +}; + +/* Encode constant in K12 format for data processing instructions. */ +static uint32_t emit_isk12(ARMIns ai, int32_t n) +{ + uint32_t invai, i, m = (uint32_t)n; + /* K12: unsigned 8 bit value, rotated in steps of two bits. */ + for (i = 0; i < 4096; i += 256, m = lj_rol(m, 2)) + if (m <= 255) return ARMI_K12|m|i; + /* Otherwise try negation/complement with the inverse instruction. */ + invai = emit_invai[((ai >> 21) & 15)]; + if (!invai) return 0; /* Failed. No inverse instruction. */ + m = ~(uint32_t)n; + if (invai == ((ARMI_SUB^ARMI_ADD) >> 21) || + invai == (ARMI_CMP^ARMI_CMN) >> 21) m++; + for (i = 0; i < 4096; i += 256, m = lj_rol(m, 2)) + if (m <= 255) return ARMI_K12|(invai<<21)|m|i; + return 0; /* Failed. */ +} + +/* -- Emit basic instructions --------------------------------------------- */ + +static void emit_dnm(ASMState *as, ARMIns ai, Reg rd, Reg rn, Reg rm) +{ + *--as->mcp = ai | ARMF_D(rd) | ARMF_N(rn) | ARMF_M(rm); +} + +static void emit_dm(ASMState *as, ARMIns ai, Reg rd, Reg rm) +{ + *--as->mcp = ai | ARMF_D(rd) | ARMF_M(rm); +} + +static void emit_dn(ASMState *as, ARMIns ai, Reg rd, Reg rn) +{ + *--as->mcp = ai | ARMF_D(rd) | ARMF_N(rn); +} + +static void emit_nm(ASMState *as, ARMIns ai, Reg rn, Reg rm) +{ + *--as->mcp = ai | ARMF_N(rn) | ARMF_M(rm); +} + +static void emit_d(ASMState *as, ARMIns ai, Reg rd) +{ + *--as->mcp = ai | ARMF_D(rd); +} + +static void emit_n(ASMState *as, ARMIns ai, Reg rn) +{ + *--as->mcp = ai | ARMF_N(rn); +} + +static void emit_m(ASMState *as, ARMIns ai, Reg rm) +{ + *--as->mcp = ai | ARMF_M(rm); +} + +static void emit_lsox(ASMState *as, ARMIns ai, Reg rd, Reg rn, int32_t ofs) +{ + lua_assert(ofs >= -255 && ofs <= 255); + if (ofs < 0) ofs = -ofs; else ai |= ARMI_LS_U; + *--as->mcp = ai | ARMI_LS_P | ARMI_LSX_I | ARMF_D(rd) | ARMF_N(rn) | + ((ofs & 0xf0) << 4) | (ofs & 0x0f); +} + +static void emit_lso(ASMState *as, ARMIns ai, Reg rd, Reg rn, int32_t ofs) +{ + lua_assert(ofs >= -4095 && ofs <= 4095); + /* Combine LDR/STR pairs to LDRD/STRD. */ + if (*as->mcp == (ai|ARMI_LS_P|ARMI_LS_U|ARMF_D(rd^1)|ARMF_N(rn)|(ofs^4)) && + (ai & ~(ARMI_LDR^ARMI_STR)) == ARMI_STR && rd != rn && + (uint32_t)ofs <= 252 && !(ofs & 3) && !((rd ^ (ofs >>2)) & 1) && + as->mcp != as->mcloop) { + as->mcp++; + emit_lsox(as, ai == ARMI_LDR ? ARMI_LDRD : ARMI_STRD, rd&~1, rn, ofs&~4); + return; + } + if (ofs < 0) ofs = -ofs; else ai |= ARMI_LS_U; + *--as->mcp = ai | ARMI_LS_P | ARMF_D(rd) | ARMF_N(rn) | ofs; +} + +#if !LJ_SOFTFP +static void emit_vlso(ASMState *as, ARMIns ai, Reg rd, Reg rn, int32_t ofs) +{ + lua_assert(ofs >= -1020 && ofs <= 1020 && (ofs&3) == 0); + if (ofs < 0) ofs = -ofs; else ai |= ARMI_LS_U; + *--as->mcp = ai | ARMI_LS_P | ARMF_D(rd & 15) | ARMF_N(rn) | (ofs >> 2); +} +#endif + +/* -- Emit loads/stores --------------------------------------------------- */ + +/* Prefer spills of BASE/L. */ +#define emit_canremat(ref) ((ref) < ASMREF_L) + +/* Try to find a one step delta relative to another constant. */ +static int emit_kdelta1(ASMState *as, Reg d, int32_t i) +{ + RegSet work = ~as->freeset & RSET_GPR; + while (work) { + Reg r = rset_picktop(work); + IRRef ref = regcost_ref(as->cost[r]); + lua_assert(r != d); + if (emit_canremat(ref)) { + int32_t delta = i - (ra_iskref(ref) ? ra_krefk(as, ref) : IR(ref)->i); + uint32_t k = emit_isk12(ARMI_ADD, delta); + if (k) { + if (k == ARMI_K12) + emit_dm(as, ARMI_MOV, d, r); + else + emit_dn(as, ARMI_ADD^k, d, r); + return 1; + } + } + rset_clear(work, r); + } + return 0; /* Failed. */ +} + +/* Try to find a two step delta relative to another constant. */ +static int emit_kdelta2(ASMState *as, Reg d, int32_t i) +{ + RegSet work = ~as->freeset & RSET_GPR; + while (work) { + Reg r = rset_picktop(work); + IRRef ref = regcost_ref(as->cost[r]); + lua_assert(r != d); + if (emit_canremat(ref)) { + int32_t other = ra_iskref(ref) ? ra_krefk(as, ref) : IR(ref)->i; + if (other) { + int32_t delta = i - other; + uint32_t sh, inv = 0, k2, k; + if (delta < 0) { delta = -delta; inv = ARMI_ADD^ARMI_SUB; } + sh = lj_ffs(delta) & ~1; + k2 = emit_isk12(0, delta & (255 << sh)); + k = emit_isk12(0, delta & ~(255 << sh)); + if (k) { + emit_dn(as, ARMI_ADD^k2^inv, d, d); + emit_dn(as, ARMI_ADD^k^inv, d, r); + return 1; + } + } + } + rset_clear(work, r); + } + return 0; /* Failed. */ +} + +/* Load a 32 bit constant into a GPR. */ +static void emit_loadi(ASMState *as, Reg r, int32_t i) +{ + uint32_t k = emit_isk12(ARMI_MOV, i); + lua_assert(rset_test(as->freeset, r) || r == RID_TMP); + if (k) { + /* Standard K12 constant. */ + emit_d(as, ARMI_MOV^k, r); + } else if ((as->flags & JIT_F_ARMV6T2) && (uint32_t)i < 0x00010000u) { + /* 16 bit loword constant for ARMv6T2. */ + emit_d(as, ARMI_MOVW|(i & 0x0fff)|((i & 0xf000)<<4), r); + } else if (emit_kdelta1(as, r, i)) { + /* One step delta relative to another constant. */ + } else if ((as->flags & JIT_F_ARMV6T2)) { + /* 32 bit hiword/loword constant for ARMv6T2. */ + emit_d(as, ARMI_MOVT|((i>>16) & 0x0fff)|(((i>>16) & 0xf000)<<4), r); + emit_d(as, ARMI_MOVW|(i & 0x0fff)|((i & 0xf000)<<4), r); + } else if (emit_kdelta2(as, r, i)) { + /* Two step delta relative to another constant. */ + } else { + /* Otherwise construct the constant with up to 4 instructions. */ + /* NYI: use mvn+bic, use pc-relative loads. */ + for (;;) { + uint32_t sh = lj_ffs(i) & ~1; + int32_t m = i & (255 << sh); + i &= ~(255 << sh); + if (i == 0) { + emit_d(as, ARMI_MOV ^ emit_isk12(0, m), r); + break; + } + emit_dn(as, ARMI_ORR ^ emit_isk12(0, m), r, r); + } + } +} + +#define emit_loada(as, r, addr) emit_loadi(as, (r), i32ptr((addr))) + +static Reg ra_allock(ASMState *as, intptr_t k, RegSet allow); + +/* Get/set from constant pointer. */ +static void emit_lsptr(ASMState *as, ARMIns ai, Reg r, void *p) +{ + int32_t i = i32ptr(p); + emit_lso(as, ai, r, ra_allock(as, (i & ~4095), rset_exclude(RSET_GPR, r)), + (i & 4095)); +} + +#if !LJ_SOFTFP +/* Load a number constant into an FPR. */ +static void emit_loadk64(ASMState *as, Reg r, IRIns *ir) +{ + cTValue *tv = ir_knum(ir); + int32_t i; + if ((as->flags & JIT_F_VFPV3) && !tv->u32.lo) { + uint32_t hi = tv->u32.hi; + uint32_t b = ((hi >> 22) & 0x1ff); + if (!(hi & 0xffff) && (b == 0x100 || b == 0x0ff)) { + *--as->mcp = ARMI_VMOVI_D | ARMF_D(r & 15) | + ((tv->u32.hi >> 12) & 0x00080000) | + ((tv->u32.hi >> 4) & 0x00070000) | + ((tv->u32.hi >> 16) & 0x0000000f); + return; + } + } + i = i32ptr(tv); + emit_vlso(as, ARMI_VLDR_D, r, + ra_allock(as, (i & ~1020), RSET_GPR), (i & 1020)); +} +#endif + +/* Get/set global_State fields. */ +#define emit_getgl(as, r, field) \ + emit_lsptr(as, ARMI_LDR, (r), (void *)&J2G(as->J)->field) +#define emit_setgl(as, r, field) \ + emit_lsptr(as, ARMI_STR, (r), (void *)&J2G(as->J)->field) + +/* Trace number is determined from pc of exit instruction. */ +#define emit_setvmstate(as, i) UNUSED(i) + +/* -- Emit control-flow instructions -------------------------------------- */ + +/* Label for internal jumps. */ +typedef MCode *MCLabel; + +/* Return label pointing to current PC. */ +#define emit_label(as) ((as)->mcp) + +static void emit_branch(ASMState *as, ARMIns ai, MCode *target) +{ + MCode *p = as->mcp; + ptrdiff_t delta = (target - p) - 1; + lua_assert(((delta + 0x00800000) >> 24) == 0); + *--p = ai | ((uint32_t)delta & 0x00ffffffu); + as->mcp = p; +} + +#define emit_jmp(as, target) emit_branch(as, ARMI_B, (target)) + +static void emit_call(ASMState *as, void *target) +{ + MCode *p = --as->mcp; + ptrdiff_t delta = ((char *)target - (char *)p) - 8; + if ((((delta>>2) + 0x00800000) >> 24) == 0) { + if ((delta & 1)) + *p = ARMI_BLX | ((uint32_t)(delta>>2) & 0x00ffffffu) | ((delta&2) << 23); + else + *p = ARMI_BL | ((uint32_t)(delta>>2) & 0x00ffffffu); + } else { /* Target out of range: need indirect call. But don't use R0-R3. */ + Reg r = ra_allock(as, i32ptr(target), RSET_RANGE(RID_R4, RID_R12+1)); + *p = ARMI_BLXr | ARMF_M(r); + } +} + +/* -- Emit generic operations --------------------------------------------- */ + +/* Generic move between two regs. */ +static void emit_movrr(ASMState *as, IRIns *ir, Reg dst, Reg src) +{ +#if LJ_SOFTFP + lua_assert(!irt_isnum(ir->t)); UNUSED(ir); +#else + if (dst >= RID_MAX_GPR) { + emit_dm(as, irt_isnum(ir->t) ? ARMI_VMOV_D : ARMI_VMOV_S, + (dst & 15), (src & 15)); + return; + } +#endif + if (as->mcp != as->mcloop) { /* Swap early registers for loads/stores. */ + MCode ins = *as->mcp, swp = (src^dst); + if ((ins & 0x0c000000) == 0x04000000 && (ins & 0x02000010) != 0x02000010) { + if (!((ins ^ (dst << 16)) & 0x000f0000)) + *as->mcp = ins ^ (swp << 16); /* Swap N in load/store. */ + if (!(ins & 0x00100000) && !((ins ^ (dst << 12)) & 0x0000f000)) + *as->mcp = ins ^ (swp << 12); /* Swap D in store. */ + } + } + emit_dm(as, ARMI_MOV, dst, src); +} + +/* Generic load of register with base and (small) offset address. */ +static void emit_loadofs(ASMState *as, IRIns *ir, Reg r, Reg base, int32_t ofs) +{ +#if LJ_SOFTFP + lua_assert(!irt_isnum(ir->t)); UNUSED(ir); +#else + if (r >= RID_MAX_GPR) + emit_vlso(as, irt_isnum(ir->t) ? ARMI_VLDR_D : ARMI_VLDR_S, r, base, ofs); + else +#endif + emit_lso(as, ARMI_LDR, r, base, ofs); +} + +/* Generic store of register with base and (small) offset address. */ +static void emit_storeofs(ASMState *as, IRIns *ir, Reg r, Reg base, int32_t ofs) +{ +#if LJ_SOFTFP + lua_assert(!irt_isnum(ir->t)); UNUSED(ir); +#else + if (r >= RID_MAX_GPR) + emit_vlso(as, irt_isnum(ir->t) ? ARMI_VSTR_D : ARMI_VSTR_S, r, base, ofs); + else +#endif + emit_lso(as, ARMI_STR, r, base, ofs); +} + +/* Emit an arithmetic/logic operation with a constant operand. */ +static void emit_opk(ASMState *as, ARMIns ai, Reg dest, Reg src, + int32_t i, RegSet allow) +{ + uint32_t k = emit_isk12(ai, i); + if (k) + emit_dn(as, ai^k, dest, src); + else + emit_dnm(as, ai, dest, src, ra_allock(as, i, allow)); +} + +/* Add offset to pointer. */ +static void emit_addptr(ASMState *as, Reg r, int32_t ofs) +{ + if (ofs) + emit_opk(as, ARMI_ADD, r, r, ofs, rset_exclude(RSET_GPR, r)); +} + +#define emit_spsub(as, ofs) emit_addptr(as, RID_SP, -(ofs)) + diff --git a/lib/LuaJIT/lj_emit_arm64.h b/lib/LuaJIT/lj_emit_arm64.h new file mode 100644 index 0000000..1001b1d --- /dev/null +++ b/lib/LuaJIT/lj_emit_arm64.h @@ -0,0 +1,419 @@ +/* +** ARM64 instruction emitter. +** Copyright (C) 2005-2017 Mike Pall. See Copyright Notice in luajit.h +** +** Contributed by Djordje Kovacevic and Stefan Pejic from RT-RK.com. +** Sponsored by Cisco Systems, Inc. +*/ + +/* -- Constant encoding --------------------------------------------------- */ + +static uint64_t get_k64val(IRIns *ir) +{ + if (ir->o == IR_KINT64) { + return ir_kint64(ir)->u64; + } else if (ir->o == IR_KGC) { + return (uint64_t)ir_kgc(ir); + } else if (ir->o == IR_KPTR || ir->o == IR_KKPTR) { + return (uint64_t)ir_kptr(ir); + } else { + lua_assert(ir->o == IR_KINT || ir->o == IR_KNULL); + return ir->i; /* Sign-extended. */ + } +} + +/* Encode constant in K12 format for data processing instructions. */ +static uint32_t emit_isk12(int64_t n) +{ + uint64_t k = (n < 0) ? -n : n; + uint32_t m = (n < 0) ? 0x40000000 : 0; + if (k < 0x1000) { + return A64I_K12|m|A64F_U12(k); + } else if ((k & 0xfff000) == k) { + return A64I_K12|m|0x400000|A64F_U12(k>>12); + } + return 0; +} + +#define emit_clz64(n) __builtin_clzll(n) +#define emit_ctz64(n) __builtin_ctzll(n) + +/* Encode constant in K13 format for logical data processing instructions. */ +static uint32_t emit_isk13(uint64_t n, int is64) +{ + int inv = 0, w = 128, lz, tz; + if (n & 1) { n = ~n; w = 64; inv = 1; } /* Avoid wrap-around of ones. */ + if (!n) return 0; /* Neither all-zero nor all-ones are allowed. */ + do { /* Find the repeat width. */ + if (is64 && (uint32_t)(n^(n>>32))) break; + n = (uint32_t)n; + if (!n) return 0; /* Ditto when passing n=0xffffffff and is64=0. */ + w = 32; if ((n^(n>>16)) & 0xffff) break; + n = n & 0xffff; w = 16; if ((n^(n>>8)) & 0xff) break; + n = n & 0xff; w = 8; if ((n^(n>>4)) & 0xf) break; + n = n & 0xf; w = 4; if ((n^(n>>2)) & 0x3) break; + n = n & 0x3; w = 2; + } while (0); + lz = emit_clz64(n); + tz = emit_ctz64(n); + if ((int64_t)(n << lz) >> (lz+tz) != -1ll) return 0; /* Non-contiguous? */ + if (inv) + return A64I_K13 | (((lz-w) & 127) << 16) | (((lz+tz-w-1) & 63) << 10); + else + return A64I_K13 | ((w-tz) << 16) | (((63-lz-tz-w-w) & 63) << 10); +} + +static uint32_t emit_isfpk64(uint64_t n) +{ + uint64_t etop9 = ((n >> 54) & 0x1ff); + if ((n << 16) == 0 && (etop9 == 0x100 || etop9 == 0x0ff)) { + return (uint32_t)(((n >> 48) & 0x7f) | ((n >> 56) & 0x80)); + } + return ~0u; +} + +/* -- Emit basic instructions --------------------------------------------- */ + +static void emit_dnma(ASMState *as, A64Ins ai, Reg rd, Reg rn, Reg rm, Reg ra) +{ + *--as->mcp = ai | A64F_D(rd) | A64F_N(rn) | A64F_M(rm) | A64F_A(ra); +} + +static void emit_dnm(ASMState *as, A64Ins ai, Reg rd, Reg rn, Reg rm) +{ + *--as->mcp = ai | A64F_D(rd) | A64F_N(rn) | A64F_M(rm); +} + +static void emit_dm(ASMState *as, A64Ins ai, Reg rd, Reg rm) +{ + *--as->mcp = ai | A64F_D(rd) | A64F_M(rm); +} + +static void emit_dn(ASMState *as, A64Ins ai, Reg rd, Reg rn) +{ + *--as->mcp = ai | A64F_D(rd) | A64F_N(rn); +} + +static void emit_nm(ASMState *as, A64Ins ai, Reg rn, Reg rm) +{ + *--as->mcp = ai | A64F_N(rn) | A64F_M(rm); +} + +static void emit_d(ASMState *as, A64Ins ai, Reg rd) +{ + *--as->mcp = ai | A64F_D(rd); +} + +static void emit_n(ASMState *as, A64Ins ai, Reg rn) +{ + *--as->mcp = ai | A64F_N(rn); +} + +static int emit_checkofs(A64Ins ai, int64_t ofs) +{ + int scale = (ai >> 30) & 3; + if (ofs < 0 || (ofs & ((1<= -256 && ofs <= 255) ? -1 : 0; + } else { + return (ofs < (4096<> 30) & 3; + lua_assert(ot); + /* Combine LDR/STR pairs to LDP/STP. */ + if ((sc == 2 || sc == 3) && + (!(ai & 0x400000) || rd != rn) && + as->mcp != as->mcloop) { + uint32_t prev = *as->mcp & ~A64F_D(31); + int ofsm = ofs - (1<>sc)) || + prev == ((ai^A64I_LS_U) | A64F_N(rn) | A64F_S9(ofsm&0x1ff))) { + aip = (A64F_A(rd) | A64F_D(*as->mcp & 31)); + } else if (prev == (ai | A64F_N(rn) | A64F_U12(ofsp>>sc)) || + prev == ((ai^A64I_LS_U) | A64F_N(rn) | A64F_S9(ofsp&0x1ff))) { + aip = (A64F_D(rd) | A64F_A(*as->mcp & 31)); + ofsm = ofs; + } else { + goto nopair; + } + if (ofsm >= (int)((unsigned int)-64<mcp = aip | A64F_N(rn) | ((ofsm >> sc) << 15) | + (ai ^ ((ai == A64I_LDRx || ai == A64I_STRx) ? 0x50000000 : 0x90000000)); + return; + } + } +nopair: + if (ot == 1) + *--as->mcp = ai | A64F_D(rd) | A64F_N(rn) | A64F_U12(ofs >> sc); + else + *--as->mcp = (ai^A64I_LS_U) | A64F_D(rd) | A64F_N(rn) | A64F_S9(ofs & 0x1ff); +} + +/* -- Emit loads/stores --------------------------------------------------- */ + +/* Prefer rematerialization of BASE/L from global_State over spills. */ +#define emit_canremat(ref) ((ref) <= ASMREF_L) + +/* Try to find an N-step delta relative to other consts with N < lim. */ +static int emit_kdelta(ASMState *as, Reg rd, uint64_t k, int lim) +{ + RegSet work = ~as->freeset & RSET_GPR; + if (lim <= 1) return 0; /* Can't beat that. */ + while (work) { + Reg r = rset_picktop(work); + IRRef ref = regcost_ref(as->cost[r]); + lua_assert(r != rd); + if (ref < REF_TRUE) { + uint64_t kx = ra_iskref(ref) ? (uint64_t)ra_krefk(as, ref) : + get_k64val(IR(ref)); + int64_t delta = (int64_t)(k - kx); + if (delta == 0) { + emit_dm(as, A64I_MOVx, rd, r); + return 1; + } else { + uint32_t k12 = emit_isk12(delta < 0 ? -delta : delta); + if (k12) { + emit_dn(as, (delta < 0 ? A64I_SUBx : A64I_ADDx)^k12, rd, r); + return 1; + } + /* Do other ops or multi-step deltas pay off? Probably not. + ** E.g. XOR rarely helps with pointer consts. + */ + } + } + rset_clear(work, r); + } + return 0; /* Failed. */ +} + +static void emit_loadk(ASMState *as, Reg rd, uint64_t u64, int is64) +{ + uint32_t k13 = emit_isk13(u64, is64); + if (k13) { /* Can the constant be represented as a bitmask immediate? */ + emit_dn(as, (is64|A64I_ORRw)^k13, rd, RID_ZERO); + } else { + int i, zeros = 0, ones = 0, neg; + if (!is64) u64 = (int64_t)(int32_t)u64; /* Sign-extend. */ + /* Count homogeneous 16 bit fragments. */ + for (i = 0; i < 4; i++) { + uint64_t frag = (u64 >> i*16) & 0xffff; + zeros += (frag == 0); + ones += (frag == 0xffff); + } + neg = ones > zeros; /* Use MOVN if it pays off. */ + if (!emit_kdelta(as, rd, u64, 4 - (neg ? ones : zeros))) { + int shift = 0, lshift = 0; + uint64_t n64 = neg ? ~u64 : u64; + if (n64 != 0) { + /* Find first/last fragment to be filled. */ + shift = (63-emit_clz64(n64)) & ~15; + lshift = emit_ctz64(n64) & ~15; + } + /* MOVK requires the original value (u64). */ + while (shift > lshift) { + uint32_t u16 = (u64 >> shift) & 0xffff; + /* Skip fragments that are correctly filled by MOVN/MOVZ. */ + if (u16 != (neg ? 0xffff : 0)) + emit_d(as, is64 | A64I_MOVKw | A64F_U16(u16) | A64F_LSL16(shift), rd); + shift -= 16; + } + /* But MOVN needs an inverted value (n64). */ + emit_d(as, (neg ? A64I_MOVNx : A64I_MOVZx) | + A64F_U16((n64 >> lshift) & 0xffff) | A64F_LSL16(lshift), rd); + } + } +} + +/* Load a 32 bit constant into a GPR. */ +#define emit_loadi(as, rd, i) emit_loadk(as, rd, i, 0) + +/* Load a 64 bit constant into a GPR. */ +#define emit_loadu64(as, rd, i) emit_loadk(as, rd, i, A64I_X) + +#define emit_loada(as, r, addr) emit_loadu64(as, (r), (uintptr_t)(addr)) + +#define glofs(as, k) \ + ((intptr_t)((uintptr_t)(k) - (uintptr_t)&J2GG(as->J)->g)) +#define mcpofs(as, k) \ + ((intptr_t)((uintptr_t)(k) - (uintptr_t)(as->mcp - 1))) +#define checkmcpofs(as, k) \ + (A64F_S_OK(mcpofs(as, k)>>2, 19)) + +static Reg ra_allock(ASMState *as, intptr_t k, RegSet allow); + +/* Get/set from constant pointer. */ +static void emit_lsptr(ASMState *as, A64Ins ai, Reg r, void *p) +{ + /* First, check if ip + offset is in range. */ + if ((ai & 0x00400000) && checkmcpofs(as, p)) { + emit_d(as, A64I_LDRLx | A64F_S19(mcpofs(as, p)>>2), r); + } else { + Reg base = RID_GL; /* Next, try GL + offset. */ + int64_t ofs = glofs(as, p); + if (!emit_checkofs(ai, ofs)) { /* Else split up into base reg + offset. */ + int64_t i64 = i64ptr(p); + base = ra_allock(as, (i64 & ~0x7fffull), rset_exclude(RSET_GPR, r)); + ofs = i64 & 0x7fffull; + } + emit_lso(as, ai, r, base, ofs); + } +} + +/* Load 64 bit IR constant into register. */ +static void emit_loadk64(ASMState *as, Reg r, IRIns *ir) +{ + const uint64_t *k = &ir_k64(ir)->u64; + int64_t ofs; + if (r >= RID_MAX_GPR) { + uint32_t fpk = emit_isfpk64(*k); + if (fpk != ~0u) { + emit_d(as, A64I_FMOV_DI | A64F_FP8(fpk), (r & 31)); + return; + } + } + ofs = glofs(as, k); + if (emit_checkofs(A64I_LDRx, ofs)) { + emit_lso(as, r >= RID_MAX_GPR ? A64I_LDRd : A64I_LDRx, + (r & 31), RID_GL, ofs); + } else { + if (r >= RID_MAX_GPR) { + emit_dn(as, A64I_FMOV_D_R, (r & 31), RID_TMP); + r = RID_TMP; + } + if (checkmcpofs(as, k)) + emit_d(as, A64I_LDRLx | A64F_S19(mcpofs(as, k)>>2), r); + else + emit_loadu64(as, r, *k); + } +} + +/* Get/set global_State fields. */ +#define emit_getgl(as, r, field) \ + emit_lsptr(as, A64I_LDRx, (r), (void *)&J2G(as->J)->field) +#define emit_setgl(as, r, field) \ + emit_lsptr(as, A64I_STRx, (r), (void *)&J2G(as->J)->field) + +/* Trace number is determined from pc of exit instruction. */ +#define emit_setvmstate(as, i) UNUSED(i) + +/* -- Emit control-flow instructions -------------------------------------- */ + +/* Label for internal jumps. */ +typedef MCode *MCLabel; + +/* Return label pointing to current PC. */ +#define emit_label(as) ((as)->mcp) + +static void emit_cond_branch(ASMState *as, A64CC cond, MCode *target) +{ + MCode *p = --as->mcp; + ptrdiff_t delta = target - p; + lua_assert(A64F_S_OK(delta, 19)); + *p = A64I_BCC | A64F_S19(delta) | cond; +} + +static void emit_branch(ASMState *as, A64Ins ai, MCode *target) +{ + MCode *p = --as->mcp; + ptrdiff_t delta = target - p; + lua_assert(A64F_S_OK(delta, 26)); + *p = ai | A64F_S26(delta); +} + +static void emit_tnb(ASMState *as, A64Ins ai, Reg r, uint32_t bit, MCode *target) +{ + MCode *p = --as->mcp; + ptrdiff_t delta = target - p; + lua_assert(bit < 63 && A64F_S_OK(delta, 14)); + if (bit > 31) ai |= A64I_X; + *p = ai | A64F_BIT(bit & 31) | A64F_S14(delta) | r; +} + +static void emit_cnb(ASMState *as, A64Ins ai, Reg r, MCode *target) +{ + MCode *p = --as->mcp; + ptrdiff_t delta = target - p; + lua_assert(A64F_S_OK(delta, 19)); + *p = ai | A64F_S19(delta) | r; +} + +#define emit_jmp(as, target) emit_branch(as, A64I_B, (target)) + +static void emit_call(ASMState *as, void *target) +{ + MCode *p = --as->mcp; + ptrdiff_t delta = (char *)target - (char *)p; + if (A64F_S_OK(delta>>2, 26)) { + *p = A64I_BL | A64F_S26(delta>>2); + } else { /* Target out of range: need indirect call. But don't use R0-R7. */ + Reg r = ra_allock(as, i64ptr(target), + RSET_RANGE(RID_X8, RID_MAX_GPR)-RSET_FIXED); + *p = A64I_BLR | A64F_N(r); + } +} + +/* -- Emit generic operations --------------------------------------------- */ + +/* Generic move between two regs. */ +static void emit_movrr(ASMState *as, IRIns *ir, Reg dst, Reg src) +{ + if (dst >= RID_MAX_GPR) { + emit_dn(as, irt_isnum(ir->t) ? A64I_FMOV_D : A64I_FMOV_S, + (dst & 31), (src & 31)); + return; + } + if (as->mcp != as->mcloop) { /* Swap early registers for loads/stores. */ + MCode ins = *as->mcp, swp = (src^dst); + if ((ins & 0xbf800000) == 0xb9000000) { + if (!((ins ^ (dst << 5)) & 0x000003e0)) + *as->mcp = ins ^ (swp << 5); /* Swap N in load/store. */ + if (!(ins & 0x00400000) && !((ins ^ dst) & 0x0000001f)) + *as->mcp = ins ^ swp; /* Swap D in store. */ + } + } + emit_dm(as, A64I_MOVx, dst, src); +} + +/* Generic load of register with base and (small) offset address. */ +static void emit_loadofs(ASMState *as, IRIns *ir, Reg r, Reg base, int32_t ofs) +{ + if (r >= RID_MAX_GPR) + emit_lso(as, irt_isnum(ir->t) ? A64I_LDRd : A64I_LDRs, (r & 31), base, ofs); + else + emit_lso(as, irt_is64(ir->t) ? A64I_LDRx : A64I_LDRw, r, base, ofs); +} + +/* Generic store of register with base and (small) offset address. */ +static void emit_storeofs(ASMState *as, IRIns *ir, Reg r, Reg base, int32_t ofs) +{ + if (r >= RID_MAX_GPR) + emit_lso(as, irt_isnum(ir->t) ? A64I_STRd : A64I_STRs, (r & 31), base, ofs); + else + emit_lso(as, irt_is64(ir->t) ? A64I_STRx : A64I_STRw, r, base, ofs); +} + +/* Emit an arithmetic operation with a constant operand. */ +static void emit_opk(ASMState *as, A64Ins ai, Reg dest, Reg src, + int32_t i, RegSet allow) +{ + uint32_t k = emit_isk12(i); + if (k) + emit_dn(as, ai^k, dest, src); + else + emit_dnm(as, ai, dest, src, ra_allock(as, i, allow)); +} + +/* Add offset to pointer. */ +static void emit_addptr(ASMState *as, Reg r, int32_t ofs) +{ + if (ofs) + emit_opk(as, ofs < 0 ? A64I_SUBx : A64I_ADDx, r, r, + ofs < 0 ? -ofs : ofs, rset_exclude(RSET_GPR, r)); +} + +#define emit_spsub(as, ofs) emit_addptr(as, RID_SP, -(ofs)) + diff --git a/lib/LuaJIT/lj_emit_mips.h b/lib/LuaJIT/lj_emit_mips.h new file mode 100644 index 0000000..bb6593a --- /dev/null +++ b/lib/LuaJIT/lj_emit_mips.h @@ -0,0 +1,295 @@ +/* +** MIPS instruction emitter. +** Copyright (C) 2005-2017 Mike Pall. See Copyright Notice in luajit.h +*/ + +#if LJ_64 +static intptr_t get_k64val(IRIns *ir) +{ + if (ir->o == IR_KINT64) { + return (intptr_t)ir_kint64(ir)->u64; + } else if (ir->o == IR_KGC) { + return (intptr_t)ir_kgc(ir); + } else if (ir->o == IR_KPTR || ir->o == IR_KKPTR) { + return (intptr_t)ir_kptr(ir); + } else if (LJ_SOFTFP && ir->o == IR_KNUM) { + return (intptr_t)ir_knum(ir)->u64; + } else { + lua_assert(ir->o == IR_KINT || ir->o == IR_KNULL); + return ir->i; /* Sign-extended. */ + } +} +#endif + +#if LJ_64 +#define get_kval(ir) get_k64val(ir) +#else +#define get_kval(ir) ((ir)->i) +#endif + +/* -- Emit basic instructions --------------------------------------------- */ + +static void emit_dst(ASMState *as, MIPSIns mi, Reg rd, Reg rs, Reg rt) +{ + *--as->mcp = mi | MIPSF_D(rd) | MIPSF_S(rs) | MIPSF_T(rt); +} + +static void emit_dta(ASMState *as, MIPSIns mi, Reg rd, Reg rt, uint32_t a) +{ + *--as->mcp = mi | MIPSF_D(rd) | MIPSF_T(rt) | MIPSF_A(a); +} + +#define emit_ds(as, mi, rd, rs) emit_dst(as, (mi), (rd), (rs), 0) +#define emit_tg(as, mi, rt, rg) emit_dst(as, (mi), (rg)&31, 0, (rt)) + +static void emit_tsi(ASMState *as, MIPSIns mi, Reg rt, Reg rs, int32_t i) +{ + *--as->mcp = mi | MIPSF_T(rt) | MIPSF_S(rs) | (i & 0xffff); +} + +#define emit_ti(as, mi, rt, i) emit_tsi(as, (mi), (rt), 0, (i)) +#define emit_hsi(as, mi, rh, rs, i) emit_tsi(as, (mi), (rh) & 31, (rs), (i)) + +static void emit_fgh(ASMState *as, MIPSIns mi, Reg rf, Reg rg, Reg rh) +{ + *--as->mcp = mi | MIPSF_F(rf&31) | MIPSF_G(rg&31) | MIPSF_H(rh&31); +} + +#define emit_fg(as, mi, rf, rg) emit_fgh(as, (mi), (rf), (rg), 0) + +static void emit_rotr(ASMState *as, Reg dest, Reg src, Reg tmp, uint32_t shift) +{ + if (LJ_64 || (as->flags & JIT_F_MIPSXXR2)) { + emit_dta(as, MIPSI_ROTR, dest, src, shift); + } else { + emit_dst(as, MIPSI_OR, dest, dest, tmp); + emit_dta(as, MIPSI_SLL, dest, src, (-shift)&31); + emit_dta(as, MIPSI_SRL, tmp, src, shift); + } +} + +#if LJ_64 +static void emit_tsml(ASMState *as, MIPSIns mi, Reg rt, Reg rs, uint32_t msb, + uint32_t lsb) +{ + *--as->mcp = mi | MIPSF_T(rt) | MIPSF_S(rs) | MIPSF_M(msb) | MIPSF_L(lsb); +} +#endif + +/* -- Emit loads/stores --------------------------------------------------- */ + +/* Prefer rematerialization of BASE/L from global_State over spills. */ +#define emit_canremat(ref) ((ref) <= REF_BASE) + +/* Try to find a one step delta relative to another constant. */ +static int emit_kdelta1(ASMState *as, Reg t, intptr_t i) +{ + RegSet work = ~as->freeset & RSET_GPR; + while (work) { + Reg r = rset_picktop(work); + IRRef ref = regcost_ref(as->cost[r]); + lua_assert(r != t); + if (ref < ASMREF_L) { + intptr_t delta = (intptr_t)((uintptr_t)i - + (uintptr_t)(ra_iskref(ref) ? ra_krefk(as, ref) : get_kval(IR(ref)))); + if (checki16(delta)) { + emit_tsi(as, MIPSI_AADDIU, t, r, delta); + return 1; + } + } + rset_clear(work, r); + } + return 0; /* Failed. */ +} + +/* Load a 32 bit constant into a GPR. */ +static void emit_loadi(ASMState *as, Reg r, int32_t i) +{ + if (checki16(i)) { + emit_ti(as, MIPSI_LI, r, i); + } else { + if ((i & 0xffff)) { + intptr_t jgl = (intptr_t)(void *)J2G(as->J); + if ((uintptr_t)(i-jgl) < 65536) { + emit_tsi(as, MIPSI_ADDIU, r, RID_JGL, i-jgl-32768); + return; + } else if (emit_kdelta1(as, r, i)) { + return; + } else if ((i >> 16) == 0) { + emit_tsi(as, MIPSI_ORI, r, RID_ZERO, i); + return; + } + emit_tsi(as, MIPSI_ORI, r, r, i); + } + emit_ti(as, MIPSI_LUI, r, (i >> 16)); + } +} + +#if LJ_64 +/* Load a 64 bit constant into a GPR. */ +static void emit_loadu64(ASMState *as, Reg r, uint64_t u64) +{ + if (checki32((int64_t)u64)) { + emit_loadi(as, r, (int32_t)u64); + } else { + uint64_t delta = u64 - (uint64_t)(void *)J2G(as->J); + if (delta < 65536) { + emit_tsi(as, MIPSI_DADDIU, r, RID_JGL, (int32_t)(delta-32768)); + } else if (emit_kdelta1(as, r, (intptr_t)u64)) { + return; + } else { + if ((u64 & 0xffff)) { + emit_tsi(as, MIPSI_ORI, r, r, u64 & 0xffff); + } + if (((u64 >> 16) & 0xffff)) { + emit_dta(as, MIPSI_DSLL, r, r, 16); + emit_tsi(as, MIPSI_ORI, r, r, (u64 >> 16) & 0xffff); + emit_dta(as, MIPSI_DSLL, r, r, 16); + } else { + emit_dta(as, MIPSI_DSLL32, r, r, 0); + } + emit_loadi(as, r, (int32_t)(u64 >> 32)); + } + /* TODO: There are probably more optimization opportunities. */ + } +} + +#define emit_loada(as, r, addr) emit_loadu64(as, (r), u64ptr((addr))) +#else +#define emit_loada(as, r, addr) emit_loadi(as, (r), i32ptr((addr))) +#endif + +static Reg ra_allock(ASMState *as, intptr_t k, RegSet allow); +static void ra_allockreg(ASMState *as, intptr_t k, Reg r); + +/* Get/set from constant pointer. */ +static void emit_lsptr(ASMState *as, MIPSIns mi, Reg r, void *p, RegSet allow) +{ + intptr_t jgl = (intptr_t)(J2G(as->J)); + intptr_t i = (intptr_t)(p); + Reg base; + if ((uint32_t)(i-jgl) < 65536) { + i = i-jgl-32768; + base = RID_JGL; + } else { + base = ra_allock(as, i-(int16_t)i, allow); + } + emit_tsi(as, mi, r, base, i); +} + +#if LJ_64 +static void emit_loadk64(ASMState *as, Reg r, IRIns *ir) +{ + const uint64_t *k = &ir_k64(ir)->u64; + Reg r64 = r; + if (rset_test(RSET_FPR, r)) { + r64 = RID_TMP; + emit_tg(as, MIPSI_DMTC1, r64, r); + } + if ((uint32_t)((intptr_t)k-(intptr_t)J2G(as->J)) < 65536) + emit_lsptr(as, MIPSI_LD, r64, (void *)k, 0); + else + emit_loadu64(as, r64, *k); +} +#else +#define emit_loadk64(as, r, ir) \ + emit_lsptr(as, MIPSI_LDC1, ((r) & 31), (void *)&ir_knum((ir))->u64, RSET_GPR) +#endif + +/* Get/set global_State fields. */ +static void emit_lsglptr(ASMState *as, MIPSIns mi, Reg r, int32_t ofs) +{ + emit_tsi(as, mi, r, RID_JGL, ofs-32768); +} + +#define emit_getgl(as, r, field) \ + emit_lsglptr(as, MIPSI_AL, (r), (int32_t)offsetof(global_State, field)) +#define emit_setgl(as, r, field) \ + emit_lsglptr(as, MIPSI_AS, (r), (int32_t)offsetof(global_State, field)) + +/* Trace number is determined from per-trace exit stubs. */ +#define emit_setvmstate(as, i) UNUSED(i) + +/* -- Emit control-flow instructions -------------------------------------- */ + +/* Label for internal jumps. */ +typedef MCode *MCLabel; + +/* Return label pointing to current PC. */ +#define emit_label(as) ((as)->mcp) + +static void emit_branch(ASMState *as, MIPSIns mi, Reg rs, Reg rt, MCode *target) +{ + MCode *p = as->mcp; + ptrdiff_t delta = target - p; + lua_assert(((delta + 0x8000) >> 16) == 0); + *--p = mi | MIPSF_S(rs) | MIPSF_T(rt) | ((uint32_t)delta & 0xffffu); + as->mcp = p; +} + +static void emit_jmp(ASMState *as, MCode *target) +{ + *--as->mcp = MIPSI_NOP; + emit_branch(as, MIPSI_B, RID_ZERO, RID_ZERO, (target)); +} + +static void emit_call(ASMState *as, void *target, int needcfa) +{ + MCode *p = as->mcp; + *--p = MIPSI_NOP; + if ((((uintptr_t)target ^ (uintptr_t)p) >> 28) == 0) { + *--p = (((uintptr_t)target & 1) ? MIPSI_JALX : MIPSI_JAL) | + (((uintptr_t)target >>2) & 0x03ffffffu); + } else { /* Target out of range: need indirect call. */ + *--p = MIPSI_JALR | MIPSF_S(RID_CFUNCADDR); + needcfa = 1; + } + as->mcp = p; + if (needcfa) ra_allockreg(as, (intptr_t)target, RID_CFUNCADDR); +} + +/* -- Emit generic operations --------------------------------------------- */ + +#define emit_move(as, dst, src) \ + emit_ds(as, MIPSI_MOVE, (dst), (src)) + +/* Generic move between two regs. */ +static void emit_movrr(ASMState *as, IRIns *ir, Reg dst, Reg src) +{ + if (dst < RID_MAX_GPR) + emit_move(as, dst, src); + else + emit_fg(as, irt_isnum(ir->t) ? MIPSI_MOV_D : MIPSI_MOV_S, dst, src); +} + +/* Generic load of register with base and (small) offset address. */ +static void emit_loadofs(ASMState *as, IRIns *ir, Reg r, Reg base, int32_t ofs) +{ + if (r < RID_MAX_GPR) + emit_tsi(as, irt_is64(ir->t) ? MIPSI_LD : MIPSI_LW, r, base, ofs); + else + emit_tsi(as, irt_isnum(ir->t) ? MIPSI_LDC1 : MIPSI_LWC1, + (r & 31), base, ofs); +} + +/* Generic store of register with base and (small) offset address. */ +static void emit_storeofs(ASMState *as, IRIns *ir, Reg r, Reg base, int32_t ofs) +{ + if (r < RID_MAX_GPR) + emit_tsi(as, irt_is64(ir->t) ? MIPSI_SD : MIPSI_SW, r, base, ofs); + else + emit_tsi(as, irt_isnum(ir->t) ? MIPSI_SDC1 : MIPSI_SWC1, + (r&31), base, ofs); +} + +/* Add offset to pointer. */ +static void emit_addptr(ASMState *as, Reg r, int32_t ofs) +{ + if (ofs) { + lua_assert(checki16(ofs)); + emit_tsi(as, MIPSI_AADDIU, r, r, ofs); + } +} + +#define emit_spsub(as, ofs) emit_addptr(as, RID_SP, -(ofs)) + diff --git a/lib/LuaJIT/lj_emit_ppc.h b/lib/LuaJIT/lj_emit_ppc.h new file mode 100644 index 0000000..21c3c2a --- /dev/null +++ b/lib/LuaJIT/lj_emit_ppc.h @@ -0,0 +1,238 @@ +/* +** PPC instruction emitter. +** Copyright (C) 2005-2017 Mike Pall. See Copyright Notice in luajit.h +*/ + +/* -- Emit basic instructions --------------------------------------------- */ + +static void emit_tab(ASMState *as, PPCIns pi, Reg rt, Reg ra, Reg rb) +{ + *--as->mcp = pi | PPCF_T(rt) | PPCF_A(ra) | PPCF_B(rb); +} + +#define emit_asb(as, pi, ra, rs, rb) emit_tab(as, (pi), (rs), (ra), (rb)) +#define emit_as(as, pi, ra, rs) emit_tab(as, (pi), (rs), (ra), 0) +#define emit_ab(as, pi, ra, rb) emit_tab(as, (pi), 0, (ra), (rb)) + +static void emit_tai(ASMState *as, PPCIns pi, Reg rt, Reg ra, int32_t i) +{ + *--as->mcp = pi | PPCF_T(rt) | PPCF_A(ra) | (i & 0xffff); +} + +#define emit_ti(as, pi, rt, i) emit_tai(as, (pi), (rt), 0, (i)) +#define emit_ai(as, pi, ra, i) emit_tai(as, (pi), 0, (ra), (i)) +#define emit_asi(as, pi, ra, rs, i) emit_tai(as, (pi), (rs), (ra), (i)) + +#define emit_fab(as, pi, rf, ra, rb) \ + emit_tab(as, (pi), (rf)&31, (ra)&31, (rb)&31) +#define emit_fb(as, pi, rf, rb) emit_tab(as, (pi), (rf)&31, 0, (rb)&31) +#define emit_fac(as, pi, rf, ra, rc) \ + emit_tab(as, (pi) | PPCF_C((rc) & 31), (rf)&31, (ra)&31, 0) +#define emit_facb(as, pi, rf, ra, rc, rb) \ + emit_tab(as, (pi) | PPCF_C((rc) & 31), (rf)&31, (ra)&31, (rb)&31) +#define emit_fai(as, pi, rf, ra, i) emit_tai(as, (pi), (rf)&31, (ra), (i)) + +static void emit_rot(ASMState *as, PPCIns pi, Reg ra, Reg rs, + int32_t n, int32_t b, int32_t e) +{ + *--as->mcp = pi | PPCF_T(rs) | PPCF_A(ra) | PPCF_B(n) | + PPCF_MB(b) | PPCF_ME(e); +} + +static void emit_slwi(ASMState *as, Reg ra, Reg rs, int32_t n) +{ + lua_assert(n >= 0 && n < 32); + emit_rot(as, PPCI_RLWINM, ra, rs, n, 0, 31-n); +} + +static void emit_rotlwi(ASMState *as, Reg ra, Reg rs, int32_t n) +{ + lua_assert(n >= 0 && n < 32); + emit_rot(as, PPCI_RLWINM, ra, rs, n, 0, 31); +} + +/* -- Emit loads/stores --------------------------------------------------- */ + +/* Prefer rematerialization of BASE/L from global_State over spills. */ +#define emit_canremat(ref) ((ref) <= REF_BASE) + +/* Try to find a one step delta relative to another constant. */ +static int emit_kdelta1(ASMState *as, Reg t, int32_t i) +{ + RegSet work = ~as->freeset & RSET_GPR; + while (work) { + Reg r = rset_picktop(work); + IRRef ref = regcost_ref(as->cost[r]); + lua_assert(r != t); + if (ref < ASMREF_L) { + int32_t delta = i - (ra_iskref(ref) ? ra_krefk(as, ref) : IR(ref)->i); + if (checki16(delta)) { + emit_tai(as, PPCI_ADDI, t, r, delta); + return 1; + } + } + rset_clear(work, r); + } + return 0; /* Failed. */ +} + +/* Load a 32 bit constant into a GPR. */ +static void emit_loadi(ASMState *as, Reg r, int32_t i) +{ + if (checki16(i)) { + emit_ti(as, PPCI_LI, r, i); + } else { + if ((i & 0xffff)) { + int32_t jgl = i32ptr(J2G(as->J)); + if ((uint32_t)(i-jgl) < 65536) { + emit_tai(as, PPCI_ADDI, r, RID_JGL, i-jgl-32768); + return; + } else if (emit_kdelta1(as, r, i)) { + return; + } + emit_asi(as, PPCI_ORI, r, r, i); + } + emit_ti(as, PPCI_LIS, r, (i >> 16)); + } +} + +#define emit_loada(as, r, addr) emit_loadi(as, (r), i32ptr((addr))) + +static Reg ra_allock(ASMState *as, intptr_t k, RegSet allow); + +/* Get/set from constant pointer. */ +static void emit_lsptr(ASMState *as, PPCIns pi, Reg r, void *p, RegSet allow) +{ + int32_t jgl = i32ptr(J2G(as->J)); + int32_t i = i32ptr(p); + Reg base; + if ((uint32_t)(i-jgl) < 65536) { + i = i-jgl-32768; + base = RID_JGL; + } else { + base = ra_allock(as, i-(int16_t)i, allow); + } + emit_tai(as, pi, r, base, i); +} + +#define emit_loadk64(as, r, ir) \ + emit_lsptr(as, PPCI_LFD, ((r) & 31), (void *)&ir_knum((ir))->u64, RSET_GPR) + +/* Get/set global_State fields. */ +static void emit_lsglptr(ASMState *as, PPCIns pi, Reg r, int32_t ofs) +{ + emit_tai(as, pi, r, RID_JGL, ofs-32768); +} + +#define emit_getgl(as, r, field) \ + emit_lsglptr(as, PPCI_LWZ, (r), (int32_t)offsetof(global_State, field)) +#define emit_setgl(as, r, field) \ + emit_lsglptr(as, PPCI_STW, (r), (int32_t)offsetof(global_State, field)) + +/* Trace number is determined from per-trace exit stubs. */ +#define emit_setvmstate(as, i) UNUSED(i) + +/* -- Emit control-flow instructions -------------------------------------- */ + +/* Label for internal jumps. */ +typedef MCode *MCLabel; + +/* Return label pointing to current PC. */ +#define emit_label(as) ((as)->mcp) + +static void emit_condbranch(ASMState *as, PPCIns pi, PPCCC cc, MCode *target) +{ + MCode *p = --as->mcp; + ptrdiff_t delta = (char *)target - (char *)p; + lua_assert(((delta + 0x8000) >> 16) == 0); + pi ^= (delta & 0x8000) * (PPCF_Y/0x8000); + *p = pi | PPCF_CC(cc) | ((uint32_t)delta & 0xffffu); +} + +static void emit_jmp(ASMState *as, MCode *target) +{ + MCode *p = --as->mcp; + ptrdiff_t delta = (char *)target - (char *)p; + *p = PPCI_B | (delta & 0x03fffffcu); +} + +static void emit_call(ASMState *as, void *target) +{ + MCode *p = --as->mcp; + ptrdiff_t delta = (char *)target - (char *)p; + if ((((delta>>2) + 0x00800000) >> 24) == 0) { + *p = PPCI_BL | (delta & 0x03fffffcu); + } else { /* Target out of range: need indirect call. Don't use arg reg. */ + RegSet allow = RSET_GPR & ~RSET_RANGE(RID_R0, REGARG_LASTGPR+1); + Reg r = ra_allock(as, i32ptr(target), allow); + *p = PPCI_BCTRL; + p[-1] = PPCI_MTCTR | PPCF_T(r); + as->mcp = p-1; + } +} + +/* -- Emit generic operations --------------------------------------------- */ + +#define emit_mr(as, dst, src) \ + emit_asb(as, PPCI_MR, (dst), (src), (src)) + +/* Generic move between two regs. */ +static void emit_movrr(ASMState *as, IRIns *ir, Reg dst, Reg src) +{ + UNUSED(ir); + if (dst < RID_MAX_GPR) + emit_mr(as, dst, src); + else + emit_fb(as, PPCI_FMR, dst, src); +} + +/* Generic load of register with base and (small) offset address. */ +static void emit_loadofs(ASMState *as, IRIns *ir, Reg r, Reg base, int32_t ofs) +{ + if (r < RID_MAX_GPR) + emit_tai(as, PPCI_LWZ, r, base, ofs); + else + emit_fai(as, irt_isnum(ir->t) ? PPCI_LFD : PPCI_LFS, r, base, ofs); +} + +/* Generic store of register with base and (small) offset address. */ +static void emit_storeofs(ASMState *as, IRIns *ir, Reg r, Reg base, int32_t ofs) +{ + if (r < RID_MAX_GPR) + emit_tai(as, PPCI_STW, r, base, ofs); + else + emit_fai(as, irt_isnum(ir->t) ? PPCI_STFD : PPCI_STFS, r, base, ofs); +} + +/* Emit a compare (for equality) with a constant operand. */ +static void emit_cmpi(ASMState *as, Reg r, int32_t k) +{ + if (checki16(k)) { + emit_ai(as, PPCI_CMPWI, r, k); + } else if (checku16(k)) { + emit_ai(as, PPCI_CMPLWI, r, k); + } else { + emit_ai(as, PPCI_CMPLWI, RID_TMP, k); + emit_asi(as, PPCI_XORIS, RID_TMP, r, (k >> 16)); + } +} + +/* Add offset to pointer. */ +static void emit_addptr(ASMState *as, Reg r, int32_t ofs) +{ + if (ofs) { + emit_tai(as, PPCI_ADDI, r, r, ofs); + if (!checki16(ofs)) + emit_tai(as, PPCI_ADDIS, r, r, (ofs + 32768) >> 16); + } +} + +static void emit_spsub(ASMState *as, int32_t ofs) +{ + if (ofs) { + emit_tai(as, PPCI_STWU, RID_TMP, RID_SP, -ofs); + emit_tai(as, PPCI_ADDI, RID_TMP, RID_SP, + CFRAME_SIZE + (as->parent ? as->parent->spadjust : 0)); + } +} + diff --git a/lib/LuaJIT/lj_emit_x86.h b/lib/LuaJIT/lj_emit_x86.h new file mode 100644 index 0000000..b3dc4ea --- /dev/null +++ b/lib/LuaJIT/lj_emit_x86.h @@ -0,0 +1,573 @@ +/* +** x86/x64 instruction emitter. +** Copyright (C) 2005-2017 Mike Pall. See Copyright Notice in luajit.h +*/ + +/* -- Emit basic instructions --------------------------------------------- */ + +#define MODRM(mode, r1, r2) ((MCode)((mode)+(((r1)&7)<<3)+((r2)&7))) + +#if LJ_64 +#define REXRB(p, rr, rb) \ + { MCode rex = 0x40 + (((rr)>>1)&4) + (((rb)>>3)&1); \ + if (rex != 0x40) *--(p) = rex; } +#define FORCE_REX 0x200 +#define REX_64 (FORCE_REX|0x080000) +#define VEX_64 0x800000 +#else +#define REXRB(p, rr, rb) ((void)0) +#define FORCE_REX 0 +#define REX_64 0 +#define VEX_64 0 +#endif +#if LJ_GC64 +#define REX_GC64 REX_64 +#else +#define REX_GC64 0 +#endif + +#define emit_i8(as, i) (*--as->mcp = (MCode)(i)) +#define emit_i32(as, i) (*(int32_t *)(as->mcp-4) = (i), as->mcp -= 4) +#define emit_u32(as, u) (*(uint32_t *)(as->mcp-4) = (u), as->mcp -= 4) + +#define emit_x87op(as, xo) \ + (*(uint16_t *)(as->mcp-2) = (uint16_t)(xo), as->mcp -= 2) + +/* op */ +static LJ_AINLINE MCode *emit_op(x86Op xo, Reg rr, Reg rb, Reg rx, + MCode *p, int delta) +{ + int n = (int8_t)xo; + if (n == -60) { /* VEX-encoded instruction */ +#if LJ_64 + xo ^= (((rr>>1)&4)+((rx>>2)&2)+((rb>>3)&1))<<13; +#endif + *(uint32_t *)(p+delta-5) = (uint32_t)xo; + return p+delta-5; + } +#if defined(__GNUC__) + if (__builtin_constant_p(xo) && n == -2) + p[delta-2] = (MCode)(xo >> 24); + else if (__builtin_constant_p(xo) && n == -3) + *(uint16_t *)(p+delta-3) = (uint16_t)(xo >> 16); + else +#endif + *(uint32_t *)(p+delta-5) = (uint32_t)xo; + p += n + delta; +#if LJ_64 + { + uint32_t rex = 0x40 + ((rr>>1)&(4+(FORCE_REX>>1)))+((rx>>2)&2)+((rb>>3)&1); + if (rex != 0x40) { + rex |= (rr >> 16); + if (n == -4) { *p = (MCode)rex; rex = (MCode)(xo >> 8); } + else if ((xo & 0xffffff) == 0x6600fd) { *p = (MCode)rex; rex = 0x66; } + *--p = (MCode)rex; + } + } +#else + UNUSED(rr); UNUSED(rb); UNUSED(rx); +#endif + return p; +} + +/* op + modrm */ +#define emit_opm(xo, mode, rr, rb, p, delta) \ + (p[(delta)-1] = MODRM((mode), (rr), (rb)), \ + emit_op((xo), (rr), (rb), 0, (p), (delta))) + +/* op + modrm + sib */ +#define emit_opmx(xo, mode, scale, rr, rb, rx, p) \ + (p[-1] = MODRM((scale), (rx), (rb)), \ + p[-2] = MODRM((mode), (rr), RID_ESP), \ + emit_op((xo), (rr), (rb), (rx), (p), -1)) + +/* op r1, r2 */ +static void emit_rr(ASMState *as, x86Op xo, Reg r1, Reg r2) +{ + MCode *p = as->mcp; + as->mcp = emit_opm(xo, XM_REG, r1, r2, p, 0); +} + +#if LJ_64 && defined(LUA_USE_ASSERT) +/* [addr] is sign-extended in x64 and must be in lower 2G (not 4G). */ +static int32_t ptr2addr(const void *p) +{ + lua_assert((uintptr_t)p < (uintptr_t)0x80000000); + return i32ptr(p); +} +#else +#define ptr2addr(p) (i32ptr((p))) +#endif + +/* op r, [base+ofs] */ +static void emit_rmro(ASMState *as, x86Op xo, Reg rr, Reg rb, int32_t ofs) +{ + MCode *p = as->mcp; + x86Mode mode; + if (ra_hasreg(rb)) { + if (LJ_GC64 && rb == RID_RIP) { + mode = XM_OFS0; + p -= 4; + *(int32_t *)p = ofs; + } else if (ofs == 0 && (rb&7) != RID_EBP) { + mode = XM_OFS0; + } else if (checki8(ofs)) { + *--p = (MCode)ofs; + mode = XM_OFS8; + } else { + p -= 4; + *(int32_t *)p = ofs; + mode = XM_OFS32; + } + if ((rb&7) == RID_ESP) + *--p = MODRM(XM_SCALE1, RID_ESP, RID_ESP); + } else { + *(int32_t *)(p-4) = ofs; +#if LJ_64 + p[-5] = MODRM(XM_SCALE1, RID_ESP, RID_EBP); + p -= 5; + rb = RID_ESP; +#else + p -= 4; + rb = RID_EBP; +#endif + mode = XM_OFS0; + } + as->mcp = emit_opm(xo, mode, rr, rb, p, 0); +} + +/* op r, [base+idx*scale+ofs] */ +static void emit_rmrxo(ASMState *as, x86Op xo, Reg rr, Reg rb, Reg rx, + x86Mode scale, int32_t ofs) +{ + MCode *p = as->mcp; + x86Mode mode; + if (ofs == 0 && (rb&7) != RID_EBP) { + mode = XM_OFS0; + } else if (checki8(ofs)) { + mode = XM_OFS8; + *--p = (MCode)ofs; + } else { + mode = XM_OFS32; + p -= 4; + *(int32_t *)p = ofs; + } + as->mcp = emit_opmx(xo, mode, scale, rr, rb, rx, p); +} + +/* op r, i */ +static void emit_gri(ASMState *as, x86Group xg, Reg rb, int32_t i) +{ + MCode *p = as->mcp; + x86Op xo; + if (checki8(i)) { + *--p = (MCode)i; + xo = XG_TOXOi8(xg); + } else { + p -= 4; + *(int32_t *)p = i; + xo = XG_TOXOi(xg); + } + as->mcp = emit_opm(xo, XM_REG, (Reg)(xg & 7) | (rb & REX_64), rb, p, 0); +} + +/* op [base+ofs], i */ +static void emit_gmroi(ASMState *as, x86Group xg, Reg rb, int32_t ofs, + int32_t i) +{ + x86Op xo; + if (checki8(i)) { + emit_i8(as, i); + xo = XG_TOXOi8(xg); + } else { + emit_i32(as, i); + xo = XG_TOXOi(xg); + } + emit_rmro(as, xo, (Reg)(xg & 7), rb, ofs); +} + +#define emit_shifti(as, xg, r, i) \ + (emit_i8(as, (i)), emit_rr(as, XO_SHIFTi, (Reg)(xg), (r))) + +/* op r, rm/mrm */ +static void emit_mrm(ASMState *as, x86Op xo, Reg rr, Reg rb) +{ + MCode *p = as->mcp; + x86Mode mode = XM_REG; + if (rb == RID_MRM) { + rb = as->mrm.base; + if (rb == RID_NONE) { + rb = RID_EBP; + mode = XM_OFS0; + p -= 4; + *(int32_t *)p = as->mrm.ofs; + if (as->mrm.idx != RID_NONE) + goto mrmidx; +#if LJ_64 + *--p = MODRM(XM_SCALE1, RID_ESP, RID_EBP); + rb = RID_ESP; +#endif + } else if (LJ_GC64 && rb == RID_RIP) { + lua_assert(as->mrm.idx == RID_NONE); + mode = XM_OFS0; + p -= 4; + *(int32_t *)p = as->mrm.ofs; + } else { + if (as->mrm.ofs == 0 && (rb&7) != RID_EBP) { + mode = XM_OFS0; + } else if (checki8(as->mrm.ofs)) { + *--p = (MCode)as->mrm.ofs; + mode = XM_OFS8; + } else { + p -= 4; + *(int32_t *)p = as->mrm.ofs; + mode = XM_OFS32; + } + if (as->mrm.idx != RID_NONE) { + mrmidx: + as->mcp = emit_opmx(xo, mode, as->mrm.scale, rr, rb, as->mrm.idx, p); + return; + } + if ((rb&7) == RID_ESP) + *--p = MODRM(XM_SCALE1, RID_ESP, RID_ESP); + } + } + as->mcp = emit_opm(xo, mode, rr, rb, p, 0); +} + +/* op rm/mrm, i */ +static void emit_gmrmi(ASMState *as, x86Group xg, Reg rb, int32_t i) +{ + x86Op xo; + if (checki8(i)) { + emit_i8(as, i); + xo = XG_TOXOi8(xg); + } else { + emit_i32(as, i); + xo = XG_TOXOi(xg); + } + emit_mrm(as, xo, (Reg)(xg & 7) | (rb & REX_64), (rb & ~REX_64)); +} + +/* -- Emit loads/stores --------------------------------------------------- */ + +/* mov [base+ofs], i */ +static void emit_movmroi(ASMState *as, Reg base, int32_t ofs, int32_t i) +{ + emit_i32(as, i); + emit_rmro(as, XO_MOVmi, 0, base, ofs); +} + +/* mov [base+ofs], r */ +#define emit_movtomro(as, r, base, ofs) \ + emit_rmro(as, XO_MOVto, (r), (base), (ofs)) + +/* Get/set global_State fields. */ +#define emit_opgl(as, xo, r, field) \ + emit_rma(as, (xo), (r), (void *)&J2G(as->J)->field) +#define emit_getgl(as, r, field) emit_opgl(as, XO_MOV, (r)|REX_GC64, field) +#define emit_setgl(as, r, field) emit_opgl(as, XO_MOVto, (r)|REX_GC64, field) + +#define emit_setvmstate(as, i) \ + (emit_i32(as, i), emit_opgl(as, XO_MOVmi, 0, vmstate)) + +/* mov r, i / xor r, r */ +static void emit_loadi(ASMState *as, Reg r, int32_t i) +{ + /* XOR r,r is shorter, but modifies the flags. This is bad for HIOP/jcc. */ + if (i == 0 && !(LJ_32 && (IR(as->curins)->o == IR_HIOP || + (as->curins+1 < as->T->nins && + IR(as->curins+1)->o == IR_HIOP))) && + !((*as->mcp == 0x0f && (as->mcp[1] & 0xf0) == XI_JCCn) || + (*as->mcp & 0xf0) == XI_JCCs)) { + emit_rr(as, XO_ARITH(XOg_XOR), r, r); + } else { + MCode *p = as->mcp; + *(int32_t *)(p-4) = i; + p[-5] = (MCode)(XI_MOVri+(r&7)); + p -= 5; + REXRB(p, 0, r); + as->mcp = p; + } +} + +#if LJ_GC64 +#define dispofs(as, k) \ + ((intptr_t)((uintptr_t)(k) - (uintptr_t)J2GG(as->J)->dispatch)) +#define mcpofs(as, k) \ + ((intptr_t)((uintptr_t)(k) - (uintptr_t)as->mcp)) +#define mctopofs(as, k) \ + ((intptr_t)((uintptr_t)(k) - (uintptr_t)as->mctop)) +/* mov r, addr */ +#define emit_loada(as, r, addr) \ + emit_loadu64(as, (r), (uintptr_t)(addr)) +#else +/* mov r, addr */ +#define emit_loada(as, r, addr) \ + emit_loadi(as, (r), ptr2addr((addr))) +#endif + +#if LJ_64 +/* mov r, imm64 or shorter 32 bit extended load. */ +static void emit_loadu64(ASMState *as, Reg r, uint64_t u64) +{ + if (checku32(u64)) { /* 32 bit load clears upper 32 bits. */ + emit_loadi(as, r, (int32_t)u64); + } else if (checki32((int64_t)u64)) { /* Sign-extended 32 bit load. */ + MCode *p = as->mcp; + *(int32_t *)(p-4) = (int32_t)u64; + as->mcp = emit_opm(XO_MOVmi, XM_REG, REX_64, r, p, -4); +#if LJ_GC64 + } else if (checki32(dispofs(as, u64))) { + emit_rmro(as, XO_LEA, r|REX_64, RID_DISPATCH, (int32_t)dispofs(as, u64)); + } else if (checki32(mcpofs(as, u64)) && checki32(mctopofs(as, u64))) { + /* Since as->realign assumes the code size doesn't change, check + ** RIP-relative addressing reachability for both as->mcp and as->mctop. + */ + emit_rmro(as, XO_LEA, r|REX_64, RID_RIP, (int32_t)mcpofs(as, u64)); +#endif + } else { /* Full-size 64 bit load. */ + MCode *p = as->mcp; + *(uint64_t *)(p-8) = u64; + p[-9] = (MCode)(XI_MOVri+(r&7)); + p[-10] = 0x48 + ((r>>3)&1); + p -= 10; + as->mcp = p; + } +} +#endif + +/* op r, [addr] */ +static void emit_rma(ASMState *as, x86Op xo, Reg rr, const void *addr) +{ +#if LJ_GC64 + if (checki32(dispofs(as, addr))) { + emit_rmro(as, xo, rr, RID_DISPATCH, (int32_t)dispofs(as, addr)); + } else if (checki32(mcpofs(as, addr)) && checki32(mctopofs(as, addr))) { + emit_rmro(as, xo, rr, RID_RIP, (int32_t)mcpofs(as, addr)); + } else if (!checki32((intptr_t)addr)) { + Reg ra = (rr & 15); + if (xo != XO_MOV) { + /* We can't allocate a register here. Use and restore DISPATCH. Ugly. */ + uint64_t dispaddr = (uintptr_t)J2GG(as->J)->dispatch; + uint8_t i8 = xo == XO_GROUP3b ? *as->mcp++ : 0; + ra = RID_DISPATCH; + if (checku32(dispaddr)) { + emit_loadi(as, ra, (int32_t)dispaddr); + } else { /* Full-size 64 bit load. */ + MCode *p = as->mcp; + *(uint64_t *)(p-8) = dispaddr; + p[-9] = (MCode)(XI_MOVri+(ra&7)); + p[-10] = 0x48 + ((ra>>3)&1); + p -= 10; + as->mcp = p; + } + if (xo == XO_GROUP3b) emit_i8(as, i8); + } + emit_rmro(as, xo, rr, ra, 0); + emit_loadu64(as, ra, (uintptr_t)addr); + } else +#endif + { + MCode *p = as->mcp; + *(int32_t *)(p-4) = ptr2addr(addr); +#if LJ_64 + p[-5] = MODRM(XM_SCALE1, RID_ESP, RID_EBP); + as->mcp = emit_opm(xo, XM_OFS0, rr, RID_ESP, p, -5); +#else + as->mcp = emit_opm(xo, XM_OFS0, rr, RID_EBP, p, -4); +#endif + } +} + +/* Load 64 bit IR constant into register. */ +static void emit_loadk64(ASMState *as, Reg r, IRIns *ir) +{ + Reg r64; + x86Op xo; + const uint64_t *k = &ir_k64(ir)->u64; + if (rset_test(RSET_FPR, r)) { + r64 = r; + xo = XO_MOVSD; + } else { + r64 = r | REX_64; + xo = XO_MOV; + } + if (*k == 0) { + emit_rr(as, rset_test(RSET_FPR, r) ? XO_XORPS : XO_ARITH(XOg_XOR), r, r); +#if LJ_GC64 + } else if (checki32((intptr_t)k) || checki32(dispofs(as, k)) || + (checki32(mcpofs(as, k)) && checki32(mctopofs(as, k)))) { + emit_rma(as, xo, r64, k); + } else { + if (ir->i) { + lua_assert(*k == *(uint64_t*)(as->mctop - ir->i)); + } else if (as->curins <= as->stopins && rset_test(RSET_GPR, r)) { + emit_loadu64(as, r, *k); + return; + } else { + /* If all else fails, add the FP constant at the MCode area bottom. */ + while ((uintptr_t)as->mcbot & 7) *as->mcbot++ = XI_INT3; + *(uint64_t *)as->mcbot = *k; + ir->i = (int32_t)(as->mctop - as->mcbot); + as->mcbot += 8; + as->mclim = as->mcbot + MCLIM_REDZONE; + lj_mcode_commitbot(as->J, as->mcbot); + } + emit_rmro(as, xo, r64, RID_RIP, (int32_t)mcpofs(as, as->mctop - ir->i)); +#else + } else { + emit_rma(as, xo, r64, k); +#endif + } +} + +/* -- Emit control-flow instructions -------------------------------------- */ + +/* Label for short jumps. */ +typedef MCode *MCLabel; + +#if LJ_32 && LJ_HASFFI +/* jmp short target */ +static void emit_sjmp(ASMState *as, MCLabel target) +{ + MCode *p = as->mcp; + ptrdiff_t delta = target - p; + lua_assert(delta == (int8_t)delta); + p[-1] = (MCode)(int8_t)delta; + p[-2] = XI_JMPs; + as->mcp = p - 2; +} +#endif + +/* jcc short target */ +static void emit_sjcc(ASMState *as, int cc, MCLabel target) +{ + MCode *p = as->mcp; + ptrdiff_t delta = target - p; + lua_assert(delta == (int8_t)delta); + p[-1] = (MCode)(int8_t)delta; + p[-2] = (MCode)(XI_JCCs+(cc&15)); + as->mcp = p - 2; +} + +/* jcc short (pending target) */ +static MCLabel emit_sjcc_label(ASMState *as, int cc) +{ + MCode *p = as->mcp; + p[-1] = 0; + p[-2] = (MCode)(XI_JCCs+(cc&15)); + as->mcp = p - 2; + return p; +} + +/* Fixup jcc short target. */ +static void emit_sfixup(ASMState *as, MCLabel source) +{ + source[-1] = (MCode)(as->mcp-source); +} + +/* Return label pointing to current PC. */ +#define emit_label(as) ((as)->mcp) + +/* Compute relative 32 bit offset for jump and call instructions. */ +static LJ_AINLINE int32_t jmprel(MCode *p, MCode *target) +{ + ptrdiff_t delta = target - p; + lua_assert(delta == (int32_t)delta); + return (int32_t)delta; +} + +/* jcc target */ +static void emit_jcc(ASMState *as, int cc, MCode *target) +{ + MCode *p = as->mcp; + *(int32_t *)(p-4) = jmprel(p, target); + p[-5] = (MCode)(XI_JCCn+(cc&15)); + p[-6] = 0x0f; + as->mcp = p - 6; +} + +/* jmp target */ +static void emit_jmp(ASMState *as, MCode *target) +{ + MCode *p = as->mcp; + *(int32_t *)(p-4) = jmprel(p, target); + p[-5] = XI_JMP; + as->mcp = p - 5; +} + +/* call target */ +static void emit_call_(ASMState *as, MCode *target) +{ + MCode *p = as->mcp; +#if LJ_64 + if (target-p != (int32_t)(target-p)) { + /* Assumes RID_RET is never an argument to calls and always clobbered. */ + emit_rr(as, XO_GROUP5, XOg_CALL, RID_RET); + emit_loadu64(as, RID_RET, (uint64_t)target); + return; + } +#endif + *(int32_t *)(p-4) = jmprel(p, target); + p[-5] = XI_CALL; + as->mcp = p - 5; +} + +#define emit_call(as, f) emit_call_(as, (MCode *)(void *)(f)) + +/* -- Emit generic operations --------------------------------------------- */ + +/* Use 64 bit operations to handle 64 bit IR types. */ +#if LJ_64 +#define REX_64IR(ir, r) ((r) + (irt_is64((ir)->t) ? REX_64 : 0)) +#define VEX_64IR(ir, r) ((r) + (irt_is64((ir)->t) ? VEX_64 : 0)) +#else +#define REX_64IR(ir, r) (r) +#define VEX_64IR(ir, r) (r) +#endif + +/* Generic move between two regs. */ +static void emit_movrr(ASMState *as, IRIns *ir, Reg dst, Reg src) +{ + UNUSED(ir); + if (dst < RID_MAX_GPR) + emit_rr(as, XO_MOV, REX_64IR(ir, dst), src); + else + emit_rr(as, XO_MOVAPS, dst, src); +} + +/* Generic load of register with base and (small) offset address. */ +static void emit_loadofs(ASMState *as, IRIns *ir, Reg r, Reg base, int32_t ofs) +{ + if (r < RID_MAX_GPR) + emit_rmro(as, XO_MOV, REX_64IR(ir, r), base, ofs); + else + emit_rmro(as, irt_isnum(ir->t) ? XO_MOVSD : XO_MOVSS, r, base, ofs); +} + +/* Generic store of register with base and (small) offset address. */ +static void emit_storeofs(ASMState *as, IRIns *ir, Reg r, Reg base, int32_t ofs) +{ + if (r < RID_MAX_GPR) + emit_rmro(as, XO_MOVto, REX_64IR(ir, r), base, ofs); + else + emit_rmro(as, irt_isnum(ir->t) ? XO_MOVSDto : XO_MOVSSto, r, base, ofs); +} + +/* Add offset to pointer. */ +static void emit_addptr(ASMState *as, Reg r, int32_t ofs) +{ + if (ofs) { + if ((as->flags & JIT_F_LEA_AGU)) + emit_rmro(as, XO_LEA, r|REX_GC64, r, ofs); + else + emit_gri(as, XG_ARITHi(XOg_ADD), r|REX_GC64, ofs); + } +} + +#define emit_spsub(as, ofs) emit_addptr(as, RID_ESP|REX_64, -(ofs)) + +/* Prefer rematerialization of BASE/L from global_State over spills. */ +#define emit_canremat(ref) ((ref) <= REF_BASE) + diff --git a/lib/LuaJIT/lj_err.c b/lib/LuaJIT/lj_err.c new file mode 100644 index 0000000..abf176e --- /dev/null +++ b/lib/LuaJIT/lj_err.c @@ -0,0 +1,854 @@ +/* +** Error handling. +** Copyright (C) 2005-2017 Mike Pall. See Copyright Notice in luajit.h +*/ + +#define lj_err_c +#define LUA_CORE + +#include "lj_obj.h" +#include "lj_err.h" +#include "lj_debug.h" +#include "lj_str.h" +#include "lj_func.h" +#include "lj_state.h" +#include "lj_frame.h" +#include "lj_ff.h" +#include "lj_trace.h" +#include "lj_vm.h" +#include "lj_strfmt.h" + +/* +** LuaJIT can either use internal or external frame unwinding: +** +** - Internal frame unwinding (INT) is free-standing and doesn't require +** any OS or library support. +** +** - External frame unwinding (EXT) uses the system-provided unwind handler. +** +** Pros and Cons: +** +** - EXT requires unwind tables for *all* functions on the C stack between +** the pcall/catch and the error/throw. This is the default on x64, +** but needs to be manually enabled on x86/PPC for non-C++ code. +** +** - INT is faster when actually throwing errors (but this happens rarely). +** Setting up error handlers is zero-cost in any case. +** +** - EXT provides full interoperability with C++ exceptions. You can throw +** Lua errors or C++ exceptions through a mix of Lua frames and C++ frames. +** C++ destructors are called as needed. C++ exceptions caught by pcall +** are converted to the string "C++ exception". Lua errors can be caught +** with catch (...) in C++. +** +** - INT has only limited support for automatically catching C++ exceptions +** on POSIX systems using DWARF2 stack unwinding. Other systems may use +** the wrapper function feature. Lua errors thrown through C++ frames +** cannot be caught by C++ code and C++ destructors are not run. +** +** EXT is the default on x64 systems and on Windows, INT is the default on all +** other systems. +** +** EXT can be manually enabled on POSIX systems using GCC and DWARF2 stack +** unwinding with -DLUAJIT_UNWIND_EXTERNAL. *All* C code must be compiled +** with -funwind-tables (or -fexceptions). This includes LuaJIT itself (set +** TARGET_CFLAGS), all of your C/Lua binding code, all loadable C modules +** and all C libraries that have callbacks which may be used to call back +** into Lua. C++ code must *not* be compiled with -fno-exceptions. +** +** EXT is mandatory on WIN64 since the calling convention has an abundance +** of callee-saved registers (rbx, rbp, rsi, rdi, r12-r15, xmm6-xmm15). +** The POSIX/x64 interpreter only saves r12/r13 for INT (e.g. PS4). +*/ + +#if defined(__GNUC__) && (LJ_TARGET_X64 || defined(LUAJIT_UNWIND_EXTERNAL)) && !LJ_NO_UNWIND +#define LJ_UNWIND_EXT 1 +#elif LJ_TARGET_WINDOWS +#define LJ_UNWIND_EXT 1 +#endif + +/* -- Error messages ------------------------------------------------------ */ + +/* Error message strings. */ +LJ_DATADEF const char *lj_err_allmsg = +#define ERRDEF(name, msg) msg "\0" +#include "lj_errmsg.h" +; + +/* -- Internal frame unwinding -------------------------------------------- */ + +/* Unwind Lua stack and move error message to new top. */ +LJ_NOINLINE static void unwindstack(lua_State *L, TValue *top) +{ + lj_func_closeuv(L, top); + if (top < L->top-1) { + copyTV(L, top, L->top-1); + L->top = top+1; + } + lj_state_relimitstack(L); +} + +/* Unwind until stop frame. Optionally cleanup frames. */ +static void *err_unwind(lua_State *L, void *stopcf, int errcode) +{ + TValue *frame = L->base-1; + void *cf = L->cframe; + while (cf) { + int32_t nres = cframe_nres(cframe_raw(cf)); + if (nres < 0) { /* C frame without Lua frame? */ + TValue *top = restorestack(L, -nres); + if (frame < top) { /* Frame reached? */ + if (errcode) { + L->base = frame+1; + L->cframe = cframe_prev(cf); + unwindstack(L, top); + } + return cf; + } + } + if (frame <= tvref(L->stack)+LJ_FR2) + break; + switch (frame_typep(frame)) { + case FRAME_LUA: /* Lua frame. */ + case FRAME_LUAP: + frame = frame_prevl(frame); + break; + case FRAME_C: /* C frame. */ + unwind_c: +#if LJ_UNWIND_EXT + if (errcode) { + L->base = frame_prevd(frame) + 1; + L->cframe = cframe_prev(cf); + unwindstack(L, frame - LJ_FR2); + } else if (cf != stopcf) { + cf = cframe_prev(cf); + frame = frame_prevd(frame); + break; + } + return NULL; /* Continue unwinding. */ +#else + UNUSED(stopcf); + cf = cframe_prev(cf); + frame = frame_prevd(frame); + break; +#endif + case FRAME_CP: /* Protected C frame. */ + if (cframe_canyield(cf)) { /* Resume? */ + if (errcode) { + hook_leave(G(L)); /* Assumes nobody uses coroutines inside hooks. */ + L->cframe = NULL; + L->status = (uint8_t)errcode; + } + return cf; + } + if (errcode) { + L->base = frame_prevd(frame) + 1; + L->cframe = cframe_prev(cf); + unwindstack(L, frame - LJ_FR2); + } + return cf; + case FRAME_CONT: /* Continuation frame. */ + if (frame_iscont_fficb(frame)) + goto unwind_c; + /* fallthrough */ + case FRAME_VARG: /* Vararg frame. */ + frame = frame_prevd(frame); + break; + case FRAME_PCALL: /* FF pcall() frame. */ + case FRAME_PCALLH: /* FF pcall() frame inside hook. */ + if (errcode) { + if (errcode == LUA_YIELD) { + frame = frame_prevd(frame); + break; + } + if (frame_typep(frame) == FRAME_PCALL) + hook_leave(G(L)); + L->base = frame_prevd(frame) + 1; + L->cframe = cf; + unwindstack(L, L->base); + } + return (void *)((intptr_t)cf | CFRAME_UNWIND_FF); + } + } + /* No C frame. */ + if (errcode) { + L->base = tvref(L->stack)+1+LJ_FR2; + L->cframe = NULL; + unwindstack(L, L->base); + if (G(L)->panic) + G(L)->panic(L); + exit(EXIT_FAILURE); + } + return L; /* Anything non-NULL will do. */ +} + +/* -- External frame unwinding -------------------------------------------- */ + +#if defined(__GNUC__) && !LJ_NO_UNWIND && !LJ_ABI_WIN + +/* +** We have to use our own definitions instead of the mandatory (!) unwind.h, +** since various OS, distros and compilers mess up the header installation. +*/ + +typedef struct _Unwind_Context _Unwind_Context; + +#define _URC_OK 0 +#define _URC_FATAL_PHASE1_ERROR 3 +#define _URC_HANDLER_FOUND 6 +#define _URC_INSTALL_CONTEXT 7 +#define _URC_CONTINUE_UNWIND 8 +#define _URC_FAILURE 9 + +#define LJ_UEXCLASS 0x4c55414a49543200ULL /* LUAJIT2\0 */ +#define LJ_UEXCLASS_MAKE(c) (LJ_UEXCLASS | (uint64_t)(c)) +#define LJ_UEXCLASS_CHECK(cl) (((cl) ^ LJ_UEXCLASS) <= 0xff) +#define LJ_UEXCLASS_ERRCODE(cl) ((int)((cl) & 0xff)) + +#if !LJ_TARGET_ARM + +typedef struct _Unwind_Exception +{ + uint64_t exclass; + void (*excleanup)(int, struct _Unwind_Exception *); + uintptr_t p1, p2; +} __attribute__((__aligned__)) _Unwind_Exception; + +extern uintptr_t _Unwind_GetCFA(_Unwind_Context *); +extern void _Unwind_SetGR(_Unwind_Context *, int, uintptr_t); +extern void _Unwind_SetIP(_Unwind_Context *, uintptr_t); +extern void _Unwind_DeleteException(_Unwind_Exception *); +extern int _Unwind_RaiseException(_Unwind_Exception *); + +#define _UA_SEARCH_PHASE 1 +#define _UA_CLEANUP_PHASE 2 +#define _UA_HANDLER_FRAME 4 +#define _UA_FORCE_UNWIND 8 + +/* DWARF2 personality handler referenced from interpreter .eh_frame. */ +LJ_FUNCA int lj_err_unwind_dwarf(int version, int actions, + uint64_t uexclass, _Unwind_Exception *uex, _Unwind_Context *ctx) +{ + void *cf; + lua_State *L; + if (version != 1) + return _URC_FATAL_PHASE1_ERROR; + UNUSED(uexclass); + cf = (void *)_Unwind_GetCFA(ctx); + L = cframe_L(cf); + if ((actions & _UA_SEARCH_PHASE)) { +#if LJ_UNWIND_EXT + if (err_unwind(L, cf, 0) == NULL) + return _URC_CONTINUE_UNWIND; +#endif + if (!LJ_UEXCLASS_CHECK(uexclass)) { + setstrV(L, L->top++, lj_err_str(L, LJ_ERR_ERRCPP)); + } + return _URC_HANDLER_FOUND; + } + if ((actions & _UA_CLEANUP_PHASE)) { + int errcode; + if (LJ_UEXCLASS_CHECK(uexclass)) { + errcode = LJ_UEXCLASS_ERRCODE(uexclass); + } else { + if ((actions & _UA_HANDLER_FRAME)) + _Unwind_DeleteException(uex); + errcode = LUA_ERRRUN; + } +#if LJ_UNWIND_EXT + cf = err_unwind(L, cf, errcode); + if ((actions & _UA_FORCE_UNWIND)) { + return _URC_CONTINUE_UNWIND; + } else if (cf) { + _Unwind_SetGR(ctx, LJ_TARGET_EHRETREG, errcode); + _Unwind_SetIP(ctx, (uintptr_t)(cframe_unwind_ff(cf) ? + lj_vm_unwind_ff_eh : + lj_vm_unwind_c_eh)); + return _URC_INSTALL_CONTEXT; + } +#if LJ_TARGET_X86ORX64 + else if ((actions & _UA_HANDLER_FRAME)) { + /* Workaround for ancient libgcc bug. Still present in RHEL 5.5. :-/ + ** Real fix: http://gcc.gnu.org/viewcvs/trunk/gcc/unwind-dw2.c?r1=121165&r2=124837&pathrev=153877&diff_format=h + */ + _Unwind_SetGR(ctx, LJ_TARGET_EHRETREG, errcode); + _Unwind_SetIP(ctx, (uintptr_t)lj_vm_unwind_rethrow); + return _URC_INSTALL_CONTEXT; + } +#endif +#else + /* This is not the proper way to escape from the unwinder. We get away with + ** it on non-x64 because the interpreter restores all callee-saved regs. + */ + lj_err_throw(L, errcode); +#endif + } + return _URC_CONTINUE_UNWIND; +} + +#if LJ_UNWIND_EXT +#if LJ_TARGET_OSX || defined(__OpenBSD__) +/* Sorry, no thread safety for OSX. Complain to Apple, not me. */ +static _Unwind_Exception static_uex; +#else +static __thread _Unwind_Exception static_uex; +#endif + +/* Raise DWARF2 exception. */ +static void err_raise_ext(int errcode) +{ + static_uex.exclass = LJ_UEXCLASS_MAKE(errcode); + static_uex.excleanup = NULL; + _Unwind_RaiseException(&static_uex); +} +#endif + +#else /* LJ_TARGET_ARM */ + +#define _US_VIRTUAL_UNWIND_FRAME 0 +#define _US_UNWIND_FRAME_STARTING 1 +#define _US_ACTION_MASK 3 +#define _US_FORCE_UNWIND 8 + +typedef struct _Unwind_Control_Block _Unwind_Control_Block; + +struct _Unwind_Control_Block { + uint64_t exclass; + uint32_t misc[20]; +}; + +extern int _Unwind_RaiseException(_Unwind_Control_Block *); +extern int __gnu_unwind_frame(_Unwind_Control_Block *, _Unwind_Context *); +extern int _Unwind_VRS_Set(_Unwind_Context *, int, uint32_t, int, void *); +extern int _Unwind_VRS_Get(_Unwind_Context *, int, uint32_t, int, void *); + +static inline uint32_t _Unwind_GetGR(_Unwind_Context *ctx, int r) +{ + uint32_t v; + _Unwind_VRS_Get(ctx, 0, r, 0, &v); + return v; +} + +static inline void _Unwind_SetGR(_Unwind_Context *ctx, int r, uint32_t v) +{ + _Unwind_VRS_Set(ctx, 0, r, 0, &v); +} + +extern void lj_vm_unwind_ext(void); + +/* ARM unwinder personality handler referenced from interpreter .ARM.extab. */ +LJ_FUNCA int lj_err_unwind_arm(int state, _Unwind_Control_Block *ucb, + _Unwind_Context *ctx) +{ + void *cf = (void *)_Unwind_GetGR(ctx, 13); + lua_State *L = cframe_L(cf); + int errcode; + + switch ((state & _US_ACTION_MASK)) { + case _US_VIRTUAL_UNWIND_FRAME: + if ((state & _US_FORCE_UNWIND)) break; + return _URC_HANDLER_FOUND; + case _US_UNWIND_FRAME_STARTING: + if (LJ_UEXCLASS_CHECK(ucb->exclass)) { + errcode = LJ_UEXCLASS_ERRCODE(ucb->exclass); + } else { + errcode = LUA_ERRRUN; + setstrV(L, L->top++, lj_err_str(L, LJ_ERR_ERRCPP)); + } + cf = err_unwind(L, cf, errcode); + if ((state & _US_FORCE_UNWIND) || cf == NULL) break; + _Unwind_SetGR(ctx, 15, (uint32_t)lj_vm_unwind_ext); + _Unwind_SetGR(ctx, 0, (uint32_t)ucb); + _Unwind_SetGR(ctx, 1, (uint32_t)errcode); + _Unwind_SetGR(ctx, 2, cframe_unwind_ff(cf) ? + (uint32_t)lj_vm_unwind_ff_eh : + (uint32_t)lj_vm_unwind_c_eh); + return _URC_INSTALL_CONTEXT; + default: + return _URC_FAILURE; + } + if (__gnu_unwind_frame(ucb, ctx) != _URC_OK) + return _URC_FAILURE; + return _URC_CONTINUE_UNWIND; +} + +#if LJ_UNWIND_EXT +static __thread _Unwind_Control_Block static_uex; + +static void err_raise_ext(int errcode) +{ + memset(&static_uex, 0, sizeof(static_uex)); + static_uex.exclass = LJ_UEXCLASS_MAKE(errcode); + _Unwind_RaiseException(&static_uex); +} +#endif + +#endif /* LJ_TARGET_ARM */ + +#elif LJ_ABI_WIN + +/* +** Someone in Redmond owes me several days of my life. A lot of this is +** undocumented or just plain wrong on MSDN. Some of it can be gathered +** from 3rd party docs or must be found by trial-and-error. They really +** don't want you to write your own language-specific exception handler +** or to interact gracefully with MSVC. :-( +** +** Apparently MSVC doesn't call C++ destructors for foreign exceptions +** unless you compile your C++ code with /EHa. Unfortunately this means +** catch (...) also catches things like access violations. The use of +** _set_se_translator doesn't really help, because it requires /EHa, too. +*/ + +#define WIN32_LEAN_AND_MEAN +#include + +#if LJ_TARGET_X64 +/* Taken from: http://www.nynaeve.net/?p=99 */ +typedef struct UndocumentedDispatcherContext { + ULONG64 ControlPc; + ULONG64 ImageBase; + PRUNTIME_FUNCTION FunctionEntry; + ULONG64 EstablisherFrame; + ULONG64 TargetIp; + PCONTEXT ContextRecord; + void (*LanguageHandler)(void); + PVOID HandlerData; + PUNWIND_HISTORY_TABLE HistoryTable; + ULONG ScopeIndex; + ULONG Fill0; +} UndocumentedDispatcherContext; +#else +typedef void *UndocumentedDispatcherContext; +#endif + +/* Another wild guess. */ +extern void __DestructExceptionObject(EXCEPTION_RECORD *rec, int nothrow); + +#if LJ_TARGET_X64 && defined(MINGW_SDK_INIT) +/* Workaround for broken MinGW64 declaration. */ +VOID RtlUnwindEx_FIXED(PVOID,PVOID,PVOID,PVOID,PVOID,PVOID) asm("RtlUnwindEx"); +#define RtlUnwindEx RtlUnwindEx_FIXED +#endif + +#define LJ_MSVC_EXCODE ((DWORD)0xe06d7363) +#define LJ_GCC_EXCODE ((DWORD)0x20474343) + +#define LJ_EXCODE ((DWORD)0xe24c4a00) +#define LJ_EXCODE_MAKE(c) (LJ_EXCODE | (DWORD)(c)) +#define LJ_EXCODE_CHECK(cl) (((cl) ^ LJ_EXCODE) <= 0xff) +#define LJ_EXCODE_ERRCODE(cl) ((int)((cl) & 0xff)) + +/* Windows exception handler for interpreter frame. */ +LJ_FUNCA int lj_err_unwind_win(EXCEPTION_RECORD *rec, + void *f, CONTEXT *ctx, UndocumentedDispatcherContext *dispatch) +{ +#if LJ_TARGET_X64 + void *cf = f; +#else + void *cf = (char *)f - CFRAME_OFS_SEH; +#endif + lua_State *L = cframe_L(cf); + int errcode = LJ_EXCODE_CHECK(rec->ExceptionCode) ? + LJ_EXCODE_ERRCODE(rec->ExceptionCode) : LUA_ERRRUN; + if ((rec->ExceptionFlags & 6)) { /* EH_UNWINDING|EH_EXIT_UNWIND */ + /* Unwind internal frames. */ + err_unwind(L, cf, errcode); + } else { + void *cf2 = err_unwind(L, cf, 0); + if (cf2) { /* We catch it, so start unwinding the upper frames. */ + if (rec->ExceptionCode == LJ_MSVC_EXCODE || + rec->ExceptionCode == LJ_GCC_EXCODE) { +#if LJ_TARGET_WINDOWS + __DestructExceptionObject(rec, 1); +#endif + setstrV(L, L->top++, lj_err_str(L, LJ_ERR_ERRCPP)); + } else if (!LJ_EXCODE_CHECK(rec->ExceptionCode)) { + /* Don't catch access violations etc. */ + return 1; /* ExceptionContinueSearch */ + } +#if LJ_TARGET_X64 + /* Unwind the stack and call all handlers for all lower C frames + ** (including ourselves) again with EH_UNWINDING set. Then set + ** rsp = cf, rax = errcode and jump to the specified target. + */ + RtlUnwindEx(cf, (void *)((cframe_unwind_ff(cf2) && errcode != LUA_YIELD) ? + lj_vm_unwind_ff_eh : + lj_vm_unwind_c_eh), + rec, (void *)(uintptr_t)errcode, ctx, dispatch->HistoryTable); + /* RtlUnwindEx should never return. */ +#else + UNUSED(ctx); + UNUSED(dispatch); + /* Call all handlers for all lower C frames (including ourselves) again + ** with EH_UNWINDING set. Then call the specified function, passing cf + ** and errcode. + */ + lj_vm_rtlunwind(cf, (void *)rec, + (cframe_unwind_ff(cf2) && errcode != LUA_YIELD) ? + (void *)lj_vm_unwind_ff : (void *)lj_vm_unwind_c, errcode); + /* lj_vm_rtlunwind does not return. */ +#endif + } + } + return 1; /* ExceptionContinueSearch */ +} + +/* Raise Windows exception. */ +static void err_raise_ext(int errcode) +{ + RaiseException(LJ_EXCODE_MAKE(errcode), 1 /* EH_NONCONTINUABLE */, 0, NULL); +} + +#endif + +/* -- Error handling ------------------------------------------------------ */ + +/* Throw error. Find catch frame, unwind stack and continue. */ +LJ_NOINLINE void LJ_FASTCALL lj_err_throw(lua_State *L, int errcode) +{ + global_State *g = G(L); + lj_trace_abort(g); + setmref(g->jit_base, NULL); + L->status = LUA_OK; +#if LJ_UNWIND_EXT + err_raise_ext(errcode); + /* + ** A return from this function signals a corrupt C stack that cannot be + ** unwound. We have no choice but to call the panic function and exit. + ** + ** Usually this is caused by a C function without unwind information. + ** This should never happen on x64, but may happen if you've manually + ** enabled LUAJIT_UNWIND_EXTERNAL and forgot to recompile *every* + ** non-C++ file with -funwind-tables. + */ + if (G(L)->panic) + G(L)->panic(L); +#else + { + void *cf = err_unwind(L, NULL, errcode); + if (cframe_unwind_ff(cf)) + lj_vm_unwind_ff(cframe_raw(cf)); + else + lj_vm_unwind_c(cframe_raw(cf), errcode); + } +#endif + exit(EXIT_FAILURE); +} + +/* Return string object for error message. */ +LJ_NOINLINE GCstr *lj_err_str(lua_State *L, ErrMsg em) +{ + return lj_str_newz(L, err2msg(em)); +} + +/* Out-of-memory error. */ +LJ_NOINLINE void lj_err_mem(lua_State *L) +{ + if (L->status == LUA_ERRERR+1) /* Don't touch the stack during lua_open. */ + lj_vm_unwind_c(L->cframe, LUA_ERRMEM); + setstrV(L, L->top++, lj_err_str(L, LJ_ERR_ERRMEM)); + lj_err_throw(L, LUA_ERRMEM); +} + +/* Find error function for runtime errors. Requires an extra stack traversal. */ +static ptrdiff_t finderrfunc(lua_State *L) +{ + cTValue *frame = L->base-1, *bot = tvref(L->stack)+LJ_FR2; + void *cf = L->cframe; + while (frame > bot && cf) { + while (cframe_nres(cframe_raw(cf)) < 0) { /* cframe without frame? */ + if (frame >= restorestack(L, -cframe_nres(cf))) + break; + if (cframe_errfunc(cf) >= 0) /* Error handler not inherited (-1)? */ + return cframe_errfunc(cf); + cf = cframe_prev(cf); /* Else unwind cframe and continue searching. */ + if (cf == NULL) + return 0; + } + switch (frame_typep(frame)) { + case FRAME_LUA: + case FRAME_LUAP: + frame = frame_prevl(frame); + break; + case FRAME_C: + cf = cframe_prev(cf); + /* fallthrough */ + case FRAME_VARG: + frame = frame_prevd(frame); + break; + case FRAME_CONT: + if (frame_iscont_fficb(frame)) + cf = cframe_prev(cf); + frame = frame_prevd(frame); + break; + case FRAME_CP: + if (cframe_canyield(cf)) return 0; + if (cframe_errfunc(cf) >= 0) + return cframe_errfunc(cf); + frame = frame_prevd(frame); + break; + case FRAME_PCALL: + case FRAME_PCALLH: + if (frame_func(frame_prevd(frame))->c.ffid == FF_xpcall) + return savestack(L, frame_prevd(frame)+1); /* xpcall's errorfunc. */ + return 0; + default: + lua_assert(0); + return 0; + } + } + return 0; +} + +/* Runtime error. */ +LJ_NOINLINE void lj_err_run(lua_State *L) +{ + ptrdiff_t ef = finderrfunc(L); + if (ef) { + TValue *errfunc = restorestack(L, ef); + TValue *top = L->top; + lj_trace_abort(G(L)); + if (!tvisfunc(errfunc) || L->status == LUA_ERRERR) { + setstrV(L, top-1, lj_err_str(L, LJ_ERR_ERRERR)); + lj_err_throw(L, LUA_ERRERR); + } + L->status = LUA_ERRERR; + copyTV(L, top+LJ_FR2, top-1); + copyTV(L, top-1, errfunc); + if (LJ_FR2) setnilV(top++); + L->top = top+1; + lj_vm_call(L, top, 1+1); /* Stack: |errfunc|msg| -> |msg| */ + } + lj_err_throw(L, LUA_ERRRUN); +} + +/* Formatted runtime error message. */ +LJ_NORET LJ_NOINLINE static void err_msgv(lua_State *L, ErrMsg em, ...) +{ + const char *msg; + va_list argp; + va_start(argp, em); + if (curr_funcisL(L)) L->top = curr_topL(L); + msg = lj_strfmt_pushvf(L, err2msg(em), argp); + va_end(argp); + lj_debug_addloc(L, msg, L->base-1, NULL); + lj_err_run(L); +} + +/* Non-vararg variant for better calling conventions. */ +LJ_NOINLINE void lj_err_msg(lua_State *L, ErrMsg em) +{ + err_msgv(L, em); +} + +/* Lexer error. */ +LJ_NOINLINE void lj_err_lex(lua_State *L, GCstr *src, const char *tok, + BCLine line, ErrMsg em, va_list argp) +{ + char buff[LUA_IDSIZE]; + const char *msg; + lj_debug_shortname(buff, src, line); + msg = lj_strfmt_pushvf(L, err2msg(em), argp); + msg = lj_strfmt_pushf(L, "%s:%d: %s", buff, line, msg); + if (tok) + lj_strfmt_pushf(L, err2msg(LJ_ERR_XNEAR), msg, tok); + lj_err_throw(L, LUA_ERRSYNTAX); +} + +/* Typecheck error for operands. */ +LJ_NOINLINE void lj_err_optype(lua_State *L, cTValue *o, ErrMsg opm) +{ + const char *tname = lj_typename(o); + const char *opname = err2msg(opm); + if (curr_funcisL(L)) { + GCproto *pt = curr_proto(L); + const BCIns *pc = cframe_Lpc(L) - 1; + const char *oname = NULL; + const char *kind = lj_debug_slotname(pt, pc, (BCReg)(o-L->base), &oname); + if (kind) + err_msgv(L, LJ_ERR_BADOPRT, opname, kind, oname, tname); + } + err_msgv(L, LJ_ERR_BADOPRV, opname, tname); +} + +/* Typecheck error for ordered comparisons. */ +LJ_NOINLINE void lj_err_comp(lua_State *L, cTValue *o1, cTValue *o2) +{ + const char *t1 = lj_typename(o1); + const char *t2 = lj_typename(o2); + err_msgv(L, t1 == t2 ? LJ_ERR_BADCMPV : LJ_ERR_BADCMPT, t1, t2); + /* This assumes the two "boolean" entries are commoned by the C compiler. */ +} + +/* Typecheck error for __call. */ +LJ_NOINLINE void lj_err_optype_call(lua_State *L, TValue *o) +{ + /* Gross hack if lua_[p]call or pcall/xpcall fail for a non-callable object: + ** L->base still points to the caller. So add a dummy frame with L instead + ** of a function. See lua_getstack(). + */ + const BCIns *pc = cframe_Lpc(L); + if (((ptrdiff_t)pc & FRAME_TYPE) != FRAME_LUA) { + const char *tname = lj_typename(o); + if (LJ_FR2) o++; + setframe_pc(o, pc); + setframe_gc(o, obj2gco(L), LJ_TTHREAD); + L->top = L->base = o+1; + err_msgv(L, LJ_ERR_BADCALL, tname); + } + lj_err_optype(L, o, LJ_ERR_OPCALL); +} + +/* Error in context of caller. */ +LJ_NOINLINE void lj_err_callermsg(lua_State *L, const char *msg) +{ + TValue *frame = L->base-1; + TValue *pframe = NULL; + if (frame_islua(frame)) { + pframe = frame_prevl(frame); + } else if (frame_iscont(frame)) { + if (frame_iscont_fficb(frame)) { + pframe = frame; + frame = NULL; + } else { + pframe = frame_prevd(frame); +#if LJ_HASFFI + /* Remove frame for FFI metamethods. */ + if (frame_func(frame)->c.ffid >= FF_ffi_meta___index && + frame_func(frame)->c.ffid <= FF_ffi_meta___tostring) { + L->base = pframe+1; + L->top = frame; + setcframe_pc(cframe_raw(L->cframe), frame_contpc(frame)); + } +#endif + } + } + lj_debug_addloc(L, msg, pframe, frame); + lj_err_run(L); +} + +/* Formatted error in context of caller. */ +LJ_NOINLINE void lj_err_callerv(lua_State *L, ErrMsg em, ...) +{ + const char *msg; + va_list argp; + va_start(argp, em); + msg = lj_strfmt_pushvf(L, err2msg(em), argp); + va_end(argp); + lj_err_callermsg(L, msg); +} + +/* Error in context of caller. */ +LJ_NOINLINE void lj_err_caller(lua_State *L, ErrMsg em) +{ + lj_err_callermsg(L, err2msg(em)); +} + +/* Argument error message. */ +LJ_NORET LJ_NOINLINE static void err_argmsg(lua_State *L, int narg, + const char *msg) +{ + const char *fname = "?"; + const char *ftype = lj_debug_funcname(L, L->base - 1, &fname); + if (narg < 0 && narg > LUA_REGISTRYINDEX) + narg = (int)(L->top - L->base) + narg + 1; + if (ftype && ftype[3] == 'h' && --narg == 0) /* Check for "method". */ + msg = lj_strfmt_pushf(L, err2msg(LJ_ERR_BADSELF), fname, msg); + else + msg = lj_strfmt_pushf(L, err2msg(LJ_ERR_BADARG), narg, fname, msg); + lj_err_callermsg(L, msg); +} + +/* Formatted argument error. */ +LJ_NOINLINE void lj_err_argv(lua_State *L, int narg, ErrMsg em, ...) +{ + const char *msg; + va_list argp; + va_start(argp, em); + msg = lj_strfmt_pushvf(L, err2msg(em), argp); + va_end(argp); + err_argmsg(L, narg, msg); +} + +/* Argument error. */ +LJ_NOINLINE void lj_err_arg(lua_State *L, int narg, ErrMsg em) +{ + err_argmsg(L, narg, err2msg(em)); +} + +/* Typecheck error for arguments. */ +LJ_NOINLINE void lj_err_argtype(lua_State *L, int narg, const char *xname) +{ + const char *tname, *msg; + if (narg <= LUA_REGISTRYINDEX) { + if (narg >= LUA_GLOBALSINDEX) { + tname = lj_obj_itypename[~LJ_TTAB]; + } else { + GCfunc *fn = curr_func(L); + int idx = LUA_GLOBALSINDEX - narg; + if (idx <= fn->c.nupvalues) + tname = lj_typename(&fn->c.upvalue[idx-1]); + else + tname = lj_obj_typename[0]; + } + } else { + TValue *o = narg < 0 ? L->top + narg : L->base + narg-1; + tname = o < L->top ? lj_typename(o) : lj_obj_typename[0]; + } + msg = lj_strfmt_pushf(L, err2msg(LJ_ERR_BADTYPE), xname, tname); + err_argmsg(L, narg, msg); +} + +/* Typecheck error for arguments. */ +LJ_NOINLINE void lj_err_argt(lua_State *L, int narg, int tt) +{ + lj_err_argtype(L, narg, lj_obj_typename[tt+1]); +} + +/* -- Public error handling API ------------------------------------------- */ + +LUA_API lua_CFunction lua_atpanic(lua_State *L, lua_CFunction panicf) +{ + lua_CFunction old = G(L)->panic; + G(L)->panic = panicf; + return old; +} + +/* Forwarders for the public API (C calling convention and no LJ_NORET). */ +LUA_API int lua_error(lua_State *L) +{ + lj_err_run(L); + return 0; /* unreachable */ +} + +LUALIB_API int luaL_argerror(lua_State *L, int narg, const char *msg) +{ + err_argmsg(L, narg, msg); + return 0; /* unreachable */ +} + +LUALIB_API int luaL_typerror(lua_State *L, int narg, const char *xname) +{ + lj_err_argtype(L, narg, xname); + return 0; /* unreachable */ +} + +LUALIB_API void luaL_where(lua_State *L, int level) +{ + int size; + cTValue *frame = lj_debug_frame(L, level, &size); + lj_debug_addloc(L, "", frame, size ? frame+size : NULL); +} + +LUALIB_API int luaL_error(lua_State *L, const char *fmt, ...) +{ + const char *msg; + va_list argp; + va_start(argp, fmt); + msg = lj_strfmt_pushvf(L, fmt, argp); + va_end(argp); + lj_err_callermsg(L, msg); + return 0; /* unreachable */ +} + diff --git a/lib/LuaJIT/lj_err.h b/lib/LuaJIT/lj_err.h new file mode 100644 index 0000000..cba5fb7 --- /dev/null +++ b/lib/LuaJIT/lj_err.h @@ -0,0 +1,41 @@ +/* +** Error handling. +** Copyright (C) 2005-2017 Mike Pall. See Copyright Notice in luajit.h +*/ + +#ifndef _LJ_ERR_H +#define _LJ_ERR_H + +#include + +#include "lj_obj.h" + +typedef enum { +#define ERRDEF(name, msg) \ + LJ_ERR_##name, LJ_ERR_##name##_ = LJ_ERR_##name + sizeof(msg)-1, +#include "lj_errmsg.h" + LJ_ERR__MAX +} ErrMsg; + +LJ_DATA const char *lj_err_allmsg; +#define err2msg(em) (lj_err_allmsg+(int)(em)) + +LJ_FUNC GCstr *lj_err_str(lua_State *L, ErrMsg em); +LJ_FUNCA_NORET void LJ_FASTCALL lj_err_throw(lua_State *L, int errcode); +LJ_FUNC_NORET void lj_err_mem(lua_State *L); +LJ_FUNC_NORET void lj_err_run(lua_State *L); +LJ_FUNC_NORET void lj_err_msg(lua_State *L, ErrMsg em); +LJ_FUNC_NORET void lj_err_lex(lua_State *L, GCstr *src, const char *tok, + BCLine line, ErrMsg em, va_list argp); +LJ_FUNC_NORET void lj_err_optype(lua_State *L, cTValue *o, ErrMsg opm); +LJ_FUNC_NORET void lj_err_comp(lua_State *L, cTValue *o1, cTValue *o2); +LJ_FUNC_NORET void lj_err_optype_call(lua_State *L, TValue *o); +LJ_FUNC_NORET void lj_err_callermsg(lua_State *L, const char *msg); +LJ_FUNC_NORET void lj_err_callerv(lua_State *L, ErrMsg em, ...); +LJ_FUNC_NORET void lj_err_caller(lua_State *L, ErrMsg em); +LJ_FUNC_NORET void lj_err_arg(lua_State *L, int narg, ErrMsg em); +LJ_FUNC_NORET void lj_err_argv(lua_State *L, int narg, ErrMsg em, ...); +LJ_FUNC_NORET void lj_err_argtype(lua_State *L, int narg, const char *xname); +LJ_FUNC_NORET void lj_err_argt(lua_State *L, int narg, int tt); + +#endif diff --git a/lib/LuaJIT/lj_err.o b/lib/LuaJIT/lj_err.o new file mode 100644 index 0000000000000000000000000000000000000000..b368f2fa5e39c92472936e594594ab5b3e578bdc GIT binary patch literal 15272 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 A%m4rY literal 0 HcmV?d00001 diff --git a/lib/LuaJIT/lj_err_dyn.o b/lib/LuaJIT/lj_err_dyn.o new file mode 100644 index 0000000000000000000000000000000000000000..9e8ea14303cbad9b01c28e9444d61608061f53bf GIT binary patch literal 15344 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 U_5cYRc!ClGZ7;w0zrpeU2kLidYXATM literal 0 HcmV?d00001 diff --git a/lib/LuaJIT/lj_errmsg.h b/lib/LuaJIT/lj_errmsg.h new file mode 100644 index 0000000..060a9f8 --- /dev/null +++ b/lib/LuaJIT/lj_errmsg.h @@ -0,0 +1,190 @@ +/* +** VM error messages. +** Copyright (C) 2005-2017 Mike Pall. See Copyright Notice in luajit.h +*/ + +/* This file may be included multiple times with different ERRDEF macros. */ + +/* Basic error handling. */ +ERRDEF(ERRMEM, "not enough memory") +ERRDEF(ERRERR, "error in error handling") +ERRDEF(ERRCPP, "C++ exception") + +/* Allocations. */ +ERRDEF(STROV, "string length overflow") +ERRDEF(UDATAOV, "userdata length overflow") +ERRDEF(STKOV, "stack overflow") +ERRDEF(STKOVM, "stack overflow (%s)") +ERRDEF(TABOV, "table overflow") + +/* Table indexing. */ +ERRDEF(NANIDX, "table index is NaN") +ERRDEF(NILIDX, "table index is nil") +ERRDEF(NEXTIDX, "invalid key to " LUA_QL("next")) + +/* Metamethod resolving. */ +ERRDEF(BADCALL, "attempt to call a %s value") +ERRDEF(BADOPRT, "attempt to %s %s " LUA_QS " (a %s value)") +ERRDEF(BADOPRV, "attempt to %s a %s value") +ERRDEF(BADCMPT, "attempt to compare %s with %s") +ERRDEF(BADCMPV, "attempt to compare two %s values") +ERRDEF(GETLOOP, "loop in gettable") +ERRDEF(SETLOOP, "loop in settable") +ERRDEF(OPCALL, "call") +ERRDEF(OPINDEX, "index") +ERRDEF(OPARITH, "perform arithmetic on") +ERRDEF(OPCAT, "concatenate") +ERRDEF(OPLEN, "get length of") + +/* Type checks. */ +ERRDEF(BADSELF, "calling " LUA_QS " on bad self (%s)") +ERRDEF(BADARG, "bad argument #%d to " LUA_QS " (%s)") +ERRDEF(BADTYPE, "%s expected, got %s") +ERRDEF(BADVAL, "invalid value") +ERRDEF(NOVAL, "value expected") +ERRDEF(NOCORO, "coroutine expected") +ERRDEF(NOTABN, "nil or table expected") +ERRDEF(NOLFUNC, "Lua function expected") +ERRDEF(NOFUNCL, "function or level expected") +ERRDEF(NOSFT, "string/function/table expected") +ERRDEF(NOPROXY, "boolean or proxy expected") +ERRDEF(FORINIT, LUA_QL("for") " initial value must be a number") +ERRDEF(FORLIM, LUA_QL("for") " limit must be a number") +ERRDEF(FORSTEP, LUA_QL("for") " step must be a number") + +/* C API checks. */ +ERRDEF(NOENV, "no calling environment") +ERRDEF(CYIELD, "attempt to yield across C-call boundary") +ERRDEF(BADLU, "bad light userdata pointer") +ERRDEF(NOGCMM, "bad action while in __gc metamethod") +#if LJ_TARGET_WINDOWS +ERRDEF(BADFPU, "bad FPU precision (use D3DCREATE_FPU_PRESERVE with DirectX)") +#endif + +/* Standard library function errors. */ +ERRDEF(ASSERT, "assertion failed!") +ERRDEF(PROTMT, "cannot change a protected metatable") +ERRDEF(UNPACK, "too many results to unpack") +ERRDEF(RDRSTR, "reader function must return a string") +ERRDEF(PRTOSTR, LUA_QL("tostring") " must return a string to " LUA_QL("print")) +ERRDEF(IDXRNG, "index out of range") +ERRDEF(BASERNG, "base out of range") +ERRDEF(LVLRNG, "level out of range") +ERRDEF(INVLVL, "invalid level") +ERRDEF(INVOPT, "invalid option") +ERRDEF(INVOPTM, "invalid option " LUA_QS) +ERRDEF(INVFMT, "invalid format") +ERRDEF(SETFENV, LUA_QL("setfenv") " cannot change environment of given object") +ERRDEF(CORUN, "cannot resume running coroutine") +ERRDEF(CODEAD, "cannot resume dead coroutine") +ERRDEF(COSUSP, "cannot resume non-suspended coroutine") +ERRDEF(TABINS, "wrong number of arguments to " LUA_QL("insert")) +ERRDEF(TABCAT, "invalid value (%s) at index %d in table for " LUA_QL("concat")) +ERRDEF(TABSORT, "invalid order function for sorting") +ERRDEF(IOCLFL, "attempt to use a closed file") +ERRDEF(IOSTDCL, "standard file is closed") +ERRDEF(OSUNIQF, "unable to generate a unique filename") +ERRDEF(OSDATEF, "field " LUA_QS " missing in date table") +ERRDEF(STRDUMP, "unable to dump given function") +ERRDEF(STRSLC, "string slice too long") +ERRDEF(STRPATB, "missing " LUA_QL("[") " after " LUA_QL("%f") " in pattern") +ERRDEF(STRPATC, "invalid pattern capture") +ERRDEF(STRPATE, "malformed pattern (ends with " LUA_QL("%") ")") +ERRDEF(STRPATM, "malformed pattern (missing " LUA_QL("]") ")") +ERRDEF(STRPATU, "unbalanced pattern") +ERRDEF(STRPATX, "pattern too complex") +ERRDEF(STRCAPI, "invalid capture index") +ERRDEF(STRCAPN, "too many captures") +ERRDEF(STRCAPU, "unfinished capture") +ERRDEF(STRFMT, "invalid option " LUA_QS " to " LUA_QL("format")) +ERRDEF(STRGSRV, "invalid replacement value (a %s)") +ERRDEF(BADMODN, "name conflict for module " LUA_QS) +#if LJ_HASJIT +ERRDEF(JITPROT, "runtime code generation failed, restricted kernel?") +#if LJ_TARGET_X86ORX64 +ERRDEF(NOJIT, "JIT compiler disabled, CPU does not support SSE2") +#else +ERRDEF(NOJIT, "JIT compiler disabled") +#endif +#elif defined(LJ_ARCH_NOJIT) +ERRDEF(NOJIT, "no JIT compiler for this architecture (yet)") +#else +ERRDEF(NOJIT, "JIT compiler permanently disabled by build option") +#endif +ERRDEF(JITOPT, "unknown or malformed optimization flag " LUA_QS) + +/* Lexer/parser errors. */ +ERRDEF(XMODE, "attempt to load chunk with wrong mode") +ERRDEF(XNEAR, "%s near " LUA_QS) +ERRDEF(XLINES, "chunk has too many lines") +ERRDEF(XLEVELS, "chunk has too many syntax levels") +ERRDEF(XNUMBER, "malformed number") +ERRDEF(XLSTR, "unfinished long string") +ERRDEF(XLCOM, "unfinished long comment") +ERRDEF(XSTR, "unfinished string") +ERRDEF(XESC, "invalid escape sequence") +ERRDEF(XLDELIM, "invalid long string delimiter") +ERRDEF(XTOKEN, LUA_QS " expected") +ERRDEF(XJUMP, "control structure too long") +ERRDEF(XSLOTS, "function or expression too complex") +ERRDEF(XLIMC, "chunk has more than %d local variables") +ERRDEF(XLIMM, "main function has more than %d %s") +ERRDEF(XLIMF, "function at line %d has more than %d %s") +ERRDEF(XMATCH, LUA_QS " expected (to close " LUA_QS " at line %d)") +ERRDEF(XFIXUP, "function too long for return fixup") +ERRDEF(XPARAM, " or " LUA_QL("...") " expected") +#if !LJ_52 +ERRDEF(XAMBIG, "ambiguous syntax (function call x new statement)") +#endif +ERRDEF(XFUNARG, "function arguments expected") +ERRDEF(XSYMBOL, "unexpected symbol") +ERRDEF(XDOTS, "cannot use " LUA_QL("...") " outside a vararg function") +ERRDEF(XSYNTAX, "syntax error") +ERRDEF(XFOR, LUA_QL("=") " or " LUA_QL("in") " expected") +ERRDEF(XBREAK, "no loop to break") +ERRDEF(XLUNDEF, "undefined label " LUA_QS) +ERRDEF(XLDUP, "duplicate label " LUA_QS) +ERRDEF(XGSCOPE, " jumps into the scope of local " LUA_QS) + +/* Bytecode reader errors. */ +ERRDEF(BCFMT, "cannot load incompatible bytecode") +ERRDEF(BCBAD, "cannot load malformed bytecode") + +#if LJ_HASFFI +/* FFI errors. */ +ERRDEF(FFI_INVTYPE, "invalid C type") +ERRDEF(FFI_INVSIZE, "size of C type is unknown or too large") +ERRDEF(FFI_BADSCL, "bad storage class") +ERRDEF(FFI_DECLSPEC, "declaration specifier expected") +ERRDEF(FFI_BADTAG, "undeclared or implicit tag " LUA_QS) +ERRDEF(FFI_REDEF, "attempt to redefine " LUA_QS) +ERRDEF(FFI_NUMPARAM, "wrong number of type parameters") +ERRDEF(FFI_INITOV, "too many initializers for " LUA_QS) +ERRDEF(FFI_BADCONV, "cannot convert " LUA_QS " to " LUA_QS) +ERRDEF(FFI_BADLEN, "attempt to get length of " LUA_QS) +ERRDEF(FFI_BADCONCAT, "attempt to concatenate " LUA_QS " and " LUA_QS) +ERRDEF(FFI_BADARITH, "attempt to perform arithmetic on " LUA_QS " and " LUA_QS) +ERRDEF(FFI_BADCOMP, "attempt to compare " LUA_QS " with " LUA_QS) +ERRDEF(FFI_BADCALL, LUA_QS " is not callable") +ERRDEF(FFI_NUMARG, "wrong number of arguments for function call") +ERRDEF(FFI_BADMEMBER, LUA_QS " has no member named " LUA_QS) +ERRDEF(FFI_BADIDX, LUA_QS " cannot be indexed") +ERRDEF(FFI_BADIDXW, LUA_QS " cannot be indexed with " LUA_QS) +ERRDEF(FFI_BADMM, LUA_QS " has no " LUA_QS " metamethod") +ERRDEF(FFI_WRCONST, "attempt to write to constant location") +ERRDEF(FFI_NODECL, "missing declaration for symbol " LUA_QS) +ERRDEF(FFI_BADCBACK, "bad callback") +#if LJ_OS_NOJIT +ERRDEF(FFI_CBACKOV, "no support for callbacks on this OS") +#else +ERRDEF(FFI_CBACKOV, "too many callbacks") +#endif +ERRDEF(FFI_NYIPACKBIT, "NYI: packed bit fields") +ERRDEF(FFI_NYICALL, "NYI: cannot call this C function (yet)") +#endif + +#undef ERRDEF + +/* Detecting unused error messages: + awk -F, '/^ERRDEF/ { gsub(/ERRDEF./, ""); printf "grep -q LJ_ERR_%s *.[ch] || echo %s\n", $1, $1}' lj_errmsg.h | sh +*/ diff --git a/lib/LuaJIT/lj_ff.h b/lib/LuaJIT/lj_ff.h new file mode 100644 index 0000000..31d65a0 --- /dev/null +++ b/lib/LuaJIT/lj_ff.h @@ -0,0 +1,18 @@ +/* +** Fast function IDs. +** Copyright (C) 2005-2017 Mike Pall. See Copyright Notice in luajit.h +*/ + +#ifndef _LJ_FF_H +#define _LJ_FF_H + +/* Fast function ID. */ +typedef enum { + FF_LUA_ = FF_LUA, /* Lua function (must be 0). */ + FF_C_ = FF_C, /* Regular C function (must be 1). */ +#define FFDEF(name) FF_##name, +#include "lj_ffdef.h" + FF__MAX +} FastFunc; + +#endif diff --git a/lib/LuaJIT/lj_ffrecord.c b/lib/LuaJIT/lj_ffrecord.c new file mode 100644 index 0000000..849d7a2 --- /dev/null +++ b/lib/LuaJIT/lj_ffrecord.c @@ -0,0 +1,1226 @@ +/* +** Fast function call recorder. +** Copyright (C) 2005-2017 Mike Pall. See Copyright Notice in luajit.h +*/ + +#define lj_ffrecord_c +#define LUA_CORE + +#include "lj_obj.h" + +#if LJ_HASJIT + +#include "lj_err.h" +#include "lj_str.h" +#include "lj_tab.h" +#include "lj_frame.h" +#include "lj_bc.h" +#include "lj_ff.h" +#include "lj_ir.h" +#include "lj_jit.h" +#include "lj_ircall.h" +#include "lj_iropt.h" +#include "lj_trace.h" +#include "lj_record.h" +#include "lj_ffrecord.h" +#include "lj_crecord.h" +#include "lj_dispatch.h" +#include "lj_vm.h" +#include "lj_strscan.h" +#include "lj_strfmt.h" + +/* Some local macros to save typing. Undef'd at the end. */ +#define IR(ref) (&J->cur.ir[(ref)]) + +/* Pass IR on to next optimization in chain (FOLD). */ +#define emitir(ot, a, b) (lj_ir_set(J, (ot), (a), (b)), lj_opt_fold(J)) + +/* -- Fast function recording handlers ------------------------------------ */ + +/* Conventions for fast function call handlers: +** +** The argument slots start at J->base[0]. All of them are guaranteed to be +** valid and type-specialized references. J->base[J->maxslot] is set to 0 +** as a sentinel. The runtime argument values start at rd->argv[0]. +** +** In general fast functions should check for presence of all of their +** arguments and for the correct argument types. Some simplifications +** are allowed if the interpreter throws instead. But even if recording +** is aborted, the generated IR must be consistent (no zero-refs). +** +** The number of results in rd->nres is set to 1. Handlers that return +** a different number of results need to override it. A negative value +** prevents return processing (e.g. for pending calls). +** +** Results need to be stored starting at J->base[0]. Return processing +** moves them to the right slots later. +** +** The per-ffid auxiliary data is the value of the 2nd part of the +** LJLIB_REC() annotation. This allows handling similar functionality +** in a common handler. +*/ + +/* Type of handler to record a fast function. */ +typedef void (LJ_FASTCALL *RecordFunc)(jit_State *J, RecordFFData *rd); + +/* Get runtime value of int argument. */ +static int32_t argv2int(jit_State *J, TValue *o) +{ + if (!lj_strscan_numberobj(o)) + lj_trace_err(J, LJ_TRERR_BADTYPE); + return tvisint(o) ? intV(o) : lj_num2int(numV(o)); +} + +/* Get runtime value of string argument. */ +static GCstr *argv2str(jit_State *J, TValue *o) +{ + if (LJ_LIKELY(tvisstr(o))) { + return strV(o); + } else { + GCstr *s; + if (!tvisnumber(o)) + lj_trace_err(J, LJ_TRERR_BADTYPE); + s = lj_strfmt_number(J->L, o); + setstrV(J->L, o, s); + return s; + } +} + +/* Return number of results wanted by caller. */ +static ptrdiff_t results_wanted(jit_State *J) +{ + TValue *frame = J->L->base-1; + if (frame_islua(frame)) + return (ptrdiff_t)bc_b(frame_pc(frame)[-1]) - 1; + else + return -1; +} + +/* Trace stitching: add continuation below frame to start a new trace. */ +static void recff_stitch(jit_State *J) +{ + ASMFunction cont = lj_cont_stitch; + lua_State *L = J->L; + TValue *base = L->base; + BCReg nslot = J->maxslot + 1 + LJ_FR2; + TValue *nframe = base + 1 + LJ_FR2; + const BCIns *pc = frame_pc(base-1); + TValue *pframe = frame_prevl(base-1); + + /* Move func + args up in Lua stack and insert continuation. */ + memmove(&base[1], &base[-1-LJ_FR2], sizeof(TValue)*nslot); + setframe_ftsz(nframe, ((char *)nframe - (char *)pframe) + FRAME_CONT); + setcont(base-LJ_FR2, cont); + setframe_pc(base, pc); + setnilV(base-1-LJ_FR2); /* Incorrect, but rec_check_slots() won't run anymore. */ + L->base += 2 + LJ_FR2; + L->top += 2 + LJ_FR2; + + /* Ditto for the IR. */ + memmove(&J->base[1], &J->base[-1-LJ_FR2], sizeof(TRef)*nslot); +#if LJ_FR2 + J->base[2] = TREF_FRAME; + J->base[-1] = lj_ir_k64(J, IR_KNUM, u64ptr(contptr(cont))); + J->base[0] = lj_ir_k64(J, IR_KNUM, u64ptr(pc)) | TREF_CONT; +#else + J->base[0] = lj_ir_kptr(J, contptr(cont)) | TREF_CONT; +#endif + J->ktrace = tref_ref((J->base[-1-LJ_FR2] = lj_ir_ktrace(J))); + J->base += 2 + LJ_FR2; + J->baseslot += 2 + LJ_FR2; + J->framedepth++; + + lj_record_stop(J, LJ_TRLINK_STITCH, 0); + + /* Undo Lua stack changes. */ + memmove(&base[-1-LJ_FR2], &base[1], sizeof(TValue)*nslot); + setframe_pc(base-1, pc); + L->base -= 2 + LJ_FR2; + L->top -= 2 + LJ_FR2; +} + +/* Fallback handler for fast functions that are not recorded (yet). */ +static void LJ_FASTCALL recff_nyi(jit_State *J, RecordFFData *rd) +{ + if (J->cur.nins < (IRRef)J->param[JIT_P_minstitch] + REF_BASE) { + lj_trace_err_info(J, LJ_TRERR_TRACEUV); + } else { + /* Can only stitch from Lua call. */ + if (J->framedepth && frame_islua(J->L->base-1)) { + BCOp op = bc_op(*frame_pc(J->L->base-1)); + /* Stitched trace cannot start with *M op with variable # of args. */ + if (!(op == BC_CALLM || op == BC_CALLMT || + op == BC_RETM || op == BC_TSETM)) { + switch (J->fn->c.ffid) { + case FF_error: + case FF_debug_sethook: + case FF_jit_flush: + break; /* Don't stitch across special builtins. */ + default: + recff_stitch(J); /* Use trace stitching. */ + rd->nres = -1; + return; + } + } + } + /* Otherwise stop trace and return to interpreter. */ + lj_record_stop(J, LJ_TRLINK_RETURN, 0); + rd->nres = -1; + } +} + +/* Fallback handler for unsupported variants of fast functions. */ +#define recff_nyiu recff_nyi + +/* Must stop the trace for classic C functions with arbitrary side-effects. */ +#define recff_c recff_nyi + +/* Emit BUFHDR for the global temporary buffer. */ +static TRef recff_bufhdr(jit_State *J) +{ + return emitir(IRT(IR_BUFHDR, IRT_PGC), + lj_ir_kptr(J, &J2G(J)->tmpbuf), IRBUFHDR_RESET); +} + +/* -- Base library fast functions ----------------------------------------- */ + +static void LJ_FASTCALL recff_assert(jit_State *J, RecordFFData *rd) +{ + /* Arguments already specialized. The interpreter throws for nil/false. */ + rd->nres = J->maxslot; /* Pass through all arguments. */ +} + +static void LJ_FASTCALL recff_type(jit_State *J, RecordFFData *rd) +{ + /* Arguments already specialized. Result is a constant string. Neat, huh? */ + uint32_t t; + if (tvisnumber(&rd->argv[0])) + t = ~LJ_TNUMX; + else if (LJ_64 && !LJ_GC64 && tvislightud(&rd->argv[0])) + t = ~LJ_TLIGHTUD; + else + t = ~itype(&rd->argv[0]); + J->base[0] = lj_ir_kstr(J, strV(&J->fn->c.upvalue[t])); + UNUSED(rd); +} + +static void LJ_FASTCALL recff_getmetatable(jit_State *J, RecordFFData *rd) +{ + TRef tr = J->base[0]; + if (tr) { + RecordIndex ix; + ix.tab = tr; + copyTV(J->L, &ix.tabv, &rd->argv[0]); + if (lj_record_mm_lookup(J, &ix, MM_metatable)) + J->base[0] = ix.mobj; + else + J->base[0] = ix.mt; + } /* else: Interpreter will throw. */ +} + +static void LJ_FASTCALL recff_setmetatable(jit_State *J, RecordFFData *rd) +{ + TRef tr = J->base[0]; + TRef mt = J->base[1]; + if (tref_istab(tr) && (tref_istab(mt) || (mt && tref_isnil(mt)))) { + TRef fref, mtref; + RecordIndex ix; + ix.tab = tr; + copyTV(J->L, &ix.tabv, &rd->argv[0]); + lj_record_mm_lookup(J, &ix, MM_metatable); /* Guard for no __metatable. */ + fref = emitir(IRT(IR_FREF, IRT_PGC), tr, IRFL_TAB_META); + mtref = tref_isnil(mt) ? lj_ir_knull(J, IRT_TAB) : mt; + emitir(IRT(IR_FSTORE, IRT_TAB), fref, mtref); + if (!tref_isnil(mt)) + emitir(IRT(IR_TBAR, IRT_TAB), tr, 0); + J->base[0] = tr; + J->needsnap = 1; + } /* else: Interpreter will throw. */ +} + +static void LJ_FASTCALL recff_rawget(jit_State *J, RecordFFData *rd) +{ + RecordIndex ix; + ix.tab = J->base[0]; ix.key = J->base[1]; + if (tref_istab(ix.tab) && ix.key) { + ix.val = 0; ix.idxchain = 0; + settabV(J->L, &ix.tabv, tabV(&rd->argv[0])); + copyTV(J->L, &ix.keyv, &rd->argv[1]); + J->base[0] = lj_record_idx(J, &ix); + } /* else: Interpreter will throw. */ +} + +static void LJ_FASTCALL recff_rawset(jit_State *J, RecordFFData *rd) +{ + RecordIndex ix; + ix.tab = J->base[0]; ix.key = J->base[1]; ix.val = J->base[2]; + if (tref_istab(ix.tab) && ix.key && ix.val) { + ix.idxchain = 0; + settabV(J->L, &ix.tabv, tabV(&rd->argv[0])); + copyTV(J->L, &ix.keyv, &rd->argv[1]); + copyTV(J->L, &ix.valv, &rd->argv[2]); + lj_record_idx(J, &ix); + /* Pass through table at J->base[0] as result. */ + } /* else: Interpreter will throw. */ +} + +static void LJ_FASTCALL recff_rawequal(jit_State *J, RecordFFData *rd) +{ + TRef tra = J->base[0]; + TRef trb = J->base[1]; + if (tra && trb) { + int diff = lj_record_objcmp(J, tra, trb, &rd->argv[0], &rd->argv[1]); + J->base[0] = diff ? TREF_FALSE : TREF_TRUE; + } /* else: Interpreter will throw. */ +} + +#if LJ_52 +static void LJ_FASTCALL recff_rawlen(jit_State *J, RecordFFData *rd) +{ + TRef tr = J->base[0]; + if (tref_isstr(tr)) + J->base[0] = emitir(IRTI(IR_FLOAD), tr, IRFL_STR_LEN); + else if (tref_istab(tr)) + J->base[0] = lj_ir_call(J, IRCALL_lj_tab_len, tr); + /* else: Interpreter will throw. */ + UNUSED(rd); +} +#endif + +/* Determine mode of select() call. */ +int32_t lj_ffrecord_select_mode(jit_State *J, TRef tr, TValue *tv) +{ + if (tref_isstr(tr) && *strVdata(tv) == '#') { /* select('#', ...) */ + if (strV(tv)->len == 1) { + emitir(IRTG(IR_EQ, IRT_STR), tr, lj_ir_kstr(J, strV(tv))); + } else { + TRef trptr = emitir(IRT(IR_STRREF, IRT_PGC), tr, lj_ir_kint(J, 0)); + TRef trchar = emitir(IRT(IR_XLOAD, IRT_U8), trptr, IRXLOAD_READONLY); + emitir(IRTG(IR_EQ, IRT_INT), trchar, lj_ir_kint(J, '#')); + } + return 0; + } else { /* select(n, ...) */ + int32_t start = argv2int(J, tv); + if (start == 0) lj_trace_err(J, LJ_TRERR_BADTYPE); /* A bit misleading. */ + return start; + } +} + +static void LJ_FASTCALL recff_select(jit_State *J, RecordFFData *rd) +{ + TRef tr = J->base[0]; + if (tr) { + ptrdiff_t start = lj_ffrecord_select_mode(J, tr, &rd->argv[0]); + if (start == 0) { /* select('#', ...) */ + J->base[0] = lj_ir_kint(J, J->maxslot - 1); + } else if (tref_isk(tr)) { /* select(k, ...) */ + ptrdiff_t n = (ptrdiff_t)J->maxslot; + if (start < 0) start += n; + else if (start > n) start = n; + rd->nres = n - start; + if (start >= 1) { + ptrdiff_t i; + for (i = 0; i < n - start; i++) + J->base[i] = J->base[start+i]; + } /* else: Interpreter will throw. */ + } else { + recff_nyiu(J, rd); + return; + } + } /* else: Interpreter will throw. */ +} + +static void LJ_FASTCALL recff_tonumber(jit_State *J, RecordFFData *rd) +{ + TRef tr = J->base[0]; + TRef base = J->base[1]; + if (tr && !tref_isnil(base)) { + base = lj_opt_narrow_toint(J, base); + if (!tref_isk(base) || IR(tref_ref(base))->i != 10) { + recff_nyiu(J, rd); + return; + } + } + if (tref_isnumber_str(tr)) { + if (tref_isstr(tr)) { + TValue tmp; + if (!lj_strscan_num(strV(&rd->argv[0]), &tmp)) { + recff_nyiu(J, rd); /* Would need an inverted STRTO for this case. */ + return; + } + tr = emitir(IRTG(IR_STRTO, IRT_NUM), tr, 0); + } +#if LJ_HASFFI + } else if (tref_iscdata(tr)) { + lj_crecord_tonumber(J, rd); + return; +#endif + } else { + tr = TREF_NIL; + } + J->base[0] = tr; + UNUSED(rd); +} + +static TValue *recff_metacall_cp(lua_State *L, lua_CFunction dummy, void *ud) +{ + jit_State *J = (jit_State *)ud; + lj_record_tailcall(J, 0, 1); + UNUSED(L); UNUSED(dummy); + return NULL; +} + +static int recff_metacall(jit_State *J, RecordFFData *rd, MMS mm) +{ + RecordIndex ix; + ix.tab = J->base[0]; + copyTV(J->L, &ix.tabv, &rd->argv[0]); + if (lj_record_mm_lookup(J, &ix, mm)) { /* Has metamethod? */ + int errcode; + TValue argv0; + /* Temporarily insert metamethod below object. */ + J->base[1+LJ_FR2] = J->base[0]; + J->base[0] = ix.mobj; + copyTV(J->L, &argv0, &rd->argv[0]); + copyTV(J->L, &rd->argv[1+LJ_FR2], &rd->argv[0]); + copyTV(J->L, &rd->argv[0], &ix.mobjv); + /* Need to protect lj_record_tailcall because it may throw. */ + errcode = lj_vm_cpcall(J->L, NULL, J, recff_metacall_cp); + /* Always undo Lua stack changes to avoid confusing the interpreter. */ + copyTV(J->L, &rd->argv[0], &argv0); + if (errcode) + lj_err_throw(J->L, errcode); /* Propagate errors. */ + rd->nres = -1; /* Pending call. */ + return 1; /* Tailcalled to metamethod. */ + } + return 0; +} + +static void LJ_FASTCALL recff_tostring(jit_State *J, RecordFFData *rd) +{ + TRef tr = J->base[0]; + if (tref_isstr(tr)) { + /* Ignore __tostring in the string base metatable. */ + /* Pass on result in J->base[0]. */ + } else if (tr && !recff_metacall(J, rd, MM_tostring)) { + if (tref_isnumber(tr)) { + J->base[0] = emitir(IRT(IR_TOSTR, IRT_STR), tr, + tref_isnum(tr) ? IRTOSTR_NUM : IRTOSTR_INT); + } else if (tref_ispri(tr)) { + J->base[0] = lj_ir_kstr(J, lj_strfmt_obj(J->L, &rd->argv[0])); + } else { + recff_nyiu(J, rd); + return; + } + } +} + +static void LJ_FASTCALL recff_ipairs_aux(jit_State *J, RecordFFData *rd) +{ + RecordIndex ix; + ix.tab = J->base[0]; + if (tref_istab(ix.tab)) { + if (!tvisnumber(&rd->argv[1])) /* No support for string coercion. */ + lj_trace_err(J, LJ_TRERR_BADTYPE); + setintV(&ix.keyv, numberVint(&rd->argv[1])+1); + settabV(J->L, &ix.tabv, tabV(&rd->argv[0])); + ix.val = 0; ix.idxchain = 0; + ix.key = lj_opt_narrow_toint(J, J->base[1]); + J->base[0] = ix.key = emitir(IRTI(IR_ADD), ix.key, lj_ir_kint(J, 1)); + J->base[1] = lj_record_idx(J, &ix); + rd->nres = tref_isnil(J->base[1]) ? 0 : 2; + } /* else: Interpreter will throw. */ +} + +static void LJ_FASTCALL recff_xpairs(jit_State *J, RecordFFData *rd) +{ + TRef tr = J->base[0]; + if (!((LJ_52 || (LJ_HASFFI && tref_iscdata(tr))) && + recff_metacall(J, rd, MM_pairs + rd->data))) { + if (tref_istab(tr)) { + J->base[0] = lj_ir_kfunc(J, funcV(&J->fn->c.upvalue[0])); + J->base[1] = tr; + J->base[2] = rd->data ? lj_ir_kint(J, 0) : TREF_NIL; + rd->nres = 3; + } /* else: Interpreter will throw. */ + } +} + +static void LJ_FASTCALL recff_pcall(jit_State *J, RecordFFData *rd) +{ + if (J->maxslot >= 1) { +#if LJ_FR2 + /* Shift function arguments up. */ + memmove(J->base + 1, J->base, sizeof(TRef) * J->maxslot); +#endif + lj_record_call(J, 0, J->maxslot - 1); + rd->nres = -1; /* Pending call. */ + } /* else: Interpreter will throw. */ +} + +static TValue *recff_xpcall_cp(lua_State *L, lua_CFunction dummy, void *ud) +{ + jit_State *J = (jit_State *)ud; + lj_record_call(J, 1, J->maxslot - 2); + UNUSED(L); UNUSED(dummy); + return NULL; +} + +static void LJ_FASTCALL recff_xpcall(jit_State *J, RecordFFData *rd) +{ + if (J->maxslot >= 2) { + TValue argv0, argv1; + TRef tmp; + int errcode; + /* Swap function and traceback. */ + tmp = J->base[0]; J->base[0] = J->base[1]; J->base[1] = tmp; + copyTV(J->L, &argv0, &rd->argv[0]); + copyTV(J->L, &argv1, &rd->argv[1]); + copyTV(J->L, &rd->argv[0], &argv1); + copyTV(J->L, &rd->argv[1], &argv0); +#if LJ_FR2 + /* Shift function arguments up. */ + memmove(J->base + 2, J->base + 1, sizeof(TRef) * (J->maxslot-1)); +#endif + /* Need to protect lj_record_call because it may throw. */ + errcode = lj_vm_cpcall(J->L, NULL, J, recff_xpcall_cp); + /* Always undo Lua stack swap to avoid confusing the interpreter. */ + copyTV(J->L, &rd->argv[0], &argv0); + copyTV(J->L, &rd->argv[1], &argv1); + if (errcode) + lj_err_throw(J->L, errcode); /* Propagate errors. */ + rd->nres = -1; /* Pending call. */ + } /* else: Interpreter will throw. */ +} + +static void LJ_FASTCALL recff_getfenv(jit_State *J, RecordFFData *rd) +{ + TRef tr = J->base[0]; + /* Only support getfenv(0) for now. */ + if (tref_isint(tr) && tref_isk(tr) && IR(tref_ref(tr))->i == 0) { + TRef trl = emitir(IRT(IR_LREF, IRT_THREAD), 0, 0); + J->base[0] = emitir(IRT(IR_FLOAD, IRT_TAB), trl, IRFL_THREAD_ENV); + return; + } + recff_nyiu(J, rd); +} + +/* -- Math library fast functions ----------------------------------------- */ + +static void LJ_FASTCALL recff_math_abs(jit_State *J, RecordFFData *rd) +{ + TRef tr = lj_ir_tonum(J, J->base[0]); + J->base[0] = emitir(IRTN(IR_ABS), tr, lj_ir_ksimd(J, LJ_KSIMD_ABS)); + UNUSED(rd); +} + +/* Record rounding functions math.floor and math.ceil. */ +static void LJ_FASTCALL recff_math_round(jit_State *J, RecordFFData *rd) +{ + TRef tr = J->base[0]; + if (!tref_isinteger(tr)) { /* Pass through integers unmodified. */ + tr = emitir(IRTN(IR_FPMATH), lj_ir_tonum(J, tr), rd->data); + /* Result is integral (or NaN/Inf), but may not fit an int32_t. */ + if (LJ_DUALNUM) { /* Try to narrow using a guarded conversion to int. */ + lua_Number n = lj_vm_foldfpm(numberVnum(&rd->argv[0]), rd->data); + if (n == (lua_Number)lj_num2int(n)) + tr = emitir(IRTGI(IR_CONV), tr, IRCONV_INT_NUM|IRCONV_CHECK); + } + J->base[0] = tr; + } +} + +/* Record unary math.* functions, mapped to IR_FPMATH opcode. */ +static void LJ_FASTCALL recff_math_unary(jit_State *J, RecordFFData *rd) +{ + J->base[0] = emitir(IRTN(IR_FPMATH), lj_ir_tonum(J, J->base[0]), rd->data); +} + +/* Record math.log. */ +static void LJ_FASTCALL recff_math_log(jit_State *J, RecordFFData *rd) +{ + TRef tr = lj_ir_tonum(J, J->base[0]); + if (J->base[1]) { +#ifdef LUAJIT_NO_LOG2 + uint32_t fpm = IRFPM_LOG; +#else + uint32_t fpm = IRFPM_LOG2; +#endif + TRef trb = lj_ir_tonum(J, J->base[1]); + tr = emitir(IRTN(IR_FPMATH), tr, fpm); + trb = emitir(IRTN(IR_FPMATH), trb, fpm); + trb = emitir(IRTN(IR_DIV), lj_ir_knum_one(J), trb); + tr = emitir(IRTN(IR_MUL), tr, trb); + } else { + tr = emitir(IRTN(IR_FPMATH), tr, IRFPM_LOG); + } + J->base[0] = tr; + UNUSED(rd); +} + +/* Record math.atan2. */ +static void LJ_FASTCALL recff_math_atan2(jit_State *J, RecordFFData *rd) +{ + TRef tr = lj_ir_tonum(J, J->base[0]); + TRef tr2 = lj_ir_tonum(J, J->base[1]); + J->base[0] = emitir(IRTN(IR_ATAN2), tr, tr2); + UNUSED(rd); +} + +/* Record math.ldexp. */ +static void LJ_FASTCALL recff_math_ldexp(jit_State *J, RecordFFData *rd) +{ + TRef tr = lj_ir_tonum(J, J->base[0]); +#if LJ_TARGET_X86ORX64 + TRef tr2 = lj_ir_tonum(J, J->base[1]); +#else + TRef tr2 = lj_opt_narrow_toint(J, J->base[1]); +#endif + J->base[0] = emitir(IRTN(IR_LDEXP), tr, tr2); + UNUSED(rd); +} + +/* Record math.asin, math.acos, math.atan. */ +static void LJ_FASTCALL recff_math_atrig(jit_State *J, RecordFFData *rd) +{ + TRef y = lj_ir_tonum(J, J->base[0]); + TRef x = lj_ir_knum_one(J); + uint32_t ffid = rd->data; + if (ffid != FF_math_atan) { + TRef tmp = emitir(IRTN(IR_MUL), y, y); + tmp = emitir(IRTN(IR_SUB), x, tmp); + tmp = emitir(IRTN(IR_FPMATH), tmp, IRFPM_SQRT); + if (ffid == FF_math_asin) { x = tmp; } else { x = y; y = tmp; } + } + J->base[0] = emitir(IRTN(IR_ATAN2), y, x); +} + +static void LJ_FASTCALL recff_math_htrig(jit_State *J, RecordFFData *rd) +{ + TRef tr = lj_ir_tonum(J, J->base[0]); + J->base[0] = emitir(IRTN(IR_CALLN), tr, rd->data); +} + +static void LJ_FASTCALL recff_math_modf(jit_State *J, RecordFFData *rd) +{ + TRef tr = J->base[0]; + if (tref_isinteger(tr)) { + J->base[0] = tr; + J->base[1] = lj_ir_kint(J, 0); + } else { + TRef trt; + tr = lj_ir_tonum(J, tr); + trt = emitir(IRTN(IR_FPMATH), tr, IRFPM_TRUNC); + J->base[0] = trt; + J->base[1] = emitir(IRTN(IR_SUB), tr, trt); + } + rd->nres = 2; +} + +static void LJ_FASTCALL recff_math_pow(jit_State *J, RecordFFData *rd) +{ + J->base[0] = lj_opt_narrow_pow(J, J->base[0], J->base[1], + &rd->argv[0], &rd->argv[1]); + UNUSED(rd); +} + +static void LJ_FASTCALL recff_math_minmax(jit_State *J, RecordFFData *rd) +{ + TRef tr = lj_ir_tonumber(J, J->base[0]); + uint32_t op = rd->data; + BCReg i; + for (i = 1; J->base[i] != 0; i++) { + TRef tr2 = lj_ir_tonumber(J, J->base[i]); + IRType t = IRT_INT; + if (!(tref_isinteger(tr) && tref_isinteger(tr2))) { + if (tref_isinteger(tr)) tr = emitir(IRTN(IR_CONV), tr, IRCONV_NUM_INT); + if (tref_isinteger(tr2)) tr2 = emitir(IRTN(IR_CONV), tr2, IRCONV_NUM_INT); + t = IRT_NUM; + } + tr = emitir(IRT(op, t), tr, tr2); + } + J->base[0] = tr; +} + +static void LJ_FASTCALL recff_math_random(jit_State *J, RecordFFData *rd) +{ + GCudata *ud = udataV(&J->fn->c.upvalue[0]); + TRef tr, one; + lj_ir_kgc(J, obj2gco(ud), IRT_UDATA); /* Prevent collection. */ + tr = lj_ir_call(J, IRCALL_lj_math_random_step, lj_ir_kptr(J, uddata(ud))); + one = lj_ir_knum_one(J); + tr = emitir(IRTN(IR_SUB), tr, one); + if (J->base[0]) { + TRef tr1 = lj_ir_tonum(J, J->base[0]); + if (J->base[1]) { /* d = floor(d*(r2-r1+1.0)) + r1 */ + TRef tr2 = lj_ir_tonum(J, J->base[1]); + tr2 = emitir(IRTN(IR_SUB), tr2, tr1); + tr2 = emitir(IRTN(IR_ADD), tr2, one); + tr = emitir(IRTN(IR_MUL), tr, tr2); + tr = emitir(IRTN(IR_FPMATH), tr, IRFPM_FLOOR); + tr = emitir(IRTN(IR_ADD), tr, tr1); + } else { /* d = floor(d*r1) + 1.0 */ + tr = emitir(IRTN(IR_MUL), tr, tr1); + tr = emitir(IRTN(IR_FPMATH), tr, IRFPM_FLOOR); + tr = emitir(IRTN(IR_ADD), tr, one); + } + } + J->base[0] = tr; + UNUSED(rd); +} + +/* -- Bit library fast functions ------------------------------------------ */ + +/* Record bit.tobit. */ +static void LJ_FASTCALL recff_bit_tobit(jit_State *J, RecordFFData *rd) +{ + TRef tr = J->base[0]; +#if LJ_HASFFI + if (tref_iscdata(tr)) { recff_bit64_tobit(J, rd); return; } +#endif + J->base[0] = lj_opt_narrow_tobit(J, tr); + UNUSED(rd); +} + +/* Record unary bit.bnot, bit.bswap. */ +static void LJ_FASTCALL recff_bit_unary(jit_State *J, RecordFFData *rd) +{ +#if LJ_HASFFI + if (recff_bit64_unary(J, rd)) + return; +#endif + J->base[0] = emitir(IRTI(rd->data), lj_opt_narrow_tobit(J, J->base[0]), 0); +} + +/* Record N-ary bit.band, bit.bor, bit.bxor. */ +static void LJ_FASTCALL recff_bit_nary(jit_State *J, RecordFFData *rd) +{ +#if LJ_HASFFI + if (recff_bit64_nary(J, rd)) + return; +#endif + { + TRef tr = lj_opt_narrow_tobit(J, J->base[0]); + uint32_t ot = IRTI(rd->data); + BCReg i; + for (i = 1; J->base[i] != 0; i++) + tr = emitir(ot, tr, lj_opt_narrow_tobit(J, J->base[i])); + J->base[0] = tr; + } +} + +/* Record bit shifts. */ +static void LJ_FASTCALL recff_bit_shift(jit_State *J, RecordFFData *rd) +{ +#if LJ_HASFFI + if (recff_bit64_shift(J, rd)) + return; +#endif + { + TRef tr = lj_opt_narrow_tobit(J, J->base[0]); + TRef tsh = lj_opt_narrow_tobit(J, J->base[1]); + IROp op = (IROp)rd->data; + if (!(op < IR_BROL ? LJ_TARGET_MASKSHIFT : LJ_TARGET_MASKROT) && + !tref_isk(tsh)) + tsh = emitir(IRTI(IR_BAND), tsh, lj_ir_kint(J, 31)); +#ifdef LJ_TARGET_UNIFYROT + if (op == (LJ_TARGET_UNIFYROT == 1 ? IR_BROR : IR_BROL)) { + op = LJ_TARGET_UNIFYROT == 1 ? IR_BROL : IR_BROR; + tsh = emitir(IRTI(IR_NEG), tsh, tsh); + } +#endif + J->base[0] = emitir(IRTI(op), tr, tsh); + } +} + +static void LJ_FASTCALL recff_bit_tohex(jit_State *J, RecordFFData *rd) +{ +#if LJ_HASFFI + TRef hdr = recff_bufhdr(J); + TRef tr = recff_bit64_tohex(J, rd, hdr); + J->base[0] = emitir(IRT(IR_BUFSTR, IRT_STR), tr, hdr); +#else + recff_nyiu(J, rd); /* Don't bother working around this NYI. */ +#endif +} + +/* -- String library fast functions --------------------------------------- */ + +/* Specialize to relative starting position for string. */ +static TRef recff_string_start(jit_State *J, GCstr *s, int32_t *st, TRef tr, + TRef trlen, TRef tr0) +{ + int32_t start = *st; + if (start < 0) { + emitir(IRTGI(IR_LT), tr, tr0); + tr = emitir(IRTI(IR_ADD), trlen, tr); + start = start + (int32_t)s->len; + emitir(start < 0 ? IRTGI(IR_LT) : IRTGI(IR_GE), tr, tr0); + if (start < 0) { + tr = tr0; + start = 0; + } + } else if (start == 0) { + emitir(IRTGI(IR_EQ), tr, tr0); + tr = tr0; + } else { + tr = emitir(IRTI(IR_ADD), tr, lj_ir_kint(J, -1)); + emitir(IRTGI(IR_GE), tr, tr0); + start--; + } + *st = start; + return tr; +} + +/* Handle string.byte (rd->data = 0) and string.sub (rd->data = 1). */ +static void LJ_FASTCALL recff_string_range(jit_State *J, RecordFFData *rd) +{ + TRef trstr = lj_ir_tostr(J, J->base[0]); + TRef trlen = emitir(IRTI(IR_FLOAD), trstr, IRFL_STR_LEN); + TRef tr0 = lj_ir_kint(J, 0); + TRef trstart, trend; + GCstr *str = argv2str(J, &rd->argv[0]); + int32_t start, end; + if (rd->data) { /* string.sub(str, start [,end]) */ + start = argv2int(J, &rd->argv[1]); + trstart = lj_opt_narrow_toint(J, J->base[1]); + trend = J->base[2]; + if (tref_isnil(trend)) { + trend = lj_ir_kint(J, -1); + end = -1; + } else { + trend = lj_opt_narrow_toint(J, trend); + end = argv2int(J, &rd->argv[2]); + } + } else { /* string.byte(str, [,start [,end]]) */ + if (tref_isnil(J->base[1])) { + start = 1; + trstart = lj_ir_kint(J, 1); + } else { + start = argv2int(J, &rd->argv[1]); + trstart = lj_opt_narrow_toint(J, J->base[1]); + } + if (J->base[1] && !tref_isnil(J->base[2])) { + trend = lj_opt_narrow_toint(J, J->base[2]); + end = argv2int(J, &rd->argv[2]); + } else { + trend = trstart; + end = start; + } + } + if (end < 0) { + emitir(IRTGI(IR_LT), trend, tr0); + trend = emitir(IRTI(IR_ADD), emitir(IRTI(IR_ADD), trlen, trend), + lj_ir_kint(J, 1)); + end = end+(int32_t)str->len+1; + } else if ((MSize)end <= str->len) { + emitir(IRTGI(IR_ULE), trend, trlen); + } else { + emitir(IRTGI(IR_UGT), trend, trlen); + end = (int32_t)str->len; + trend = trlen; + } + trstart = recff_string_start(J, str, &start, trstart, trlen, tr0); + if (rd->data) { /* Return string.sub result. */ + if (end - start >= 0) { + /* Also handle empty range here, to avoid extra traces. */ + TRef trptr, trslen = emitir(IRTI(IR_SUB), trend, trstart); + emitir(IRTGI(IR_GE), trslen, tr0); + trptr = emitir(IRT(IR_STRREF, IRT_PGC), trstr, trstart); + J->base[0] = emitir(IRT(IR_SNEW, IRT_STR), trptr, trslen); + } else { /* Range underflow: return empty string. */ + emitir(IRTGI(IR_LT), trend, trstart); + J->base[0] = lj_ir_kstr(J, &J2G(J)->strempty); + } + } else { /* Return string.byte result(s). */ + ptrdiff_t i, len = end - start; + if (len > 0) { + TRef trslen = emitir(IRTI(IR_SUB), trend, trstart); + emitir(IRTGI(IR_EQ), trslen, lj_ir_kint(J, (int32_t)len)); + if (J->baseslot + len > LJ_MAX_JSLOTS) + lj_trace_err_info(J, LJ_TRERR_STACKOV); + rd->nres = len; + for (i = 0; i < len; i++) { + TRef tmp = emitir(IRTI(IR_ADD), trstart, lj_ir_kint(J, (int32_t)i)); + tmp = emitir(IRT(IR_STRREF, IRT_PGC), trstr, tmp); + J->base[i] = emitir(IRT(IR_XLOAD, IRT_U8), tmp, IRXLOAD_READONLY); + } + } else { /* Empty range or range underflow: return no results. */ + emitir(IRTGI(IR_LE), trend, trstart); + rd->nres = 0; + } + } +} + +static void LJ_FASTCALL recff_string_char(jit_State *J, RecordFFData *rd) +{ + TRef k255 = lj_ir_kint(J, 255); + BCReg i; + for (i = 0; J->base[i] != 0; i++) { /* Convert char values to strings. */ + TRef tr = lj_opt_narrow_toint(J, J->base[i]); + emitir(IRTGI(IR_ULE), tr, k255); + J->base[i] = emitir(IRT(IR_TOSTR, IRT_STR), tr, IRTOSTR_CHAR); + } + if (i > 1) { /* Concatenate the strings, if there's more than one. */ + TRef hdr = recff_bufhdr(J), tr = hdr; + for (i = 0; J->base[i] != 0; i++) + tr = emitir(IRT(IR_BUFPUT, IRT_PGC), tr, J->base[i]); + J->base[0] = emitir(IRT(IR_BUFSTR, IRT_STR), tr, hdr); + } + UNUSED(rd); +} + +static void LJ_FASTCALL recff_string_rep(jit_State *J, RecordFFData *rd) +{ + TRef str = lj_ir_tostr(J, J->base[0]); + TRef rep = lj_opt_narrow_toint(J, J->base[1]); + TRef hdr, tr, str2 = 0; + if (!tref_isnil(J->base[2])) { + TRef sep = lj_ir_tostr(J, J->base[2]); + int32_t vrep = argv2int(J, &rd->argv[1]); + emitir(IRTGI(vrep > 1 ? IR_GT : IR_LE), rep, lj_ir_kint(J, 1)); + if (vrep > 1) { + TRef hdr2 = recff_bufhdr(J); + TRef tr2 = emitir(IRT(IR_BUFPUT, IRT_PGC), hdr2, sep); + tr2 = emitir(IRT(IR_BUFPUT, IRT_PGC), tr2, str); + str2 = emitir(IRT(IR_BUFSTR, IRT_STR), tr2, hdr2); + } + } + tr = hdr = recff_bufhdr(J); + if (str2) { + tr = emitir(IRT(IR_BUFPUT, IRT_PGC), tr, str); + str = str2; + rep = emitir(IRTI(IR_ADD), rep, lj_ir_kint(J, -1)); + } + tr = lj_ir_call(J, IRCALL_lj_buf_putstr_rep, tr, str, rep); + J->base[0] = emitir(IRT(IR_BUFSTR, IRT_STR), tr, hdr); +} + +static void LJ_FASTCALL recff_string_op(jit_State *J, RecordFFData *rd) +{ + TRef str = lj_ir_tostr(J, J->base[0]); + TRef hdr = recff_bufhdr(J); + TRef tr = lj_ir_call(J, rd->data, hdr, str); + J->base[0] = emitir(IRT(IR_BUFSTR, IRT_STR), tr, hdr); +} + +static void LJ_FASTCALL recff_string_find(jit_State *J, RecordFFData *rd) +{ + TRef trstr = lj_ir_tostr(J, J->base[0]); + TRef trpat = lj_ir_tostr(J, J->base[1]); + TRef trlen = emitir(IRTI(IR_FLOAD), trstr, IRFL_STR_LEN); + TRef tr0 = lj_ir_kint(J, 0); + TRef trstart; + GCstr *str = argv2str(J, &rd->argv[0]); + GCstr *pat = argv2str(J, &rd->argv[1]); + int32_t start; + J->needsnap = 1; + if (tref_isnil(J->base[2])) { + trstart = lj_ir_kint(J, 1); + start = 1; + } else { + trstart = lj_opt_narrow_toint(J, J->base[2]); + start = argv2int(J, &rd->argv[2]); + } + trstart = recff_string_start(J, str, &start, trstart, trlen, tr0); + if ((MSize)start <= str->len) { + emitir(IRTGI(IR_ULE), trstart, trlen); + } else { + emitir(IRTGI(IR_UGT), trstart, trlen); +#if LJ_52 + J->base[0] = TREF_NIL; + return; +#else + trstart = trlen; + start = str->len; +#endif + } + /* Fixed arg or no pattern matching chars? (Specialized to pattern string.) */ + if ((J->base[2] && tref_istruecond(J->base[3])) || + (emitir(IRTG(IR_EQ, IRT_STR), trpat, lj_ir_kstr(J, pat)), + !lj_str_haspattern(pat))) { /* Search for fixed string. */ + TRef trsptr = emitir(IRT(IR_STRREF, IRT_PGC), trstr, trstart); + TRef trpptr = emitir(IRT(IR_STRREF, IRT_PGC), trpat, tr0); + TRef trslen = emitir(IRTI(IR_SUB), trlen, trstart); + TRef trplen = emitir(IRTI(IR_FLOAD), trpat, IRFL_STR_LEN); + TRef tr = lj_ir_call(J, IRCALL_lj_str_find, trsptr, trpptr, trslen, trplen); + TRef trp0 = lj_ir_kkptr(J, NULL); + if (lj_str_find(strdata(str)+(MSize)start, strdata(pat), + str->len-(MSize)start, pat->len)) { + TRef pos; + emitir(IRTG(IR_NE, IRT_PGC), tr, trp0); + pos = emitir(IRTI(IR_SUB), tr, emitir(IRT(IR_STRREF, IRT_PGC), trstr, tr0)); + J->base[0] = emitir(IRTI(IR_ADD), pos, lj_ir_kint(J, 1)); + J->base[1] = emitir(IRTI(IR_ADD), pos, trplen); + rd->nres = 2; + } else { + emitir(IRTG(IR_EQ, IRT_PGC), tr, trp0); + J->base[0] = TREF_NIL; + } + } else { /* Search for pattern. */ + recff_nyiu(J, rd); + return; + } +} + +static void LJ_FASTCALL recff_string_format(jit_State *J, RecordFFData *rd) +{ + TRef trfmt = lj_ir_tostr(J, J->base[0]); + GCstr *fmt = argv2str(J, &rd->argv[0]); + int arg = 1; + TRef hdr, tr; + FormatState fs; + SFormat sf; + /* Specialize to the format string. */ + emitir(IRTG(IR_EQ, IRT_STR), trfmt, lj_ir_kstr(J, fmt)); + tr = hdr = recff_bufhdr(J); + lj_strfmt_init(&fs, strdata(fmt), fmt->len); + while ((sf = lj_strfmt_parse(&fs)) != STRFMT_EOF) { /* Parse format. */ + TRef tra = sf == STRFMT_LIT ? 0 : J->base[arg++]; + TRef trsf = lj_ir_kint(J, (int32_t)sf); + IRCallID id; + switch (STRFMT_TYPE(sf)) { + case STRFMT_LIT: + tr = emitir(IRT(IR_BUFPUT, IRT_PGC), tr, + lj_ir_kstr(J, lj_str_new(J->L, fs.str, fs.len))); + break; + case STRFMT_INT: + id = IRCALL_lj_strfmt_putfnum_int; + handle_int: + if (!tref_isinteger(tra)) + goto handle_num; + if (sf == STRFMT_INT) { /* Shortcut for plain %d. */ + tr = emitir(IRT(IR_BUFPUT, IRT_PGC), tr, + emitir(IRT(IR_TOSTR, IRT_STR), tra, IRTOSTR_INT)); + } else { +#if LJ_HASFFI + tra = emitir(IRT(IR_CONV, IRT_U64), tra, + (IRT_INT|(IRT_U64<<5)|IRCONV_SEXT)); + tr = lj_ir_call(J, IRCALL_lj_strfmt_putfxint, tr, trsf, tra); + lj_needsplit(J); +#else + recff_nyiu(J, rd); /* Don't bother working around this NYI. */ + return; +#endif + } + break; + case STRFMT_UINT: + id = IRCALL_lj_strfmt_putfnum_uint; + goto handle_int; + case STRFMT_NUM: + id = IRCALL_lj_strfmt_putfnum; + handle_num: + tra = lj_ir_tonum(J, tra); + tr = lj_ir_call(J, id, tr, trsf, tra); + if (LJ_SOFTFP32) lj_needsplit(J); + break; + case STRFMT_STR: + if (!tref_isstr(tra)) { + recff_nyiu(J, rd); /* NYI: __tostring and non-string types for %s. */ + return; + } + if (sf == STRFMT_STR) /* Shortcut for plain %s. */ + tr = emitir(IRT(IR_BUFPUT, IRT_PGC), tr, tra); + else if ((sf & STRFMT_T_QUOTED)) + tr = lj_ir_call(J, IRCALL_lj_strfmt_putquoted, tr, tra); + else + tr = lj_ir_call(J, IRCALL_lj_strfmt_putfstr, tr, trsf, tra); + break; + case STRFMT_CHAR: + tra = lj_opt_narrow_toint(J, tra); + if (sf == STRFMT_CHAR) /* Shortcut for plain %c. */ + tr = emitir(IRT(IR_BUFPUT, IRT_PGC), tr, + emitir(IRT(IR_TOSTR, IRT_STR), tra, IRTOSTR_CHAR)); + else + tr = lj_ir_call(J, IRCALL_lj_strfmt_putfchar, tr, trsf, tra); + break; + case STRFMT_PTR: /* NYI */ + case STRFMT_ERR: + default: + recff_nyiu(J, rd); + return; + } + } + J->base[0] = emitir(IRT(IR_BUFSTR, IRT_STR), tr, hdr); +} + +/* -- Table library fast functions ---------------------------------------- */ + +static void LJ_FASTCALL recff_table_insert(jit_State *J, RecordFFData *rd) +{ + RecordIndex ix; + ix.tab = J->base[0]; + ix.val = J->base[1]; + rd->nres = 0; + if (tref_istab(ix.tab) && ix.val) { + if (!J->base[2]) { /* Simple push: t[#t+1] = v */ + TRef trlen = lj_ir_call(J, IRCALL_lj_tab_len, ix.tab); + GCtab *t = tabV(&rd->argv[0]); + ix.key = emitir(IRTI(IR_ADD), trlen, lj_ir_kint(J, 1)); + settabV(J->L, &ix.tabv, t); + setintV(&ix.keyv, lj_tab_len(t) + 1); + ix.idxchain = 0; + lj_record_idx(J, &ix); /* Set new value. */ + } else { /* Complex case: insert in the middle. */ + recff_nyiu(J, rd); + return; + } + } /* else: Interpreter will throw. */ +} + +static void LJ_FASTCALL recff_table_concat(jit_State *J, RecordFFData *rd) +{ + TRef tab = J->base[0]; + if (tref_istab(tab)) { + TRef sep = !tref_isnil(J->base[1]) ? + lj_ir_tostr(J, J->base[1]) : lj_ir_knull(J, IRT_STR); + TRef tri = (J->base[1] && !tref_isnil(J->base[2])) ? + lj_opt_narrow_toint(J, J->base[2]) : lj_ir_kint(J, 1); + TRef tre = (J->base[1] && J->base[2] && !tref_isnil(J->base[3])) ? + lj_opt_narrow_toint(J, J->base[3]) : + lj_ir_call(J, IRCALL_lj_tab_len, tab); + TRef hdr = recff_bufhdr(J); + TRef tr = lj_ir_call(J, IRCALL_lj_buf_puttab, hdr, tab, sep, tri, tre); + emitir(IRTG(IR_NE, IRT_PTR), tr, lj_ir_kptr(J, NULL)); + J->base[0] = emitir(IRT(IR_BUFSTR, IRT_STR), tr, hdr); + } /* else: Interpreter will throw. */ + UNUSED(rd); +} + +static void LJ_FASTCALL recff_table_new(jit_State *J, RecordFFData *rd) +{ + TRef tra = lj_opt_narrow_toint(J, J->base[0]); + TRef trh = lj_opt_narrow_toint(J, J->base[1]); + J->base[0] = lj_ir_call(J, IRCALL_lj_tab_new_ah, tra, trh); + UNUSED(rd); +} + +static void LJ_FASTCALL recff_table_clear(jit_State *J, RecordFFData *rd) +{ + TRef tr = J->base[0]; + if (tref_istab(tr)) { + rd->nres = 0; + lj_ir_call(J, IRCALL_lj_tab_clear, tr); + J->needsnap = 1; + } /* else: Interpreter will throw. */ +} + +/* -- I/O library fast functions ------------------------------------------ */ + +/* Get FILE* for I/O function. Any I/O error aborts recording, so there's +** no need to encode the alternate cases for any of the guards. +*/ +static TRef recff_io_fp(jit_State *J, TRef *udp, int32_t id) +{ + TRef tr, ud, fp; + if (id) { /* io.func() */ +#if LJ_GC64 + /* TODO: fix ARM32 asm_fload(), so we can use this for all archs. */ + ud = lj_ir_ggfload(J, IRT_UDATA, GG_OFS(g.gcroot[id])); +#else + tr = lj_ir_kptr(J, &J2G(J)->gcroot[id]); + ud = emitir(IRT(IR_XLOAD, IRT_UDATA), tr, 0); +#endif + } else { /* fp:method() */ + ud = J->base[0]; + if (!tref_isudata(ud)) + lj_trace_err(J, LJ_TRERR_BADTYPE); + tr = emitir(IRT(IR_FLOAD, IRT_U8), ud, IRFL_UDATA_UDTYPE); + emitir(IRTGI(IR_EQ), tr, lj_ir_kint(J, UDTYPE_IO_FILE)); + } + *udp = ud; + fp = emitir(IRT(IR_FLOAD, IRT_PTR), ud, IRFL_UDATA_FILE); + emitir(IRTG(IR_NE, IRT_PTR), fp, lj_ir_knull(J, IRT_PTR)); + return fp; +} + +static void LJ_FASTCALL recff_io_write(jit_State *J, RecordFFData *rd) +{ + TRef ud, fp = recff_io_fp(J, &ud, rd->data); + TRef zero = lj_ir_kint(J, 0); + TRef one = lj_ir_kint(J, 1); + ptrdiff_t i = rd->data == 0 ? 1 : 0; + for (; J->base[i]; i++) { + TRef str = lj_ir_tostr(J, J->base[i]); + TRef buf = emitir(IRT(IR_STRREF, IRT_PGC), str, zero); + TRef len = emitir(IRTI(IR_FLOAD), str, IRFL_STR_LEN); + if (tref_isk(len) && IR(tref_ref(len))->i == 1) { + IRIns *irs = IR(tref_ref(str)); + TRef tr = (irs->o == IR_TOSTR && irs->op2 == IRTOSTR_CHAR) ? + irs->op1 : + emitir(IRT(IR_XLOAD, IRT_U8), buf, IRXLOAD_READONLY); + tr = lj_ir_call(J, IRCALL_fputc, tr, fp); + if (results_wanted(J) != 0) /* Check result only if not ignored. */ + emitir(IRTGI(IR_NE), tr, lj_ir_kint(J, -1)); + } else { + TRef tr = lj_ir_call(J, IRCALL_fwrite, buf, one, len, fp); + if (results_wanted(J) != 0) /* Check result only if not ignored. */ + emitir(IRTGI(IR_EQ), tr, len); + } + } + J->base[0] = LJ_52 ? ud : TREF_TRUE; +} + +static void LJ_FASTCALL recff_io_flush(jit_State *J, RecordFFData *rd) +{ + TRef ud, fp = recff_io_fp(J, &ud, rd->data); + TRef tr = lj_ir_call(J, IRCALL_fflush, fp); + if (results_wanted(J) != 0) /* Check result only if not ignored. */ + emitir(IRTGI(IR_EQ), tr, lj_ir_kint(J, 0)); + J->base[0] = TREF_TRUE; +} + +/* -- Debug library fast functions ---------------------------------------- */ + +static void LJ_FASTCALL recff_debug_getmetatable(jit_State *J, RecordFFData *rd) +{ + GCtab *mt; + TRef mtref; + TRef tr = J->base[0]; + if (tref_istab(tr)) { + mt = tabref(tabV(&rd->argv[0])->metatable); + mtref = emitir(IRT(IR_FLOAD, IRT_TAB), tr, IRFL_TAB_META); + } else if (tref_isudata(tr)) { + mt = tabref(udataV(&rd->argv[0])->metatable); + mtref = emitir(IRT(IR_FLOAD, IRT_TAB), tr, IRFL_UDATA_META); + } else { + mt = tabref(basemt_obj(J2G(J), &rd->argv[0])); + J->base[0] = mt ? lj_ir_ktab(J, mt) : TREF_NIL; + return; + } + emitir(IRTG(mt ? IR_NE : IR_EQ, IRT_TAB), mtref, lj_ir_knull(J, IRT_TAB)); + J->base[0] = mt ? mtref : TREF_NIL; +} + +/* -- Record calls to fast functions -------------------------------------- */ + +#include "lj_recdef.h" + +static uint32_t recdef_lookup(GCfunc *fn) +{ + if (fn->c.ffid < sizeof(recff_idmap)/sizeof(recff_idmap[0])) + return recff_idmap[fn->c.ffid]; + else + return 0; +} + +/* Record entry to a fast function or C function. */ +void lj_ffrecord_func(jit_State *J) +{ + RecordFFData rd; + uint32_t m = recdef_lookup(J->fn); + rd.data = m & 0xff; + rd.nres = 1; /* Default is one result. */ + rd.argv = J->L->base; + J->base[J->maxslot] = 0; /* Mark end of arguments. */ + (recff_func[m >> 8])(J, &rd); /* Call recff_* handler. */ + if (rd.nres >= 0) { + if (J->postproc == LJ_POST_NONE) J->postproc = LJ_POST_FFRETRY; + lj_record_ret(J, 0, rd.nres); + } +} + +#undef IR +#undef emitir + +#endif diff --git a/lib/LuaJIT/lj_ffrecord.h b/lib/LuaJIT/lj_ffrecord.h new file mode 100644 index 0000000..3b40745 --- /dev/null +++ b/lib/LuaJIT/lj_ffrecord.h @@ -0,0 +1,24 @@ +/* +** Fast function call recorder. +** Copyright (C) 2005-2017 Mike Pall. See Copyright Notice in luajit.h +*/ + +#ifndef _LJ_FFRECORD_H +#define _LJ_FFRECORD_H + +#include "lj_obj.h" +#include "lj_jit.h" + +#if LJ_HASJIT +/* Data used by handlers to record a fast function. */ +typedef struct RecordFFData { + TValue *argv; /* Runtime argument values. */ + ptrdiff_t nres; /* Number of returned results (defaults to 1). */ + uint32_t data; /* Per-ffid auxiliary data (opcode, literal etc.). */ +} RecordFFData; + +LJ_FUNC int32_t lj_ffrecord_select_mode(jit_State *J, TRef tr, TValue *tv); +LJ_FUNC void lj_ffrecord_func(jit_State *J); +#endif + +#endif diff --git a/lib/LuaJIT/lj_ffrecord.o b/lib/LuaJIT/lj_ffrecord.o new file mode 100644 index 0000000000000000000000000000000000000000..da3fc4980a90fb8785419bc146046853c695960f GIT binary patch literal 33592 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 z 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 z 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 +#define frame_pc(f) ((const BCIns *)frame_ftsz(f)) +#define setframe_gc(f, p, tp) (setgcVraw((f)-1, (p), (tp))) +#define setframe_ftsz(f, sz) ((f)->ftsz = (sz)) +#define setframe_pc(f, pc) ((f)->ftsz = (int64_t)(intptr_t)(pc)) +#else +/* One-slot frame info, sufficient for 32 bit PC/GCRef: +** +** base-1 | base base+1 ... +** lo hi | +** [func | PC/delta/ft] | [slots ...] +** ^-- frame | ^-- base ^-- top +** +** Continuation frames: +** +** base-2 base-1 | base base+1 ... +** lo hi lo hi | +** [cont | PC] [func | PC/delta/ft] | [slots ...] +** ^-- frame | ^-- base ^-- top +*/ +#define frame_gc(f) (gcref((f)->fr.func)) +#define frame_ftsz(f) ((ptrdiff_t)(f)->fr.tp.ftsz) +#define frame_pc(f) (mref((f)->fr.tp.pcr, const BCIns)) +#define setframe_gc(f, p, tp) (setgcref((f)->fr.func, (p)), UNUSED(tp)) +#define setframe_ftsz(f, sz) ((f)->fr.tp.ftsz = (int32_t)(sz)) +#define setframe_pc(f, pc) (setmref((f)->fr.tp.pcr, (pc))) +#endif + +#define frame_type(f) (frame_ftsz(f) & FRAME_TYPE) +#define frame_typep(f) (frame_ftsz(f) & FRAME_TYPEP) +#define frame_islua(f) (frame_type(f) == FRAME_LUA) +#define frame_isc(f) (frame_type(f) == FRAME_C) +#define frame_iscont(f) (frame_typep(f) == FRAME_CONT) +#define frame_isvarg(f) (frame_typep(f) == FRAME_VARG) +#define frame_ispcall(f) ((frame_ftsz(f) & 6) == FRAME_PCALL) + +#define frame_func(f) (&frame_gc(f)->fn) +#define frame_delta(f) (frame_ftsz(f) >> 3) +#define frame_sized(f) (frame_ftsz(f) & ~FRAME_TYPEP) + +enum { LJ_CONT_TAILCALL, LJ_CONT_FFI_CALLBACK }; /* Special continuations. */ + +#if LJ_FR2 +#define frame_contpc(f) (frame_pc((f)-2)) +#define frame_contv(f) (((f)-3)->u64) +#else +#define frame_contpc(f) (frame_pc((f)-1)) +#define frame_contv(f) (((f)-1)->u32.lo) +#endif +#if LJ_FR2 +#define frame_contf(f) ((ASMFunction)(uintptr_t)((f)-3)->u64) +#elif LJ_64 +#define frame_contf(f) \ + ((ASMFunction)(void *)((intptr_t)lj_vm_asm_begin + \ + (intptr_t)(int32_t)((f)-1)->u32.lo)) +#else +#define frame_contf(f) ((ASMFunction)gcrefp(((f)-1)->gcr, void)) +#endif +#define frame_iscont_fficb(f) \ + (LJ_HASFFI && frame_contv(f) == LJ_CONT_FFI_CALLBACK) + +#define frame_prevl(f) ((f) - (1+LJ_FR2+bc_a(frame_pc(f)[-1]))) +#define frame_prevd(f) ((TValue *)((char *)(f) - frame_sized(f))) +#define frame_prev(f) (frame_islua(f)?frame_prevl(f):frame_prevd(f)) +/* Note: this macro does not skip over FRAME_VARG. */ + +/* -- C stack frame ------------------------------------------------------- */ + +/* Macros to access and modify the C stack frame chain. */ + +/* These definitions must match with the arch-specific *.dasc files. */ +#if LJ_TARGET_X86 +#if LJ_ABI_WIN +#define CFRAME_OFS_ERRF (19*4) +#define CFRAME_OFS_NRES (18*4) +#define CFRAME_OFS_PREV (17*4) +#define CFRAME_OFS_L (16*4) +#define CFRAME_OFS_SEH (9*4) +#define CFRAME_OFS_PC (6*4) +#define CFRAME_OFS_MULTRES (5*4) +#define CFRAME_SIZE (16*4) +#define CFRAME_SHIFT_MULTRES 0 +#else +#define CFRAME_OFS_ERRF (15*4) +#define CFRAME_OFS_NRES (14*4) +#define CFRAME_OFS_PREV (13*4) +#define CFRAME_OFS_L (12*4) +#define CFRAME_OFS_PC (6*4) +#define CFRAME_OFS_MULTRES (5*4) +#define CFRAME_SIZE (12*4) +#define CFRAME_SHIFT_MULTRES 0 +#endif +#elif LJ_TARGET_X64 +#if LJ_ABI_WIN +#define CFRAME_OFS_PREV (13*8) +#if LJ_GC64 +#define CFRAME_OFS_PC (12*8) +#define CFRAME_OFS_L (11*8) +#define CFRAME_OFS_ERRF (21*4) +#define CFRAME_OFS_NRES (20*4) +#define CFRAME_OFS_MULTRES (8*4) +#else +#define CFRAME_OFS_PC (25*4) +#define CFRAME_OFS_L (24*4) +#define CFRAME_OFS_ERRF (23*4) +#define CFRAME_OFS_NRES (22*4) +#define CFRAME_OFS_MULTRES (21*4) +#endif +#define CFRAME_SIZE (10*8) +#define CFRAME_SIZE_JIT (CFRAME_SIZE + 9*16 + 4*8) +#define CFRAME_SHIFT_MULTRES 0 +#else +#define CFRAME_OFS_PREV (4*8) +#if LJ_GC64 +#define CFRAME_OFS_PC (3*8) +#define CFRAME_OFS_L (2*8) +#define CFRAME_OFS_ERRF (3*4) +#define CFRAME_OFS_NRES (2*4) +#define CFRAME_OFS_MULTRES (0*4) +#else +#define CFRAME_OFS_PC (7*4) +#define CFRAME_OFS_L (6*4) +#define CFRAME_OFS_ERRF (5*4) +#define CFRAME_OFS_NRES (4*4) +#define CFRAME_OFS_MULTRES (1*4) +#endif +#if LJ_NO_UNWIND +#define CFRAME_SIZE (12*8) +#else +#define CFRAME_SIZE (10*8) +#endif +#define CFRAME_SIZE_JIT (CFRAME_SIZE + 16) +#define CFRAME_SHIFT_MULTRES 0 +#endif +#elif LJ_TARGET_ARM +#define CFRAME_OFS_ERRF 24 +#define CFRAME_OFS_NRES 20 +#define CFRAME_OFS_PREV 16 +#define CFRAME_OFS_L 12 +#define CFRAME_OFS_PC 8 +#define CFRAME_OFS_MULTRES 4 +#if LJ_ARCH_HASFPU +#define CFRAME_SIZE 128 +#else +#define CFRAME_SIZE 64 +#endif +#define CFRAME_SHIFT_MULTRES 3 +#elif LJ_TARGET_ARM64 +#define CFRAME_OFS_ERRF 196 +#define CFRAME_OFS_NRES 200 +#define CFRAME_OFS_PREV 160 +#define CFRAME_OFS_L 176 +#define CFRAME_OFS_PC 168 +#define CFRAME_OFS_MULTRES 192 +#define CFRAME_SIZE 208 +#define CFRAME_SHIFT_MULTRES 3 +#elif LJ_TARGET_PPC +#if LJ_TARGET_XBOX360 +#define CFRAME_OFS_ERRF 424 +#define CFRAME_OFS_NRES 420 +#define CFRAME_OFS_PREV 400 +#define CFRAME_OFS_L 416 +#define CFRAME_OFS_PC 412 +#define CFRAME_OFS_MULTRES 408 +#define CFRAME_SIZE 384 +#define CFRAME_SHIFT_MULTRES 3 +#elif LJ_ARCH_PPC32ON64 +#define CFRAME_OFS_ERRF 472 +#define CFRAME_OFS_NRES 468 +#define CFRAME_OFS_PREV 448 +#define CFRAME_OFS_L 464 +#define CFRAME_OFS_PC 460 +#define CFRAME_OFS_MULTRES 456 +#define CFRAME_SIZE 400 +#define CFRAME_SHIFT_MULTRES 3 +#else +#define CFRAME_OFS_ERRF 48 +#define CFRAME_OFS_NRES 44 +#define CFRAME_OFS_PREV 40 +#define CFRAME_OFS_L 36 +#define CFRAME_OFS_PC 32 +#define CFRAME_OFS_MULTRES 28 +#define CFRAME_SIZE (LJ_ARCH_HASFPU ? 272 : 128) +#define CFRAME_SHIFT_MULTRES 3 +#endif +#elif LJ_TARGET_MIPS32 +#if LJ_ARCH_HASFPU +#define CFRAME_OFS_ERRF 124 +#define CFRAME_OFS_NRES 120 +#define CFRAME_OFS_PREV 116 +#define CFRAME_OFS_L 112 +#define CFRAME_SIZE 112 +#else +#define CFRAME_OFS_ERRF 76 +#define CFRAME_OFS_NRES 72 +#define CFRAME_OFS_PREV 68 +#define CFRAME_OFS_L 64 +#define CFRAME_SIZE 64 +#endif +#define CFRAME_OFS_PC 20 +#define CFRAME_OFS_MULTRES 16 +#define CFRAME_SHIFT_MULTRES 3 +#elif LJ_TARGET_MIPS64 +#if LJ_ARCH_HASFPU +#define CFRAME_OFS_ERRF 188 +#define CFRAME_OFS_NRES 184 +#define CFRAME_OFS_PREV 176 +#define CFRAME_OFS_L 168 +#define CFRAME_OFS_PC 160 +#define CFRAME_SIZE 192 +#else +#define CFRAME_OFS_ERRF 124 +#define CFRAME_OFS_NRES 120 +#define CFRAME_OFS_PREV 112 +#define CFRAME_OFS_L 104 +#define CFRAME_OFS_PC 96 +#define CFRAME_SIZE 128 +#endif +#define CFRAME_OFS_MULTRES 0 +#define CFRAME_SHIFT_MULTRES 3 +#else +#error "Missing CFRAME_* definitions for this architecture" +#endif + +#ifndef CFRAME_SIZE_JIT +#define CFRAME_SIZE_JIT CFRAME_SIZE +#endif + +#define CFRAME_RESUME 1 +#define CFRAME_UNWIND_FF 2 /* Only used in unwinder. */ +#define CFRAME_RAWMASK (~(intptr_t)(CFRAME_RESUME|CFRAME_UNWIND_FF)) + +#define cframe_errfunc(cf) (*(int32_t *)(((char *)(cf))+CFRAME_OFS_ERRF)) +#define cframe_nres(cf) (*(int32_t *)(((char *)(cf))+CFRAME_OFS_NRES)) +#define cframe_prev(cf) (*(void **)(((char *)(cf))+CFRAME_OFS_PREV)) +#define cframe_multres(cf) (*(uint32_t *)(((char *)(cf))+CFRAME_OFS_MULTRES)) +#define cframe_multres_n(cf) (cframe_multres((cf)) >> CFRAME_SHIFT_MULTRES) +#define cframe_L(cf) \ + (&gcref(*(GCRef *)(((char *)(cf))+CFRAME_OFS_L))->th) +#define cframe_pc(cf) \ + (mref(*(MRef *)(((char *)(cf))+CFRAME_OFS_PC), const BCIns)) +#define setcframe_L(cf, L) \ + (setmref(*(MRef *)(((char *)(cf))+CFRAME_OFS_L), (L))) +#define setcframe_pc(cf, pc) \ + (setmref(*(MRef *)(((char *)(cf))+CFRAME_OFS_PC), (pc))) +#define cframe_canyield(cf) ((intptr_t)(cf) & CFRAME_RESUME) +#define cframe_unwind_ff(cf) ((intptr_t)(cf) & CFRAME_UNWIND_FF) +#define cframe_raw(cf) ((void *)((intptr_t)(cf) & CFRAME_RAWMASK)) +#define cframe_Lpc(L) cframe_pc(cframe_raw(L->cframe)) + +#endif diff --git a/lib/LuaJIT/lj_func.c b/lib/LuaJIT/lj_func.c new file mode 100644 index 0000000..639dad8 --- /dev/null +++ b/lib/LuaJIT/lj_func.c @@ -0,0 +1,187 @@ +/* +** Function handling (prototypes, functions and upvalues). +** Copyright (C) 2005-2017 Mike Pall. See Copyright Notice in luajit.h +** +** Portions taken verbatim or adapted from the Lua interpreter. +** Copyright (C) 1994-2008 Lua.org, PUC-Rio. See Copyright Notice in lua.h +*/ + +#define lj_func_c +#define LUA_CORE + +#include "lj_obj.h" +#include "lj_gc.h" +#include "lj_func.h" +#include "lj_trace.h" +#include "lj_vm.h" + +/* -- Prototypes ---------------------------------------------------------- */ + +void LJ_FASTCALL lj_func_freeproto(global_State *g, GCproto *pt) +{ + lj_mem_free(g, pt, pt->sizept); +} + +/* -- Upvalues ------------------------------------------------------------ */ + +static void unlinkuv(GCupval *uv) +{ + lua_assert(uvprev(uvnext(uv)) == uv && uvnext(uvprev(uv)) == uv); + setgcrefr(uvnext(uv)->prev, uv->prev); + setgcrefr(uvprev(uv)->next, uv->next); +} + +/* Find existing open upvalue for a stack slot or create a new one. */ +static GCupval *func_finduv(lua_State *L, TValue *slot) +{ + global_State *g = G(L); + GCRef *pp = &L->openupval; + GCupval *p; + GCupval *uv; + /* Search the sorted list of open upvalues. */ + while (gcref(*pp) != NULL && uvval((p = gco2uv(gcref(*pp)))) >= slot) { + lua_assert(!p->closed && uvval(p) != &p->tv); + if (uvval(p) == slot) { /* Found open upvalue pointing to same slot? */ + if (isdead(g, obj2gco(p))) /* Resurrect it, if it's dead. */ + flipwhite(obj2gco(p)); + return p; + } + pp = &p->nextgc; + } + /* No matching upvalue found. Create a new one. */ + uv = lj_mem_newt(L, sizeof(GCupval), GCupval); + newwhite(g, uv); + uv->gct = ~LJ_TUPVAL; + uv->closed = 0; /* Still open. */ + setmref(uv->v, slot); /* Pointing to the stack slot. */ + /* NOBARRIER: The GCupval is new (marked white) and open. */ + setgcrefr(uv->nextgc, *pp); /* Insert into sorted list of open upvalues. */ + setgcref(*pp, obj2gco(uv)); + setgcref(uv->prev, obj2gco(&g->uvhead)); /* Insert into GC list, too. */ + setgcrefr(uv->next, g->uvhead.next); + setgcref(uvnext(uv)->prev, obj2gco(uv)); + setgcref(g->uvhead.next, obj2gco(uv)); + lua_assert(uvprev(uvnext(uv)) == uv && uvnext(uvprev(uv)) == uv); + return uv; +} + +/* Create an empty and closed upvalue. */ +static GCupval *func_emptyuv(lua_State *L) +{ + GCupval *uv = (GCupval *)lj_mem_newgco(L, sizeof(GCupval)); + uv->gct = ~LJ_TUPVAL; + uv->closed = 1; + setnilV(&uv->tv); + setmref(uv->v, &uv->tv); + return uv; +} + +/* Close all open upvalues pointing to some stack level or above. */ +void LJ_FASTCALL lj_func_closeuv(lua_State *L, TValue *level) +{ + GCupval *uv; + global_State *g = G(L); + while (gcref(L->openupval) != NULL && + uvval((uv = gco2uv(gcref(L->openupval)))) >= level) { + GCobj *o = obj2gco(uv); + lua_assert(!isblack(o) && !uv->closed && uvval(uv) != &uv->tv); + setgcrefr(L->openupval, uv->nextgc); /* No longer in open list. */ + if (isdead(g, o)) { + lj_func_freeuv(g, uv); + } else { + unlinkuv(uv); + lj_gc_closeuv(g, uv); + } + } +} + +void LJ_FASTCALL lj_func_freeuv(global_State *g, GCupval *uv) +{ + if (!uv->closed) + unlinkuv(uv); + lj_mem_freet(g, uv); +} + +/* -- Functions (closures) ------------------------------------------------ */ + +GCfunc *lj_func_newC(lua_State *L, MSize nelems, GCtab *env) +{ + GCfunc *fn = (GCfunc *)lj_mem_newgco(L, sizeCfunc(nelems)); + fn->c.gct = ~LJ_TFUNC; + fn->c.ffid = FF_C; + fn->c.nupvalues = (uint8_t)nelems; + /* NOBARRIER: The GCfunc is new (marked white). */ + setmref(fn->c.pc, &G(L)->bc_cfunc_ext); + setgcref(fn->c.env, obj2gco(env)); + return fn; +} + +static GCfunc *func_newL(lua_State *L, GCproto *pt, GCtab *env) +{ + uint32_t count; + GCfunc *fn = (GCfunc *)lj_mem_newgco(L, sizeLfunc((MSize)pt->sizeuv)); + fn->l.gct = ~LJ_TFUNC; + fn->l.ffid = FF_LUA; + fn->l.nupvalues = 0; /* Set to zero until upvalues are initialized. */ + /* NOBARRIER: Really a setgcref. But the GCfunc is new (marked white). */ + setmref(fn->l.pc, proto_bc(pt)); + setgcref(fn->l.env, obj2gco(env)); + /* Saturating 3 bit counter (0..7) for created closures. */ + count = (uint32_t)pt->flags + PROTO_CLCOUNT; + pt->flags = (uint8_t)(count - ((count >> PROTO_CLC_BITS) & PROTO_CLCOUNT)); + return fn; +} + +/* Create a new Lua function with empty upvalues. */ +GCfunc *lj_func_newL_empty(lua_State *L, GCproto *pt, GCtab *env) +{ + GCfunc *fn = func_newL(L, pt, env); + MSize i, nuv = pt->sizeuv; + /* NOBARRIER: The GCfunc is new (marked white). */ + for (i = 0; i < nuv; i++) { + GCupval *uv = func_emptyuv(L); + int32_t v = proto_uv(pt)[i]; + uv->immutable = ((v / PROTO_UV_IMMUTABLE) & 1); + uv->dhash = (uint32_t)(uintptr_t)pt ^ (v << 24); + setgcref(fn->l.uvptr[i], obj2gco(uv)); + } + fn->l.nupvalues = (uint8_t)nuv; + return fn; +} + +/* Do a GC check and create a new Lua function with inherited upvalues. */ +GCfunc *lj_func_newL_gc(lua_State *L, GCproto *pt, GCfuncL *parent) +{ + GCfunc *fn; + GCRef *puv; + MSize i, nuv; + TValue *base; + lj_gc_check_fixtop(L); + fn = func_newL(L, pt, tabref(parent->env)); + /* NOBARRIER: The GCfunc is new (marked white). */ + puv = parent->uvptr; + nuv = pt->sizeuv; + base = L->base; + for (i = 0; i < nuv; i++) { + uint32_t v = proto_uv(pt)[i]; + GCupval *uv; + if ((v & PROTO_UV_LOCAL)) { + uv = func_finduv(L, base + (v & 0xff)); + uv->immutable = ((v / PROTO_UV_IMMUTABLE) & 1); + uv->dhash = (uint32_t)(uintptr_t)mref(parent->pc, char) ^ (v << 24); + } else { + uv = &gcref(puv[v])->uv; + } + setgcref(fn->l.uvptr[i], obj2gco(uv)); + } + fn->l.nupvalues = (uint8_t)nuv; + return fn; +} + +void LJ_FASTCALL lj_func_free(global_State *g, GCfunc *fn) +{ + MSize size = isluafunc(fn) ? sizeLfunc((MSize)fn->l.nupvalues) : + sizeCfunc((MSize)fn->c.nupvalues); + lj_mem_free(g, fn, size); +} + diff --git a/lib/LuaJIT/lj_func.h b/lib/LuaJIT/lj_func.h new file mode 100644 index 0000000..901751b --- /dev/null +++ b/lib/LuaJIT/lj_func.h @@ -0,0 +1,24 @@ +/* +** Function handling (prototypes, functions and upvalues). +** Copyright (C) 2005-2017 Mike Pall. See Copyright Notice in luajit.h +*/ + +#ifndef _LJ_FUNC_H +#define _LJ_FUNC_H + +#include "lj_obj.h" + +/* Prototypes. */ +LJ_FUNC void LJ_FASTCALL lj_func_freeproto(global_State *g, GCproto *pt); + +/* Upvalues. */ +LJ_FUNCA void LJ_FASTCALL lj_func_closeuv(lua_State *L, TValue *level); +LJ_FUNC void LJ_FASTCALL lj_func_freeuv(global_State *g, GCupval *uv); + +/* Functions (closures). */ +LJ_FUNC GCfunc *lj_func_newC(lua_State *L, MSize nelems, GCtab *env); +LJ_FUNC GCfunc *lj_func_newL_empty(lua_State *L, GCproto *pt, GCtab *env); +LJ_FUNCA GCfunc *lj_func_newL_gc(lua_State *L, GCproto *pt, GCfuncL *parent); +LJ_FUNC void LJ_FASTCALL lj_func_free(global_State *g, GCfunc *c); + +#endif diff --git a/lib/LuaJIT/lj_func.o b/lib/LuaJIT/lj_func.o new file mode 100644 index 0000000000000000000000000000000000000000..2bb99a349a1069c8868e24712cf5dce4e6bdfdfa GIT binary patch literal 3400 zcmbtXeQXnD7=Qb*?S$p-AT_CBai<`5T~J1H52xq^_OroB+?Gh39rp;k@N{^{3nNgMOycH`uVl0KE>_WIVn{7iDopo zjTXswe%&@nH5x>Xp3Ba}<6fb1(2S}y1?epadNXV^c#H@)G}c!=m&)HCe5;i7l^gY( zv9+8k997HpeUe)>-f;WQpK*hWY*OFc`^8ZDw)eW`zayFnBRs4!aV(SLP3 zT<<*L=sTHnqtYL8p3I_{ao^;Qol5KE$ldX<0zDyF@$y9ol>tI9|BKD^W@f`p39^CK z{bjm6!$H@fT_r}C^QSw?)I$@0zSbka`xMl7NuEff)?CneRt3H-^pf!bPAG} z(*aq4(%SV|n?k>@^JTS6C$Z=I^C|W~rN88Ymaw2|CWK*(ugVi@)wlaT7DgeS0zu1QhHj0{V5Z7WWtS3xabmrY zZ;&pSDTje1&<;+eF4;)R)k#Uvk3$BkQ6Xz|L8S*|4$7ZBaXXt;4LE3-9j+jqR*+bl zu}y?Hn}^1P!NY|cRpE&beY^iVVRSNJ^mCfASLPZs&rHm@gi2zB#9;N8{i}q*Z$KZK zs~Aoh$ad{CHqdv$x1iBhxk{r=vPXl+l14SzjU|XVnh2vU9?bxiVCKjtV4@kQ3CxoM zbU@UvI5YzdpiD6xa-Jyx6&$}}xMV8Q357aks&a~PWD0J8#y~-Z4x5LsRbzuZ{-b;R zI4ZM-cxYa~>_~l} zFF0V-&bjyEBj!-dip+^z4*}H*UPqJvFrKo$2Qx17%}4sfyX!l@NPKe+8P~J~h0M*6 zhT7UqlBZ#7^E#>8>+@Ct3W6UE6x-`B65D`iTT@=fjlh^?z$(leXd$i&FAm!SyjyoP z@Y0*S8_o*P5fQEeOz(cSdSKno_D6B8;l;yfJM0MXlJ2x0GR#4~auHX6nV-6)eiZZ*;1Ajy2W%^rb5*|*%(1Vn%%2bT|0JmDB=gzj z^c>6nG7#c`f6IvjJQWM(WH0bz&Xg%RNNKUUd3KPpd*JxiEESx+XkUuq=@EEV+ifJo zt}Ko`#qcD^q&Q~JwzV4_uN^psE$NDt_SXZnI_wCUHp3JLwPb`+~Nu=^fv@_8g z>w66~+M|(z)1rRv((+up=nKO{VqMA9{-scR6wesEC)U>4nJ|ywyEhd}M&d6Iq!P(R z1I&luR~7{sbRRso@K*`s27(}nm&0S>0oLDQ{nPirU5v7*zpB8`)dGBV0WQHF1;@ok zW}a2^ZyvY!)mnsG{7M($7LSe>;TDfRE5a=viL5RwAB#u3i*Sn<#M`^SE7i6K)>Kb! z?XY$|vCcMcDmIWJ-o0(9HsamW+e^IBL|0d=8?f#~D&~dn^^?7+w&*_MHPvGsk$6vA zSB$0izZVHSVCuzR06$t=7#<3wxL5cMSk8Wy<^NKFEpChL{{|CyT-yKu literal 0 HcmV?d00001 diff --git a/lib/LuaJIT/lj_func_dyn.o b/lib/LuaJIT/lj_func_dyn.o new file mode 100644 index 0000000000000000000000000000000000000000..2bb99a349a1069c8868e24712cf5dce4e6bdfdfa GIT binary patch literal 3400 zcmbtXeQXnD7=Qb*?S$p-AT_CBai<`5T~J1H52xq^_OroB+?Gh39rp;k@N{^{3nNgMOycH`uVl0KE>_WIVn{7iDopo zjTXswe%&@nH5x>Xp3Ba}<6fb1(2S}y1?epadNXV^c#H@)G}c!=m&)HCe5;i7l^gY( zv9+8k997HpeUe)>-f;WQpK*hWY*OFc`^8ZDw)eW`zayFnBRs4!aV(SLP3 zT<<*L=sTHnqtYL8p3I_{ao^;Qol5KE$ldX<0zDyF@$y9ol>tI9|BKD^W@f`p39^CK z{bjm6!$H@fT_r}C^QSw?)I$@0zSbka`xMl7NuEff)?CneRt3H-^pf!bPAG} z(*aq4(%SV|n?k>@^JTS6C$Z=I^C|W~rN88Ymaw2|CWK*(ugVi@)wlaT7DgeS0zu1QhHj0{V5Z7WWtS3xabmrY zZ;&pSDTje1&<;+eF4;)R)k#Uvk3$BkQ6Xz|L8S*|4$7ZBaXXt;4LE3-9j+jqR*+bl zu}y?Hn}^1P!NY|cRpE&beY^iVVRSNJ^mCfASLPZs&rHm@gi2zB#9;N8{i}q*Z$KZK zs~Aoh$ad{CHqdv$x1iBhxk{r=vPXl+l14SzjU|XVnh2vU9?bxiVCKjtV4@kQ3CxoM zbU@UvI5YzdpiD6xa-Jyx6&$}}xMV8Q357aks&a~PWD0J8#y~-Z4x5LsRbzuZ{-b;R zI4ZM-cxYa~>_~l} zFF0V-&bjyEBj!-dip+^z4*}H*UPqJvFrKo$2Qx17%}4sfyX!l@NPKe+8P~J~h0M*6 zhT7UqlBZ#7^E#>8>+@Ct3W6UE6x-`B65D`iTT@=fjlh^?z$(leXd$i&FAm!SyjyoP z@Y0*S8_o*P5fQEeOz(cSdSKno_D6B8;l;yfJM0MXlJ2x0GR#4~auHX6nV-6)eiZZ*;1Ajy2W%^rb5*|*%(1Vn%%2bT|0JmDB=gzj z^c>6nG7#c`f6IvjJQWM(WH0bz&Xg%RNNKUUd3KPpd*JxiEESx+XkUuq=@EEV+ifJo zt}Ko`#qcD^q&Q~JwzV4_uN^psE$NDt_SXZnI_wCUHp3JLwPb`+~Nu=^fv@_8g z>w66~+M|(z)1rRv((+up=nKO{VqMA9{-scR6wesEC)U>4nJ|ywyEhd}M&d6Iq!P(R z1I&luR~7{sbRRso@K*`s27(}nm&0S>0oLDQ{nPirU5v7*zpB8`)dGBV0WQHF1;@ok zW}a2^ZyvY!)mnsG{7M($7LSe>;TDfRE5a=viL5RwAB#u3i*Sn<#M`^SE7i6K)>Kb! z?XY$|vCcMcDmIWJ-o0(9HsamW+e^IBL|0d=8?f#~D&~dn^^?7+w&*_MHPvGsk$6vA zSB$0izZVHSVCuzR06$t=7#<3wxL5cMSk8Wy<^NKFEpChL{{|CyT-yKu literal 0 HcmV?d00001 diff --git a/lib/LuaJIT/lj_gc.c b/lib/LuaJIT/lj_gc.c new file mode 100644 index 0000000..2aaf5b2 --- /dev/null +++ b/lib/LuaJIT/lj_gc.c @@ -0,0 +1,854 @@ +/* +** Garbage collector. +** Copyright (C) 2005-2017 Mike Pall. See Copyright Notice in luajit.h +** +** Major portions taken verbatim or adapted from the Lua interpreter. +** Copyright (C) 1994-2008 Lua.org, PUC-Rio. See Copyright Notice in lua.h +*/ + +#define lj_gc_c +#define LUA_CORE + +#include "lj_obj.h" +#include "lj_gc.h" +#include "lj_err.h" +#include "lj_buf.h" +#include "lj_str.h" +#include "lj_tab.h" +#include "lj_func.h" +#include "lj_udata.h" +#include "lj_meta.h" +#include "lj_state.h" +#include "lj_frame.h" +#if LJ_HASFFI +#include "lj_ctype.h" +#include "lj_cdata.h" +#endif +#include "lj_trace.h" +#include "lj_vm.h" + +#define GCSTEPSIZE 1024u +#define GCSWEEPMAX 40 +#define GCSWEEPCOST 10 +#define GCFINALIZECOST 100 + +/* Macros to set GCobj colors and flags. */ +#define white2gray(x) ((x)->gch.marked &= (uint8_t)~LJ_GC_WHITES) +#define gray2black(x) ((x)->gch.marked |= LJ_GC_BLACK) +#define isfinalized(u) ((u)->marked & LJ_GC_FINALIZED) + +/* -- Mark phase ---------------------------------------------------------- */ + +/* Mark a TValue (if needed). */ +#define gc_marktv(g, tv) \ + { lua_assert(!tvisgcv(tv) || (~itype(tv) == gcval(tv)->gch.gct)); \ + if (tviswhite(tv)) gc_mark(g, gcV(tv)); } + +/* Mark a GCobj (if needed). */ +#define gc_markobj(g, o) \ + { if (iswhite(obj2gco(o))) gc_mark(g, obj2gco(o)); } + +/* Mark a string object. */ +#define gc_mark_str(s) ((s)->marked &= (uint8_t)~LJ_GC_WHITES) + +/* Mark a white GCobj. */ +static void gc_mark(global_State *g, GCobj *o) +{ + int gct = o->gch.gct; + lua_assert(iswhite(o) && !isdead(g, o)); + white2gray(o); + if (LJ_UNLIKELY(gct == ~LJ_TUDATA)) { + GCtab *mt = tabref(gco2ud(o)->metatable); + gray2black(o); /* Userdata are never gray. */ + if (mt) gc_markobj(g, mt); + gc_markobj(g, tabref(gco2ud(o)->env)); + } else if (LJ_UNLIKELY(gct == ~LJ_TUPVAL)) { + GCupval *uv = gco2uv(o); + gc_marktv(g, uvval(uv)); + if (uv->closed) + gray2black(o); /* Closed upvalues are never gray. */ + } else if (gct != ~LJ_TSTR && gct != ~LJ_TCDATA) { + lua_assert(gct == ~LJ_TFUNC || gct == ~LJ_TTAB || + gct == ~LJ_TTHREAD || gct == ~LJ_TPROTO || gct == ~LJ_TTRACE); + setgcrefr(o->gch.gclist, g->gc.gray); + setgcref(g->gc.gray, o); + } +} + +/* Mark GC roots. */ +static void gc_mark_gcroot(global_State *g) +{ + ptrdiff_t i; + for (i = 0; i < GCROOT_MAX; i++) + if (gcref(g->gcroot[i]) != NULL) + gc_markobj(g, gcref(g->gcroot[i])); +} + +/* Start a GC cycle and mark the root set. */ +static void gc_mark_start(global_State *g) +{ + setgcrefnull(g->gc.gray); + setgcrefnull(g->gc.grayagain); + setgcrefnull(g->gc.weak); + gc_markobj(g, mainthread(g)); + gc_markobj(g, tabref(mainthread(g)->env)); + gc_marktv(g, &g->registrytv); + gc_mark_gcroot(g); + g->gc.state = GCSpropagate; +} + +/* Mark open upvalues. */ +static void gc_mark_uv(global_State *g) +{ + GCupval *uv; + for (uv = uvnext(&g->uvhead); uv != &g->uvhead; uv = uvnext(uv)) { + lua_assert(uvprev(uvnext(uv)) == uv && uvnext(uvprev(uv)) == uv); + if (isgray(obj2gco(uv))) + gc_marktv(g, uvval(uv)); + } +} + +/* Mark userdata in mmudata list. */ +static void gc_mark_mmudata(global_State *g) +{ + GCobj *root = gcref(g->gc.mmudata); + GCobj *u = root; + if (u) { + do { + u = gcnext(u); + makewhite(g, u); /* Could be from previous GC. */ + gc_mark(g, u); + } while (u != root); + } +} + +/* Separate userdata objects to be finalized to mmudata list. */ +size_t lj_gc_separateudata(global_State *g, int all) +{ + size_t m = 0; + GCRef *p = &mainthread(g)->nextgc; + GCobj *o; + while ((o = gcref(*p)) != NULL) { + if (!(iswhite(o) || all) || isfinalized(gco2ud(o))) { + p = &o->gch.nextgc; /* Nothing to do. */ + } else if (!lj_meta_fastg(g, tabref(gco2ud(o)->metatable), MM_gc)) { + markfinalized(o); /* Done, as there's no __gc metamethod. */ + p = &o->gch.nextgc; + } else { /* Otherwise move userdata to be finalized to mmudata list. */ + m += sizeudata(gco2ud(o)); + markfinalized(o); + *p = o->gch.nextgc; + if (gcref(g->gc.mmudata)) { /* Link to end of mmudata list. */ + GCobj *root = gcref(g->gc.mmudata); + setgcrefr(o->gch.nextgc, root->gch.nextgc); + setgcref(root->gch.nextgc, o); + setgcref(g->gc.mmudata, o); + } else { /* Create circular list. */ + setgcref(o->gch.nextgc, o); + setgcref(g->gc.mmudata, o); + } + } + } + return m; +} + +/* -- Propagation phase --------------------------------------------------- */ + +/* Traverse a table. */ +static int gc_traverse_tab(global_State *g, GCtab *t) +{ + int weak = 0; + cTValue *mode; + GCtab *mt = tabref(t->metatable); + if (mt) + gc_markobj(g, mt); + mode = lj_meta_fastg(g, mt, MM_mode); + if (mode && tvisstr(mode)) { /* Valid __mode field? */ + const char *modestr = strVdata(mode); + int c; + while ((c = *modestr++)) { + if (c == 'k') weak |= LJ_GC_WEAKKEY; + else if (c == 'v') weak |= LJ_GC_WEAKVAL; + } + if (weak) { /* Weak tables are cleared in the atomic phase. */ +#if LJ_HASFFI + CTState *cts = ctype_ctsG(g); + if (cts && cts->finalizer == t) { + weak = (int)(~0u & ~LJ_GC_WEAKVAL); + } else +#endif + { + t->marked = (uint8_t)((t->marked & ~LJ_GC_WEAK) | weak); + setgcrefr(t->gclist, g->gc.weak); + setgcref(g->gc.weak, obj2gco(t)); + } + } + } + if (weak == LJ_GC_WEAK) /* Nothing to mark if both keys/values are weak. */ + return 1; + if (!(weak & LJ_GC_WEAKVAL)) { /* Mark array part. */ + MSize i, asize = t->asize; + for (i = 0; i < asize; i++) + gc_marktv(g, arrayslot(t, i)); + } + if (t->hmask > 0) { /* Mark hash part. */ + Node *node = noderef(t->node); + MSize i, hmask = t->hmask; + for (i = 0; i <= hmask; i++) { + Node *n = &node[i]; + if (!tvisnil(&n->val)) { /* Mark non-empty slot. */ + lua_assert(!tvisnil(&n->key)); + if (!(weak & LJ_GC_WEAKKEY)) gc_marktv(g, &n->key); + if (!(weak & LJ_GC_WEAKVAL)) gc_marktv(g, &n->val); + } + } + } + return weak; +} + +/* Traverse a function. */ +static void gc_traverse_func(global_State *g, GCfunc *fn) +{ + gc_markobj(g, tabref(fn->c.env)); + if (isluafunc(fn)) { + uint32_t i; + lua_assert(fn->l.nupvalues <= funcproto(fn)->sizeuv); + gc_markobj(g, funcproto(fn)); + for (i = 0; i < fn->l.nupvalues; i++) /* Mark Lua function upvalues. */ + gc_markobj(g, &gcref(fn->l.uvptr[i])->uv); + } else { + uint32_t i; + for (i = 0; i < fn->c.nupvalues; i++) /* Mark C function upvalues. */ + gc_marktv(g, &fn->c.upvalue[i]); + } +} + +#if LJ_HASJIT +/* Mark a trace. */ +static void gc_marktrace(global_State *g, TraceNo traceno) +{ + GCobj *o = obj2gco(traceref(G2J(g), traceno)); + lua_assert(traceno != G2J(g)->cur.traceno); + if (iswhite(o)) { + white2gray(o); + setgcrefr(o->gch.gclist, g->gc.gray); + setgcref(g->gc.gray, o); + } +} + +/* Traverse a trace. */ +static void gc_traverse_trace(global_State *g, GCtrace *T) +{ + IRRef ref; + if (T->traceno == 0) return; + for (ref = T->nk; ref < REF_TRUE; ref++) { + IRIns *ir = &T->ir[ref]; + if (ir->o == IR_KGC) + gc_markobj(g, ir_kgc(ir)); + if (irt_is64(ir->t) && ir->o != IR_KNULL) + ref++; + } + if (T->link) gc_marktrace(g, T->link); + if (T->nextroot) gc_marktrace(g, T->nextroot); + if (T->nextside) gc_marktrace(g, T->nextside); + gc_markobj(g, gcref(T->startpt)); +} + +/* The current trace is a GC root while not anchored in the prototype (yet). */ +#define gc_traverse_curtrace(g) gc_traverse_trace(g, &G2J(g)->cur) +#else +#define gc_traverse_curtrace(g) UNUSED(g) +#endif + +/* Traverse a prototype. */ +static void gc_traverse_proto(global_State *g, GCproto *pt) +{ + ptrdiff_t i; + gc_mark_str(proto_chunkname(pt)); + for (i = -(ptrdiff_t)pt->sizekgc; i < 0; i++) /* Mark collectable consts. */ + gc_markobj(g, proto_kgc(pt, i)); +#if LJ_HASJIT + if (pt->trace) gc_marktrace(g, pt->trace); +#endif +} + +/* Traverse the frame structure of a stack. */ +static MSize gc_traverse_frames(global_State *g, lua_State *th) +{ + TValue *frame, *top = th->top-1, *bot = tvref(th->stack); + /* Note: extra vararg frame not skipped, marks function twice (harmless). */ + for (frame = th->base-1; frame > bot+LJ_FR2; frame = frame_prev(frame)) { + GCfunc *fn = frame_func(frame); + TValue *ftop = frame; + if (isluafunc(fn)) ftop += funcproto(fn)->framesize; + if (ftop > top) top = ftop; + if (!LJ_FR2) gc_markobj(g, fn); /* Need to mark hidden function (or L). */ + } + top++; /* Correct bias of -1 (frame == base-1). */ + if (top > tvref(th->maxstack)) top = tvref(th->maxstack); + return (MSize)(top - bot); /* Return minimum needed stack size. */ +} + +/* Traverse a thread object. */ +static void gc_traverse_thread(global_State *g, lua_State *th) +{ + TValue *o, *top = th->top; + for (o = tvref(th->stack)+1+LJ_FR2; o < top; o++) + gc_marktv(g, o); + if (g->gc.state == GCSatomic) { + top = tvref(th->stack) + th->stacksize; + for (; o < top; o++) /* Clear unmarked slots. */ + setnilV(o); + } + gc_markobj(g, tabref(th->env)); + lj_state_shrinkstack(th, gc_traverse_frames(g, th)); +} + +/* Propagate one gray object. Traverse it and turn it black. */ +static size_t propagatemark(global_State *g) +{ + GCobj *o = gcref(g->gc.gray); + int gct = o->gch.gct; + lua_assert(isgray(o)); + gray2black(o); + setgcrefr(g->gc.gray, o->gch.gclist); /* Remove from gray list. */ + if (LJ_LIKELY(gct == ~LJ_TTAB)) { + GCtab *t = gco2tab(o); + if (gc_traverse_tab(g, t) > 0) + black2gray(o); /* Keep weak tables gray. */ + return sizeof(GCtab) + sizeof(TValue) * t->asize + + (t->hmask ? sizeof(Node) * (t->hmask + 1) : 0); + } else if (LJ_LIKELY(gct == ~LJ_TFUNC)) { + GCfunc *fn = gco2func(o); + gc_traverse_func(g, fn); + return isluafunc(fn) ? sizeLfunc((MSize)fn->l.nupvalues) : + sizeCfunc((MSize)fn->c.nupvalues); + } else if (LJ_LIKELY(gct == ~LJ_TPROTO)) { + GCproto *pt = gco2pt(o); + gc_traverse_proto(g, pt); + return pt->sizept; + } else if (LJ_LIKELY(gct == ~LJ_TTHREAD)) { + lua_State *th = gco2th(o); + setgcrefr(th->gclist, g->gc.grayagain); + setgcref(g->gc.grayagain, o); + black2gray(o); /* Threads are never black. */ + gc_traverse_thread(g, th); + return sizeof(lua_State) + sizeof(TValue) * th->stacksize; + } else { +#if LJ_HASJIT + GCtrace *T = gco2trace(o); + gc_traverse_trace(g, T); + return ((sizeof(GCtrace)+7)&~7) + (T->nins-T->nk)*sizeof(IRIns) + + T->nsnap*sizeof(SnapShot) + T->nsnapmap*sizeof(SnapEntry); +#else + lua_assert(0); + return 0; +#endif + } +} + +/* Propagate all gray objects. */ +static size_t gc_propagate_gray(global_State *g) +{ + size_t m = 0; + while (gcref(g->gc.gray) != NULL) + m += propagatemark(g); + return m; +} + +/* -- Sweep phase --------------------------------------------------------- */ + +/* Type of GC free functions. */ +typedef void (LJ_FASTCALL *GCFreeFunc)(global_State *g, GCobj *o); + +/* GC free functions for LJ_TSTR .. LJ_TUDATA. ORDER LJ_T */ +static const GCFreeFunc gc_freefunc[] = { + (GCFreeFunc)lj_str_free, + (GCFreeFunc)lj_func_freeuv, + (GCFreeFunc)lj_state_free, + (GCFreeFunc)lj_func_freeproto, + (GCFreeFunc)lj_func_free, +#if LJ_HASJIT + (GCFreeFunc)lj_trace_free, +#else + (GCFreeFunc)0, +#endif +#if LJ_HASFFI + (GCFreeFunc)lj_cdata_free, +#else + (GCFreeFunc)0, +#endif + (GCFreeFunc)lj_tab_free, + (GCFreeFunc)lj_udata_free +}; + +/* Full sweep of a GC list. */ +#define gc_fullsweep(g, p) gc_sweep(g, (p), ~(uint32_t)0) + +/* Partial sweep of a GC list. */ +static GCRef *gc_sweep(global_State *g, GCRef *p, uint32_t lim) +{ + /* Mask with other white and LJ_GC_FIXED. Or LJ_GC_SFIXED on shutdown. */ + int ow = otherwhite(g); + GCobj *o; + while ((o = gcref(*p)) != NULL && lim-- > 0) { + if (o->gch.gct == ~LJ_TTHREAD) /* Need to sweep open upvalues, too. */ + gc_fullsweep(g, &gco2th(o)->openupval); + if (((o->gch.marked ^ LJ_GC_WHITES) & ow)) { /* Black or current white? */ + lua_assert(!isdead(g, o) || (o->gch.marked & LJ_GC_FIXED)); + makewhite(g, o); /* Value is alive, change to the current white. */ + p = &o->gch.nextgc; + } else { /* Otherwise value is dead, free it. */ + lua_assert(isdead(g, o) || ow == LJ_GC_SFIXED); + setgcrefr(*p, o->gch.nextgc); + if (o == gcref(g->gc.root)) + setgcrefr(g->gc.root, o->gch.nextgc); /* Adjust list anchor. */ + gc_freefunc[o->gch.gct - ~LJ_TSTR](g, o); + } + } + return p; +} + +/* Check whether we can clear a key or a value slot from a table. */ +static int gc_mayclear(cTValue *o, int val) +{ + if (tvisgcv(o)) { /* Only collectable objects can be weak references. */ + if (tvisstr(o)) { /* But strings cannot be used as weak references. */ + gc_mark_str(strV(o)); /* And need to be marked. */ + return 0; + } + if (iswhite(gcV(o))) + return 1; /* Object is about to be collected. */ + if (tvisudata(o) && val && isfinalized(udataV(o))) + return 1; /* Finalized userdata is dropped only from values. */ + } + return 0; /* Cannot clear. */ +} + +/* Clear collected entries from weak tables. */ +static void gc_clearweak(GCobj *o) +{ + while (o) { + GCtab *t = gco2tab(o); + lua_assert((t->marked & LJ_GC_WEAK)); + if ((t->marked & LJ_GC_WEAKVAL)) { + MSize i, asize = t->asize; + for (i = 0; i < asize; i++) { + /* Clear array slot when value is about to be collected. */ + TValue *tv = arrayslot(t, i); + if (gc_mayclear(tv, 1)) + setnilV(tv); + } + } + if (t->hmask > 0) { + Node *node = noderef(t->node); + MSize i, hmask = t->hmask; + for (i = 0; i <= hmask; i++) { + Node *n = &node[i]; + /* Clear hash slot when key or value is about to be collected. */ + if (!tvisnil(&n->val) && (gc_mayclear(&n->key, 0) || + gc_mayclear(&n->val, 1))) + setnilV(&n->val); + } + } + o = gcref(t->gclist); + } +} + +/* Call a userdata or cdata finalizer. */ +static void gc_call_finalizer(global_State *g, lua_State *L, + cTValue *mo, GCobj *o) +{ + /* Save and restore lots of state around the __gc callback. */ + uint8_t oldh = hook_save(g); + GCSize oldt = g->gc.threshold; + int errcode; + TValue *top; + lj_trace_abort(g); + hook_entergc(g); /* Disable hooks and new traces during __gc. */ + g->gc.threshold = LJ_MAX_MEM; /* Prevent GC steps. */ + top = L->top; + copyTV(L, top++, mo); + if (LJ_FR2) setnilV(top++); + setgcV(L, top, o, ~o->gch.gct); + L->top = top+1; + errcode = lj_vm_pcall(L, top, 1+0, -1); /* Stack: |mo|o| -> | */ + hook_restore(g, oldh); + g->gc.threshold = oldt; /* Restore GC threshold. */ + if (errcode) + lj_err_throw(L, errcode); /* Propagate errors. */ +} + +/* Finalize one userdata or cdata object from the mmudata list. */ +static void gc_finalize(lua_State *L) +{ + global_State *g = G(L); + GCobj *o = gcnext(gcref(g->gc.mmudata)); + cTValue *mo; + lua_assert(tvref(g->jit_base) == NULL); /* Must not be called on trace. */ + /* Unchain from list of userdata to be finalized. */ + if (o == gcref(g->gc.mmudata)) + setgcrefnull(g->gc.mmudata); + else + setgcrefr(gcref(g->gc.mmudata)->gch.nextgc, o->gch.nextgc); +#if LJ_HASFFI + if (o->gch.gct == ~LJ_TCDATA) { + TValue tmp, *tv; + /* Add cdata back to the GC list and make it white. */ + setgcrefr(o->gch.nextgc, g->gc.root); + setgcref(g->gc.root, o); + makewhite(g, o); + o->gch.marked &= (uint8_t)~LJ_GC_CDATA_FIN; + /* Resolve finalizer. */ + setcdataV(L, &tmp, gco2cd(o)); + tv = lj_tab_set(L, ctype_ctsG(g)->finalizer, &tmp); + if (!tvisnil(tv)) { + g->gc.nocdatafin = 0; + copyTV(L, &tmp, tv); + setnilV(tv); /* Clear entry in finalizer table. */ + gc_call_finalizer(g, L, &tmp, o); + } + return; + } +#endif + /* Add userdata back to the main userdata list and make it white. */ + setgcrefr(o->gch.nextgc, mainthread(g)->nextgc); + setgcref(mainthread(g)->nextgc, o); + makewhite(g, o); + /* Resolve the __gc metamethod. */ + mo = lj_meta_fastg(g, tabref(gco2ud(o)->metatable), MM_gc); + if (mo) + gc_call_finalizer(g, L, mo, o); +} + +/* Finalize all userdata objects from mmudata list. */ +void lj_gc_finalize_udata(lua_State *L) +{ + while (gcref(G(L)->gc.mmudata) != NULL) + gc_finalize(L); +} + +#if LJ_HASFFI +/* Finalize all cdata objects from finalizer table. */ +void lj_gc_finalize_cdata(lua_State *L) +{ + global_State *g = G(L); + CTState *cts = ctype_ctsG(g); + if (cts) { + GCtab *t = cts->finalizer; + Node *node = noderef(t->node); + ptrdiff_t i; + setgcrefnull(t->metatable); /* Mark finalizer table as disabled. */ + for (i = (ptrdiff_t)t->hmask; i >= 0; i--) + if (!tvisnil(&node[i].val) && tviscdata(&node[i].key)) { + GCobj *o = gcV(&node[i].key); + TValue tmp; + makewhite(g, o); + o->gch.marked &= (uint8_t)~LJ_GC_CDATA_FIN; + copyTV(L, &tmp, &node[i].val); + setnilV(&node[i].val); + gc_call_finalizer(g, L, &tmp, o); + } + } +} +#endif + +/* Free all remaining GC objects. */ +void lj_gc_freeall(global_State *g) +{ + MSize i, strmask; + /* Free everything, except super-fixed objects (the main thread). */ + g->gc.currentwhite = LJ_GC_WHITES | LJ_GC_SFIXED; + gc_fullsweep(g, &g->gc.root); + strmask = g->strmask; + for (i = 0; i <= strmask; i++) /* Free all string hash chains. */ + gc_fullsweep(g, &g->strhash[i]); +} + +/* -- Collector ----------------------------------------------------------- */ + +/* Atomic part of the GC cycle, transitioning from mark to sweep phase. */ +static void atomic(global_State *g, lua_State *L) +{ + size_t udsize; + + gc_mark_uv(g); /* Need to remark open upvalues (the thread may be dead). */ + gc_propagate_gray(g); /* Propagate any left-overs. */ + + setgcrefr(g->gc.gray, g->gc.weak); /* Empty the list of weak tables. */ + setgcrefnull(g->gc.weak); + lua_assert(!iswhite(obj2gco(mainthread(g)))); + gc_markobj(g, L); /* Mark running thread. */ + gc_traverse_curtrace(g); /* Traverse current trace. */ + gc_mark_gcroot(g); /* Mark GC roots (again). */ + gc_propagate_gray(g); /* Propagate all of the above. */ + + setgcrefr(g->gc.gray, g->gc.grayagain); /* Empty the 2nd chance list. */ + setgcrefnull(g->gc.grayagain); + gc_propagate_gray(g); /* Propagate it. */ + + udsize = lj_gc_separateudata(g, 0); /* Separate userdata to be finalized. */ + gc_mark_mmudata(g); /* Mark them. */ + udsize += gc_propagate_gray(g); /* And propagate the marks. */ + + /* All marking done, clear weak tables. */ + gc_clearweak(gcref(g->gc.weak)); + + lj_buf_shrink(L, &g->tmpbuf); /* Shrink temp buffer. */ + + /* Prepare for sweep phase. */ + g->gc.currentwhite = (uint8_t)otherwhite(g); /* Flip current white. */ + g->strempty.marked = g->gc.currentwhite; + setmref(g->gc.sweep, &g->gc.root); + g->gc.estimate = g->gc.total - (GCSize)udsize; /* Initial estimate. */ +} + +/* GC state machine. Returns a cost estimate for each step performed. */ +static size_t gc_onestep(lua_State *L) +{ + global_State *g = G(L); + switch (g->gc.state) { + case GCSpause: + gc_mark_start(g); /* Start a new GC cycle by marking all GC roots. */ + return 0; + case GCSpropagate: + if (gcref(g->gc.gray) != NULL) + return propagatemark(g); /* Propagate one gray object. */ + g->gc.state = GCSatomic; /* End of mark phase. */ + return 0; + case GCSatomic: + if (tvref(g->jit_base)) /* Don't run atomic phase on trace. */ + return LJ_MAX_MEM; + atomic(g, L); + g->gc.state = GCSsweepstring; /* Start of sweep phase. */ + g->gc.sweepstr = 0; + return 0; + case GCSsweepstring: { + GCSize old = g->gc.total; + gc_fullsweep(g, &g->strhash[g->gc.sweepstr++]); /* Sweep one chain. */ + if (g->gc.sweepstr > g->strmask) + g->gc.state = GCSsweep; /* All string hash chains sweeped. */ + lua_assert(old >= g->gc.total); + g->gc.estimate -= old - g->gc.total; + return GCSWEEPCOST; + } + case GCSsweep: { + GCSize old = g->gc.total; + setmref(g->gc.sweep, gc_sweep(g, mref(g->gc.sweep, GCRef), GCSWEEPMAX)); + lua_assert(old >= g->gc.total); + g->gc.estimate -= old - g->gc.total; + if (gcref(*mref(g->gc.sweep, GCRef)) == NULL) { + if (g->strnum <= (g->strmask >> 2) && g->strmask > LJ_MIN_STRTAB*2-1) + lj_str_resize(L, g->strmask >> 1); /* Shrink string table. */ + if (gcref(g->gc.mmudata)) { /* Need any finalizations? */ + g->gc.state = GCSfinalize; +#if LJ_HASFFI + g->gc.nocdatafin = 1; +#endif + } else { /* Otherwise skip this phase to help the JIT. */ + g->gc.state = GCSpause; /* End of GC cycle. */ + g->gc.debt = 0; + } + } + return GCSWEEPMAX*GCSWEEPCOST; + } + case GCSfinalize: + if (gcref(g->gc.mmudata) != NULL) { + if (tvref(g->jit_base)) /* Don't call finalizers on trace. */ + return LJ_MAX_MEM; + gc_finalize(L); /* Finalize one userdata object. */ + if (g->gc.estimate > GCFINALIZECOST) + g->gc.estimate -= GCFINALIZECOST; + return GCFINALIZECOST; + } +#if LJ_HASFFI + if (!g->gc.nocdatafin) lj_tab_rehash(L, ctype_ctsG(g)->finalizer); +#endif + g->gc.state = GCSpause; /* End of GC cycle. */ + g->gc.debt = 0; + return 0; + default: + lua_assert(0); + return 0; + } +} + +/* Perform a limited amount of incremental GC steps. */ +int LJ_FASTCALL lj_gc_step(lua_State *L) +{ + global_State *g = G(L); + GCSize lim; + int32_t ostate = g->vmstate; + setvmstate(g, GC); + lim = (GCSTEPSIZE/100) * g->gc.stepmul; + if (lim == 0) + lim = LJ_MAX_MEM; + if (g->gc.total > g->gc.threshold) + g->gc.debt += g->gc.total - g->gc.threshold; + do { + lim -= (GCSize)gc_onestep(L); + if (g->gc.state == GCSpause) { + g->gc.threshold = (g->gc.estimate/100) * g->gc.pause; + g->vmstate = ostate; + return 1; /* Finished a GC cycle. */ + } + } while (sizeof(lim) == 8 ? ((int64_t)lim > 0) : ((int32_t)lim > 0)); + if (g->gc.debt < GCSTEPSIZE) { + g->gc.threshold = g->gc.total + GCSTEPSIZE; + g->vmstate = ostate; + return -1; + } else { + g->gc.debt -= GCSTEPSIZE; + g->gc.threshold = g->gc.total; + g->vmstate = ostate; + return 0; + } +} + +/* Ditto, but fix the stack top first. */ +void LJ_FASTCALL lj_gc_step_fixtop(lua_State *L) +{ + if (curr_funcisL(L)) L->top = curr_topL(L); + lj_gc_step(L); +} + +#if LJ_HASJIT +/* Perform multiple GC steps. Called from JIT-compiled code. */ +int LJ_FASTCALL lj_gc_step_jit(global_State *g, MSize steps) +{ + lua_State *L = gco2th(gcref(g->cur_L)); + L->base = tvref(G(L)->jit_base); + L->top = curr_topL(L); + while (steps-- > 0 && lj_gc_step(L) == 0) + ; + /* Return 1 to force a trace exit. */ + return (G(L)->gc.state == GCSatomic || G(L)->gc.state == GCSfinalize); +} +#endif + +/* Perform a full GC cycle. */ +void lj_gc_fullgc(lua_State *L) +{ + global_State *g = G(L); + int32_t ostate = g->vmstate; + setvmstate(g, GC); + if (g->gc.state <= GCSatomic) { /* Caught somewhere in the middle. */ + setmref(g->gc.sweep, &g->gc.root); /* Sweep everything (preserving it). */ + setgcrefnull(g->gc.gray); /* Reset lists from partial propagation. */ + setgcrefnull(g->gc.grayagain); + setgcrefnull(g->gc.weak); + g->gc.state = GCSsweepstring; /* Fast forward to the sweep phase. */ + g->gc.sweepstr = 0; + } + while (g->gc.state == GCSsweepstring || g->gc.state == GCSsweep) + gc_onestep(L); /* Finish sweep. */ + lua_assert(g->gc.state == GCSfinalize || g->gc.state == GCSpause); + /* Now perform a full GC. */ + g->gc.state = GCSpause; + do { gc_onestep(L); } while (g->gc.state != GCSpause); + g->gc.threshold = (g->gc.estimate/100) * g->gc.pause; + g->vmstate = ostate; +} + +/* -- Write barriers ------------------------------------------------------ */ + +/* Move the GC propagation frontier forward. */ +void lj_gc_barrierf(global_State *g, GCobj *o, GCobj *v) +{ + lua_assert(isblack(o) && iswhite(v) && !isdead(g, v) && !isdead(g, o)); + lua_assert(g->gc.state != GCSfinalize && g->gc.state != GCSpause); + lua_assert(o->gch.gct != ~LJ_TTAB); + /* Preserve invariant during propagation. Otherwise it doesn't matter. */ + if (g->gc.state == GCSpropagate || g->gc.state == GCSatomic) + gc_mark(g, v); /* Move frontier forward. */ + else + makewhite(g, o); /* Make it white to avoid the following barrier. */ +} + +/* Specialized barrier for closed upvalue. Pass &uv->tv. */ +void LJ_FASTCALL lj_gc_barrieruv(global_State *g, TValue *tv) +{ +#define TV2MARKED(x) \ + (*((uint8_t *)(x) - offsetof(GCupval, tv) + offsetof(GCupval, marked))) + if (g->gc.state == GCSpropagate || g->gc.state == GCSatomic) + gc_mark(g, gcV(tv)); + else + TV2MARKED(tv) = (TV2MARKED(tv) & (uint8_t)~LJ_GC_COLORS) | curwhite(g); +#undef TV2MARKED +} + +/* Close upvalue. Also needs a write barrier. */ +void lj_gc_closeuv(global_State *g, GCupval *uv) +{ + GCobj *o = obj2gco(uv); + /* Copy stack slot to upvalue itself and point to the copy. */ + copyTV(mainthread(g), &uv->tv, uvval(uv)); + setmref(uv->v, &uv->tv); + uv->closed = 1; + setgcrefr(o->gch.nextgc, g->gc.root); + setgcref(g->gc.root, o); + if (isgray(o)) { /* A closed upvalue is never gray, so fix this. */ + if (g->gc.state == GCSpropagate || g->gc.state == GCSatomic) { + gray2black(o); /* Make it black and preserve invariant. */ + if (tviswhite(&uv->tv)) + lj_gc_barrierf(g, o, gcV(&uv->tv)); + } else { + makewhite(g, o); /* Make it white, i.e. sweep the upvalue. */ + lua_assert(g->gc.state != GCSfinalize && g->gc.state != GCSpause); + } + } +} + +#if LJ_HASJIT +/* Mark a trace if it's saved during the propagation phase. */ +void lj_gc_barriertrace(global_State *g, uint32_t traceno) +{ + if (g->gc.state == GCSpropagate || g->gc.state == GCSatomic) + gc_marktrace(g, traceno); +} +#endif + +/* -- Allocator ----------------------------------------------------------- */ + +/* Call pluggable memory allocator to allocate or resize a fragment. */ +void *lj_mem_realloc(lua_State *L, void *p, GCSize osz, GCSize nsz) +{ + global_State *g = G(L); + lua_assert((osz == 0) == (p == NULL)); + p = g->allocf(g->allocd, p, osz, nsz); + if (p == NULL && nsz > 0) + lj_err_mem(L); + lua_assert((nsz == 0) == (p == NULL)); + lua_assert(checkptrGC(p)); + g->gc.total = (g->gc.total - osz) + nsz; + return p; +} + +/* Allocate new GC object and link it to the root set. */ +void * LJ_FASTCALL lj_mem_newgco(lua_State *L, GCSize size) +{ + global_State *g = G(L); + GCobj *o = (GCobj *)g->allocf(g->allocd, NULL, 0, size); + if (o == NULL) + lj_err_mem(L); + lua_assert(checkptrGC(o)); + g->gc.total += size; + setgcrefr(o->gch.nextgc, g->gc.root); + setgcref(g->gc.root, o); + newwhite(g, o); + return o; +} + +/* Resize growable vector. */ +void *lj_mem_grow(lua_State *L, void *p, MSize *szp, MSize lim, MSize esz) +{ + MSize sz = (*szp) << 1; + if (sz < LJ_MIN_VECSZ) + sz = LJ_MIN_VECSZ; + if (sz > lim) + sz = lim; + p = lj_mem_realloc(L, p, (*szp)*esz, sz*esz); + *szp = sz; + return p; +} + diff --git a/lib/LuaJIT/lj_gc.h b/lib/LuaJIT/lj_gc.h new file mode 100644 index 0000000..669bbe9 --- /dev/null +++ b/lib/LuaJIT/lj_gc.h @@ -0,0 +1,134 @@ +/* +** Garbage collector. +** Copyright (C) 2005-2017 Mike Pall. See Copyright Notice in luajit.h +*/ + +#ifndef _LJ_GC_H +#define _LJ_GC_H + +#include "lj_obj.h" + +/* Garbage collector states. Order matters. */ +enum { + GCSpause, GCSpropagate, GCSatomic, GCSsweepstring, GCSsweep, GCSfinalize +}; + +/* Bitmasks for marked field of GCobj. */ +#define LJ_GC_WHITE0 0x01 +#define LJ_GC_WHITE1 0x02 +#define LJ_GC_BLACK 0x04 +#define LJ_GC_FINALIZED 0x08 +#define LJ_GC_WEAKKEY 0x08 +#define LJ_GC_WEAKVAL 0x10 +#define LJ_GC_CDATA_FIN 0x10 +#define LJ_GC_FIXED 0x20 +#define LJ_GC_SFIXED 0x40 + +#define LJ_GC_WHITES (LJ_GC_WHITE0 | LJ_GC_WHITE1) +#define LJ_GC_COLORS (LJ_GC_WHITES | LJ_GC_BLACK) +#define LJ_GC_WEAK (LJ_GC_WEAKKEY | LJ_GC_WEAKVAL) + +/* Macros to test and set GCobj colors. */ +#define iswhite(x) ((x)->gch.marked & LJ_GC_WHITES) +#define isblack(x) ((x)->gch.marked & LJ_GC_BLACK) +#define isgray(x) (!((x)->gch.marked & (LJ_GC_BLACK|LJ_GC_WHITES))) +#define tviswhite(x) (tvisgcv(x) && iswhite(gcV(x))) +#define otherwhite(g) (g->gc.currentwhite ^ LJ_GC_WHITES) +#define isdead(g, v) ((v)->gch.marked & otherwhite(g) & LJ_GC_WHITES) + +#define curwhite(g) ((g)->gc.currentwhite & LJ_GC_WHITES) +#define newwhite(g, x) (obj2gco(x)->gch.marked = (uint8_t)curwhite(g)) +#define makewhite(g, x) \ + ((x)->gch.marked = ((x)->gch.marked & (uint8_t)~LJ_GC_COLORS) | curwhite(g)) +#define flipwhite(x) ((x)->gch.marked ^= LJ_GC_WHITES) +#define black2gray(x) ((x)->gch.marked &= (uint8_t)~LJ_GC_BLACK) +#define fixstring(s) ((s)->marked |= LJ_GC_FIXED) +#define markfinalized(x) ((x)->gch.marked |= LJ_GC_FINALIZED) + +/* Collector. */ +LJ_FUNC size_t lj_gc_separateudata(global_State *g, int all); +LJ_FUNC void lj_gc_finalize_udata(lua_State *L); +#if LJ_HASFFI +LJ_FUNC void lj_gc_finalize_cdata(lua_State *L); +#else +#define lj_gc_finalize_cdata(L) UNUSED(L) +#endif +LJ_FUNC void lj_gc_freeall(global_State *g); +LJ_FUNCA int LJ_FASTCALL lj_gc_step(lua_State *L); +LJ_FUNCA void LJ_FASTCALL lj_gc_step_fixtop(lua_State *L); +#if LJ_HASJIT +LJ_FUNC int LJ_FASTCALL lj_gc_step_jit(global_State *g, MSize steps); +#endif +LJ_FUNC void lj_gc_fullgc(lua_State *L); + +/* GC check: drive collector forward if the GC threshold has been reached. */ +#define lj_gc_check(L) \ + { if (LJ_UNLIKELY(G(L)->gc.total >= G(L)->gc.threshold)) \ + lj_gc_step(L); } +#define lj_gc_check_fixtop(L) \ + { if (LJ_UNLIKELY(G(L)->gc.total >= G(L)->gc.threshold)) \ + lj_gc_step_fixtop(L); } + +/* Write barriers. */ +LJ_FUNC void lj_gc_barrierf(global_State *g, GCobj *o, GCobj *v); +LJ_FUNCA void LJ_FASTCALL lj_gc_barrieruv(global_State *g, TValue *tv); +LJ_FUNC void lj_gc_closeuv(global_State *g, GCupval *uv); +#if LJ_HASJIT +LJ_FUNC void lj_gc_barriertrace(global_State *g, uint32_t traceno); +#endif + +/* Move the GC propagation frontier back for tables (make it gray again). */ +static LJ_AINLINE void lj_gc_barrierback(global_State *g, GCtab *t) +{ + GCobj *o = obj2gco(t); + lua_assert(isblack(o) && !isdead(g, o)); + lua_assert(g->gc.state != GCSfinalize && g->gc.state != GCSpause); + black2gray(o); + setgcrefr(t->gclist, g->gc.grayagain); + setgcref(g->gc.grayagain, o); +} + +/* Barrier for stores to table objects. TValue and GCobj variant. */ +#define lj_gc_anybarriert(L, t) \ + { if (LJ_UNLIKELY(isblack(obj2gco(t)))) lj_gc_barrierback(G(L), (t)); } +#define lj_gc_barriert(L, t, tv) \ + { if (tviswhite(tv) && isblack(obj2gco(t))) \ + lj_gc_barrierback(G(L), (t)); } +#define lj_gc_objbarriert(L, t, o) \ + { if (iswhite(obj2gco(o)) && isblack(obj2gco(t))) \ + lj_gc_barrierback(G(L), (t)); } + +/* Barrier for stores to any other object. TValue and GCobj variant. */ +#define lj_gc_barrier(L, p, tv) \ + { if (tviswhite(tv) && isblack(obj2gco(p))) \ + lj_gc_barrierf(G(L), obj2gco(p), gcV(tv)); } +#define lj_gc_objbarrier(L, p, o) \ + { if (iswhite(obj2gco(o)) && isblack(obj2gco(p))) \ + lj_gc_barrierf(G(L), obj2gco(p), obj2gco(o)); } + +/* Allocator. */ +LJ_FUNC void *lj_mem_realloc(lua_State *L, void *p, GCSize osz, GCSize nsz); +LJ_FUNC void * LJ_FASTCALL lj_mem_newgco(lua_State *L, GCSize size); +LJ_FUNC void *lj_mem_grow(lua_State *L, void *p, + MSize *szp, MSize lim, MSize esz); + +#define lj_mem_new(L, s) lj_mem_realloc(L, NULL, 0, (s)) + +static LJ_AINLINE void lj_mem_free(global_State *g, void *p, size_t osize) +{ + g->gc.total -= (GCSize)osize; + g->allocf(g->allocd, p, osize, 0); +} + +#define lj_mem_newvec(L, n, t) ((t *)lj_mem_new(L, (GCSize)((n)*sizeof(t)))) +#define lj_mem_reallocvec(L, p, on, n, t) \ + ((p) = (t *)lj_mem_realloc(L, p, (on)*sizeof(t), (GCSize)((n)*sizeof(t)))) +#define lj_mem_growvec(L, p, n, m, t) \ + ((p) = (t *)lj_mem_grow(L, (p), &(n), (m), (MSize)sizeof(t))) +#define lj_mem_freevec(g, p, n, t) lj_mem_free(g, (p), (n)*sizeof(t)) + +#define lj_mem_newobj(L, t) ((t *)lj_mem_newgco(L, sizeof(t))) +#define lj_mem_newt(L, s, t) ((t *)lj_mem_new(L, (s))) +#define lj_mem_freet(g, p) lj_mem_free(g, (p), sizeof(*(p))) + +#endif diff --git a/lib/LuaJIT/lj_gc.o b/lib/LuaJIT/lj_gc.o new file mode 100644 index 0000000000000000000000000000000000000000..ba4222a7a5dbe5b988b10e39d40f4482082f6bc5 GIT binary patch literal 11912 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 H`Q!fwoIC8H literal 0 HcmV?d00001 diff --git a/lib/LuaJIT/lj_gc_dyn.o b/lib/LuaJIT/lj_gc_dyn.o new file mode 100644 index 0000000000000000000000000000000000000000..ba4222a7a5dbe5b988b10e39d40f4482082f6bc5 GIT binary patch literal 11912 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 H`Q!fwoIC8H literal 0 HcmV?d00001 diff --git a/lib/LuaJIT/lj_gdbjit.c b/lib/LuaJIT/lj_gdbjit.c new file mode 100644 index 0000000..c219ffa --- /dev/null +++ b/lib/LuaJIT/lj_gdbjit.c @@ -0,0 +1,817 @@ +/* +** Client for the GDB JIT API. +** Copyright (C) 2005-2017 Mike Pall. See Copyright Notice in luajit.h +*/ + +#define lj_gdbjit_c +#define LUA_CORE + +#include "lj_obj.h" + +#if LJ_HASJIT + +#include "lj_gc.h" +#include "lj_err.h" +#include "lj_debug.h" +#include "lj_frame.h" +#include "lj_buf.h" +#include "lj_strfmt.h" +#include "lj_jit.h" +#include "lj_dispatch.h" + +/* This is not compiled in by default. +** Enable with -DLUAJIT_USE_GDBJIT in the Makefile and recompile everything. +*/ +#ifdef LUAJIT_USE_GDBJIT + +/* The GDB JIT API allows JIT compilers to pass debug information about +** JIT-compiled code back to GDB. You need at least GDB 7.0 or higher +** to see it in action. +** +** This is a passive API, so it works even when not running under GDB +** or when attaching to an already running process. Alas, this implies +** enabling it always has a non-negligible overhead -- do not use in +** release mode! +** +** The LuaJIT GDB JIT client is rather minimal at the moment. It gives +** each trace a symbol name and adds a source location and frame unwind +** information. Obviously LuaJIT itself and any embedding C application +** should be compiled with debug symbols, too (see the Makefile). +** +** Traces are named TRACE_1, TRACE_2, ... these correspond to the trace +** numbers from -jv or -jdump. Use "break TRACE_1" or "tbreak TRACE_1" etc. +** to set breakpoints on specific traces (even ahead of their creation). +** +** The source location for each trace allows listing the corresponding +** source lines with the GDB command "list" (but only if the Lua source +** has been loaded from a file). Currently this is always set to the +** location where the trace has been started. +** +** Frame unwind information can be inspected with the GDB command +** "info frame". This also allows proper backtraces across JIT-compiled +** code with the GDB command "bt". +** +** You probably want to add the following settings to a .gdbinit file +** (or add them to ~/.gdbinit): +** set disassembly-flavor intel +** set breakpoint pending on +** +** Here's a sample GDB session: +** ------------------------------------------------------------------------ + +$ cat >x.lua +for outer=1,100 do + for inner=1,100 do end +end +^D + +$ luajit -jv x.lua +[TRACE 1 x.lua:2] +[TRACE 2 (1/3) x.lua:1 -> 1] + +$ gdb --quiet --args luajit x.lua +(gdb) tbreak TRACE_1 +Function "TRACE_1" not defined. +Temporary breakpoint 1 (TRACE_1) pending. +(gdb) run +Starting program: luajit x.lua + +Temporary breakpoint 1, TRACE_1 () at x.lua:2 +2 for inner=1,100 do end +(gdb) list +1 for outer=1,100 do +2 for inner=1,100 do end +3 end +(gdb) bt +#0 TRACE_1 () at x.lua:2 +#1 0x08053690 in lua_pcall [...] +[...] +#7 0x0806ff90 in main [...] +(gdb) disass TRACE_1 +Dump of assembler code for function TRACE_1: +0xf7fd9fba : mov DWORD PTR ds:0xf7e0e2a0,0x1 +0xf7fd9fc4 : movsd xmm7,QWORD PTR [edx+0x20] +[...] +0xf7fd9ff8 : jmp 0xf7fd2014 +End of assembler dump. +(gdb) tbreak TRACE_2 +Function "TRACE_2" not defined. +Temporary breakpoint 2 (TRACE_2) pending. +(gdb) cont +Continuing. + +Temporary breakpoint 2, TRACE_2 () at x.lua:1 +1 for outer=1,100 do +(gdb) info frame +Stack level 0, frame at 0xffffd7c0: + eip = 0xf7fd9f60 in TRACE_2 (x.lua:1); saved eip 0x8053690 + called by frame at 0xffffd7e0 + source language unknown. + Arglist at 0xffffd78c, args: + Locals at 0xffffd78c, Previous frame's sp is 0xffffd7c0 + Saved registers: + ebx at 0xffffd7ac, ebp at 0xffffd7b8, esi at 0xffffd7b0, edi at 0xffffd7b4, + eip at 0xffffd7bc +(gdb) + +** ------------------------------------------------------------------------ +*/ + +/* -- GDB JIT API --------------------------------------------------------- */ + +/* GDB JIT actions. */ +enum { + GDBJIT_NOACTION = 0, + GDBJIT_REGISTER, + GDBJIT_UNREGISTER +}; + +/* GDB JIT entry. */ +typedef struct GDBJITentry { + struct GDBJITentry *next_entry; + struct GDBJITentry *prev_entry; + const char *symfile_addr; + uint64_t symfile_size; +} GDBJITentry; + +/* GDB JIT descriptor. */ +typedef struct GDBJITdesc { + uint32_t version; + uint32_t action_flag; + GDBJITentry *relevant_entry; + GDBJITentry *first_entry; +} GDBJITdesc; + +GDBJITdesc __jit_debug_descriptor = { + 1, GDBJIT_NOACTION, NULL, NULL +}; + +/* GDB sets a breakpoint at this function. */ +void LJ_NOINLINE __jit_debug_register_code() +{ + __asm__ __volatile__(""); +}; + +/* -- In-memory ELF object definitions ------------------------------------ */ + +/* ELF definitions. */ +typedef struct ELFheader { + uint8_t emagic[4]; + uint8_t eclass; + uint8_t eendian; + uint8_t eversion; + uint8_t eosabi; + uint8_t eabiversion; + uint8_t epad[7]; + uint16_t type; + uint16_t machine; + uint32_t version; + uintptr_t entry; + uintptr_t phofs; + uintptr_t shofs; + uint32_t flags; + uint16_t ehsize; + uint16_t phentsize; + uint16_t phnum; + uint16_t shentsize; + uint16_t shnum; + uint16_t shstridx; +} ELFheader; + +typedef struct ELFsectheader { + uint32_t name; + uint32_t type; + uintptr_t flags; + uintptr_t addr; + uintptr_t ofs; + uintptr_t size; + uint32_t link; + uint32_t info; + uintptr_t align; + uintptr_t entsize; +} ELFsectheader; + +#define ELFSECT_IDX_ABS 0xfff1 + +enum { + ELFSECT_TYPE_PROGBITS = 1, + ELFSECT_TYPE_SYMTAB = 2, + ELFSECT_TYPE_STRTAB = 3, + ELFSECT_TYPE_NOBITS = 8 +}; + +#define ELFSECT_FLAGS_WRITE 1 +#define ELFSECT_FLAGS_ALLOC 2 +#define ELFSECT_FLAGS_EXEC 4 + +typedef struct ELFsymbol { +#if LJ_64 + uint32_t name; + uint8_t info; + uint8_t other; + uint16_t sectidx; + uintptr_t value; + uint64_t size; +#else + uint32_t name; + uintptr_t value; + uint32_t size; + uint8_t info; + uint8_t other; + uint16_t sectidx; +#endif +} ELFsymbol; + +enum { + ELFSYM_TYPE_FUNC = 2, + ELFSYM_TYPE_FILE = 4, + ELFSYM_BIND_LOCAL = 0 << 4, + ELFSYM_BIND_GLOBAL = 1 << 4, +}; + +/* DWARF definitions. */ +#define DW_CIE_VERSION 1 + +enum { + DW_CFA_nop = 0x0, + DW_CFA_offset_extended = 0x5, + DW_CFA_def_cfa = 0xc, + DW_CFA_def_cfa_offset = 0xe, + DW_CFA_offset_extended_sf = 0x11, + DW_CFA_advance_loc = 0x40, + DW_CFA_offset = 0x80 +}; + +enum { + DW_EH_PE_udata4 = 3, + DW_EH_PE_textrel = 0x20 +}; + +enum { + DW_TAG_compile_unit = 0x11 +}; + +enum { + DW_children_no = 0, + DW_children_yes = 1 +}; + +enum { + DW_AT_name = 0x03, + DW_AT_stmt_list = 0x10, + DW_AT_low_pc = 0x11, + DW_AT_high_pc = 0x12 +}; + +enum { + DW_FORM_addr = 0x01, + DW_FORM_data4 = 0x06, + DW_FORM_string = 0x08 +}; + +enum { + DW_LNS_extended_op = 0, + DW_LNS_copy = 1, + DW_LNS_advance_pc = 2, + DW_LNS_advance_line = 3 +}; + +enum { + DW_LNE_end_sequence = 1, + DW_LNE_set_address = 2 +}; + +enum { +#if LJ_TARGET_X86 + DW_REG_AX, DW_REG_CX, DW_REG_DX, DW_REG_BX, + DW_REG_SP, DW_REG_BP, DW_REG_SI, DW_REG_DI, + DW_REG_RA, +#elif LJ_TARGET_X64 + /* Yes, the order is strange, but correct. */ + DW_REG_AX, DW_REG_DX, DW_REG_CX, DW_REG_BX, + DW_REG_SI, DW_REG_DI, DW_REG_BP, DW_REG_SP, + DW_REG_8, DW_REG_9, DW_REG_10, DW_REG_11, + DW_REG_12, DW_REG_13, DW_REG_14, DW_REG_15, + DW_REG_RA, +#elif LJ_TARGET_ARM + DW_REG_SP = 13, + DW_REG_RA = 14, +#elif LJ_TARGET_ARM64 + DW_REG_SP = 31, + DW_REG_RA = 30, +#elif LJ_TARGET_PPC + DW_REG_SP = 1, + DW_REG_RA = 65, + DW_REG_CR = 70, +#elif LJ_TARGET_MIPS + DW_REG_SP = 29, + DW_REG_RA = 31, +#else +#error "Unsupported target architecture" +#endif +}; + +/* Minimal list of sections for the in-memory ELF object. */ +enum { + GDBJIT_SECT_NULL, + GDBJIT_SECT_text, + GDBJIT_SECT_eh_frame, + GDBJIT_SECT_shstrtab, + GDBJIT_SECT_strtab, + GDBJIT_SECT_symtab, + GDBJIT_SECT_debug_info, + GDBJIT_SECT_debug_abbrev, + GDBJIT_SECT_debug_line, + GDBJIT_SECT__MAX +}; + +enum { + GDBJIT_SYM_UNDEF, + GDBJIT_SYM_FILE, + GDBJIT_SYM_FUNC, + GDBJIT_SYM__MAX +}; + +/* In-memory ELF object. */ +typedef struct GDBJITobj { + ELFheader hdr; /* ELF header. */ + ELFsectheader sect[GDBJIT_SECT__MAX]; /* ELF sections. */ + ELFsymbol sym[GDBJIT_SYM__MAX]; /* ELF symbol table. */ + uint8_t space[4096]; /* Space for various section data. */ +} GDBJITobj; + +/* Combined structure for GDB JIT entry and ELF object. */ +typedef struct GDBJITentryobj { + GDBJITentry entry; + size_t sz; + GDBJITobj obj; +} GDBJITentryobj; + +/* Template for in-memory ELF header. */ +static const ELFheader elfhdr_template = { + .emagic = { 0x7f, 'E', 'L', 'F' }, + .eclass = LJ_64 ? 2 : 1, + .eendian = LJ_ENDIAN_SELECT(1, 2), + .eversion = 1, +#if LJ_TARGET_LINUX + .eosabi = 0, /* Nope, it's not 3. */ +#elif defined(__FreeBSD__) + .eosabi = 9, +#elif defined(__NetBSD__) + .eosabi = 2, +#elif defined(__OpenBSD__) + .eosabi = 12, +#elif defined(__DragonFly__) + .eosabi = 0, +#elif (defined(__sun__) && defined(__svr4__)) + .eosabi = 6, +#else + .eosabi = 0, +#endif + .eabiversion = 0, + .epad = { 0, 0, 0, 0, 0, 0, 0 }, + .type = 1, +#if LJ_TARGET_X86 + .machine = 3, +#elif LJ_TARGET_X64 + .machine = 62, +#elif LJ_TARGET_ARM + .machine = 40, +#elif LJ_TARGET_ARM64 + .machine = 183, +#elif LJ_TARGET_PPC + .machine = 20, +#elif LJ_TARGET_MIPS + .machine = 8, +#else +#error "Unsupported target architecture" +#endif + .version = 1, + .entry = 0, + .phofs = 0, + .shofs = offsetof(GDBJITobj, sect), + .flags = 0, + .ehsize = sizeof(ELFheader), + .phentsize = 0, + .phnum = 0, + .shentsize = sizeof(ELFsectheader), + .shnum = GDBJIT_SECT__MAX, + .shstridx = GDBJIT_SECT_shstrtab +}; + +/* -- In-memory ELF object generation ------------------------------------- */ + +/* Context for generating the ELF object for the GDB JIT API. */ +typedef struct GDBJITctx { + uint8_t *p; /* Pointer to next address in obj.space. */ + uint8_t *startp; /* Pointer to start address in obj.space. */ + GCtrace *T; /* Generate symbols for this trace. */ + uintptr_t mcaddr; /* Machine code address. */ + MSize szmcode; /* Size of machine code. */ + MSize spadjp; /* Stack adjustment for parent trace or interpreter. */ + MSize spadj; /* Stack adjustment for trace itself. */ + BCLine lineno; /* Starting line number. */ + const char *filename; /* Starting file name. */ + size_t objsize; /* Final size of ELF object. */ + GDBJITobj obj; /* In-memory ELF object. */ +} GDBJITctx; + +/* Add a zero-terminated string. */ +static uint32_t gdbjit_strz(GDBJITctx *ctx, const char *str) +{ + uint8_t *p = ctx->p; + uint32_t ofs = (uint32_t)(p - ctx->startp); + do { + *p++ = (uint8_t)*str; + } while (*str++); + ctx->p = p; + return ofs; +} + +/* Append a decimal number. */ +static void gdbjit_catnum(GDBJITctx *ctx, uint32_t n) +{ + if (n >= 10) { uint32_t m = n / 10; n = n % 10; gdbjit_catnum(ctx, m); } + *ctx->p++ = '0' + n; +} + +/* Add a SLEB128 value. */ +static void gdbjit_sleb128(GDBJITctx *ctx, int32_t v) +{ + uint8_t *p = ctx->p; + for (; (uint32_t)(v+0x40) >= 0x80; v >>= 7) + *p++ = (uint8_t)((v & 0x7f) | 0x80); + *p++ = (uint8_t)(v & 0x7f); + ctx->p = p; +} + +/* Shortcuts to generate DWARF structures. */ +#define DB(x) (*p++ = (x)) +#define DI8(x) (*(int8_t *)p = (x), p++) +#define DU16(x) (*(uint16_t *)p = (x), p += 2) +#define DU32(x) (*(uint32_t *)p = (x), p += 4) +#define DADDR(x) (*(uintptr_t *)p = (x), p += sizeof(uintptr_t)) +#define DUV(x) (p = (uint8_t *)lj_strfmt_wuleb128((char *)p, (x))) +#define DSV(x) (ctx->p = p, gdbjit_sleb128(ctx, (x)), p = ctx->p) +#define DSTR(str) (ctx->p = p, gdbjit_strz(ctx, (str)), p = ctx->p) +#define DALIGNNOP(s) while ((uintptr_t)p & ((s)-1)) *p++ = DW_CFA_nop +#define DSECT(name, stmt) \ + { uint32_t *szp_##name = (uint32_t *)p; p += 4; stmt \ + *szp_##name = (uint32_t)((p-(uint8_t *)szp_##name)-4); } \ + +/* Initialize ELF section headers. */ +static void LJ_FASTCALL gdbjit_secthdr(GDBJITctx *ctx) +{ + ELFsectheader *sect; + + *ctx->p++ = '\0'; /* Empty string at start of string table. */ + +#define SECTDEF(id, tp, al) \ + sect = &ctx->obj.sect[GDBJIT_SECT_##id]; \ + sect->name = gdbjit_strz(ctx, "." #id); \ + sect->type = ELFSECT_TYPE_##tp; \ + sect->align = (al) + + SECTDEF(text, NOBITS, 16); + sect->flags = ELFSECT_FLAGS_ALLOC|ELFSECT_FLAGS_EXEC; + sect->addr = ctx->mcaddr; + sect->ofs = 0; + sect->size = ctx->szmcode; + + SECTDEF(eh_frame, PROGBITS, sizeof(uintptr_t)); + sect->flags = ELFSECT_FLAGS_ALLOC; + + SECTDEF(shstrtab, STRTAB, 1); + SECTDEF(strtab, STRTAB, 1); + + SECTDEF(symtab, SYMTAB, sizeof(uintptr_t)); + sect->ofs = offsetof(GDBJITobj, sym); + sect->size = sizeof(ctx->obj.sym); + sect->link = GDBJIT_SECT_strtab; + sect->entsize = sizeof(ELFsymbol); + sect->info = GDBJIT_SYM_FUNC; + + SECTDEF(debug_info, PROGBITS, 1); + SECTDEF(debug_abbrev, PROGBITS, 1); + SECTDEF(debug_line, PROGBITS, 1); + +#undef SECTDEF +} + +/* Initialize symbol table. */ +static void LJ_FASTCALL gdbjit_symtab(GDBJITctx *ctx) +{ + ELFsymbol *sym; + + *ctx->p++ = '\0'; /* Empty string at start of string table. */ + + sym = &ctx->obj.sym[GDBJIT_SYM_FILE]; + sym->name = gdbjit_strz(ctx, "JIT mcode"); + sym->sectidx = ELFSECT_IDX_ABS; + sym->info = ELFSYM_TYPE_FILE|ELFSYM_BIND_LOCAL; + + sym = &ctx->obj.sym[GDBJIT_SYM_FUNC]; + sym->name = gdbjit_strz(ctx, "TRACE_"); ctx->p--; + gdbjit_catnum(ctx, ctx->T->traceno); *ctx->p++ = '\0'; + sym->sectidx = GDBJIT_SECT_text; + sym->value = 0; + sym->size = ctx->szmcode; + sym->info = ELFSYM_TYPE_FUNC|ELFSYM_BIND_GLOBAL; +} + +/* Initialize .eh_frame section. */ +static void LJ_FASTCALL gdbjit_ehframe(GDBJITctx *ctx) +{ + uint8_t *p = ctx->p; + uint8_t *framep = p; + + /* Emit DWARF EH CIE. */ + DSECT(CIE, + DU32(0); /* Offset to CIE itself. */ + DB(DW_CIE_VERSION); + DSTR("zR"); /* Augmentation. */ + DUV(1); /* Code alignment factor. */ + DSV(-(int32_t)sizeof(uintptr_t)); /* Data alignment factor. */ + DB(DW_REG_RA); /* Return address register. */ + DB(1); DB(DW_EH_PE_textrel|DW_EH_PE_udata4); /* Augmentation data. */ + DB(DW_CFA_def_cfa); DUV(DW_REG_SP); DUV(sizeof(uintptr_t)); +#if LJ_TARGET_PPC + DB(DW_CFA_offset_extended_sf); DB(DW_REG_RA); DSV(-1); +#else + DB(DW_CFA_offset|DW_REG_RA); DUV(1); +#endif + DALIGNNOP(sizeof(uintptr_t)); + ) + + /* Emit DWARF EH FDE. */ + DSECT(FDE, + DU32((uint32_t)(p-framep)); /* Offset to CIE. */ + DU32(0); /* Machine code offset relative to .text. */ + DU32(ctx->szmcode); /* Machine code length. */ + DB(0); /* Augmentation data. */ + /* Registers saved in CFRAME. */ +#if LJ_TARGET_X86 + DB(DW_CFA_offset|DW_REG_BP); DUV(2); + DB(DW_CFA_offset|DW_REG_DI); DUV(3); + DB(DW_CFA_offset|DW_REG_SI); DUV(4); + DB(DW_CFA_offset|DW_REG_BX); DUV(5); +#elif LJ_TARGET_X64 + DB(DW_CFA_offset|DW_REG_BP); DUV(2); + DB(DW_CFA_offset|DW_REG_BX); DUV(3); + DB(DW_CFA_offset|DW_REG_15); DUV(4); + DB(DW_CFA_offset|DW_REG_14); DUV(5); + /* Extra registers saved for JIT-compiled code. */ + DB(DW_CFA_offset|DW_REG_13); DUV(LJ_GC64 ? 10 : 9); + DB(DW_CFA_offset|DW_REG_12); DUV(LJ_GC64 ? 11 : 10); +#elif LJ_TARGET_ARM + { + int i; + for (i = 11; i >= 4; i--) { DB(DW_CFA_offset|i); DUV(2+(11-i)); } + } +#elif LJ_TARGET_ARM64 + { + int i; + DB(DW_CFA_offset|31); DUV(2); + for (i = 28; i >= 19; i--) { DB(DW_CFA_offset|i); DUV(3+(28-i)); } + for (i = 15; i >= 8; i--) { DB(DW_CFA_offset|32|i); DUV(28-i); } + } +#elif LJ_TARGET_PPC + { + int i; + DB(DW_CFA_offset_extended); DB(DW_REG_CR); DUV(55); + for (i = 14; i <= 31; i++) { + DB(DW_CFA_offset|i); DUV(37+(31-i)); + DB(DW_CFA_offset|32|i); DUV(2+2*(31-i)); + } + } +#elif LJ_TARGET_MIPS + { + int i; + DB(DW_CFA_offset|30); DUV(2); + for (i = 23; i >= 16; i--) { DB(DW_CFA_offset|i); DUV(26-i); } + for (i = 30; i >= 20; i -= 2) { DB(DW_CFA_offset|32|i); DUV(42-i); } + } +#else +#error "Unsupported target architecture" +#endif + if (ctx->spadjp != ctx->spadj) { /* Parent/interpreter stack frame size. */ + DB(DW_CFA_def_cfa_offset); DUV(ctx->spadjp); + DB(DW_CFA_advance_loc|1); /* Only an approximation. */ + } + DB(DW_CFA_def_cfa_offset); DUV(ctx->spadj); /* Trace stack frame size. */ + DALIGNNOP(sizeof(uintptr_t)); + ) + + ctx->p = p; +} + +/* Initialize .debug_info section. */ +static void LJ_FASTCALL gdbjit_debuginfo(GDBJITctx *ctx) +{ + uint8_t *p = ctx->p; + + DSECT(info, + DU16(2); /* DWARF version. */ + DU32(0); /* Abbrev offset. */ + DB(sizeof(uintptr_t)); /* Pointer size. */ + + DUV(1); /* Abbrev #1: DW_TAG_compile_unit. */ + DSTR(ctx->filename); /* DW_AT_name. */ + DADDR(ctx->mcaddr); /* DW_AT_low_pc. */ + DADDR(ctx->mcaddr + ctx->szmcode); /* DW_AT_high_pc. */ + DU32(0); /* DW_AT_stmt_list. */ + ) + + ctx->p = p; +} + +/* Initialize .debug_abbrev section. */ +static void LJ_FASTCALL gdbjit_debugabbrev(GDBJITctx *ctx) +{ + uint8_t *p = ctx->p; + + /* Abbrev #1: DW_TAG_compile_unit. */ + DUV(1); DUV(DW_TAG_compile_unit); + DB(DW_children_no); + DUV(DW_AT_name); DUV(DW_FORM_string); + DUV(DW_AT_low_pc); DUV(DW_FORM_addr); + DUV(DW_AT_high_pc); DUV(DW_FORM_addr); + DUV(DW_AT_stmt_list); DUV(DW_FORM_data4); + DB(0); DB(0); + + ctx->p = p; +} + +#define DLNE(op, s) (DB(DW_LNS_extended_op), DUV(1+(s)), DB((op))) + +/* Initialize .debug_line section. */ +static void LJ_FASTCALL gdbjit_debugline(GDBJITctx *ctx) +{ + uint8_t *p = ctx->p; + + DSECT(line, + DU16(2); /* DWARF version. */ + DSECT(header, + DB(1); /* Minimum instruction length. */ + DB(1); /* is_stmt. */ + DI8(0); /* Line base for special opcodes. */ + DB(2); /* Line range for special opcodes. */ + DB(3+1); /* Opcode base at DW_LNS_advance_line+1. */ + DB(0); DB(1); DB(1); /* Standard opcode lengths. */ + /* Directory table. */ + DB(0); + /* File name table. */ + DSTR(ctx->filename); DUV(0); DUV(0); DUV(0); + DB(0); + ) + + DLNE(DW_LNE_set_address, sizeof(uintptr_t)); DADDR(ctx->mcaddr); + if (ctx->lineno) { + DB(DW_LNS_advance_line); DSV(ctx->lineno-1); + } + DB(DW_LNS_copy); + DB(DW_LNS_advance_pc); DUV(ctx->szmcode); + DLNE(DW_LNE_end_sequence, 0); + ) + + ctx->p = p; +} + +#undef DLNE + +/* Undef shortcuts. */ +#undef DB +#undef DI8 +#undef DU16 +#undef DU32 +#undef DADDR +#undef DUV +#undef DSV +#undef DSTR +#undef DALIGNNOP +#undef DSECT + +/* Type of a section initializer callback. */ +typedef void (LJ_FASTCALL *GDBJITinitf)(GDBJITctx *ctx); + +/* Call section initializer and set the section offset and size. */ +static void gdbjit_initsect(GDBJITctx *ctx, int sect, GDBJITinitf initf) +{ + ctx->startp = ctx->p; + ctx->obj.sect[sect].ofs = (uintptr_t)((char *)ctx->p - (char *)&ctx->obj); + initf(ctx); + ctx->obj.sect[sect].size = (uintptr_t)(ctx->p - ctx->startp); +} + +#define SECTALIGN(p, a) \ + ((p) = (uint8_t *)(((uintptr_t)(p) + ((a)-1)) & ~(uintptr_t)((a)-1))) + +/* Build in-memory ELF object. */ +static void gdbjit_buildobj(GDBJITctx *ctx) +{ + GDBJITobj *obj = &ctx->obj; + /* Fill in ELF header and clear structures. */ + memcpy(&obj->hdr, &elfhdr_template, sizeof(ELFheader)); + memset(&obj->sect, 0, sizeof(ELFsectheader)*GDBJIT_SECT__MAX); + memset(&obj->sym, 0, sizeof(ELFsymbol)*GDBJIT_SYM__MAX); + /* Initialize sections. */ + ctx->p = obj->space; + gdbjit_initsect(ctx, GDBJIT_SECT_shstrtab, gdbjit_secthdr); + gdbjit_initsect(ctx, GDBJIT_SECT_strtab, gdbjit_symtab); + gdbjit_initsect(ctx, GDBJIT_SECT_debug_info, gdbjit_debuginfo); + gdbjit_initsect(ctx, GDBJIT_SECT_debug_abbrev, gdbjit_debugabbrev); + gdbjit_initsect(ctx, GDBJIT_SECT_debug_line, gdbjit_debugline); + SECTALIGN(ctx->p, sizeof(uintptr_t)); + gdbjit_initsect(ctx, GDBJIT_SECT_eh_frame, gdbjit_ehframe); + ctx->objsize = (size_t)((char *)ctx->p - (char *)obj); + lua_assert(ctx->objsize < sizeof(GDBJITobj)); +} + +#undef SECTALIGN + +/* -- Interface to GDB JIT API -------------------------------------------- */ + +static int gdbjit_lock; + +static void gdbjit_lock_acquire() +{ + while (__sync_lock_test_and_set(&gdbjit_lock, 1)) { + /* Just spin; futexes or pthreads aren't worth the portability cost. */ + } +} + +static void gdbjit_lock_release() +{ + __sync_lock_release(&gdbjit_lock); +} + +/* Add new entry to GDB JIT symbol chain. */ +static void gdbjit_newentry(lua_State *L, GDBJITctx *ctx) +{ + /* Allocate memory for GDB JIT entry and ELF object. */ + MSize sz = (MSize)(sizeof(GDBJITentryobj) - sizeof(GDBJITobj) + ctx->objsize); + GDBJITentryobj *eo = lj_mem_newt(L, sz, GDBJITentryobj); + memcpy(&eo->obj, &ctx->obj, ctx->objsize); /* Copy ELF object. */ + eo->sz = sz; + ctx->T->gdbjit_entry = (void *)eo; + /* Link new entry to chain and register it. */ + eo->entry.prev_entry = NULL; + gdbjit_lock_acquire(); + eo->entry.next_entry = __jit_debug_descriptor.first_entry; + if (eo->entry.next_entry) + eo->entry.next_entry->prev_entry = &eo->entry; + eo->entry.symfile_addr = (const char *)&eo->obj; + eo->entry.symfile_size = ctx->objsize; + __jit_debug_descriptor.first_entry = &eo->entry; + __jit_debug_descriptor.relevant_entry = &eo->entry; + __jit_debug_descriptor.action_flag = GDBJIT_REGISTER; + __jit_debug_register_code(); + gdbjit_lock_release(); +} + +/* Add debug info for newly compiled trace and notify GDB. */ +void lj_gdbjit_addtrace(jit_State *J, GCtrace *T) +{ + GDBJITctx ctx; + GCproto *pt = &gcref(T->startpt)->pt; + TraceNo parent = T->ir[REF_BASE].op1; + const BCIns *startpc = mref(T->startpc, const BCIns); + ctx.T = T; + ctx.mcaddr = (uintptr_t)T->mcode; + ctx.szmcode = T->szmcode; + ctx.spadjp = CFRAME_SIZE_JIT + + (MSize)(parent ? traceref(J, parent)->spadjust : 0); + ctx.spadj = CFRAME_SIZE_JIT + T->spadjust; + lua_assert(startpc >= proto_bc(pt) && startpc < proto_bc(pt) + pt->sizebc); + ctx.lineno = lj_debug_line(pt, proto_bcpos(pt, startpc)); + ctx.filename = proto_chunknamestr(pt); + if (*ctx.filename == '@' || *ctx.filename == '=') + ctx.filename++; + else + ctx.filename = "(string)"; + gdbjit_buildobj(&ctx); + gdbjit_newentry(J->L, &ctx); +} + +/* Delete debug info for trace and notify GDB. */ +void lj_gdbjit_deltrace(jit_State *J, GCtrace *T) +{ + GDBJITentryobj *eo = (GDBJITentryobj *)T->gdbjit_entry; + if (eo) { + gdbjit_lock_acquire(); + if (eo->entry.prev_entry) + eo->entry.prev_entry->next_entry = eo->entry.next_entry; + else + __jit_debug_descriptor.first_entry = eo->entry.next_entry; + if (eo->entry.next_entry) + eo->entry.next_entry->prev_entry = eo->entry.prev_entry; + __jit_debug_descriptor.relevant_entry = &eo->entry; + __jit_debug_descriptor.action_flag = GDBJIT_UNREGISTER; + __jit_debug_register_code(); + gdbjit_lock_release(); + lj_mem_free(J2G(J), eo, eo->sz); + } +} + +#endif +#endif diff --git a/lib/LuaJIT/lj_gdbjit.h b/lib/LuaJIT/lj_gdbjit.h new file mode 100644 index 0000000..bbaa156 --- /dev/null +++ b/lib/LuaJIT/lj_gdbjit.h @@ -0,0 +1,22 @@ +/* +** Client for the GDB JIT API. +** Copyright (C) 2005-2017 Mike Pall. See Copyright Notice in luajit.h +*/ + +#ifndef _LJ_GDBJIT_H +#define _LJ_GDBJIT_H + +#include "lj_obj.h" +#include "lj_jit.h" + +#if LJ_HASJIT && defined(LUAJIT_USE_GDBJIT) + +LJ_FUNC void lj_gdbjit_addtrace(jit_State *J, GCtrace *T); +LJ_FUNC void lj_gdbjit_deltrace(jit_State *J, GCtrace *T); + +#else +#define lj_gdbjit_addtrace(J, T) UNUSED(T) +#define lj_gdbjit_deltrace(J, T) UNUSED(T) +#endif + +#endif diff --git a/lib/LuaJIT/lj_gdbjit.o b/lib/LuaJIT/lj_gdbjit.o new file mode 100644 index 0000000000000000000000000000000000000000..ec16eb764e27e77170accb4e1d49724abd5fe5a1 GIT binary patch literal 920 zcmb<-^>JfjWMqH=Mg}_u1P><4zz~5X=l~XWVBlonU|?`}cD7Q`aQ6$8btTr<+Ff%Y=Qw|ho#v#s%L!1RuJSQtYJtZkCvqUeMfkCghGPfi# z2}qX|LFfz^t0c9egh4MQu_Tc}FR8egK`%K!H#ap8D3+IBlBx#`0Nvt}#N=#D8^{uo z2I>NZ6&spv2Q)s6ixfRzAw?hw5IIpJo*o>4Vjv&{ zq2MIUei&^El!39~ihztDWDXJ=T`h=@E!9Lo^{jx>>_8fXL4E}R2?zyD+n9a_OF=E- Yg{p&5==KZZ(9a81=nB&arP1{R0E%fR8UO$Q literal 0 HcmV?d00001 diff --git a/lib/LuaJIT/lj_gdbjit_dyn.o b/lib/LuaJIT/lj_gdbjit_dyn.o new file mode 100644 index 0000000000000000000000000000000000000000..ec16eb764e27e77170accb4e1d49724abd5fe5a1 GIT binary patch literal 920 zcmb<-^>JfjWMqH=Mg}_u1P><4zz~5X=l~XWVBlonU|?`}cD7Q`aQ6$8btTr<+Ff%Y=Qw|ho#v#s%L!1RuJSQtYJtZkCvqUeMfkCghGPfi# z2}qX|LFfz^t0c9egh4MQu_Tc}FR8egK`%K!H#ap8D3+IBlBx#`0Nvt}#N=#D8^{uo z2I>NZ6&spv2Q)s6ixfRzAw?hw5IIpJo*o>4Vjv&{ zq2MIUei&^El!39~ihztDWDXJ=T`h=@E!9Lo^{jx>>_8fXL4E}R2?zyD+n9a_OF=E- Yg{p&5==KZZ(9a81=nB&arP1{R0E%fR8UO$Q literal 0 HcmV?d00001 diff --git a/lib/LuaJIT/lj_ir.c b/lib/LuaJIT/lj_ir.c new file mode 100644 index 0000000..5baece6 --- /dev/null +++ b/lib/LuaJIT/lj_ir.c @@ -0,0 +1,494 @@ +/* +** SSA IR (Intermediate Representation) emitter. +** Copyright (C) 2005-2017 Mike Pall. See Copyright Notice in luajit.h +*/ + +#define lj_ir_c +#define LUA_CORE + +/* For pointers to libc/libm functions. */ +#include +#include + +#include "lj_obj.h" + +#if LJ_HASJIT + +#include "lj_gc.h" +#include "lj_buf.h" +#include "lj_str.h" +#include "lj_tab.h" +#include "lj_ir.h" +#include "lj_jit.h" +#include "lj_ircall.h" +#include "lj_iropt.h" +#include "lj_trace.h" +#if LJ_HASFFI +#include "lj_ctype.h" +#include "lj_cdata.h" +#include "lj_carith.h" +#endif +#include "lj_vm.h" +#include "lj_strscan.h" +#include "lj_strfmt.h" +#include "lj_lib.h" + +/* Some local macros to save typing. Undef'd at the end. */ +#define IR(ref) (&J->cur.ir[(ref)]) +#define fins (&J->fold.ins) + +/* Pass IR on to next optimization in chain (FOLD). */ +#define emitir(ot, a, b) (lj_ir_set(J, (ot), (a), (b)), lj_opt_fold(J)) + +/* -- IR tables ----------------------------------------------------------- */ + +/* IR instruction modes. */ +LJ_DATADEF const uint8_t lj_ir_mode[IR__MAX+1] = { +IRDEF(IRMODE) + 0 +}; + +/* IR type sizes. */ +LJ_DATADEF const uint8_t lj_ir_type_size[IRT__MAX+1] = { +#define IRTSIZE(name, size) size, +IRTDEF(IRTSIZE) +#undef IRTSIZE + 0 +}; + +/* C call info for CALL* instructions. */ +LJ_DATADEF const CCallInfo lj_ir_callinfo[] = { +#define IRCALLCI(cond, name, nargs, kind, type, flags) \ + { (ASMFunction)IRCALLCOND_##cond(name), \ + (nargs)|(CCI_CALL_##kind)|(IRT_##type<irbuf + J->irbotlim; + MSize szins = J->irtoplim - J->irbotlim; + if (szins) { + baseir = (IRIns *)lj_mem_realloc(J->L, baseir, szins*sizeof(IRIns), + 2*szins*sizeof(IRIns)); + J->irtoplim = J->irbotlim + 2*szins; + } else { + baseir = (IRIns *)lj_mem_realloc(J->L, NULL, 0, LJ_MIN_IRSZ*sizeof(IRIns)); + J->irbotlim = REF_BASE - LJ_MIN_IRSZ/4; + J->irtoplim = J->irbotlim + LJ_MIN_IRSZ; + } + J->cur.ir = J->irbuf = baseir - J->irbotlim; +} + +/* Grow IR buffer at the bottom or shift it up. */ +static void lj_ir_growbot(jit_State *J) +{ + IRIns *baseir = J->irbuf + J->irbotlim; + MSize szins = J->irtoplim - J->irbotlim; + lua_assert(szins != 0); + lua_assert(J->cur.nk == J->irbotlim || J->cur.nk-1 == J->irbotlim); + if (J->cur.nins + (szins >> 1) < J->irtoplim) { + /* More than half of the buffer is free on top: shift up by a quarter. */ + MSize ofs = szins >> 2; + memmove(baseir + ofs, baseir, (J->cur.nins - J->irbotlim)*sizeof(IRIns)); + J->irbotlim -= ofs; + J->irtoplim -= ofs; + J->cur.ir = J->irbuf = baseir - J->irbotlim; + } else { + /* Double the buffer size, but split the growth amongst top/bottom. */ + IRIns *newbase = lj_mem_newt(J->L, 2*szins*sizeof(IRIns), IRIns); + MSize ofs = szins >= 256 ? 128 : (szins >> 1); /* Limit bottom growth. */ + memcpy(newbase + ofs, baseir, (J->cur.nins - J->irbotlim)*sizeof(IRIns)); + lj_mem_free(G(J->L), baseir, szins*sizeof(IRIns)); + J->irbotlim -= ofs; + J->irtoplim = J->irbotlim + 2*szins; + J->cur.ir = J->irbuf = newbase - J->irbotlim; + } +} + +/* Emit IR without any optimizations. */ +TRef LJ_FASTCALL lj_ir_emit(jit_State *J) +{ + IRRef ref = lj_ir_nextins(J); + IRIns *ir = IR(ref); + IROp op = fins->o; + ir->prev = J->chain[op]; + J->chain[op] = (IRRef1)ref; + ir->o = op; + ir->op1 = fins->op1; + ir->op2 = fins->op2; + J->guardemit.irt |= fins->t.irt; + return TREF(ref, irt_t((ir->t = fins->t))); +} + +/* Emit call to a C function. */ +TRef lj_ir_call(jit_State *J, IRCallID id, ...) +{ + const CCallInfo *ci = &lj_ir_callinfo[id]; + uint32_t n = CCI_NARGS(ci); + TRef tr = TREF_NIL; + va_list argp; + va_start(argp, id); + if ((ci->flags & CCI_L)) n--; + if (n > 0) + tr = va_arg(argp, IRRef); + while (n-- > 1) + tr = emitir(IRT(IR_CARG, IRT_NIL), tr, va_arg(argp, IRRef)); + va_end(argp); + if (CCI_OP(ci) == IR_CALLS) + J->needsnap = 1; /* Need snapshot after call with side effect. */ + return emitir(CCI_OPTYPE(ci), tr, id); +} + +/* Load field of type t from GG_State + offset. Must be 32 bit aligned. */ +LJ_FUNC TRef lj_ir_ggfload(jit_State *J, IRType t, uintptr_t ofs) +{ + lua_assert((ofs & 3) == 0); + ofs >>= 2; + lua_assert(ofs >= IRFL__MAX && ofs <= 0x3ff); /* 10 bit FOLD key limit. */ + lj_ir_set(J, IRT(IR_FLOAD, t), REF_NIL, ofs); + return lj_opt_fold(J); +} + +/* -- Interning of constants ---------------------------------------------- */ + +/* +** IR instructions for constants are kept between J->cur.nk >= ref < REF_BIAS. +** They are chained like all other instructions, but grow downwards. +** The are interned (like strings in the VM) to facilitate reference +** comparisons. The same constant must get the same reference. +*/ + +/* Get ref of next IR constant and optionally grow IR. +** Note: this may invalidate all IRIns *! +*/ +static LJ_AINLINE IRRef ir_nextk(jit_State *J) +{ + IRRef ref = J->cur.nk; + if (LJ_UNLIKELY(ref <= J->irbotlim)) lj_ir_growbot(J); + J->cur.nk = --ref; + return ref; +} + +/* Get ref of next 64 bit IR constant and optionally grow IR. +** Note: this may invalidate all IRIns *! +*/ +static LJ_AINLINE IRRef ir_nextk64(jit_State *J) +{ + IRRef ref = J->cur.nk - 2; + lua_assert(J->state != LJ_TRACE_ASM); + if (LJ_UNLIKELY(ref < J->irbotlim)) lj_ir_growbot(J); + J->cur.nk = ref; + return ref; +} + +#if LJ_GC64 +#define ir_nextkgc ir_nextk64 +#else +#define ir_nextkgc ir_nextk +#endif + +/* Intern int32_t constant. */ +TRef LJ_FASTCALL lj_ir_kint(jit_State *J, int32_t k) +{ + IRIns *ir, *cir = J->cur.ir; + IRRef ref; + for (ref = J->chain[IR_KINT]; ref; ref = cir[ref].prev) + if (cir[ref].i == k) + goto found; + ref = ir_nextk(J); + ir = IR(ref); + ir->i = k; + ir->t.irt = IRT_INT; + ir->o = IR_KINT; + ir->prev = J->chain[IR_KINT]; + J->chain[IR_KINT] = (IRRef1)ref; +found: + return TREF(ref, IRT_INT); +} + +/* Intern 64 bit constant, given by its 64 bit pattern. */ +TRef lj_ir_k64(jit_State *J, IROp op, uint64_t u64) +{ + IRIns *ir, *cir = J->cur.ir; + IRRef ref; + IRType t = op == IR_KNUM ? IRT_NUM : IRT_I64; + for (ref = J->chain[op]; ref; ref = cir[ref].prev) + if (ir_k64(&cir[ref])->u64 == u64) + goto found; + ref = ir_nextk64(J); + ir = IR(ref); + ir[1].tv.u64 = u64; + ir->t.irt = t; + ir->o = op; + ir->op12 = 0; + ir->prev = J->chain[op]; + J->chain[op] = (IRRef1)ref; +found: + return TREF(ref, t); +} + +/* Intern FP constant, given by its 64 bit pattern. */ +TRef lj_ir_knum_u64(jit_State *J, uint64_t u64) +{ + return lj_ir_k64(J, IR_KNUM, u64); +} + +/* Intern 64 bit integer constant. */ +TRef lj_ir_kint64(jit_State *J, uint64_t u64) +{ + return lj_ir_k64(J, IR_KINT64, u64); +} + +/* Check whether a number is int and return it. -0 is NOT considered an int. */ +static int numistrueint(lua_Number n, int32_t *kp) +{ + int32_t k = lj_num2int(n); + if (n == (lua_Number)k) { + if (kp) *kp = k; + if (k == 0) { /* Special check for -0. */ + TValue tv; + setnumV(&tv, n); + if (tv.u32.hi != 0) + return 0; + } + return 1; + } + return 0; +} + +/* Intern number as int32_t constant if possible, otherwise as FP constant. */ +TRef lj_ir_knumint(jit_State *J, lua_Number n) +{ + int32_t k; + if (numistrueint(n, &k)) + return lj_ir_kint(J, k); + else + return lj_ir_knum(J, n); +} + +/* Intern GC object "constant". */ +TRef lj_ir_kgc(jit_State *J, GCobj *o, IRType t) +{ + IRIns *ir, *cir = J->cur.ir; + IRRef ref; + lua_assert(!isdead(J2G(J), o)); + for (ref = J->chain[IR_KGC]; ref; ref = cir[ref].prev) + if (ir_kgc(&cir[ref]) == o) + goto found; + ref = ir_nextkgc(J); + ir = IR(ref); + /* NOBARRIER: Current trace is a GC root. */ + ir->op12 = 0; + setgcref(ir[LJ_GC64].gcr, o); + ir->t.irt = (uint8_t)t; + ir->o = IR_KGC; + ir->prev = J->chain[IR_KGC]; + J->chain[IR_KGC] = (IRRef1)ref; +found: + return TREF(ref, t); +} + +/* Allocate GCtrace constant placeholder (no interning). */ +TRef lj_ir_ktrace(jit_State *J) +{ + IRRef ref = ir_nextkgc(J); + IRIns *ir = IR(ref); + lua_assert(irt_toitype_(IRT_P64) == LJ_TTRACE); + ir->t.irt = IRT_P64; + ir->o = LJ_GC64 ? IR_KNUM : IR_KNULL; /* Not IR_KGC yet, but same size. */ + ir->op12 = 0; + ir->prev = 0; + return TREF(ref, IRT_P64); +} + +/* Intern pointer constant. */ +TRef lj_ir_kptr_(jit_State *J, IROp op, void *ptr) +{ + IRIns *ir, *cir = J->cur.ir; + IRRef ref; +#if LJ_64 && !LJ_GC64 + lua_assert((void *)(uintptr_t)u32ptr(ptr) == ptr); +#endif + for (ref = J->chain[op]; ref; ref = cir[ref].prev) + if (ir_kptr(&cir[ref]) == ptr) + goto found; +#if LJ_GC64 + ref = ir_nextk64(J); +#else + ref = ir_nextk(J); +#endif + ir = IR(ref); + ir->op12 = 0; + setmref(ir[LJ_GC64].ptr, ptr); + ir->t.irt = IRT_PGC; + ir->o = op; + ir->prev = J->chain[op]; + J->chain[op] = (IRRef1)ref; +found: + return TREF(ref, IRT_PGC); +} + +/* Intern typed NULL constant. */ +TRef lj_ir_knull(jit_State *J, IRType t) +{ + IRIns *ir, *cir = J->cur.ir; + IRRef ref; + for (ref = J->chain[IR_KNULL]; ref; ref = cir[ref].prev) + if (irt_t(cir[ref].t) == t) + goto found; + ref = ir_nextk(J); + ir = IR(ref); + ir->i = 0; + ir->t.irt = (uint8_t)t; + ir->o = IR_KNULL; + ir->prev = J->chain[IR_KNULL]; + J->chain[IR_KNULL] = (IRRef1)ref; +found: + return TREF(ref, t); +} + +/* Intern key slot. */ +TRef lj_ir_kslot(jit_State *J, TRef key, IRRef slot) +{ + IRIns *ir, *cir = J->cur.ir; + IRRef2 op12 = IRREF2((IRRef1)key, (IRRef1)slot); + IRRef ref; + /* Const part is not touched by CSE/DCE, so 0-65535 is ok for IRMlit here. */ + lua_assert(tref_isk(key) && slot == (IRRef)(IRRef1)slot); + for (ref = J->chain[IR_KSLOT]; ref; ref = cir[ref].prev) + if (cir[ref].op12 == op12) + goto found; + ref = ir_nextk(J); + ir = IR(ref); + ir->op12 = op12; + ir->t.irt = IRT_P32; + ir->o = IR_KSLOT; + ir->prev = J->chain[IR_KSLOT]; + J->chain[IR_KSLOT] = (IRRef1)ref; +found: + return TREF(ref, IRT_P32); +} + +/* -- Access to IR constants ---------------------------------------------- */ + +/* Copy value of IR constant. */ +void lj_ir_kvalue(lua_State *L, TValue *tv, const IRIns *ir) +{ + UNUSED(L); + lua_assert(ir->o != IR_KSLOT); /* Common mistake. */ + switch (ir->o) { + case IR_KPRI: setpriV(tv, irt_toitype(ir->t)); break; + case IR_KINT: setintV(tv, ir->i); break; + case IR_KGC: setgcV(L, tv, ir_kgc(ir), irt_toitype(ir->t)); break; + case IR_KPTR: case IR_KKPTR: setlightudV(tv, ir_kptr(ir)); break; + case IR_KNULL: setlightudV(tv, NULL); break; + case IR_KNUM: setnumV(tv, ir_knum(ir)->n); break; +#if LJ_HASFFI + case IR_KINT64: { + GCcdata *cd = lj_cdata_new_(L, CTID_INT64, 8); + *(uint64_t *)cdataptr(cd) = ir_kint64(ir)->u64; + setcdataV(L, tv, cd); + break; + } +#endif + default: lua_assert(0); break; + } +} + +/* -- Convert IR operand types -------------------------------------------- */ + +/* Convert from string to number. */ +TRef LJ_FASTCALL lj_ir_tonumber(jit_State *J, TRef tr) +{ + if (!tref_isnumber(tr)) { + if (tref_isstr(tr)) + tr = emitir(IRTG(IR_STRTO, IRT_NUM), tr, 0); + else + lj_trace_err(J, LJ_TRERR_BADTYPE); + } + return tr; +} + +/* Convert from integer or string to number. */ +TRef LJ_FASTCALL lj_ir_tonum(jit_State *J, TRef tr) +{ + if (!tref_isnum(tr)) { + if (tref_isinteger(tr)) + tr = emitir(IRTN(IR_CONV), tr, IRCONV_NUM_INT); + else if (tref_isstr(tr)) + tr = emitir(IRTG(IR_STRTO, IRT_NUM), tr, 0); + else + lj_trace_err(J, LJ_TRERR_BADTYPE); + } + return tr; +} + +/* Convert from integer or number to string. */ +TRef LJ_FASTCALL lj_ir_tostr(jit_State *J, TRef tr) +{ + if (!tref_isstr(tr)) { + if (!tref_isnumber(tr)) + lj_trace_err(J, LJ_TRERR_BADTYPE); + tr = emitir(IRT(IR_TOSTR, IRT_STR), tr, + tref_isnum(tr) ? IRTOSTR_NUM : IRTOSTR_INT); + } + return tr; +} + +/* -- Miscellaneous IR ops ------------------------------------------------ */ + +/* Evaluate numeric comparison. */ +int lj_ir_numcmp(lua_Number a, lua_Number b, IROp op) +{ + switch (op) { + case IR_EQ: return (a == b); + case IR_NE: return (a != b); + case IR_LT: return (a < b); + case IR_GE: return (a >= b); + case IR_LE: return (a <= b); + case IR_GT: return (a > b); + case IR_ULT: return !(a >= b); + case IR_UGE: return !(a < b); + case IR_ULE: return !(a > b); + case IR_UGT: return !(a <= b); + default: lua_assert(0); return 0; + } +} + +/* Evaluate string comparison. */ +int lj_ir_strcmp(GCstr *a, GCstr *b, IROp op) +{ + int res = lj_str_cmp(a, b); + switch (op) { + case IR_LT: return (res < 0); + case IR_GE: return (res >= 0); + case IR_LE: return (res <= 0); + case IR_GT: return (res > 0); + default: lua_assert(0); return 0; + } +} + +/* Rollback IR to previous state. */ +void lj_ir_rollback(jit_State *J, IRRef ref) +{ + IRRef nins = J->cur.nins; + while (nins > ref) { + IRIns *ir; + nins--; + ir = IR(nins); + J->chain[ir->o] = ir->prev; + } + J->cur.nins = nins; +} + +#undef IR +#undef fins +#undef emitir + +#endif diff --git a/lib/LuaJIT/lj_ir.h b/lib/LuaJIT/lj_ir.h new file mode 100644 index 0000000..8057a75 --- /dev/null +++ b/lib/LuaJIT/lj_ir.h @@ -0,0 +1,590 @@ +/* +** SSA IR (Intermediate Representation) format. +** Copyright (C) 2005-2017 Mike Pall. See Copyright Notice in luajit.h +*/ + +#ifndef _LJ_IR_H +#define _LJ_IR_H + +#include "lj_obj.h" + +/* -- IR instructions ----------------------------------------------------- */ + +/* IR instruction definition. Order matters, see below. ORDER IR */ +#define IRDEF(_) \ + /* Guarded assertions. */ \ + /* Must be properly aligned to flip opposites (^1) and (un)ordered (^4). */ \ + _(LT, N , ref, ref) \ + _(GE, N , ref, ref) \ + _(LE, N , ref, ref) \ + _(GT, N , ref, ref) \ + \ + _(ULT, N , ref, ref) \ + _(UGE, N , ref, ref) \ + _(ULE, N , ref, ref) \ + _(UGT, N , ref, ref) \ + \ + _(EQ, C , ref, ref) \ + _(NE, C , ref, ref) \ + \ + _(ABC, N , ref, ref) \ + _(RETF, S , ref, ref) \ + \ + /* Miscellaneous ops. */ \ + _(NOP, N , ___, ___) \ + _(BASE, N , lit, lit) \ + _(PVAL, N , lit, ___) \ + _(GCSTEP, S , ___, ___) \ + _(HIOP, S , ref, ref) \ + _(LOOP, S , ___, ___) \ + _(USE, S , ref, ___) \ + _(PHI, S , ref, ref) \ + _(RENAME, S , ref, lit) \ + _(PROF, S , ___, ___) \ + \ + /* Constants. */ \ + _(KPRI, N , ___, ___) \ + _(KINT, N , cst, ___) \ + _(KGC, N , cst, ___) \ + _(KPTR, N , cst, ___) \ + _(KKPTR, N , cst, ___) \ + _(KNULL, N , cst, ___) \ + _(KNUM, N , cst, ___) \ + _(KINT64, N , cst, ___) \ + _(KSLOT, N , ref, lit) \ + \ + /* Bit ops. */ \ + _(BNOT, N , ref, ___) \ + _(BSWAP, N , ref, ___) \ + _(BAND, C , ref, ref) \ + _(BOR, C , ref, ref) \ + _(BXOR, C , ref, ref) \ + _(BSHL, N , ref, ref) \ + _(BSHR, N , ref, ref) \ + _(BSAR, N , ref, ref) \ + _(BROL, N , ref, ref) \ + _(BROR, N , ref, ref) \ + \ + /* Arithmetic ops. ORDER ARITH */ \ + _(ADD, C , ref, ref) \ + _(SUB, N , ref, ref) \ + _(MUL, C , ref, ref) \ + _(DIV, N , ref, ref) \ + _(MOD, N , ref, ref) \ + _(POW, N , ref, ref) \ + _(NEG, N , ref, ref) \ + \ + _(ABS, N , ref, ref) \ + _(ATAN2, N , ref, ref) \ + _(LDEXP, N , ref, ref) \ + _(MIN, C , ref, ref) \ + _(MAX, C , ref, ref) \ + _(FPMATH, N , ref, lit) \ + \ + /* Overflow-checking arithmetic ops. */ \ + _(ADDOV, CW, ref, ref) \ + _(SUBOV, NW, ref, ref) \ + _(MULOV, CW, ref, ref) \ + \ + /* Memory ops. A = array, H = hash, U = upvalue, F = field, S = stack. */ \ + \ + /* Memory references. */ \ + _(AREF, R , ref, ref) \ + _(HREFK, R , ref, ref) \ + _(HREF, L , ref, ref) \ + _(NEWREF, S , ref, ref) \ + _(UREFO, LW, ref, lit) \ + _(UREFC, LW, ref, lit) \ + _(FREF, R , ref, lit) \ + _(STRREF, N , ref, ref) \ + _(LREF, L , ___, ___) \ + \ + /* Loads and Stores. These must be in the same order. */ \ + _(ALOAD, L , ref, ___) \ + _(HLOAD, L , ref, ___) \ + _(ULOAD, L , ref, ___) \ + _(FLOAD, L , ref, lit) \ + _(XLOAD, L , ref, lit) \ + _(SLOAD, L , lit, lit) \ + _(VLOAD, L , ref, ___) \ + \ + _(ASTORE, S , ref, ref) \ + _(HSTORE, S , ref, ref) \ + _(USTORE, S , ref, ref) \ + _(FSTORE, S , ref, ref) \ + _(XSTORE, S , ref, ref) \ + \ + /* Allocations. */ \ + _(SNEW, N , ref, ref) /* CSE is ok, not marked as A. */ \ + _(XSNEW, A , ref, ref) \ + _(TNEW, AW, lit, lit) \ + _(TDUP, AW, ref, ___) \ + _(CNEW, AW, ref, ref) \ + _(CNEWI, NW, ref, ref) /* CSE is ok, not marked as A. */ \ + \ + /* Buffer operations. */ \ + _(BUFHDR, L , ref, lit) \ + _(BUFPUT, L , ref, ref) \ + _(BUFSTR, A , ref, ref) \ + \ + /* Barriers. */ \ + _(TBAR, S , ref, ___) \ + _(OBAR, S , ref, ref) \ + _(XBAR, S , ___, ___) \ + \ + /* Type conversions. */ \ + _(CONV, NW, ref, lit) \ + _(TOBIT, N , ref, ref) \ + _(TOSTR, N , ref, lit) \ + _(STRTO, N , ref, ___) \ + \ + /* Calls. */ \ + _(CALLN, N , ref, lit) \ + _(CALLA, A , ref, lit) \ + _(CALLL, L , ref, lit) \ + _(CALLS, S , ref, lit) \ + _(CALLXS, S , ref, ref) \ + _(CARG, N , ref, ref) \ + \ + /* End of list. */ + +/* IR opcodes (max. 256). */ +typedef enum { +#define IRENUM(name, m, m1, m2) IR_##name, +IRDEF(IRENUM) +#undef IRENUM + IR__MAX +} IROp; + +/* Stored opcode. */ +typedef uint8_t IROp1; + +LJ_STATIC_ASSERT(((int)IR_EQ^1) == (int)IR_NE); +LJ_STATIC_ASSERT(((int)IR_LT^1) == (int)IR_GE); +LJ_STATIC_ASSERT(((int)IR_LE^1) == (int)IR_GT); +LJ_STATIC_ASSERT(((int)IR_LT^3) == (int)IR_GT); +LJ_STATIC_ASSERT(((int)IR_LT^4) == (int)IR_ULT); + +/* Delta between xLOAD and xSTORE. */ +#define IRDELTA_L2S ((int)IR_ASTORE - (int)IR_ALOAD) + +LJ_STATIC_ASSERT((int)IR_HLOAD + IRDELTA_L2S == (int)IR_HSTORE); +LJ_STATIC_ASSERT((int)IR_ULOAD + IRDELTA_L2S == (int)IR_USTORE); +LJ_STATIC_ASSERT((int)IR_FLOAD + IRDELTA_L2S == (int)IR_FSTORE); +LJ_STATIC_ASSERT((int)IR_XLOAD + IRDELTA_L2S == (int)IR_XSTORE); + +/* -- Named IR literals --------------------------------------------------- */ + +/* FPMATH sub-functions. ORDER FPM. */ +#define IRFPMDEF(_) \ + _(FLOOR) _(CEIL) _(TRUNC) /* Must be first and in this order. */ \ + _(SQRT) _(EXP) _(EXP2) _(LOG) _(LOG2) _(LOG10) \ + _(SIN) _(COS) _(TAN) \ + _(OTHER) + +typedef enum { +#define FPMENUM(name) IRFPM_##name, +IRFPMDEF(FPMENUM) +#undef FPMENUM + IRFPM__MAX +} IRFPMathOp; + +/* FLOAD fields. */ +#define IRFLDEF(_) \ + _(STR_LEN, offsetof(GCstr, len)) \ + _(FUNC_ENV, offsetof(GCfunc, l.env)) \ + _(FUNC_PC, offsetof(GCfunc, l.pc)) \ + _(FUNC_FFID, offsetof(GCfunc, l.ffid)) \ + _(THREAD_ENV, offsetof(lua_State, env)) \ + _(TAB_META, offsetof(GCtab, metatable)) \ + _(TAB_ARRAY, offsetof(GCtab, array)) \ + _(TAB_NODE, offsetof(GCtab, node)) \ + _(TAB_ASIZE, offsetof(GCtab, asize)) \ + _(TAB_HMASK, offsetof(GCtab, hmask)) \ + _(TAB_NOMM, offsetof(GCtab, nomm)) \ + _(UDATA_META, offsetof(GCudata, metatable)) \ + _(UDATA_UDTYPE, offsetof(GCudata, udtype)) \ + _(UDATA_FILE, sizeof(GCudata)) \ + _(CDATA_CTYPEID, offsetof(GCcdata, ctypeid)) \ + _(CDATA_PTR, sizeof(GCcdata)) \ + _(CDATA_INT, sizeof(GCcdata)) \ + _(CDATA_INT64, sizeof(GCcdata)) \ + _(CDATA_INT64_4, sizeof(GCcdata) + 4) + +typedef enum { +#define FLENUM(name, ofs) IRFL_##name, +IRFLDEF(FLENUM) +#undef FLENUM + IRFL__MAX +} IRFieldID; + +/* SLOAD mode bits, stored in op2. */ +#define IRSLOAD_PARENT 0x01 /* Coalesce with parent trace. */ +#define IRSLOAD_FRAME 0x02 /* Load 32 bits of ftsz. */ +#define IRSLOAD_TYPECHECK 0x04 /* Needs type check. */ +#define IRSLOAD_CONVERT 0x08 /* Number to integer conversion. */ +#define IRSLOAD_READONLY 0x10 /* Read-only, omit slot store. */ +#define IRSLOAD_INHERIT 0x20 /* Inherited by exits/side traces. */ + +/* XLOAD mode, stored in op2. */ +#define IRXLOAD_READONLY 1 /* Load from read-only data. */ +#define IRXLOAD_VOLATILE 2 /* Load from volatile data. */ +#define IRXLOAD_UNALIGNED 4 /* Unaligned load. */ + +/* BUFHDR mode, stored in op2. */ +#define IRBUFHDR_RESET 0 /* Reset buffer. */ +#define IRBUFHDR_APPEND 1 /* Append to buffer. */ + +/* CONV mode, stored in op2. */ +#define IRCONV_SRCMASK 0x001f /* Source IRType. */ +#define IRCONV_DSTMASK 0x03e0 /* Dest. IRType (also in ir->t). */ +#define IRCONV_DSH 5 +#define IRCONV_NUM_INT ((IRT_NUM<>2)&3)) +#define irm_iscomm(m) ((m) & IRM_C) +#define irm_kind(m) ((m) & IRM_S) + +#define IRMODE(name, m, m1, m2) (((IRM##m1)|((IRM##m2)<<2)|(IRM_##m))^IRM_W), + +LJ_DATA const uint8_t lj_ir_mode[IR__MAX+1]; + +/* -- IR instruction types ------------------------------------------------ */ + +#define IRTSIZE_PGC (LJ_GC64 ? 8 : 4) + +/* Map of itypes to non-negative numbers and their sizes. ORDER LJ_T. +** LJ_TUPVAL/LJ_TTRACE never appear in a TValue. Use these itypes for +** IRT_P32 and IRT_P64, which never escape the IR. +** The various integers are only used in the IR and can only escape to +** a TValue after implicit or explicit conversion. Their types must be +** contiguous and next to IRT_NUM (see the typerange macros below). +*/ +#define IRTDEF(_) \ + _(NIL, 4) _(FALSE, 4) _(TRUE, 4) _(LIGHTUD, LJ_64 ? 8 : 4) \ + _(STR, IRTSIZE_PGC) _(P32, 4) _(THREAD, IRTSIZE_PGC) _(PROTO, IRTSIZE_PGC) \ + _(FUNC, IRTSIZE_PGC) _(P64, 8) _(CDATA, IRTSIZE_PGC) _(TAB, IRTSIZE_PGC) \ + _(UDATA, IRTSIZE_PGC) \ + _(FLOAT, 4) _(NUM, 8) _(I8, 1) _(U8, 1) _(I16, 2) _(U16, 2) \ + _(INT, 4) _(U32, 4) _(I64, 8) _(U64, 8) \ + _(SOFTFP, 4) /* There is room for 8 more types. */ + +/* IR result type and flags (8 bit). */ +typedef enum { +#define IRTENUM(name, size) IRT_##name, +IRTDEF(IRTENUM) +#undef IRTENUM + IRT__MAX, + + /* Native pointer type and the corresponding integer type. */ + IRT_PTR = LJ_64 ? IRT_P64 : IRT_P32, + IRT_PGC = LJ_GC64 ? IRT_P64 : IRT_P32, + IRT_IGC = LJ_GC64 ? IRT_I64 : IRT_INT, + IRT_INTP = LJ_64 ? IRT_I64 : IRT_INT, + IRT_UINTP = LJ_64 ? IRT_U64 : IRT_U32, + + /* Additional flags. */ + IRT_MARK = 0x20, /* Marker for misc. purposes. */ + IRT_ISPHI = 0x40, /* Instruction is left or right PHI operand. */ + IRT_GUARD = 0x80, /* Instruction is a guard. */ + + /* Masks. */ + IRT_TYPE = 0x1f, + IRT_T = 0xff +} IRType; + +#define irtype_ispri(irt) ((uint32_t)(irt) <= IRT_TRUE) + +/* Stored IRType. */ +typedef struct IRType1 { uint8_t irt; } IRType1; + +#define IRT(o, t) ((uint32_t)(((o)<<8) | (t))) +#define IRTI(o) (IRT((o), IRT_INT)) +#define IRTN(o) (IRT((o), IRT_NUM)) +#define IRTG(o, t) (IRT((o), IRT_GUARD|(t))) +#define IRTGI(o) (IRT((o), IRT_GUARD|IRT_INT)) + +#define irt_t(t) ((IRType)(t).irt) +#define irt_type(t) ((IRType)((t).irt & IRT_TYPE)) +#define irt_sametype(t1, t2) ((((t1).irt ^ (t2).irt) & IRT_TYPE) == 0) +#define irt_typerange(t, first, last) \ + ((uint32_t)((t).irt & IRT_TYPE) - (uint32_t)(first) <= (uint32_t)(last-first)) + +#define irt_isnil(t) (irt_type(t) == IRT_NIL) +#define irt_ispri(t) ((uint32_t)irt_type(t) <= IRT_TRUE) +#define irt_islightud(t) (irt_type(t) == IRT_LIGHTUD) +#define irt_isstr(t) (irt_type(t) == IRT_STR) +#define irt_istab(t) (irt_type(t) == IRT_TAB) +#define irt_iscdata(t) (irt_type(t) == IRT_CDATA) +#define irt_isfloat(t) (irt_type(t) == IRT_FLOAT) +#define irt_isnum(t) (irt_type(t) == IRT_NUM) +#define irt_isint(t) (irt_type(t) == IRT_INT) +#define irt_isi8(t) (irt_type(t) == IRT_I8) +#define irt_isu8(t) (irt_type(t) == IRT_U8) +#define irt_isi16(t) (irt_type(t) == IRT_I16) +#define irt_isu16(t) (irt_type(t) == IRT_U16) +#define irt_isu32(t) (irt_type(t) == IRT_U32) +#define irt_isi64(t) (irt_type(t) == IRT_I64) +#define irt_isu64(t) (irt_type(t) == IRT_U64) + +#define irt_isfp(t) (irt_isnum(t) || irt_isfloat(t)) +#define irt_isinteger(t) (irt_typerange((t), IRT_I8, IRT_INT)) +#define irt_isgcv(t) (irt_typerange((t), IRT_STR, IRT_UDATA)) +#define irt_isaddr(t) (irt_typerange((t), IRT_LIGHTUD, IRT_UDATA)) +#define irt_isint64(t) (irt_typerange((t), IRT_I64, IRT_U64)) + +#if LJ_GC64 +/* Include IRT_NIL, so IR(ASMREF_L) (aka REF_NIL) is considered 64 bit. */ +#define IRT_IS64 \ + ((1u<> irt_type(t)) & 1) +#define irt_is64orfp(t) (((IRT_IS64|(1u<>irt_type(t)) & 1) + +#define irt_size(t) (lj_ir_type_size[irt_t((t))]) + +LJ_DATA const uint8_t lj_ir_type_size[]; + +static LJ_AINLINE IRType itype2irt(const TValue *tv) +{ + if (tvisint(tv)) + return IRT_INT; + else if (tvisnum(tv)) + return IRT_NUM; +#if LJ_64 && !LJ_GC64 + else if (tvislightud(tv)) + return IRT_LIGHTUD; +#endif + else + return (IRType)~itype(tv); +} + +static LJ_AINLINE uint32_t irt_toitype_(IRType t) +{ + lua_assert(!LJ_64 || LJ_GC64 || t != IRT_LIGHTUD); + if (LJ_DUALNUM && t > IRT_NUM) { + return LJ_TISNUM; + } else { + lua_assert(t <= IRT_NUM); + return ~(uint32_t)t; + } +} + +#define irt_toitype(t) irt_toitype_(irt_type((t))) + +#define irt_isguard(t) ((t).irt & IRT_GUARD) +#define irt_ismarked(t) ((t).irt & IRT_MARK) +#define irt_setmark(t) ((t).irt |= IRT_MARK) +#define irt_clearmark(t) ((t).irt &= ~IRT_MARK) +#define irt_isphi(t) ((t).irt & IRT_ISPHI) +#define irt_setphi(t) ((t).irt |= IRT_ISPHI) +#define irt_clearphi(t) ((t).irt &= ~IRT_ISPHI) + +/* Stored combined IR opcode and type. */ +typedef uint16_t IROpT; + +/* -- IR references ------------------------------------------------------- */ + +/* IR references. */ +typedef uint16_t IRRef1; /* One stored reference. */ +typedef uint32_t IRRef2; /* Two stored references. */ +typedef uint32_t IRRef; /* Used to pass around references. */ + +/* Fixed references. */ +enum { + REF_BIAS = 0x8000, + REF_TRUE = REF_BIAS-3, + REF_FALSE = REF_BIAS-2, + REF_NIL = REF_BIAS-1, /* \--- Constants grow downwards. */ + REF_BASE = REF_BIAS, /* /--- IR grows upwards. */ + REF_FIRST = REF_BIAS+1, + REF_DROP = 0xffff +}; + +/* Note: IRMlit operands must be < REF_BIAS, too! +** This allows for fast and uniform manipulation of all operands +** without looking up the operand mode in lj_ir_mode: +** - CSE calculates the maximum reference of two operands. +** This must work with mixed reference/literal operands, too. +** - DCE marking only checks for operand >= REF_BIAS. +** - LOOP needs to substitute reference operands. +** Constant references and literals must not be modified. +*/ + +#define IRREF2(lo, hi) ((IRRef2)(lo) | ((IRRef2)(hi) << 16)) + +#define irref_isk(ref) ((ref) < REF_BIAS) + +/* Tagged IR references (32 bit). +** +** +-------+-------+---------------+ +** | irt | flags | ref | +** +-------+-------+---------------+ +** +** The tag holds a copy of the IRType and speeds up IR type checks. +*/ +typedef uint32_t TRef; + +#define TREF_REFMASK 0x0000ffff +#define TREF_FRAME 0x00010000 +#define TREF_CONT 0x00020000 + +#define TREF(ref, t) ((TRef)((ref) + ((t)<<24))) + +#define tref_ref(tr) ((IRRef1)(tr)) +#define tref_t(tr) ((IRType)((tr)>>24)) +#define tref_type(tr) ((IRType)(((tr)>>24) & IRT_TYPE)) +#define tref_typerange(tr, first, last) \ + ((((tr)>>24) & IRT_TYPE) - (TRef)(first) <= (TRef)(last-first)) + +#define tref_istype(tr, t) (((tr) & (IRT_TYPE<<24)) == ((t)<<24)) +#define tref_isnil(tr) (tref_istype((tr), IRT_NIL)) +#define tref_isfalse(tr) (tref_istype((tr), IRT_FALSE)) +#define tref_istrue(tr) (tref_istype((tr), IRT_TRUE)) +#define tref_islightud(tr) (tref_istype((tr), IRT_LIGHTUD)) +#define tref_isstr(tr) (tref_istype((tr), IRT_STR)) +#define tref_isfunc(tr) (tref_istype((tr), IRT_FUNC)) +#define tref_iscdata(tr) (tref_istype((tr), IRT_CDATA)) +#define tref_istab(tr) (tref_istype((tr), IRT_TAB)) +#define tref_isudata(tr) (tref_istype((tr), IRT_UDATA)) +#define tref_isnum(tr) (tref_istype((tr), IRT_NUM)) +#define tref_isint(tr) (tref_istype((tr), IRT_INT)) + +#define tref_isbool(tr) (tref_typerange((tr), IRT_FALSE, IRT_TRUE)) +#define tref_ispri(tr) (tref_typerange((tr), IRT_NIL, IRT_TRUE)) +#define tref_istruecond(tr) (!tref_typerange((tr), IRT_NIL, IRT_FALSE)) +#define tref_isinteger(tr) (tref_typerange((tr), IRT_I8, IRT_INT)) +#define tref_isnumber(tr) (tref_typerange((tr), IRT_NUM, IRT_INT)) +#define tref_isnumber_str(tr) (tref_isnumber((tr)) || tref_isstr((tr))) +#define tref_isgcv(tr) (tref_typerange((tr), IRT_STR, IRT_UDATA)) + +#define tref_isk(tr) (irref_isk(tref_ref((tr)))) +#define tref_isk2(tr1, tr2) (irref_isk(tref_ref((tr1) | (tr2)))) + +#define TREF_PRI(t) (TREF(REF_NIL-(t), (t))) +#define TREF_NIL (TREF_PRI(IRT_NIL)) +#define TREF_FALSE (TREF_PRI(IRT_FALSE)) +#define TREF_TRUE (TREF_PRI(IRT_TRUE)) + +/* -- IR format ----------------------------------------------------------- */ + +/* IR instruction format (64 bit). +** +** 16 16 8 8 8 8 +** +-------+-------+---+---+---+---+ +** | op1 | op2 | t | o | r | s | +** +-------+-------+---+---+---+---+ +** | op12/i/gco32 | ot | prev | (alternative fields in union) +** +-------+-------+---+---+---+---+ +** | TValue/gco64 | (2nd IR slot for 64 bit constants) +** +---------------+-------+-------+ +** 32 16 16 +** +** prev is only valid prior to register allocation and then reused for r + s. +*/ + +typedef union IRIns { + struct { + LJ_ENDIAN_LOHI( + IRRef1 op1; /* IR operand 1. */ + , IRRef1 op2; /* IR operand 2. */ + ) + IROpT ot; /* IR opcode and type (overlaps t and o). */ + IRRef1 prev; /* Previous ins in same chain (overlaps r and s). */ + }; + struct { + IRRef2 op12; /* IR operand 1 and 2 (overlaps op1 and op2). */ + LJ_ENDIAN_LOHI( + IRType1 t; /* IR type. */ + , IROp1 o; /* IR opcode. */ + ) + LJ_ENDIAN_LOHI( + uint8_t r; /* Register allocation (overlaps prev). */ + , uint8_t s; /* Spill slot allocation (overlaps prev). */ + ) + }; + int32_t i; /* 32 bit signed integer literal (overlaps op12). */ + GCRef gcr; /* GCobj constant (overlaps op12 or entire slot). */ + MRef ptr; /* Pointer constant (overlaps op12 or entire slot). */ + TValue tv; /* TValue constant (overlaps entire slot). */ +} IRIns; + +#define ir_kgc(ir) check_exp((ir)->o == IR_KGC, gcref((ir)[LJ_GC64].gcr)) +#define ir_kstr(ir) (gco2str(ir_kgc((ir)))) +#define ir_ktab(ir) (gco2tab(ir_kgc((ir)))) +#define ir_kfunc(ir) (gco2func(ir_kgc((ir)))) +#define ir_kcdata(ir) (gco2cd(ir_kgc((ir)))) +#define ir_knum(ir) check_exp((ir)->o == IR_KNUM, &(ir)[1].tv) +#define ir_kint64(ir) check_exp((ir)->o == IR_KINT64, &(ir)[1].tv) +#define ir_k64(ir) \ + check_exp((ir)->o == IR_KNUM || (ir)->o == IR_KINT64 || \ + (LJ_GC64 && \ + ((ir)->o == IR_KGC || \ + (ir)->o == IR_KPTR || (ir)->o == IR_KKPTR)), \ + &(ir)[1].tv) +#define ir_kptr(ir) \ + check_exp((ir)->o == IR_KPTR || (ir)->o == IR_KKPTR, \ + mref((ir)[LJ_GC64].ptr, void)) + +/* A store or any other op with a non-weak guard has a side-effect. */ +static LJ_AINLINE int ir_sideeff(IRIns *ir) +{ + return (((ir->t.irt | ~IRT_GUARD) & lj_ir_mode[ir->o]) >= IRM_S); +} + +LJ_STATIC_ASSERT((int)IRT_GUARD == (int)IRM_W); + +#endif diff --git a/lib/LuaJIT/lj_ir.o b/lib/LuaJIT/lj_ir.o new file mode 100644 index 0000000000000000000000000000000000000000..d489f97ef37a1069b9be6dc77296b65d957c217b GIT binary patch literal 14160 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 & 0xff) /* # of args. */ +#define CCI_NARGS_MAX 32 /* Max. # of args. */ + +#define CCI_OTSHIFT 16 +#define CCI_OPTYPE(ci) ((ci)->flags >> CCI_OTSHIFT) /* Get op/type. */ +#define CCI_OPSHIFT 24 +#define CCI_OP(ci) ((ci)->flags >> CCI_OPSHIFT) /* Get op. */ + +#define CCI_CALL_N (IR_CALLN << CCI_OPSHIFT) +#define CCI_CALL_A (IR_CALLA << CCI_OPSHIFT) +#define CCI_CALL_L (IR_CALLL << CCI_OPSHIFT) +#define CCI_CALL_S (IR_CALLS << CCI_OPSHIFT) +#define CCI_CALL_FN (CCI_CALL_N|CCI_CC_FASTCALL) +#define CCI_CALL_FL (CCI_CALL_L|CCI_CC_FASTCALL) +#define CCI_CALL_FS (CCI_CALL_S|CCI_CC_FASTCALL) + +/* C call info flags. */ +#define CCI_L 0x0100 /* Implicit L arg. */ +#define CCI_CASTU64 0x0200 /* Cast u64 result to number. */ +#define CCI_NOFPRCLOBBER 0x0400 /* Does not clobber any FPRs. */ +#define CCI_VARARG 0x0800 /* Vararg function. */ + +#define CCI_CC_MASK 0x3000 /* Calling convention mask. */ +#define CCI_CC_SHIFT 12 +/* ORDER CC */ +#define CCI_CC_CDECL 0x0000 /* Default cdecl calling convention. */ +#define CCI_CC_THISCALL 0x1000 /* Thiscall calling convention. */ +#define CCI_CC_FASTCALL 0x2000 /* Fastcall calling convention. */ +#define CCI_CC_STDCALL 0x3000 /* Stdcall calling convention. */ + +/* Extra args for SOFTFP, SPLIT 64 bit. */ +#define CCI_XARGS_SHIFT 14 +#define CCI_XARGS(ci) (((ci)->flags >> CCI_XARGS_SHIFT) & 3) +#define CCI_XA (1u << CCI_XARGS_SHIFT) + +#if LJ_SOFTFP32 || (LJ_32 && LJ_HASFFI) +#define CCI_XNARGS(ci) (CCI_NARGS((ci)) + CCI_XARGS((ci))) +#else +#define CCI_XNARGS(ci) CCI_NARGS((ci)) +#endif + +/* Helpers for conditional function definitions. */ +#define IRCALLCOND_ANY(x) x + +#if LJ_TARGET_X86ORX64 +#define IRCALLCOND_FPMATH(x) NULL +#else +#define IRCALLCOND_FPMATH(x) x +#endif + +#if LJ_SOFTFP +#define IRCALLCOND_SOFTFP(x) x +#if LJ_HASFFI +#define IRCALLCOND_SOFTFP_FFI(x) x +#else +#define IRCALLCOND_SOFTFP_FFI(x) NULL +#endif +#else +#define IRCALLCOND_SOFTFP(x) NULL +#define IRCALLCOND_SOFTFP_FFI(x) NULL +#endif + +#if LJ_SOFTFP && LJ_TARGET_MIPS +#define IRCALLCOND_SOFTFP_MIPS(x) x +#else +#define IRCALLCOND_SOFTFP_MIPS(x) NULL +#endif + +#if LJ_SOFTFP && LJ_TARGET_MIPS64 +#define IRCALLCOND_SOFTFP_MIPS64(x) x +#else +#define IRCALLCOND_SOFTFP_MIPS64(x) NULL +#endif + +#define LJ_NEED_FP64 (LJ_TARGET_ARM || LJ_TARGET_PPC || LJ_TARGET_MIPS) + +#if LJ_HASFFI && (LJ_SOFTFP || LJ_NEED_FP64) +#define IRCALLCOND_FP64_FFI(x) x +#else +#define IRCALLCOND_FP64_FFI(x) NULL +#endif + +#if LJ_HASFFI +#define IRCALLCOND_FFI(x) x +#if LJ_32 +#define IRCALLCOND_FFI32(x) x +#else +#define IRCALLCOND_FFI32(x) NULL +#endif +#else +#define IRCALLCOND_FFI(x) NULL +#define IRCALLCOND_FFI32(x) NULL +#endif + +#if LJ_SOFTFP +#define XA_FP CCI_XA +#define XA2_FP (CCI_XA+CCI_XA) +#else +#define XA_FP 0 +#define XA2_FP 0 +#endif + +#if LJ_SOFTFP32 +#define XA_FP32 CCI_XA +#define XA2_FP32 (CCI_XA+CCI_XA) +#else +#define XA_FP32 0 +#define XA2_FP32 0 +#endif + +#if LJ_32 +#define XA_64 CCI_XA +#define XA2_64 (CCI_XA+CCI_XA) +#else +#define XA_64 0 +#define XA2_64 0 +#endif + +/* Function definitions for CALL* instructions. */ +#define IRCALLDEF(_) \ + _(ANY, lj_str_cmp, 2, FN, INT, CCI_NOFPRCLOBBER) \ + _(ANY, lj_str_find, 4, N, PGC, 0) \ + _(ANY, lj_str_new, 3, S, STR, CCI_L) \ + _(ANY, lj_strscan_num, 2, FN, INT, 0) \ + _(ANY, lj_strfmt_int, 2, FN, STR, CCI_L) \ + _(ANY, lj_strfmt_num, 2, FN, STR, CCI_L) \ + _(ANY, lj_strfmt_char, 2, FN, STR, CCI_L) \ + _(ANY, lj_strfmt_putint, 2, FL, PGC, 0) \ + _(ANY, lj_strfmt_putnum, 2, FL, PGC, 0) \ + _(ANY, lj_strfmt_putquoted, 2, FL, PGC, 0) \ + _(ANY, lj_strfmt_putfxint, 3, L, PGC, XA_64) \ + _(ANY, lj_strfmt_putfnum_int, 3, L, PGC, XA_FP) \ + _(ANY, lj_strfmt_putfnum_uint, 3, L, PGC, XA_FP) \ + _(ANY, lj_strfmt_putfnum, 3, L, PGC, XA_FP) \ + _(ANY, lj_strfmt_putfstr, 3, L, PGC, 0) \ + _(ANY, lj_strfmt_putfchar, 3, L, PGC, 0) \ + _(ANY, lj_buf_putmem, 3, S, PGC, 0) \ + _(ANY, lj_buf_putstr, 2, FL, PGC, 0) \ + _(ANY, lj_buf_putchar, 2, FL, PGC, 0) \ + _(ANY, lj_buf_putstr_reverse, 2, FL, PGC, 0) \ + _(ANY, lj_buf_putstr_lower, 2, FL, PGC, 0) \ + _(ANY, lj_buf_putstr_upper, 2, FL, PGC, 0) \ + _(ANY, lj_buf_putstr_rep, 3, L, PGC, 0) \ + _(ANY, lj_buf_puttab, 5, L, PGC, 0) \ + _(ANY, lj_buf_tostr, 1, FL, STR, 0) \ + _(ANY, lj_tab_new_ah, 3, A, TAB, CCI_L) \ + _(ANY, lj_tab_new1, 2, FS, TAB, CCI_L) \ + _(ANY, lj_tab_dup, 2, FS, TAB, CCI_L) \ + _(ANY, lj_tab_clear, 1, FS, NIL, 0) \ + _(ANY, lj_tab_newkey, 3, S, PGC, CCI_L) \ + _(ANY, lj_tab_len, 1, FL, INT, 0) \ + _(ANY, lj_gc_step_jit, 2, FS, NIL, CCI_L) \ + _(ANY, lj_gc_barrieruv, 2, FS, NIL, 0) \ + _(ANY, lj_mem_newgco, 2, FS, PGC, CCI_L) \ + _(ANY, lj_math_random_step, 1, FS, NUM, CCI_CASTU64) \ + _(ANY, lj_vm_modi, 2, FN, INT, 0) \ + _(ANY, sinh, 1, N, NUM, XA_FP) \ + _(ANY, cosh, 1, N, NUM, XA_FP) \ + _(ANY, tanh, 1, N, NUM, XA_FP) \ + _(ANY, fputc, 2, S, INT, 0) \ + _(ANY, fwrite, 4, S, INT, 0) \ + _(ANY, fflush, 1, S, INT, 0) \ + /* ORDER FPM */ \ + _(FPMATH, lj_vm_floor, 1, N, NUM, XA_FP) \ + _(FPMATH, lj_vm_ceil, 1, N, NUM, XA_FP) \ + _(FPMATH, lj_vm_trunc, 1, N, NUM, XA_FP) \ + _(FPMATH, sqrt, 1, N, NUM, XA_FP) \ + _(ANY, exp, 1, N, NUM, XA_FP) \ + _(ANY, lj_vm_exp2, 1, N, NUM, XA_FP) \ + _(ANY, log, 1, N, NUM, XA_FP) \ + _(ANY, lj_vm_log2, 1, N, NUM, XA_FP) \ + _(ANY, log10, 1, N, NUM, XA_FP) \ + _(ANY, sin, 1, N, NUM, XA_FP) \ + _(ANY, cos, 1, N, NUM, XA_FP) \ + _(ANY, tan, 1, N, NUM, XA_FP) \ + _(ANY, lj_vm_powi, 2, N, NUM, XA_FP) \ + _(ANY, pow, 2, N, NUM, XA2_FP) \ + _(ANY, atan2, 2, N, NUM, XA2_FP) \ + _(ANY, ldexp, 2, N, NUM, XA_FP) \ + _(SOFTFP, lj_vm_tobit, 1, N, INT, XA_FP32) \ + _(SOFTFP, softfp_add, 2, N, NUM, XA2_FP32) \ + _(SOFTFP, softfp_sub, 2, N, NUM, XA2_FP32) \ + _(SOFTFP, softfp_mul, 2, N, NUM, XA2_FP32) \ + _(SOFTFP, softfp_div, 2, N, NUM, XA2_FP32) \ + _(SOFTFP, softfp_cmp, 2, N, NIL, XA2_FP32) \ + _(SOFTFP, softfp_i2d, 1, N, NUM, 0) \ + _(SOFTFP, softfp_d2i, 1, N, INT, XA_FP32) \ + _(SOFTFP_MIPS, lj_vm_sfmin, 2, N, NUM, XA2_FP32) \ + _(SOFTFP_MIPS, lj_vm_sfmax, 2, N, NUM, XA2_FP32) \ + _(SOFTFP_MIPS64, lj_vm_tointg, 1, N, INT, 0) \ + _(SOFTFP_FFI, softfp_ui2d, 1, N, NUM, 0) \ + _(SOFTFP_FFI, softfp_f2d, 1, N, NUM, 0) \ + _(SOFTFP_FFI, softfp_d2ui, 1, N, INT, XA_FP32) \ + _(SOFTFP_FFI, softfp_d2f, 1, N, FLOAT, XA_FP32) \ + _(SOFTFP_FFI, softfp_i2f, 1, N, FLOAT, 0) \ + _(SOFTFP_FFI, softfp_ui2f, 1, N, FLOAT, 0) \ + _(SOFTFP_FFI, softfp_f2i, 1, N, INT, 0) \ + _(SOFTFP_FFI, softfp_f2ui, 1, N, INT, 0) \ + _(FP64_FFI, fp64_l2d, 1, N, NUM, XA_64) \ + _(FP64_FFI, fp64_ul2d, 1, N, NUM, XA_64) \ + _(FP64_FFI, fp64_l2f, 1, N, FLOAT, XA_64) \ + _(FP64_FFI, fp64_ul2f, 1, N, FLOAT, XA_64) \ + _(FP64_FFI, fp64_d2l, 1, N, I64, XA_FP) \ + _(FP64_FFI, fp64_d2ul, 1, N, U64, XA_FP) \ + _(FP64_FFI, fp64_f2l, 1, N, I64, 0) \ + _(FP64_FFI, fp64_f2ul, 1, N, U64, 0) \ + _(FFI, lj_carith_divi64, 2, N, I64, XA2_64|CCI_NOFPRCLOBBER) \ + _(FFI, lj_carith_divu64, 2, N, U64, XA2_64|CCI_NOFPRCLOBBER) \ + _(FFI, lj_carith_modi64, 2, N, I64, XA2_64|CCI_NOFPRCLOBBER) \ + _(FFI, lj_carith_modu64, 2, N, U64, XA2_64|CCI_NOFPRCLOBBER) \ + _(FFI, lj_carith_powi64, 2, N, I64, XA2_64|CCI_NOFPRCLOBBER) \ + _(FFI, lj_carith_powu64, 2, N, U64, XA2_64|CCI_NOFPRCLOBBER) \ + _(FFI, lj_cdata_newv, 4, S, CDATA, CCI_L) \ + _(FFI, lj_cdata_setfin, 4, S, NIL, CCI_L) \ + _(FFI, strlen, 1, L, INTP, 0) \ + _(FFI, memcpy, 3, S, PTR, 0) \ + _(FFI, memset, 3, S, PTR, 0) \ + _(FFI, lj_vm_errno, 0, S, INT, CCI_NOFPRCLOBBER) \ + _(FFI32, lj_carith_mul64, 2, N, I64, XA2_64|CCI_NOFPRCLOBBER) \ + _(FFI32, lj_carith_shl64, 2, N, U64, XA_64|CCI_NOFPRCLOBBER) \ + _(FFI32, lj_carith_shr64, 2, N, U64, XA_64|CCI_NOFPRCLOBBER) \ + _(FFI32, lj_carith_sar64, 2, N, U64, XA_64|CCI_NOFPRCLOBBER) \ + _(FFI32, lj_carith_rol64, 2, N, U64, XA_64|CCI_NOFPRCLOBBER) \ + _(FFI32, lj_carith_ror64, 2, N, U64, XA_64|CCI_NOFPRCLOBBER) \ + \ + /* End of list. */ + +typedef enum { +#define IRCALLENUM(cond, name, nargs, kind, type, flags) IRCALL_##name, +IRCALLDEF(IRCALLENUM) +#undef IRCALLENUM + IRCALL__MAX +} IRCallID; + +LJ_FUNC TRef lj_ir_call(jit_State *J, IRCallID id, ...); + +LJ_DATA const CCallInfo lj_ir_callinfo[IRCALL__MAX+1]; + +/* Soft-float declarations. */ +#if LJ_SOFTFP +#if LJ_TARGET_ARM +#define softfp_add __aeabi_dadd +#define softfp_sub __aeabi_dsub +#define softfp_mul __aeabi_dmul +#define softfp_div __aeabi_ddiv +#define softfp_cmp __aeabi_cdcmple +#define softfp_i2d __aeabi_i2d +#define softfp_d2i __aeabi_d2iz +#define softfp_ui2d __aeabi_ui2d +#define softfp_f2d __aeabi_f2d +#define softfp_d2ui __aeabi_d2uiz +#define softfp_d2f __aeabi_d2f +#define softfp_i2f __aeabi_i2f +#define softfp_ui2f __aeabi_ui2f +#define softfp_f2i __aeabi_f2iz +#define softfp_f2ui __aeabi_f2uiz +#define fp64_l2d __aeabi_l2d +#define fp64_ul2d __aeabi_ul2d +#define fp64_l2f __aeabi_l2f +#define fp64_ul2f __aeabi_ul2f +#if LJ_TARGET_IOS +#define fp64_d2l __fixdfdi +#define fp64_d2ul __fixunsdfdi +#define fp64_f2l __fixsfdi +#define fp64_f2ul __fixunssfdi +#else +#define fp64_d2l __aeabi_d2lz +#define fp64_d2ul __aeabi_d2ulz +#define fp64_f2l __aeabi_f2lz +#define fp64_f2ul __aeabi_f2ulz +#endif +#elif LJ_TARGET_MIPS || LJ_TARGET_PPC +#define softfp_add __adddf3 +#define softfp_sub __subdf3 +#define softfp_mul __muldf3 +#define softfp_div __divdf3 +#define softfp_cmp __ledf2 +#define softfp_i2d __floatsidf +#define softfp_d2i __fixdfsi +#define softfp_ui2d __floatunsidf +#define softfp_f2d __extendsfdf2 +#define softfp_d2ui __fixunsdfsi +#define softfp_d2f __truncdfsf2 +#define softfp_i2f __floatsisf +#define softfp_ui2f __floatunsisf +#define softfp_f2i __fixsfsi +#define softfp_f2ui __fixunssfsi +#else +#error "Missing soft-float definitions for target architecture" +#endif +extern double softfp_add(double a, double b); +extern double softfp_sub(double a, double b); +extern double softfp_mul(double a, double b); +extern double softfp_div(double a, double b); +extern void softfp_cmp(double a, double b); +extern double softfp_i2d(int32_t a); +extern int32_t softfp_d2i(double a); +#if LJ_HASFFI +extern double softfp_ui2d(uint32_t a); +extern double softfp_f2d(float a); +extern uint32_t softfp_d2ui(double a); +extern float softfp_d2f(double a); +extern float softfp_i2f(int32_t a); +extern float softfp_ui2f(uint32_t a); +extern int32_t softfp_f2i(float a); +extern uint32_t softfp_f2ui(float a); +#endif +#if LJ_TARGET_MIPS +extern double lj_vm_sfmin(double a, double b); +extern double lj_vm_sfmax(double a, double b); +#endif +#endif + +#if LJ_HASFFI && LJ_NEED_FP64 && !(LJ_TARGET_ARM && LJ_SOFTFP) +#ifdef __GNUC__ +#define fp64_l2d __floatdidf +#define fp64_ul2d __floatundidf +#define fp64_l2f __floatdisf +#define fp64_ul2f __floatundisf +#define fp64_d2l __fixdfdi +#define fp64_d2ul __fixunsdfdi +#define fp64_f2l __fixsfdi +#define fp64_f2ul __fixunssfdi +#else +#error "Missing fp64 helper definitions for this compiler" +#endif +#endif + +#if LJ_HASFFI && (LJ_SOFTFP || LJ_NEED_FP64) +extern double fp64_l2d(int64_t a); +extern double fp64_ul2d(uint64_t a); +extern float fp64_l2f(int64_t a); +extern float fp64_ul2f(uint64_t a); +extern int64_t fp64_d2l(double a); +extern uint64_t fp64_d2ul(double a); +extern int64_t fp64_f2l(float a); +extern uint64_t fp64_f2ul(float a); +#endif + +#endif diff --git a/lib/LuaJIT/lj_iropt.h b/lib/LuaJIT/lj_iropt.h new file mode 100644 index 0000000..a59ba3f --- /dev/null +++ b/lib/LuaJIT/lj_iropt.h @@ -0,0 +1,162 @@ +/* +** Common header for IR emitter and optimizations. +** Copyright (C) 2005-2017 Mike Pall. See Copyright Notice in luajit.h +*/ + +#ifndef _LJ_IROPT_H +#define _LJ_IROPT_H + +#include + +#include "lj_obj.h" +#include "lj_jit.h" + +#if LJ_HASJIT +/* IR emitter. */ +LJ_FUNC void LJ_FASTCALL lj_ir_growtop(jit_State *J); +LJ_FUNC TRef LJ_FASTCALL lj_ir_emit(jit_State *J); + +/* Save current IR in J->fold.ins, but do not emit it (yet). */ +static LJ_AINLINE void lj_ir_set_(jit_State *J, uint16_t ot, IRRef1 a, IRRef1 b) +{ + J->fold.ins.ot = ot; J->fold.ins.op1 = a; J->fold.ins.op2 = b; +} + +#define lj_ir_set(J, ot, a, b) \ + lj_ir_set_(J, (uint16_t)(ot), (IRRef1)(a), (IRRef1)(b)) + +/* Get ref of next IR instruction and optionally grow IR. +** Note: this may invalidate all IRIns*! +*/ +static LJ_AINLINE IRRef lj_ir_nextins(jit_State *J) +{ + IRRef ref = J->cur.nins; + if (LJ_UNLIKELY(ref >= J->irtoplim)) lj_ir_growtop(J); + J->cur.nins = ref + 1; + return ref; +} + +LJ_FUNC TRef lj_ir_ggfload(jit_State *J, IRType t, uintptr_t ofs); + +/* Interning of constants. */ +LJ_FUNC TRef LJ_FASTCALL lj_ir_kint(jit_State *J, int32_t k); +LJ_FUNC TRef lj_ir_k64(jit_State *J, IROp op, uint64_t u64); +LJ_FUNC TRef lj_ir_knum_u64(jit_State *J, uint64_t u64); +LJ_FUNC TRef lj_ir_knumint(jit_State *J, lua_Number n); +LJ_FUNC TRef lj_ir_kint64(jit_State *J, uint64_t u64); +LJ_FUNC TRef lj_ir_kgc(jit_State *J, GCobj *o, IRType t); +LJ_FUNC TRef lj_ir_kptr_(jit_State *J, IROp op, void *ptr); +LJ_FUNC TRef lj_ir_knull(jit_State *J, IRType t); +LJ_FUNC TRef lj_ir_kslot(jit_State *J, TRef key, IRRef slot); +LJ_FUNC TRef lj_ir_ktrace(jit_State *J); + +#if LJ_64 +#define lj_ir_kintp(J, k) lj_ir_kint64(J, (uint64_t)(k)) +#else +#define lj_ir_kintp(J, k) lj_ir_kint(J, (int32_t)(k)) +#endif + +static LJ_AINLINE TRef lj_ir_knum(jit_State *J, lua_Number n) +{ + TValue tv; + tv.n = n; + return lj_ir_knum_u64(J, tv.u64); +} + +#define lj_ir_kstr(J, str) lj_ir_kgc(J, obj2gco((str)), IRT_STR) +#define lj_ir_ktab(J, tab) lj_ir_kgc(J, obj2gco((tab)), IRT_TAB) +#define lj_ir_kfunc(J, func) lj_ir_kgc(J, obj2gco((func)), IRT_FUNC) +#define lj_ir_kptr(J, ptr) lj_ir_kptr_(J, IR_KPTR, (ptr)) +#define lj_ir_kkptr(J, ptr) lj_ir_kptr_(J, IR_KKPTR, (ptr)) + +/* Special FP constants. */ +#define lj_ir_knum_zero(J) lj_ir_knum_u64(J, U64x(00000000,00000000)) +#define lj_ir_knum_one(J) lj_ir_knum_u64(J, U64x(3ff00000,00000000)) +#define lj_ir_knum_tobit(J) lj_ir_knum_u64(J, U64x(43380000,00000000)) + +/* Special 128 bit SIMD constants. */ +#define lj_ir_ksimd(J, idx) \ + lj_ir_ggfload(J, IRT_NUM, (uintptr_t)LJ_KSIMD(J, idx) - (uintptr_t)J2GG(J)) + +/* Access to constants. */ +LJ_FUNC void lj_ir_kvalue(lua_State *L, TValue *tv, const IRIns *ir); + +/* Convert IR operand types. */ +LJ_FUNC TRef LJ_FASTCALL lj_ir_tonumber(jit_State *J, TRef tr); +LJ_FUNC TRef LJ_FASTCALL lj_ir_tonum(jit_State *J, TRef tr); +LJ_FUNC TRef LJ_FASTCALL lj_ir_tostr(jit_State *J, TRef tr); + +/* Miscellaneous IR ops. */ +LJ_FUNC int lj_ir_numcmp(lua_Number a, lua_Number b, IROp op); +LJ_FUNC int lj_ir_strcmp(GCstr *a, GCstr *b, IROp op); +LJ_FUNC void lj_ir_rollback(jit_State *J, IRRef ref); + +/* Emit IR instructions with on-the-fly optimizations. */ +LJ_FUNC TRef LJ_FASTCALL lj_opt_fold(jit_State *J); +LJ_FUNC TRef LJ_FASTCALL lj_opt_cse(jit_State *J); +LJ_FUNC TRef LJ_FASTCALL lj_opt_cselim(jit_State *J, IRRef lim); + +/* Special return values for the fold functions. */ +enum { + NEXTFOLD, /* Couldn't fold, pass on. */ + RETRYFOLD, /* Retry fold with modified fins. */ + KINTFOLD, /* Return ref for int constant in fins->i. */ + FAILFOLD, /* Guard would always fail. */ + DROPFOLD, /* Guard eliminated. */ + MAX_FOLD +}; + +#define INTFOLD(k) ((J->fold.ins.i = (k)), (TRef)KINTFOLD) +#define INT64FOLD(k) (lj_ir_kint64(J, (k))) +#define CONDFOLD(cond) ((TRef)FAILFOLD + (TRef)(cond)) +#define LEFTFOLD (J->fold.ins.op1) +#define RIGHTFOLD (J->fold.ins.op2) +#define CSEFOLD (lj_opt_cse(J)) +#define EMITFOLD (lj_ir_emit(J)) + +/* Load/store forwarding. */ +LJ_FUNC TRef LJ_FASTCALL lj_opt_fwd_aload(jit_State *J); +LJ_FUNC TRef LJ_FASTCALL lj_opt_fwd_hload(jit_State *J); +LJ_FUNC TRef LJ_FASTCALL lj_opt_fwd_uload(jit_State *J); +LJ_FUNC TRef LJ_FASTCALL lj_opt_fwd_fload(jit_State *J); +LJ_FUNC TRef LJ_FASTCALL lj_opt_fwd_xload(jit_State *J); +LJ_FUNC TRef LJ_FASTCALL lj_opt_fwd_tab_len(jit_State *J); +LJ_FUNC TRef LJ_FASTCALL lj_opt_fwd_hrefk(jit_State *J); +LJ_FUNC int LJ_FASTCALL lj_opt_fwd_href_nokey(jit_State *J); +LJ_FUNC int LJ_FASTCALL lj_opt_fwd_tptr(jit_State *J, IRRef lim); +LJ_FUNC int lj_opt_fwd_wasnonnil(jit_State *J, IROpT loadop, IRRef xref); + +/* Dead-store elimination. */ +LJ_FUNC TRef LJ_FASTCALL lj_opt_dse_ahstore(jit_State *J); +LJ_FUNC TRef LJ_FASTCALL lj_opt_dse_ustore(jit_State *J); +LJ_FUNC TRef LJ_FASTCALL lj_opt_dse_fstore(jit_State *J); +LJ_FUNC TRef LJ_FASTCALL lj_opt_dse_xstore(jit_State *J); + +/* Narrowing. */ +LJ_FUNC TRef LJ_FASTCALL lj_opt_narrow_convert(jit_State *J); +LJ_FUNC TRef LJ_FASTCALL lj_opt_narrow_index(jit_State *J, TRef key); +LJ_FUNC TRef LJ_FASTCALL lj_opt_narrow_toint(jit_State *J, TRef tr); +LJ_FUNC TRef LJ_FASTCALL lj_opt_narrow_tobit(jit_State *J, TRef tr); +#if LJ_HASFFI +LJ_FUNC TRef LJ_FASTCALL lj_opt_narrow_cindex(jit_State *J, TRef key); +#endif +LJ_FUNC TRef lj_opt_narrow_arith(jit_State *J, TRef rb, TRef rc, + TValue *vb, TValue *vc, IROp op); +LJ_FUNC TRef lj_opt_narrow_unm(jit_State *J, TRef rc, TValue *vc); +LJ_FUNC TRef lj_opt_narrow_mod(jit_State *J, TRef rb, TRef rc, TValue *vb, TValue *vc); +LJ_FUNC TRef lj_opt_narrow_pow(jit_State *J, TRef rb, TRef rc, TValue *vb, TValue *vc); +LJ_FUNC IRType lj_opt_narrow_forl(jit_State *J, cTValue *forbase); + +/* Optimization passes. */ +LJ_FUNC void lj_opt_dce(jit_State *J); +LJ_FUNC int lj_opt_loop(jit_State *J); +#if LJ_SOFTFP32 || (LJ_32 && LJ_HASFFI) +LJ_FUNC void lj_opt_split(jit_State *J); +#else +#define lj_opt_split(J) UNUSED(J) +#endif +LJ_FUNC void lj_opt_sink(jit_State *J); + +#endif + +#endif diff --git a/lib/LuaJIT/lj_jit.h b/lib/LuaJIT/lj_jit.h new file mode 100644 index 0000000..5d41ef4 --- /dev/null +++ b/lib/LuaJIT/lj_jit.h @@ -0,0 +1,505 @@ +/* +** Common definitions for the JIT compiler. +** Copyright (C) 2005-2017 Mike Pall. See Copyright Notice in luajit.h +*/ + +#ifndef _LJ_JIT_H +#define _LJ_JIT_H + +#include "lj_obj.h" +#include "lj_ir.h" + +/* JIT engine flags. */ +#define JIT_F_ON 0x00000001 + +/* CPU-specific JIT engine flags. */ +#if LJ_TARGET_X86ORX64 +#define JIT_F_SSE2 0x00000010 +#define JIT_F_SSE3 0x00000020 +#define JIT_F_SSE4_1 0x00000040 +#define JIT_F_PREFER_IMUL 0x00000080 +#define JIT_F_LEA_AGU 0x00000100 +#define JIT_F_BMI2 0x00000200 + +/* Names for the CPU-specific flags. Must match the order above. */ +#define JIT_F_CPU_FIRST JIT_F_SSE2 +#define JIT_F_CPUSTRING "\4SSE2\4SSE3\6SSE4.1\3AMD\4ATOM\4BMI2" +#elif LJ_TARGET_ARM +#define JIT_F_ARMV6_ 0x00000010 +#define JIT_F_ARMV6T2_ 0x00000020 +#define JIT_F_ARMV7 0x00000040 +#define JIT_F_VFPV2 0x00000080 +#define JIT_F_VFPV3 0x00000100 + +#define JIT_F_ARMV6 (JIT_F_ARMV6_|JIT_F_ARMV6T2_|JIT_F_ARMV7) +#define JIT_F_ARMV6T2 (JIT_F_ARMV6T2_|JIT_F_ARMV7) +#define JIT_F_VFP (JIT_F_VFPV2|JIT_F_VFPV3) + +/* Names for the CPU-specific flags. Must match the order above. */ +#define JIT_F_CPU_FIRST JIT_F_ARMV6_ +#define JIT_F_CPUSTRING "\5ARMv6\7ARMv6T2\5ARMv7\5VFPv2\5VFPv3" +#elif LJ_TARGET_PPC +#define JIT_F_SQRT 0x00000010 +#define JIT_F_ROUND 0x00000020 + +/* Names for the CPU-specific flags. Must match the order above. */ +#define JIT_F_CPU_FIRST JIT_F_SQRT +#define JIT_F_CPUSTRING "\4SQRT\5ROUND" +#elif LJ_TARGET_MIPS +#define JIT_F_MIPSXXR2 0x00000010 + +/* Names for the CPU-specific flags. Must match the order above. */ +#define JIT_F_CPU_FIRST JIT_F_MIPSXXR2 +#if LJ_TARGET_MIPS32 +#define JIT_F_CPUSTRING "\010MIPS32R2" +#else +#define JIT_F_CPUSTRING "\010MIPS64R2" +#endif +#else +#define JIT_F_CPU_FIRST 0 +#define JIT_F_CPUSTRING "" +#endif + +/* Optimization flags. */ +#define JIT_F_OPT_MASK 0x0fff0000 + +#define JIT_F_OPT_FOLD 0x00010000 +#define JIT_F_OPT_CSE 0x00020000 +#define JIT_F_OPT_DCE 0x00040000 +#define JIT_F_OPT_FWD 0x00080000 +#define JIT_F_OPT_DSE 0x00100000 +#define JIT_F_OPT_NARROW 0x00200000 +#define JIT_F_OPT_LOOP 0x00400000 +#define JIT_F_OPT_ABC 0x00800000 +#define JIT_F_OPT_SINK 0x01000000 +#define JIT_F_OPT_FUSE 0x02000000 + +/* Optimizations names for -O. Must match the order above. */ +#define JIT_F_OPT_FIRST JIT_F_OPT_FOLD +#define JIT_F_OPTSTRING \ + "\4fold\3cse\3dce\3fwd\3dse\6narrow\4loop\3abc\4sink\4fuse" + +/* Optimization levels set a fixed combination of flags. */ +#define JIT_F_OPT_0 0 +#define JIT_F_OPT_1 (JIT_F_OPT_FOLD|JIT_F_OPT_CSE|JIT_F_OPT_DCE) +#define JIT_F_OPT_2 (JIT_F_OPT_1|JIT_F_OPT_NARROW|JIT_F_OPT_LOOP) +#define JIT_F_OPT_3 (JIT_F_OPT_2|\ + JIT_F_OPT_FWD|JIT_F_OPT_DSE|JIT_F_OPT_ABC|JIT_F_OPT_SINK|JIT_F_OPT_FUSE) +#define JIT_F_OPT_DEFAULT JIT_F_OPT_3 + +#if LJ_TARGET_WINDOWS || LJ_64 +/* See: http://blogs.msdn.com/oldnewthing/archive/2003/10/08/55239.aspx */ +#define JIT_P_sizemcode_DEFAULT 64 +#else +/* Could go as low as 4K, but the mmap() overhead would be rather high. */ +#define JIT_P_sizemcode_DEFAULT 32 +#endif + +/* Optimization parameters and their defaults. Length is a char in octal! */ +#define JIT_PARAMDEF(_) \ + _(\010, maxtrace, 1000) /* Max. # of traces in cache. */ \ + _(\011, maxrecord, 4000) /* Max. # of recorded IR instructions. */ \ + _(\012, maxirconst, 500) /* Max. # of IR constants of a trace. */ \ + _(\007, maxside, 100) /* Max. # of side traces of a root trace. */ \ + _(\007, maxsnap, 500) /* Max. # of snapshots for a trace. */ \ + _(\011, minstitch, 0) /* Min. # of IR ins for a stitched trace. */ \ + \ + _(\007, hotloop, 56) /* # of iter. to detect a hot loop/call. */ \ + _(\007, hotexit, 10) /* # of taken exits to start a side trace. */ \ + _(\007, tryside, 4) /* # of attempts to compile a side trace. */ \ + \ + _(\012, instunroll, 4) /* Max. unroll for instable loops. */ \ + _(\012, loopunroll, 15) /* Max. unroll for loop ops in side traces. */ \ + _(\012, callunroll, 3) /* Max. unroll for recursive calls. */ \ + _(\011, recunroll, 2) /* Min. unroll for true recursion. */ \ + \ + /* Size of each machine code area (in KBytes). */ \ + _(\011, sizemcode, JIT_P_sizemcode_DEFAULT) \ + /* Max. total size of all machine code areas (in KBytes). */ \ + _(\010, maxmcode, 512) \ + /* End of list. */ + +enum { +#define JIT_PARAMENUM(len, name, value) JIT_P_##name, +JIT_PARAMDEF(JIT_PARAMENUM) +#undef JIT_PARAMENUM + JIT_P__MAX +}; + +#define JIT_PARAMSTR(len, name, value) #len #name +#define JIT_P_STRING JIT_PARAMDEF(JIT_PARAMSTR) + +/* Trace compiler state. */ +typedef enum { + LJ_TRACE_IDLE, /* Trace compiler idle. */ + LJ_TRACE_ACTIVE = 0x10, + LJ_TRACE_RECORD, /* Bytecode recording active. */ + LJ_TRACE_START, /* New trace started. */ + LJ_TRACE_END, /* End of trace. */ + LJ_TRACE_ASM, /* Assemble trace. */ + LJ_TRACE_ERR /* Trace aborted with error. */ +} TraceState; + +/* Post-processing action. */ +typedef enum { + LJ_POST_NONE, /* No action. */ + LJ_POST_FIXCOMP, /* Fixup comparison and emit pending guard. */ + LJ_POST_FIXGUARD, /* Fixup and emit pending guard. */ + LJ_POST_FIXGUARDSNAP, /* Fixup and emit pending guard and snapshot. */ + LJ_POST_FIXBOOL, /* Fixup boolean result. */ + LJ_POST_FIXCONST, /* Fixup constant results. */ + LJ_POST_FFRETRY /* Suppress recording of retried fast functions. */ +} PostProc; + +/* Machine code type. */ +#if LJ_TARGET_X86ORX64 +typedef uint8_t MCode; +#else +typedef uint32_t MCode; +#endif + +/* Linked list of MCode areas. */ +typedef struct MCLink { + MCode *next; /* Next area. */ + size_t size; /* Size of current area. */ +} MCLink; + +/* Stack snapshot header. */ +typedef struct SnapShot { + uint32_t mapofs; /* Offset into snapshot map. */ + IRRef1 ref; /* First IR ref for this snapshot. */ + uint8_t nslots; /* Number of valid slots. */ + uint8_t topslot; /* Maximum frame extent. */ + uint8_t nent; /* Number of compressed entries. */ + uint8_t count; /* Count of taken exits for this snapshot. */ +} SnapShot; + +#define SNAPCOUNT_DONE 255 /* Already compiled and linked a side trace. */ + +/* Compressed snapshot entry. */ +typedef uint32_t SnapEntry; + +#define SNAP_FRAME 0x010000 /* Frame slot. */ +#define SNAP_CONT 0x020000 /* Continuation slot. */ +#define SNAP_NORESTORE 0x040000 /* No need to restore slot. */ +#define SNAP_SOFTFPNUM 0x080000 /* Soft-float number. */ +LJ_STATIC_ASSERT(SNAP_FRAME == TREF_FRAME); +LJ_STATIC_ASSERT(SNAP_CONT == TREF_CONT); + +#define SNAP(slot, flags, ref) (((SnapEntry)(slot) << 24) + (flags) + (ref)) +#define SNAP_TR(slot, tr) \ + (((SnapEntry)(slot) << 24) + ((tr) & (TREF_CONT|TREF_FRAME|TREF_REFMASK))) +#if !LJ_FR2 +#define SNAP_MKPC(pc) ((SnapEntry)u32ptr(pc)) +#endif +#define SNAP_MKFTSZ(ftsz) ((SnapEntry)(ftsz)) +#define snap_ref(sn) ((sn) & 0xffff) +#define snap_slot(sn) ((BCReg)((sn) >> 24)) +#define snap_isframe(sn) ((sn) & SNAP_FRAME) +#define snap_setref(sn, ref) (((sn) & (0xffff0000&~SNAP_NORESTORE)) | (ref)) + +static LJ_AINLINE const BCIns *snap_pc(SnapEntry *sn) +{ +#if LJ_FR2 + uint64_t pcbase; + memcpy(&pcbase, sn, sizeof(uint64_t)); + return (const BCIns *)(pcbase >> 8); +#else + return (const BCIns *)(uintptr_t)*sn; +#endif +} + +/* Snapshot and exit numbers. */ +typedef uint32_t SnapNo; +typedef uint32_t ExitNo; + +/* Trace number. */ +typedef uint32_t TraceNo; /* Used to pass around trace numbers. */ +typedef uint16_t TraceNo1; /* Stored trace number. */ + +/* Type of link. ORDER LJ_TRLINK */ +typedef enum { + LJ_TRLINK_NONE, /* Incomplete trace. No link, yet. */ + LJ_TRLINK_ROOT, /* Link to other root trace. */ + LJ_TRLINK_LOOP, /* Loop to same trace. */ + LJ_TRLINK_TAILREC, /* Tail-recursion. */ + LJ_TRLINK_UPREC, /* Up-recursion. */ + LJ_TRLINK_DOWNREC, /* Down-recursion. */ + LJ_TRLINK_INTERP, /* Fallback to interpreter. */ + LJ_TRLINK_RETURN, /* Return to interpreter. */ + LJ_TRLINK_STITCH /* Trace stitching. */ +} TraceLink; + +/* Trace object. */ +typedef struct GCtrace { + GCHeader; + uint16_t nsnap; /* Number of snapshots. */ + IRRef nins; /* Next IR instruction. Biased with REF_BIAS. */ +#if LJ_GC64 + uint32_t unused_gc64; +#endif + GCRef gclist; + IRIns *ir; /* IR instructions/constants. Biased with REF_BIAS. */ + IRRef nk; /* Lowest IR constant. Biased with REF_BIAS. */ + uint32_t nsnapmap; /* Number of snapshot map elements. */ + SnapShot *snap; /* Snapshot array. */ + SnapEntry *snapmap; /* Snapshot map. */ + GCRef startpt; /* Starting prototype. */ + MRef startpc; /* Bytecode PC of starting instruction. */ + BCIns startins; /* Original bytecode of starting instruction. */ + MSize szmcode; /* Size of machine code. */ + MCode *mcode; /* Start of machine code. */ + MSize mcloop; /* Offset of loop start in machine code. */ + uint16_t nchild; /* Number of child traces (root trace only). */ + uint16_t spadjust; /* Stack pointer adjustment (offset in bytes). */ + TraceNo1 traceno; /* Trace number. */ + TraceNo1 link; /* Linked trace (or self for loops). */ + TraceNo1 root; /* Root trace of side trace (or 0 for root traces). */ + TraceNo1 nextroot; /* Next root trace for same prototype. */ + TraceNo1 nextside; /* Next side trace of same root trace. */ + uint8_t sinktags; /* Trace has SINK tags. */ + uint8_t topslot; /* Top stack slot already checked to be allocated. */ + uint8_t linktype; /* Type of link. */ + uint8_t unused1; +#ifdef LUAJIT_USE_GDBJIT + void *gdbjit_entry; /* GDB JIT entry. */ +#endif +} GCtrace; + +#define gco2trace(o) check_exp((o)->gch.gct == ~LJ_TTRACE, (GCtrace *)(o)) +#define traceref(J, n) \ + check_exp((n)>0 && (MSize)(n)sizetrace, (GCtrace *)gcref(J->trace[(n)])) + +LJ_STATIC_ASSERT(offsetof(GChead, gclist) == offsetof(GCtrace, gclist)); + +static LJ_AINLINE MSize snap_nextofs(GCtrace *T, SnapShot *snap) +{ + if (snap+1 == &T->snap[T->nsnap]) + return T->nsnapmap; + else + return (snap+1)->mapofs; +} + +/* Round-robin penalty cache for bytecodes leading to aborted traces. */ +typedef struct HotPenalty { + MRef pc; /* Starting bytecode PC. */ + uint16_t val; /* Penalty value, i.e. hotcount start. */ + uint16_t reason; /* Abort reason (really TraceErr). */ +} HotPenalty; + +#define PENALTY_SLOTS 64 /* Penalty cache slot. Must be a power of 2. */ +#define PENALTY_MIN (36*2) /* Minimum penalty value. */ +#define PENALTY_MAX 60000 /* Maximum penalty value. */ +#define PENALTY_RNDBITS 4 /* # of random bits to add to penalty value. */ + +/* Round-robin backpropagation cache for narrowing conversions. */ +typedef struct BPropEntry { + IRRef1 key; /* Key: original reference. */ + IRRef1 val; /* Value: reference after conversion. */ + IRRef mode; /* Mode for this entry (currently IRCONV_*). */ +} BPropEntry; + +/* Number of slots for the backpropagation cache. Must be a power of 2. */ +#define BPROP_SLOTS 16 + +/* Scalar evolution analysis cache. */ +typedef struct ScEvEntry { + MRef pc; /* Bytecode PC of FORI. */ + IRRef1 idx; /* Index reference. */ + IRRef1 start; /* Constant start reference. */ + IRRef1 stop; /* Constant stop reference. */ + IRRef1 step; /* Constant step reference. */ + IRType1 t; /* Scalar type. */ + uint8_t dir; /* Direction. 1: +, 0: -. */ +} ScEvEntry; + +/* Reverse bytecode map (IRRef -> PC). Only for selected instructions. */ +typedef struct RBCHashEntry { + MRef pc; /* Bytecode PC. */ + GCRef pt; /* Prototype. */ + IRRef ref; /* IR reference. */ +} RBCHashEntry; + +/* Number of slots in the reverse bytecode hash table. Must be a power of 2. */ +#define RBCHASH_SLOTS 8 + +/* 128 bit SIMD constants. */ +enum { + LJ_KSIMD_ABS, + LJ_KSIMD_NEG, + LJ_KSIMD__MAX +}; + +enum { +#if LJ_TARGET_X86ORX64 + LJ_K64_TOBIT, /* 2^52 + 2^51 */ + LJ_K64_2P64, /* 2^64 */ + LJ_K64_M2P64, /* -2^64 */ +#if LJ_32 + LJ_K64_M2P64_31, /* -2^64 or -2^31 */ +#else + LJ_K64_M2P64_31 = LJ_K64_M2P64, +#endif +#endif +#if LJ_TARGET_MIPS + LJ_K64_2P31, /* 2^31 */ +#if LJ_64 + LJ_K64_2P63, /* 2^63 */ + LJ_K64_M2P64, /* -2^64 */ +#endif +#endif + LJ_K64__MAX, +}; + +enum { +#if LJ_TARGET_X86ORX64 + LJ_K32_M2P64_31, /* -2^64 or -2^31 */ +#endif +#if LJ_TARGET_PPC + LJ_K32_2P52_2P31, /* 2^52 + 2^31 */ + LJ_K32_2P52, /* 2^52 */ +#endif +#if LJ_TARGET_PPC || LJ_TARGET_MIPS + LJ_K32_2P31, /* 2^31 */ +#endif +#if LJ_TARGET_MIPS64 + LJ_K32_2P63, /* 2^63 */ + LJ_K32_M2P64, /* -2^64 */ +#endif + LJ_K32__MAX +}; + +/* Get 16 byte aligned pointer to SIMD constant. */ +#define LJ_KSIMD(J, n) \ + ((TValue *)(((intptr_t)&J->ksimd[2*(n)] + 15) & ~(intptr_t)15)) + +/* Set/reset flag to activate the SPLIT pass for the current trace. */ +#if LJ_SOFTFP32 || (LJ_32 && LJ_HASFFI) +#define lj_needsplit(J) (J->needsplit = 1) +#define lj_resetsplit(J) (J->needsplit = 0) +#else +#define lj_needsplit(J) UNUSED(J) +#define lj_resetsplit(J) UNUSED(J) +#endif + +/* Fold state is used to fold instructions on-the-fly. */ +typedef struct FoldState { + IRIns ins; /* Currently emitted instruction. */ + IRIns left[2]; /* Instruction referenced by left operand. */ + IRIns right[2]; /* Instruction referenced by right operand. */ +} FoldState; + +/* JIT compiler state. */ +typedef struct jit_State { + GCtrace cur; /* Current trace. */ + GCtrace *curfinal; /* Final address of current trace (set during asm). */ + + lua_State *L; /* Current Lua state. */ + const BCIns *pc; /* Current PC. */ + GCfunc *fn; /* Current function. */ + GCproto *pt; /* Current prototype. */ + TRef *base; /* Current frame base, points into J->slots. */ + + uint32_t flags; /* JIT engine flags. */ + BCReg maxslot; /* Relative to baseslot. */ + BCReg baseslot; /* Current frame base, offset into J->slots. */ + + uint8_t mergesnap; /* Allowed to merge with next snapshot. */ + uint8_t needsnap; /* Need snapshot before recording next bytecode. */ + IRType1 guardemit; /* Accumulated IRT_GUARD for emitted instructions. */ + uint8_t bcskip; /* Number of bytecode instructions to skip. */ + + FoldState fold; /* Fold state. */ + + const BCIns *bc_min; /* Start of allowed bytecode range for root trace. */ + MSize bc_extent; /* Extent of the range. */ + + TraceState state; /* Trace compiler state. */ + + int32_t instunroll; /* Unroll counter for instable loops. */ + int32_t loopunroll; /* Unroll counter for loop ops in side traces. */ + int32_t tailcalled; /* Number of successive tailcalls. */ + int32_t framedepth; /* Current frame depth. */ + int32_t retdepth; /* Return frame depth (count of RETF). */ + + TValue ksimd[LJ_KSIMD__MAX*2+1]; /* 16 byte aligned SIMD constants. */ + TValue k64[LJ_K64__MAX]; /* Common 8 byte constants used by backends. */ + uint32_t k32[LJ_K32__MAX]; /* Ditto for 4 byte constants. */ + + IRIns *irbuf; /* Temp. IR instruction buffer. Biased with REF_BIAS. */ + IRRef irtoplim; /* Upper limit of instuction buffer (biased). */ + IRRef irbotlim; /* Lower limit of instuction buffer (biased). */ + IRRef loopref; /* Last loop reference or ref of final LOOP (or 0). */ + + MSize sizesnap; /* Size of temp. snapshot buffer. */ + SnapShot *snapbuf; /* Temp. snapshot buffer. */ + SnapEntry *snapmapbuf; /* Temp. snapshot map buffer. */ + MSize sizesnapmap; /* Size of temp. snapshot map buffer. */ + + PostProc postproc; /* Required post-processing after execution. */ +#if LJ_SOFTFP32 || (LJ_32 && LJ_HASFFI) + uint8_t needsplit; /* Need SPLIT pass. */ +#endif + uint8_t retryrec; /* Retry recording. */ + + GCRef *trace; /* Array of traces. */ + TraceNo freetrace; /* Start of scan for next free trace. */ + MSize sizetrace; /* Size of trace array. */ + IRRef1 ktrace; /* Reference to KGC with GCtrace. */ + + IRRef1 chain[IR__MAX]; /* IR instruction skip-list chain anchors. */ + TRef slot[LJ_MAX_JSLOTS+LJ_STACK_EXTRA]; /* Stack slot map. */ + + int32_t param[JIT_P__MAX]; /* JIT engine parameters. */ + + MCode *exitstubgroup[LJ_MAX_EXITSTUBGR]; /* Exit stub group addresses. */ + + HotPenalty penalty[PENALTY_SLOTS]; /* Penalty slots. */ + uint32_t penaltyslot; /* Round-robin index into penalty slots. */ + uint32_t prngstate; /* PRNG state. */ + +#ifdef LUAJIT_ENABLE_TABLE_BUMP + RBCHashEntry rbchash[RBCHASH_SLOTS]; /* Reverse bytecode map. */ +#endif + + BPropEntry bpropcache[BPROP_SLOTS]; /* Backpropagation cache slots. */ + uint32_t bpropslot; /* Round-robin index into bpropcache slots. */ + + ScEvEntry scev; /* Scalar evolution analysis cache slots. */ + + const BCIns *startpc; /* Bytecode PC of starting instruction. */ + TraceNo parent; /* Parent of current side trace (0 for root traces). */ + ExitNo exitno; /* Exit number in parent of current side trace. */ + + BCIns *patchpc; /* PC for pending re-patch. */ + BCIns patchins; /* Instruction for pending re-patch. */ + + int mcprot; /* Protection of current mcode area. */ + MCode *mcarea; /* Base of current mcode area. */ + MCode *mctop; /* Top of current mcode area. */ + MCode *mcbot; /* Bottom of current mcode area. */ + size_t szmcarea; /* Size of current mcode area. */ + size_t szallmcarea; /* Total size of all allocated mcode areas. */ + + TValue errinfo; /* Additional info element for trace errors. */ + +#if LJ_HASPROFILE + GCproto *prev_pt; /* Previous prototype. */ + BCLine prev_line; /* Previous line. */ + int prof_mode; /* Profiling mode: 0, 'f', 'l'. */ +#endif +} +#if LJ_TARGET_ARM +LJ_ALIGN(16) /* For DISPATCH-relative addresses in assembler part. */ +#endif +jit_State; + +/* Trivial PRNG e.g. used for penalty randomization. */ +static LJ_AINLINE uint32_t LJ_PRNG_BITS(jit_State *J, int bits) +{ + /* Yes, this LCG is very weak, but that doesn't matter for our use case. */ + J->prngstate = J->prngstate * 1103515245 + 12345; + return J->prngstate >> (32-bits); +} + +#endif diff --git a/lib/LuaJIT/lj_lex.c b/lib/LuaJIT/lj_lex.c new file mode 100644 index 0000000..2d2f819 --- /dev/null +++ b/lib/LuaJIT/lj_lex.c @@ -0,0 +1,509 @@ +/* +** Lexical analyzer. +** Copyright (C) 2005-2017 Mike Pall. See Copyright Notice in luajit.h +** +** Major portions taken verbatim or adapted from the Lua interpreter. +** Copyright (C) 1994-2008 Lua.org, PUC-Rio. See Copyright Notice in lua.h +*/ + +#define lj_lex_c +#define LUA_CORE + +#include "lj_obj.h" +#include "lj_gc.h" +#include "lj_err.h" +#include "lj_buf.h" +#include "lj_str.h" +#if LJ_HASFFI +#include "lj_tab.h" +#include "lj_ctype.h" +#include "lj_cdata.h" +#include "lualib.h" +#endif +#include "lj_state.h" +#include "lj_lex.h" +#include "lj_parse.h" +#include "lj_char.h" +#include "lj_strscan.h" +#include "lj_strfmt.h" + +/* Lua lexer token names. */ +static const char *const tokennames[] = { +#define TKSTR1(name) #name, +#define TKSTR2(name, sym) #sym, +TKDEF(TKSTR1, TKSTR2) +#undef TKSTR1 +#undef TKSTR2 + NULL +}; + +/* -- Buffer handling ----------------------------------------------------- */ + +#define LEX_EOF (-1) +#define lex_iseol(ls) (ls->c == '\n' || ls->c == '\r') + +/* Get more input from reader. */ +static LJ_NOINLINE LexChar lex_more(LexState *ls) +{ + size_t sz; + const char *p = ls->rfunc(ls->L, ls->rdata, &sz); + if (p == NULL || sz == 0) return LEX_EOF; + ls->pe = p + sz; + ls->p = p + 1; + return (LexChar)(uint8_t)p[0]; +} + +/* Get next character. */ +static LJ_AINLINE LexChar lex_next(LexState *ls) +{ + return (ls->c = ls->p < ls->pe ? (LexChar)(uint8_t)*ls->p++ : lex_more(ls)); +} + +/* Save character. */ +static LJ_AINLINE void lex_save(LexState *ls, LexChar c) +{ + lj_buf_putb(&ls->sb, c); +} + +/* Save previous character and get next character. */ +static LJ_AINLINE LexChar lex_savenext(LexState *ls) +{ + lex_save(ls, ls->c); + return lex_next(ls); +} + +/* Skip line break. Handles "\n", "\r", "\r\n" or "\n\r". */ +static void lex_newline(LexState *ls) +{ + LexChar old = ls->c; + lua_assert(lex_iseol(ls)); + lex_next(ls); /* Skip "\n" or "\r". */ + if (lex_iseol(ls) && ls->c != old) lex_next(ls); /* Skip "\n\r" or "\r\n". */ + if (++ls->linenumber >= LJ_MAX_LINE) + lj_lex_error(ls, ls->tok, LJ_ERR_XLINES); +} + +/* -- Scanner for terminals ----------------------------------------------- */ + +/* Parse a number literal. */ +static void lex_number(LexState *ls, TValue *tv) +{ + StrScanFmt fmt; + LexChar c, xp = 'e'; + lua_assert(lj_char_isdigit(ls->c)); + if ((c = ls->c) == '0' && (lex_savenext(ls) | 0x20) == 'x') + xp = 'p'; + while (lj_char_isident(ls->c) || ls->c == '.' || + ((ls->c == '-' || ls->c == '+') && (c | 0x20) == xp)) { + c = ls->c; + lex_savenext(ls); + } + lex_save(ls, '\0'); + fmt = lj_strscan_scan((const uint8_t *)sbufB(&ls->sb), tv, + (LJ_DUALNUM ? STRSCAN_OPT_TOINT : STRSCAN_OPT_TONUM) | + (LJ_HASFFI ? (STRSCAN_OPT_LL|STRSCAN_OPT_IMAG) : 0)); + if (LJ_DUALNUM && fmt == STRSCAN_INT) { + setitype(tv, LJ_TISNUM); + } else if (fmt == STRSCAN_NUM) { + /* Already in correct format. */ +#if LJ_HASFFI + } else if (fmt != STRSCAN_ERROR) { + lua_State *L = ls->L; + GCcdata *cd; + lua_assert(fmt == STRSCAN_I64 || fmt == STRSCAN_U64 || fmt == STRSCAN_IMAG); + if (!ctype_ctsG(G(L))) { + ptrdiff_t oldtop = savestack(L, L->top); + luaopen_ffi(L); /* Load FFI library on-demand. */ + L->top = restorestack(L, oldtop); + } + if (fmt == STRSCAN_IMAG) { + cd = lj_cdata_new_(L, CTID_COMPLEX_DOUBLE, 2*sizeof(double)); + ((double *)cdataptr(cd))[0] = 0; + ((double *)cdataptr(cd))[1] = numV(tv); + } else { + cd = lj_cdata_new_(L, fmt==STRSCAN_I64 ? CTID_INT64 : CTID_UINT64, 8); + *(uint64_t *)cdataptr(cd) = tv->u64; + } + lj_parse_keepcdata(ls, tv, cd); +#endif + } else { + lua_assert(fmt == STRSCAN_ERROR); + lj_lex_error(ls, TK_number, LJ_ERR_XNUMBER); + } +} + +/* Skip equal signs for "[=...=[" and "]=...=]" and return their count. */ +static int lex_skipeq(LexState *ls) +{ + int count = 0; + LexChar s = ls->c; + lua_assert(s == '[' || s == ']'); + while (lex_savenext(ls) == '=') + count++; + return (ls->c == s) ? count : (-count) - 1; +} + +/* Parse a long string or long comment (tv set to NULL). */ +static void lex_longstring(LexState *ls, TValue *tv, int sep) +{ + lex_savenext(ls); /* Skip second '['. */ + if (lex_iseol(ls)) /* Skip initial newline. */ + lex_newline(ls); + for (;;) { + switch (ls->c) { + case LEX_EOF: + lj_lex_error(ls, TK_eof, tv ? LJ_ERR_XLSTR : LJ_ERR_XLCOM); + break; + case ']': + if (lex_skipeq(ls) == sep) { + lex_savenext(ls); /* Skip second ']'. */ + goto endloop; + } + break; + case '\n': + case '\r': + lex_save(ls, '\n'); + lex_newline(ls); + if (!tv) lj_buf_reset(&ls->sb); /* Don't waste space for comments. */ + break; + default: + lex_savenext(ls); + break; + } + } endloop: + if (tv) { + GCstr *str = lj_parse_keepstr(ls, sbufB(&ls->sb) + (2 + (MSize)sep), + sbuflen(&ls->sb) - 2*(2 + (MSize)sep)); + setstrV(ls->L, tv, str); + } +} + +/* Parse a string. */ +static void lex_string(LexState *ls, TValue *tv) +{ + LexChar delim = ls->c; /* Delimiter is '\'' or '"'. */ + lex_savenext(ls); + while (ls->c != delim) { + switch (ls->c) { + case LEX_EOF: + lj_lex_error(ls, TK_eof, LJ_ERR_XSTR); + continue; + case '\n': + case '\r': + lj_lex_error(ls, TK_string, LJ_ERR_XSTR); + continue; + case '\\': { + LexChar c = lex_next(ls); /* Skip the '\\'. */ + switch (c) { + case 'a': c = '\a'; break; + case 'b': c = '\b'; break; + case 'f': c = '\f'; break; + case 'n': c = '\n'; break; + case 'r': c = '\r'; break; + case 't': c = '\t'; break; + case 'v': c = '\v'; break; + case 'x': /* Hexadecimal escape '\xXX'. */ + c = (lex_next(ls) & 15u) << 4; + if (!lj_char_isdigit(ls->c)) { + if (!lj_char_isxdigit(ls->c)) goto err_xesc; + c += 9 << 4; + } + c += (lex_next(ls) & 15u); + if (!lj_char_isdigit(ls->c)) { + if (!lj_char_isxdigit(ls->c)) goto err_xesc; + c += 9; + } + break; + case 'u': /* Unicode escape '\u{XX...}'. */ + if (lex_next(ls) != '{') goto err_xesc; + lex_next(ls); + c = 0; + do { + c = (c << 4) | (ls->c & 15u); + if (!lj_char_isdigit(ls->c)) { + if (!lj_char_isxdigit(ls->c)) goto err_xesc; + c += 9; + } + if (c >= 0x110000) goto err_xesc; /* Out of Unicode range. */ + } while (lex_next(ls) != '}'); + if (c < 0x800) { + if (c < 0x80) break; + lex_save(ls, 0xc0 | (c >> 6)); + } else { + if (c >= 0x10000) { + lex_save(ls, 0xf0 | (c >> 18)); + lex_save(ls, 0x80 | ((c >> 12) & 0x3f)); + } else { + if (c >= 0xd800 && c < 0xe000) goto err_xesc; /* No surrogates. */ + lex_save(ls, 0xe0 | (c >> 12)); + } + lex_save(ls, 0x80 | ((c >> 6) & 0x3f)); + } + c = 0x80 | (c & 0x3f); + break; + case 'z': /* Skip whitespace. */ + lex_next(ls); + while (lj_char_isspace(ls->c)) + if (lex_iseol(ls)) lex_newline(ls); else lex_next(ls); + continue; + case '\n': case '\r': lex_save(ls, '\n'); lex_newline(ls); continue; + case '\\': case '\"': case '\'': break; + case LEX_EOF: continue; + default: + if (!lj_char_isdigit(c)) + goto err_xesc; + c -= '0'; /* Decimal escape '\ddd'. */ + if (lj_char_isdigit(lex_next(ls))) { + c = c*10 + (ls->c - '0'); + if (lj_char_isdigit(lex_next(ls))) { + c = c*10 + (ls->c - '0'); + if (c > 255) { + err_xesc: + lj_lex_error(ls, TK_string, LJ_ERR_XESC); + } + lex_next(ls); + } + } + lex_save(ls, c); + continue; + } + lex_save(ls, c); + lex_next(ls); + continue; + } + default: + lex_savenext(ls); + break; + } + } + lex_savenext(ls); /* Skip trailing delimiter. */ + setstrV(ls->L, tv, + lj_parse_keepstr(ls, sbufB(&ls->sb)+1, sbuflen(&ls->sb)-2)); +} + +/* -- Main lexical scanner ------------------------------------------------ */ + +/* Get next lexical token. */ +static LexToken lex_scan(LexState *ls, TValue *tv) +{ + lj_buf_reset(&ls->sb); + for (;;) { + if (lj_char_isident(ls->c)) { + GCstr *s; + if (lj_char_isdigit(ls->c)) { /* Numeric literal. */ + lex_number(ls, tv); + return TK_number; + } + /* Identifier or reserved word. */ + do { + lex_savenext(ls); + } while (lj_char_isident(ls->c)); + s = lj_parse_keepstr(ls, sbufB(&ls->sb), sbuflen(&ls->sb)); + setstrV(ls->L, tv, s); + if (s->reserved > 0) /* Reserved word? */ + return TK_OFS + s->reserved; + return TK_name; + } + switch (ls->c) { + case '\n': + case '\r': + lex_newline(ls); + continue; + case ' ': + case '\t': + case '\v': + case '\f': + lex_next(ls); + continue; + case '-': + lex_next(ls); + if (ls->c != '-') return '-'; + lex_next(ls); + if (ls->c == '[') { /* Long comment "--[=*[...]=*]". */ + int sep = lex_skipeq(ls); + lj_buf_reset(&ls->sb); /* `lex_skipeq' may dirty the buffer */ + if (sep >= 0) { + lex_longstring(ls, NULL, sep); + lj_buf_reset(&ls->sb); + continue; + } + } + /* Short comment "--.*\n". */ + while (!lex_iseol(ls) && ls->c != LEX_EOF) + lex_next(ls); + continue; + case '[': { + int sep = lex_skipeq(ls); + if (sep >= 0) { + lex_longstring(ls, tv, sep); + return TK_string; + } else if (sep == -1) { + return '['; + } else { + lj_lex_error(ls, TK_string, LJ_ERR_XLDELIM); + continue; + } + } + case '=': + lex_next(ls); + if (ls->c != '=') return '='; else { lex_next(ls); return TK_eq; } + case '<': + lex_next(ls); + if (ls->c != '=') return '<'; else { lex_next(ls); return TK_le; } + case '>': + lex_next(ls); + if (ls->c != '=') return '>'; else { lex_next(ls); return TK_ge; } + case '~': + lex_next(ls); + if (ls->c != '=') return '~'; else { lex_next(ls); return TK_ne; } + case ':': + lex_next(ls); + if (ls->c != ':') return ':'; else { lex_next(ls); return TK_label; } + case '"': + case '\'': + lex_string(ls, tv); + return TK_string; + case '.': + if (lex_savenext(ls) == '.') { + lex_next(ls); + if (ls->c == '.') { + lex_next(ls); + return TK_dots; /* ... */ + } + return TK_concat; /* .. */ + } else if (!lj_char_isdigit(ls->c)) { + return '.'; + } else { + lex_number(ls, tv); + return TK_number; + } + case LEX_EOF: + return TK_eof; + default: { + LexChar c = ls->c; + lex_next(ls); + return c; /* Single-char tokens (+ - / ...). */ + } + } + } +} + +/* -- Lexer API ----------------------------------------------------------- */ + +/* Setup lexer state. */ +int lj_lex_setup(lua_State *L, LexState *ls) +{ + int header = 0; + ls->L = L; + ls->fs = NULL; + ls->pe = ls->p = NULL; + ls->vstack = NULL; + ls->sizevstack = 0; + ls->vtop = 0; + ls->bcstack = NULL; + ls->sizebcstack = 0; + ls->tok = 0; + ls->lookahead = TK_eof; /* No look-ahead token. */ + ls->linenumber = 1; + ls->lastline = 1; + lex_next(ls); /* Read-ahead first char. */ + if (ls->c == 0xef && ls->p + 2 <= ls->pe && (uint8_t)ls->p[0] == 0xbb && + (uint8_t)ls->p[1] == 0xbf) { /* Skip UTF-8 BOM (if buffered). */ + ls->p += 2; + lex_next(ls); + header = 1; + } + if (ls->c == '#') { /* Skip POSIX #! header line. */ + do { + lex_next(ls); + if (ls->c == LEX_EOF) return 0; + } while (!lex_iseol(ls)); + lex_newline(ls); + header = 1; + } + if (ls->c == LUA_SIGNATURE[0]) { /* Bytecode dump. */ + if (header) { + /* + ** Loading bytecode with an extra header is disabled for security + ** reasons. This may circumvent the usual check for bytecode vs. + ** Lua code by looking at the first char. Since this is a potential + ** security violation no attempt is made to echo the chunkname either. + */ + setstrV(L, L->top++, lj_err_str(L, LJ_ERR_BCBAD)); + lj_err_throw(L, LUA_ERRSYNTAX); + } + return 1; + } + return 0; +} + +/* Cleanup lexer state. */ +void lj_lex_cleanup(lua_State *L, LexState *ls) +{ + global_State *g = G(L); + lj_mem_freevec(g, ls->bcstack, ls->sizebcstack, BCInsLine); + lj_mem_freevec(g, ls->vstack, ls->sizevstack, VarInfo); + lj_buf_free(g, &ls->sb); +} + +/* Return next lexical token. */ +void lj_lex_next(LexState *ls) +{ + ls->lastline = ls->linenumber; + if (LJ_LIKELY(ls->lookahead == TK_eof)) { /* No lookahead token? */ + ls->tok = lex_scan(ls, &ls->tokval); /* Get next token. */ + } else { /* Otherwise return lookahead token. */ + ls->tok = ls->lookahead; + ls->lookahead = TK_eof; + ls->tokval = ls->lookaheadval; + } +} + +/* Look ahead for the next token. */ +LexToken lj_lex_lookahead(LexState *ls) +{ + lua_assert(ls->lookahead == TK_eof); + ls->lookahead = lex_scan(ls, &ls->lookaheadval); + return ls->lookahead; +} + +/* Convert token to string. */ +const char *lj_lex_token2str(LexState *ls, LexToken tok) +{ + if (tok > TK_OFS) + return tokennames[tok-TK_OFS-1]; + else if (!lj_char_iscntrl(tok)) + return lj_strfmt_pushf(ls->L, "%c", tok); + else + return lj_strfmt_pushf(ls->L, "char(%d)", tok); +} + +/* Lexer error. */ +void lj_lex_error(LexState *ls, LexToken tok, ErrMsg em, ...) +{ + const char *tokstr; + va_list argp; + if (tok == 0) { + tokstr = NULL; + } else if (tok == TK_name || tok == TK_string || tok == TK_number) { + lex_save(ls, '\0'); + tokstr = sbufB(&ls->sb); + } else { + tokstr = lj_lex_token2str(ls, tok); + } + va_start(argp, em); + lj_err_lex(ls->L, ls->chunkname, tokstr, ls->linenumber, em, argp); + va_end(argp); +} + +/* Initialize strings for reserved words. */ +void lj_lex_init(lua_State *L) +{ + uint32_t i; + for (i = 0; i < TK_RESERVED; i++) { + GCstr *s = lj_str_newz(L, tokennames[i]); + fixstring(s); /* Reserved words are never collected. */ + s->reserved = (uint8_t)(i+1); + } +} + diff --git a/lib/LuaJIT/lj_lex.h b/lib/LuaJIT/lj_lex.h new file mode 100644 index 0000000..33fa865 --- /dev/null +++ b/lib/LuaJIT/lj_lex.h @@ -0,0 +1,86 @@ +/* +** Lexical analyzer. +** Copyright (C) 2005-2017 Mike Pall. See Copyright Notice in luajit.h +*/ + +#ifndef _LJ_LEX_H +#define _LJ_LEX_H + +#include + +#include "lj_obj.h" +#include "lj_err.h" + +/* Lua lexer tokens. */ +#define TKDEF(_, __) \ + _(and) _(break) _(do) _(else) _(elseif) _(end) _(false) \ + _(for) _(function) _(goto) _(if) _(in) _(local) _(nil) _(not) _(or) \ + _(repeat) _(return) _(then) _(true) _(until) _(while) \ + __(concat, ..) __(dots, ...) __(eq, ==) __(ge, >=) __(le, <=) __(ne, ~=) \ + __(label, ::) __(number, ) __(name, ) __(string, ) \ + __(eof, ) + +enum { + TK_OFS = 256, +#define TKENUM1(name) TK_##name, +#define TKENUM2(name, sym) TK_##name, +TKDEF(TKENUM1, TKENUM2) +#undef TKENUM1 +#undef TKENUM2 + TK_RESERVED = TK_while - TK_OFS +}; + +typedef int LexChar; /* Lexical character. Unsigned ext. from char. */ +typedef int LexToken; /* Lexical token. */ + +/* Combined bytecode ins/line. Only used during bytecode generation. */ +typedef struct BCInsLine { + BCIns ins; /* Bytecode instruction. */ + BCLine line; /* Line number for this bytecode. */ +} BCInsLine; + +/* Info for local variables. Only used during bytecode generation. */ +typedef struct VarInfo { + GCRef name; /* Local variable name or goto/label name. */ + BCPos startpc; /* First point where the local variable is active. */ + BCPos endpc; /* First point where the local variable is dead. */ + uint8_t slot; /* Variable slot. */ + uint8_t info; /* Variable/goto/label info. */ +} VarInfo; + +/* Lua lexer state. */ +typedef struct LexState { + struct FuncState *fs; /* Current FuncState. Defined in lj_parse.c. */ + struct lua_State *L; /* Lua state. */ + TValue tokval; /* Current token value. */ + TValue lookaheadval; /* Lookahead token value. */ + const char *p; /* Current position in input buffer. */ + const char *pe; /* End of input buffer. */ + LexChar c; /* Current character. */ + LexToken tok; /* Current token. */ + LexToken lookahead; /* Lookahead token. */ + SBuf sb; /* String buffer for tokens. */ + lua_Reader rfunc; /* Reader callback. */ + void *rdata; /* Reader callback data. */ + BCLine linenumber; /* Input line counter. */ + BCLine lastline; /* Line of last token. */ + GCstr *chunkname; /* Current chunk name (interned string). */ + const char *chunkarg; /* Chunk name argument. */ + const char *mode; /* Allow loading bytecode (b) and/or source text (t). */ + VarInfo *vstack; /* Stack for names and extents of local variables. */ + MSize sizevstack; /* Size of variable stack. */ + MSize vtop; /* Top of variable stack. */ + BCInsLine *bcstack; /* Stack for bytecode instructions/line numbers. */ + MSize sizebcstack; /* Size of bytecode stack. */ + uint32_t level; /* Syntactical nesting level. */ +} LexState; + +LJ_FUNC int lj_lex_setup(lua_State *L, LexState *ls); +LJ_FUNC void lj_lex_cleanup(lua_State *L, LexState *ls); +LJ_FUNC void lj_lex_next(LexState *ls); +LJ_FUNC LexToken lj_lex_lookahead(LexState *ls); +LJ_FUNC const char *lj_lex_token2str(LexState *ls, LexToken tok); +LJ_FUNC_NORET void lj_lex_error(LexState *ls, LexToken tok, ErrMsg em, ...); +LJ_FUNC void lj_lex_init(lua_State *L); + +#endif diff --git a/lib/LuaJIT/lj_lex.o b/lib/LuaJIT/lj_lex.o new file mode 100644 index 0000000000000000000000000000000000000000..402f0793d3540463bd234d29f10886a7ff659af9 GIT binary patch literal 19424 zcmeI4e{@vUoxopyfFs0tgNqs~t>ZFwlT%Cw&31zw?7U=>ylALdNrVPQNHGGM{eJKJ z-kCSwB>(LG>pf=PyPx~L-}}Aa`@KKjo59Vcm1UV38H!99$^%L&C#jvqO(ndmNL?tMs11&_X>58NKIJ=ypBhux>8T(_>W`aCNH( z6x}%LT6Ltae-hHzr6%>xh|sD%TDtg(R-;u{#vb?8ERVK%p#jZ&Z_EuM0Y#A-0_uUh zfVtkQ8J{c-7iq?DMzq8iUGCSHjD#Kv^ycZtV1`-Z)1xJB{i`K;9mjO@5AlbgEzNvg z)(agEnB##Z@2y(%(TKKMTN6-A$AM^%9ce0%_8dZIRGNQ^qjh6H?yJ$wKQBJ99J=g? z);gl!+W}aQ9_SO7r*rk8vswAe-IL2*(eL&WTYN%~{-PT*Lua$8FdT^1_^|-0L`z(H z{v{{?mpE3Lr`Al4rrI|zZTS=jWb;?_hG9K+6R3gqHao6lXJmwL)?bw=Jg@VcRurwkI5Z~a zn5ABI`;aklle+z|<2Rw~B}c-!(b77Mpwhaflb=p*-drrkX3dd4Jz5GnO7l9uXz8dy z9iU+BO)Ea*W7i>`f0B%6-Mkn-jp%pLaN)t{9BApq#e>2?F<$*>{ar9dmZj#07+Shn zR;c#_6mwBGp9S)ferz_%>c`65bj`D_Tf6ZJNk6u=*Sf=793D)^wqlTC0=0GV2f-S% z#Jlt&fY3@~Vqxe@#>5@!_Cf5TdSGzXnj_#hInS_nC0=k+C=3n?wI?Ukf1~F=y(V-S z6O-XHSbeco8|Nubyy!zX<1_UoZ)|+GyXdv>neMKPe+$2ZD*&+0Mkvg(mIHsLXk}sO zhhR&1|Jbc^{$VRc7(-8c?+EWav~w=@;-YZhp)Ap*ixuV|j7lyj125I&22ACcKN}32 z6jG>Q1?0wF66=TdXodNyR#O>k^J&!;=4FikXt^)C!XK^m!s4MuhQaN~&V zmbojwT9&68n}DNND$GY=%yTvKF|ToLjv9FyxN+?!HPQh$eQ+0A`0O`~Yiq)5E6tDM zi=gQxaW!%hY}KMz<)-i`_*aXT`LyVxxL&D|??d6RG6&m)I@oMH@X6%lvYvC{e+R(; zRzy1XGU%UFBjuvWIiZ6#Eup1Tjqbdi8Jt)HZV`S!+x%By^p1@E5#x~7Im@$S`;+mT z#uF=`x)tZLWi(Cn;brKw9`2Fc9AcA&rJ4o+Z!(a$|kzw&y`vI#~w2 zsW7f*9Kz`bdTu|pc+fa|+m^$>b|{Llq^VC^p*?C37hFzW3u53I7jmNyd!rlu(G@<; zeAD>Yt45whMX{w&tc)$7{Yr-&ZyBu;(Xc;YexRAd6%E5$x9sHP9v>@ctVr6d| zqo0KR#tHY5{|YZLN0z)3S{Uf{ihZoqXC9UdPshJt{P#>uPDX2dV?Ryn(b$`PSW%6v z1Uuv^`%K`V6YMcdeU;`PMX$^w#zcl183Nifs76+x(a{YKEn4b`_Th>HJNARdwaien zac$Ni7l>7w7uD|`5tY>gzcY@^9rn+f150j`xpH2qq3~iw{;*Jjs4`=`!Ys{&cEURj;W7{9q7d|=`hIL!KUVC3i*iX*UD=){vv~F; zbzZS+?8W5z0IjClJ0kiYY8_UlICgxzp<@WMVXhB07~s$4641|AW&&-^#7Wn z!@>f4V}>!YdE>l_hU~52*hyGotn-u%8`ZcUmrim1l6w}8Sil^$xvwd?zp2I(b5io) zF?YZWdMg^phNrgeai~2NHz-OV8uVf$F0|GKJ!Zu_)&Oa&)a}bsRP#en2fZ*}cQx8CxNYCNfI3@dK zlHGE35(Zs;p_rPn+IT0u4QK%Uu?+liT#d|we)qc2%h4JyO#j3)7+1*%Ku1{CXNH=L ztFV)e!1=ToZ8#QgdL%1Rfd?#FS70ffP!k+tkxJjAa zlLJ*~C73j;VVS_n@VJwcxCH%6kI!ZPgD_4?{t!5;=Rcts;!W1=m37qvSos&CRGgoz zbEaxM2uJsn8}Y0OUfO|60X$B?G&{e?5mxuO)W~0fh1!V&G!EaGKa{u&IF0u~h&r%8;RHnUJULC?$maIJ98r7z z4z#7r`0f;;(#7wM-2(H6)(h(tZA?59S{5+Rn?w2z=$-Mdiv7It4b6NTdYOY(YM@s$ zUcXV>0;BAL8R4}x)UgkPwfO@G)>4?N&xC)DV&HuB`3A5+)WKQwN6f@}l)TBwrJ{K2 zc@PSi3G9E^KlE`<_lZ$`$v7P9fcL`QhR*^%a~MR-@u81r8}ELt`$YGWFsx?xfv#Q$ z7%gVnGQT-&oc$UK4;pWD=YNp+EgVj!!~+Z!`tuN$iF1PG_=EU-2mHsJLpUOTp7R`w zKgl`x1mzzCo*IO~DT824ELD5{42PwZP=4C1#cWUYbVN?WpT3X<`@Pp&)&>h(69s6Ph=TuFs*a^RUxk1U-3iauSY4 z=J&udc(_p`zkz~-UxZORgo_V|&}J^?2kk#Mg4SWBfMO-((O0K2sX&eVEE5#%ur`kl zY!}urP^=B$aUmXor75vWV7U-{fEAMKDUOUWalhL0l{BG}DZbPOK95~f)5K;?W79oq z&v`iOqzuos3CReV~DAvTj8N$RzZFwTv0boC#H)aPEbS<-O|6~6c$wTCBzlX88>k}fDQ4<~*A zV=T@K%~t*)=&{I8z-Yo2m~dhcfV>XniT&{K1M4v6i!i?q9@HqGhxx)}UM%nPfr;h4 z5VVq<0t~k5#6JsF5$u*&K=F`&Ue6zflfLoEX7%~gPz4qaST?#(4E9`6dwvbNgLabZ{)fI7(CjO9;i=W4M^Cc=^2;kTPs`4+zF*wqWDG>Q}vo(0#g-=|ik#pL>op1y1 zAV(7oqK$5>BEv+~PYgqW+<)Q3gXd-II289lj3>S{Q@vSWiB(VPOFy%`jl%^!E&$6D z??E_Y<}S><25!U5e9U~6N^Zc+TnIuextf5?+cdS_!%Q8B$#yKe|7QwW4yH<(bXzWh zEy)sm07Ey3C#n!k0c^1s8~G)~gA@zE3qngS5QRJ497=r6R-8V?GNI(4bLvul@VW!s zjgt!6pyl0S=}Uh4^INpM4&R@e<&DwuqNP|nJYSD(bj26K`e&9Dt~z4*-7GJJ*N?f! z%8O8Dm2Q^1;5C~c-k--d=7NIwhj?fyDFXS_VFBVyt2qd(Fg&Q-gKogr;&RqKn{7+!1(>lzf=3G~Y+BVYO~O+ugfzwr*UzHT*Wdu(O{1j@;d# zG&I+D zT}EcP(;c4UbZJfpzHjmY_G1~OAL2)`)Af92sWUg0RqS+c%Pw{1ZJnh#y~ga!ra8_$ z&FKy}b3vxU>39fV9C+Zb8~!jg;Rm#_HM7i_Yh(qS?#vozE`SoJ17G;=!E#77)YB1vtfb2ICq0DR2~aF`Sw z{^Hcm&+ttGjKK@&XKJSy+kv{&&O^?s%tp`#Ei-K;bA{gv(N;(}{t9@;$XqkW=_sH3 zu+yR73;Sv+zZuI#+*Cn{q7=bjJC%v?20LPzUw7ti%hH|ht>`afR)y31Z4oP)bQNlu zF=4l|l@IVG=#-R&C+KNXmV!s8sVNII@q{l}GHrUqTmbeL#H*-+h=01zQs9Z7r7u7} zLqRWC$wj8+Ll*V#BEN`O1%}_&Bi~0RiTDB*AU+DOV=#%hf$PKYhw^@Wg9J&8vvmu- zC1J|TOP0+Z$?%$`VAM=aSqi>ZN>3|Ac4~ju5a*s=OMI#(Y!|(qo*sv+i+p1*2|?!O z%nW6mpok~9H;C7ff8~0y9r$#5UY>!!N_qxKk62@X9hyP@1j+lU!(tr)_Rb9Q@6N#A zpMhh5PiMah_D#p}-Oh9z-^t^8%2U}4{1M>jXXQ4lhM4nk`xtRQaWU6{w-K)+F2@DG z4WF*v=a5e`zDmmzwJpJ}f(8XXYd5rY1jS9~`gQHWZz-X+^}$wr0O%C7HMp^5U8^P0 z($@O8^-N$DiN^)2ZbN;mQczjq#gE1KaUZs_Hryoi-%Cx_HnarmTf^-#E84mjR3gBq zq1v_ULeTCLwZNJ-glgNvoy|>BI@r+x&o@vAH`oEmJK<4Bs6)YCct+h=}>!cyA!LE?JZ)s~=U*8<8Z$;Ev4Y$ApnbkpT5=TCPACPc4 zx&{8=t%gYLFn*YD2yyL)hiH+u*!Vo)QvN$Ojxi(oD>jZ_OH00qVguW~!^R^vj-e*y zzi;DqJBw&uqMpy$IPcdc8^@!Jw10@U4wM(1;^{fsXs}(o|8GPgNZg*%8Td6?yine5XBi#@ zAaOnU*m>gicGuas-9InSz<)IZzw6eNe!HIgX5bYw@P-+9bO!#)41Cze?f&`g4E+5W z_!G2m;5edd<@k2mxP5)tNgVy3XOsVljpy5VFU1AQ-)`f7KLfwh7SDfalZRW8&`$h& z$`%J$Ao*^JS5lMYdx*<(kd)s`T<(>U?;|ds*CgLh9M@hl6(hodESux47KYQ(kb~-;sM6b z5!V@y6R%|a0`V%w$B9=newny+4o=x4{j-|MUnluG#-)E68JGTPX52ykX<2@NqisURmAr*F8$Nbc#sa%1B?&gVG7cq_)NuH9qB*Hcq8%S zj5iZM$#@I#5ysnzzr}cn_-V$wh@WA66Y){TyNRD;JVHFqcrWn_j4va$1QEp?6UVPj zF-iG;;`kE{J0BqKW_*yihw-Dt^B6x)d=cX(iQ{voz1!65q#o74iLyR}=4Nd?oP###a*`WW0{}QN|mI zA7{Lo_({fFh>tMdPW&y#L&Q%r-bMTjMSMP!pHDoO@m%5y z7?<zVSCy((w;)@tRNq+D$?j?Eo-2oQkUm@`#CV!gr_!yV-*Uz||zX8Sr zq+e%TCtk_8oWE6!SCPE@ZUc+)ubTKuCNJmjYR2XKtz%ry-$uq8Nq;lr&BR+6m-Dxs z@ph6AF&-k`#kicm^8FhY#w==$-_zuS9 z{N2g;PLkincptU9oAKQwzlZTX#P>43m-s%$_YvRE_F#;b@|GhR)6CF3iJuV#ET@jAxqh&M9cNW7WxX5uZ3w-9elDvc>(;-M z$FH_XJo$?UJk}$p*FVXtyT>-^}$ALC7w z-@*8L%I{)a+P8=CCn>*=@lBNPXM8i|2N^dgf1L5HlpkT-r2J{dw^4qS@fRo`XZ%IV zk2C%Q%1Z7OFQ;5UWW}q8Wf)?J%hwY z8Q+BpA>~lH)bFSH9AI4j4l2U9{2dg34YK}tY63% zG_-Bl5NriK@H_cn0sL6=p3YEx!+NR^Y_4tUfFHeBp8NlOx}iF{ayC=rr~EK|e=3Vz z@`kSXTa0Y@^HW}4Twk>AT=aVg+~%HCqHDS^Pl@mmeeltKl%^Hi2Lsm zU}y_#Mj2cH5Ryw8YGoBSUF8MW_37FlEVd+aNinvMZS(dq2(bw-{Mpk8{)h$=y>7NL z@F~ra*H#-#3c<};s&8j_FY8He5aa%jQ+@foF6E{DGXE^xOxOR@IIxh4s6p&Mo*iU; z8J<|o+Xkfcx$#p`*LqJeCz-z-5&f@lo;zU%fkO-Yx?>w+Q?%ZFwlT%Cw&31zw?7U=>ylALdNrVPQNHGGM{eJKJ z-kCSwB>(LG>pf=PyPx~L-}}Aa`@KKjo59Vcm1UV38H!99$^%L&C#jvqO(ndmNL?tMs11&_X>58NKIJ=ypBhux>8T(_>W`aCNH( z6x}%LT6Ltae-hHzr6%>xh|sD%TDtg(R-;u{#vb?8ERVK%p#jZ&Z_EuM0Y#A-0_uUh zfVtkQ8J{c-7iq?DMzq8iUGCSHjD#Kv^ycZtV1`-Z)1xJB{i`K;9mjO@5AlbgEzNvg z)(agEnB##Z@2y(%(TKKMTN6-A$AM^%9ce0%_8dZIRGNQ^qjh6H?yJ$wKQBJ99J=g? z);gl!+W}aQ9_SO7r*rk8vswAe-IL2*(eL&WTYN%~{-PT*Lua$8FdT^1_^|-0L`z(H z{v{{?mpE3Lr`Al4rrI|zZTS=jWb;?_hG9K+6R3gqHao6lXJmwL)?bw=Jg@VcRurwkI5Z~a zn5ABI`;aklle+z|<2Rw~B}c-!(b77Mpwhaflb=p*-drrkX3dd4Jz5GnO7l9uXz8dy z9iU+BO)Ea*W7i>`f0B%6-Mkn-jp%pLaN)t{9BApq#e>2?F<$*>{ar9dmZj#07+Shn zR;c#_6mwBGp9S)ferz_%>c`65bj`D_Tf6ZJNk6u=*Sf=793D)^wqlTC0=0GV2f-S% z#Jlt&fY3@~Vqxe@#>5@!_Cf5TdSGzXnj_#hInS_nC0=k+C=3n?wI?Ukf1~F=y(V-S z6O-XHSbeco8|Nubyy!zX<1_UoZ)|+GyXdv>neMKPe+$2ZD*&+0Mkvg(mIHsLXk}sO zhhR&1|Jbc^{$VRc7(-8c?+EWav~w=@;-YZhp)Ap*ixuV|j7lyj125I&22ACcKN}32 z6jG>Q1?0wF66=TdXodNyR#O>k^J&!;=4FikXt^)C!XK^m!s4MuhQaN~&V zmbojwT9&68n}DNND$GY=%yTvKF|ToLjv9FyxN+?!HPQh$eQ+0A`0O`~Yiq)5E6tDM zi=gQxaW!%hY}KMz<)-i`_*aXT`LyVxxL&D|??d6RG6&m)I@oMH@X6%lvYvC{e+R(; zRzy1XGU%UFBjuvWIiZ6#Eup1Tjqbdi8Jt)HZV`S!+x%By^p1@E5#x~7Im@$S`;+mT z#uF=`x)tZLWi(Cn;brKw9`2Fc9AcA&rJ4o+Z!(a$|kzw&y`vI#~w2 zsW7f*9Kz`bdTu|pc+fa|+m^$>b|{Llq^VC^p*?C37hFzW3u53I7jmNyd!rlu(G@<; zeAD>Yt45whMX{w&tc)$7{Yr-&ZyBu;(Xc;YexRAd6%E5$x9sHP9v>@ctVr6d| zqo0KR#tHY5{|YZLN0z)3S{Uf{ihZoqXC9UdPshJt{P#>uPDX2dV?Ryn(b$`PSW%6v z1Uuv^`%K`V6YMcdeU;`PMX$^w#zcl183Nifs76+x(a{YKEn4b`_Th>HJNARdwaien zac$Ni7l>7w7uD|`5tY>gzcY@^9rn+f150j`xpH2qq3~iw{;*Jjs4`=`!Ys{&cEURj;W7{9q7d|=`hIL!KUVC3i*iX*UD=){vv~F; zbzZS+?8W5z0IjClJ0kiYY8_UlICgxzp<@WMVXhB07~s$4641|AW&&-^#7Wn z!@>f4V}>!YdE>l_hU~52*hyGotn-u%8`ZcUmrim1l6w}8Sil^$xvwd?zp2I(b5io) zF?YZWdMg^phNrgeai~2NHz-OV8uVf$F0|GKJ!Zu_)&Oa&)a}bsRP#en2fZ*}cQx8CxNYCNfI3@dK zlHGE35(Zs;p_rPn+IT0u4QK%Uu?+liT#d|we)qc2%h4JyO#j3)7+1*%Ku1{CXNH=L ztFV)e!1=ToZ8#QgdL%1Rfd?#FS70ffP!k+tkxJjAa zlLJ*~C73j;VVS_n@VJwcxCH%6kI!ZPgD_4?{t!5;=Rcts;!W1=m37qvSos&CRGgoz zbEaxM2uJsn8}Y0OUfO|60X$B?G&{e?5mxuO)W~0fh1!V&G!EaGKa{u&IF0u~h&r%8;RHnUJULC?$maIJ98r7z z4z#7r`0f;;(#7wM-2(H6)(h(tZA?59S{5+Rn?w2z=$-Mdiv7It4b6NTdYOY(YM@s$ zUcXV>0;BAL8R4}x)UgkPwfO@G)>4?N&xC)DV&HuB`3A5+)WKQwN6f@}l)TBwrJ{K2 zc@PSi3G9E^KlE`<_lZ$`$v7P9fcL`QhR*^%a~MR-@u81r8}ELt`$YGWFsx?xfv#Q$ z7%gVnGQT-&oc$UK4;pWD=YNp+EgVj!!~+Z!`tuN$iF1PG_=EU-2mHsJLpUOTp7R`w zKgl`x1mzzCo*IO~DT824ELD5{42PwZP=4C1#cWUYbVN?WpT3X<`@Pp&)&>h(69s6Ph=TuFs*a^RUxk1U-3iauSY4 z=J&udc(_p`zkz~-UxZORgo_V|&}J^?2kk#Mg4SWBfMO-((O0K2sX&eVEE5#%ur`kl zY!}urP^=B$aUmXor75vWV7U-{fEAMKDUOUWalhL0l{BG}DZbPOK95~f)5K;?W79oq z&v`iOqzuos3CReV~DAvTj8N$RzZFwTv0boC#H)aPEbS<-O|6~6c$wTCBzlX88>k}fDQ4<~*A zV=T@K%~t*)=&{I8z-Yo2m~dhcfV>XniT&{K1M4v6i!i?q9@HqGhxx)}UM%nPfr;h4 z5VVq<0t~k5#6JsF5$u*&K=F`&Ue6zflfLoEX7%~gPz4qaST?#(4E9`6dwvbNgLabZ{)fI7(CjO9;i=W4M^Cc=^2;kTPs`4+zF*wqWDG>Q}vo(0#g-=|ik#pL>op1y1 zAV(7oqK$5>BEv+~PYgqW+<)Q3gXd-II289lj3>S{Q@vSWiB(VPOFy%`jl%^!E&$6D z??E_Y<}S><25!U5e9U~6N^Zc+TnIuextf5?+cdS_!%Q8B$#yKe|7QwW4yH<(bXzWh zEy)sm07Ey3C#n!k0c^1s8~G)~gA@zE3qngS5QRJ497=r6R-8V?GNI(4bLvul@VW!s zjgt!6pyl0S=}Uh4^INpM4&R@e<&DwuqNP|nJYSD(bj26K`e&9Dt~z4*-7GJJ*N?f! z%8O8Dm2Q^1;5C~c-k--d=7NIwhj?fyDFXS_VFBVyt2qd(Fg&Q-gKogr;&RqKn{7+!1(>lzf=3G~Y+BVYO~O+ugfzwr*UzHT*Wdu(O{1j@;d# zG&I+D zT}EcP(;c4UbZJfpzHjmY_G1~OAL2)`)Af92sWUg0RqS+c%Pw{1ZJnh#y~ga!ra8_$ z&FKy}b3vxU>39fV9C+Zb8~!jg;Rm#_HM7i_Yh(qS?#vozE`SoJ17G;=!E#77)YB1vtfb2ICq0DR2~aF`Sw z{^Hcm&+ttGjKK@&XKJSy+kv{&&O^?s%tp`#Ei-K;bA{gv(N;(}{t9@;$XqkW=_sH3 zu+yR73;Sv+zZuI#+*Cn{q7=bjJC%v?20LPzUw7ti%hH|ht>`afR)y31Z4oP)bQNlu zF=4l|l@IVG=#-R&C+KNXmV!s8sVNII@q{l}GHrUqTmbeL#H*-+h=01zQs9Z7r7u7} zLqRWC$wj8+Ll*V#BEN`O1%}_&Bi~0RiTDB*AU+DOV=#%hf$PKYhw^@Wg9J&8vvmu- zC1J|TOP0+Z$?%$`VAM=aSqi>ZN>3|Ac4~ju5a*s=OMI#(Y!|(qo*sv+i+p1*2|?!O z%nW6mpok~9H;C7ff8~0y9r$#5UY>!!N_qxKk62@X9hyP@1j+lU!(tr)_Rb9Q@6N#A zpMhh5PiMah_D#p}-Oh9z-^t^8%2U}4{1M>jXXQ4lhM4nk`xtRQaWU6{w-K)+F2@DG z4WF*v=a5e`zDmmzwJpJ}f(8XXYd5rY1jS9~`gQHWZz-X+^}$wr0O%C7HMp^5U8^P0 z($@O8^-N$DiN^)2ZbN;mQczjq#gE1KaUZs_Hryoi-%Cx_HnarmTf^-#E84mjR3gBq zq1v_ULeTCLwZNJ-glgNvoy|>BI@r+x&o@vAH`oEmJK<4Bs6)YCct+h=}>!cyA!LE?JZ)s~=U*8<8Z$;Ev4Y$ApnbkpT5=TCPACPc4 zx&{8=t%gYLFn*YD2yyL)hiH+u*!Vo)QvN$Ojxi(oD>jZ_OH00qVguW~!^R^vj-e*y zzi;DqJBw&uqMpy$IPcdc8^@!Jw10@U4wM(1;^{fsXs}(o|8GPgNZg*%8Td6?yine5XBi#@ zAaOnU*m>gicGuas-9InSz<)IZzw6eNe!HIgX5bYw@P-+9bO!#)41Cze?f&`g4E+5W z_!G2m;5edd<@k2mxP5)tNgVy3XOsVljpy5VFU1AQ-)`f7KLfwh7SDfalZRW8&`$h& z$`%J$Ao*^JS5lMYdx*<(kd)s`T<(>U?;|ds*CgLh9M@hl6(hodESux47KYQ(kb~-;sM6b z5!V@y6R%|a0`V%w$B9=newny+4o=x4{j-|MUnluG#-)E68JGTPX52ykX<2@NqisURmAr*F8$Nbc#sa%1B?&gVG7cq_)NuH9qB*Hcq8%S zj5iZM$#@I#5ysnzzr}cn_-V$wh@WA66Y){TyNRD;JVHFqcrWn_j4va$1QEp?6UVPj zF-iG;;`kE{J0BqKW_*yihw-Dt^B6x)d=cX(iQ{voz1!65q#o74iLyR}=4Nd?oP###a*`WW0{}QN|mI zA7{Lo_({fFh>tMdPW&y#L&Q%r-bMTjMSMP!pHDoO@m%5y z7?<zVSCy((w;)@tRNq+D$?j?Eo-2oQkUm@`#CV!gr_!yV-*Uz||zX8Sr zq+e%TCtk_8oWE6!SCPE@ZUc+)ubTKuCNJmjYR2XKtz%ry-$uq8Nq;lr&BR+6m-Dxs z@ph6AF&-k`#kicm^8FhY#w==$-_zuS9 z{N2g;PLkincptU9oAKQwzlZTX#P>43m-s%$_YvRE_F#;b@|GhR)6CF3iJuV#ET@jAxqh&M9cNW7WxX5uZ3w-9elDvc>(;-M z$FH_XJo$?UJk}$p*FVXtyT>-^}$ALC7w z-@*8L%I{)a+P8=CCn>*=@lBNPXM8i|2N^dgf1L5HlpkT-r2J{dw^4qS@fRo`XZ%IV zk2C%Q%1Z7OFQ;5UWW}q8Wf)?J%hwY z8Q+BpA>~lH)bFSH9AI4j4l2U9{2dg34YK}tY63% zG_-Bl5NriK@H_cn0sL6=p3YEx!+NR^Y_4tUfFHeBp8NlOx}iF{ayC=rr~EK|e=3Vz z@`kSXTa0Y@^HW}4Twk>AT=aVg+~%HCqHDS^Pl@mmeeltKl%^Hi2Lsm zU}y_#Mj2cH5Ryw8YGoBSUF8MW_37FlEVd+aNinvMZS(dq2(bw-{Mpk8{)h$=y>7NL z@F~ra*H#-#3c<};s&8j_FY8He5aa%jQ+@foF6E{DGXE^xOxOR@IIxh4s6p&Mo*iU; z8J<|o+Xkfcx$#p`*LqJeCz-z-5&f@lo;zU%fkO-Yx?>w+Q?%top-1)) { + L->top--; + if (luaL_findtable(L, LUA_GLOBALSINDEX, libname, hsize) != NULL) + lj_err_callerv(L, LJ_ERR_BADMODN, libname); + settabV(L, L->top, tabV(L->top-1)); + L->top++; + lua_setfield(L, -3, libname); /* _LOADED[libname] = new table */ + } + L->top--; + settabV(L, L->top-1, tabV(L->top)); + } else { + lua_createtable(L, 0, hsize); + } + return tabV(L->top-1); +} + +static const uint8_t *lib_read_lfunc(lua_State *L, const uint8_t *p, GCtab *tab) +{ + int len = *p++; + GCstr *name = lj_str_new(L, (const char *)p, len); + LexState ls; + GCproto *pt; + GCfunc *fn; + memset(&ls, 0, sizeof(ls)); + ls.L = L; + ls.p = (const char *)(p+len); + ls.pe = (const char *)~(uintptr_t)0; + ls.c = -1; + ls.level = (BCDUMP_F_STRIP|(LJ_BE*BCDUMP_F_BE)); + ls.chunkname = name; + pt = lj_bcread_proto(&ls); + pt->firstline = ~(BCLine)0; + fn = lj_func_newL_empty(L, pt, tabref(L->env)); + /* NOBARRIER: See below for common barrier. */ + setfuncV(L, lj_tab_setstr(L, tab, name), fn); + return (const uint8_t *)ls.p; +} + +void lj_lib_register(lua_State *L, const char *libname, + const uint8_t *p, const lua_CFunction *cf) +{ + GCtab *env = tabref(L->env); + GCfunc *ofn = NULL; + int ffid = *p++; + BCIns *bcff = &L2GG(L)->bcff[*p++]; + GCtab *tab = lib_create_table(L, libname, *p++); + ptrdiff_t tpos = L->top - L->base; + + /* Avoid barriers further down. */ + lj_gc_anybarriert(L, tab); + tab->nomm = 0; + + for (;;) { + uint32_t tag = *p++; + MSize len = tag & LIBINIT_LENMASK; + tag &= LIBINIT_TAGMASK; + if (tag != LIBINIT_STRING) { + const char *name; + MSize nuv = (MSize)(L->top - L->base - tpos); + GCfunc *fn = lj_func_newC(L, nuv, env); + if (nuv) { + L->top = L->base + tpos; + memcpy(fn->c.upvalue, L->top, sizeof(TValue)*nuv); + } + fn->c.ffid = (uint8_t)(ffid++); + name = (const char *)p; + p += len; + if (tag == LIBINIT_CF) + setmref(fn->c.pc, &G(L)->bc_cfunc_int); + else + setmref(fn->c.pc, bcff++); + if (tag == LIBINIT_ASM_) + fn->c.f = ofn->c.f; /* Copy handler from previous function. */ + else + fn->c.f = *cf++; /* Get cf or handler from C function table. */ + if (len) { + /* NOBARRIER: See above for common barrier. */ + setfuncV(L, lj_tab_setstr(L, tab, lj_str_new(L, name, len)), fn); + } + ofn = fn; + } else { + switch (tag | len) { + case LIBINIT_LUA: + p = lib_read_lfunc(L, p, tab); + break; + case LIBINIT_SET: + L->top -= 2; + if (tvisstr(L->top+1) && strV(L->top+1)->len == 0) + env = tabV(L->top); + else /* NOBARRIER: See above for common barrier. */ + copyTV(L, lj_tab_set(L, tab, L->top+1), L->top); + break; + case LIBINIT_NUMBER: + memcpy(&L->top->n, p, sizeof(double)); + L->top++; + p += sizeof(double); + break; + case LIBINIT_COPY: + copyTV(L, L->top, L->top - *p++); + L->top++; + break; + case LIBINIT_LASTCL: + setfuncV(L, L->top++, ofn); + break; + case LIBINIT_FFID: + ffid++; + break; + case LIBINIT_END: + return; + default: + setstrV(L, L->top++, lj_str_new(L, (const char *)p, len)); + p += len; + break; + } + } + } +} + +/* Push internal function on the stack. */ +GCfunc *lj_lib_pushcc(lua_State *L, lua_CFunction f, int id, int n) +{ + GCfunc *fn; + lua_pushcclosure(L, f, n); + fn = funcV(L->top-1); + fn->c.ffid = (uint8_t)id; + setmref(fn->c.pc, &G(L)->bc_cfunc_int); + return fn; +} + +void lj_lib_prereg(lua_State *L, const char *name, lua_CFunction f, GCtab *env) +{ + luaL_findtable(L, LUA_REGISTRYINDEX, "_PRELOAD", 4); + lua_pushcfunction(L, f); + /* NOBARRIER: The function is new (marked white). */ + setgcref(funcV(L->top-1)->c.env, obj2gco(env)); + lua_setfield(L, -2, name); + L->top--; +} + +int lj_lib_postreg(lua_State *L, lua_CFunction cf, int id, const char *name) +{ + GCfunc *fn = lj_lib_pushcf(L, cf, id); + GCtab *t = tabref(curr_func(L)->c.env); /* Reference to parent table. */ + setfuncV(L, lj_tab_setstr(L, t, lj_str_newz(L, name)), fn); + lj_gc_anybarriert(L, t); + setfuncV(L, L->top++, fn); + return 1; +} + +/* -- Type checks --------------------------------------------------------- */ + +TValue *lj_lib_checkany(lua_State *L, int narg) +{ + TValue *o = L->base + narg-1; + if (o >= L->top) + lj_err_arg(L, narg, LJ_ERR_NOVAL); + return o; +} + +GCstr *lj_lib_checkstr(lua_State *L, int narg) +{ + TValue *o = L->base + narg-1; + if (o < L->top) { + if (LJ_LIKELY(tvisstr(o))) { + return strV(o); + } else if (tvisnumber(o)) { + GCstr *s = lj_strfmt_number(L, o); + setstrV(L, o, s); + return s; + } + } + lj_err_argt(L, narg, LUA_TSTRING); + return NULL; /* unreachable */ +} + +GCstr *lj_lib_optstr(lua_State *L, int narg) +{ + TValue *o = L->base + narg-1; + return (o < L->top && !tvisnil(o)) ? lj_lib_checkstr(L, narg) : NULL; +} + +#if LJ_DUALNUM +void lj_lib_checknumber(lua_State *L, int narg) +{ + TValue *o = L->base + narg-1; + if (!(o < L->top && lj_strscan_numberobj(o))) + lj_err_argt(L, narg, LUA_TNUMBER); +} +#endif + +lua_Number lj_lib_checknum(lua_State *L, int narg) +{ + TValue *o = L->base + narg-1; + if (!(o < L->top && + (tvisnumber(o) || (tvisstr(o) && lj_strscan_num(strV(o), o))))) + lj_err_argt(L, narg, LUA_TNUMBER); + if (LJ_UNLIKELY(tvisint(o))) { + lua_Number n = (lua_Number)intV(o); + setnumV(o, n); + return n; + } else { + return numV(o); + } +} + +int32_t lj_lib_checkint(lua_State *L, int narg) +{ + TValue *o = L->base + narg-1; + if (!(o < L->top && lj_strscan_numberobj(o))) + lj_err_argt(L, narg, LUA_TNUMBER); + if (LJ_LIKELY(tvisint(o))) { + return intV(o); + } else { + int32_t i = lj_num2int(numV(o)); + if (LJ_DUALNUM) setintV(o, i); + return i; + } +} + +int32_t lj_lib_optint(lua_State *L, int narg, int32_t def) +{ + TValue *o = L->base + narg-1; + return (o < L->top && !tvisnil(o)) ? lj_lib_checkint(L, narg) : def; +} + +GCfunc *lj_lib_checkfunc(lua_State *L, int narg) +{ + TValue *o = L->base + narg-1; + if (!(o < L->top && tvisfunc(o))) + lj_err_argt(L, narg, LUA_TFUNCTION); + return funcV(o); +} + +GCtab *lj_lib_checktab(lua_State *L, int narg) +{ + TValue *o = L->base + narg-1; + if (!(o < L->top && tvistab(o))) + lj_err_argt(L, narg, LUA_TTABLE); + return tabV(o); +} + +GCtab *lj_lib_checktabornil(lua_State *L, int narg) +{ + TValue *o = L->base + narg-1; + if (o < L->top) { + if (tvistab(o)) + return tabV(o); + else if (tvisnil(o)) + return NULL; + } + lj_err_arg(L, narg, LJ_ERR_NOTABN); + return NULL; /* unreachable */ +} + +int lj_lib_checkopt(lua_State *L, int narg, int def, const char *lst) +{ + GCstr *s = def >= 0 ? lj_lib_optstr(L, narg) : lj_lib_checkstr(L, narg); + if (s) { + const char *opt = strdata(s); + MSize len = s->len; + int i; + for (i = 0; *(const uint8_t *)lst; i++) { + if (*(const uint8_t *)lst == len && memcmp(opt, lst+1, len) == 0) + return i; + lst += 1+*(const uint8_t *)lst; + } + lj_err_argv(L, narg, LJ_ERR_INVOPTM, opt); + } + return def; +} + diff --git a/lib/LuaJIT/lj_lib.h b/lib/LuaJIT/lj_lib.h new file mode 100644 index 0000000..37ec9d7 --- /dev/null +++ b/lib/LuaJIT/lj_lib.h @@ -0,0 +1,115 @@ +/* +** Library function support. +** Copyright (C) 2005-2017 Mike Pall. See Copyright Notice in luajit.h +*/ + +#ifndef _LJ_LIB_H +#define _LJ_LIB_H + +#include "lj_obj.h" + +/* +** A fallback handler is called by the assembler VM if the fast path fails: +** +** - too few arguments: unrecoverable. +** - wrong argument type: recoverable, if coercion succeeds. +** - bad argument value: unrecoverable. +** - stack overflow: recoverable, if stack reallocation succeeds. +** - extra handling: recoverable. +** +** The unrecoverable cases throw an error with lj_err_arg(), lj_err_argtype(), +** lj_err_caller() or lj_err_callermsg(). +** The recoverable cases return 0 or the number of results + 1. +** The assembler VM retries the fast path only if 0 is returned. +** This time the fallback must not be called again or it gets stuck in a loop. +*/ + +/* Return values from fallback handler. */ +#define FFH_RETRY 0 +#define FFH_UNREACHABLE FFH_RETRY +#define FFH_RES(n) ((n)+1) +#define FFH_TAILCALL (-1) + +LJ_FUNC TValue *lj_lib_checkany(lua_State *L, int narg); +LJ_FUNC GCstr *lj_lib_checkstr(lua_State *L, int narg); +LJ_FUNC GCstr *lj_lib_optstr(lua_State *L, int narg); +#if LJ_DUALNUM +LJ_FUNC void lj_lib_checknumber(lua_State *L, int narg); +#else +#define lj_lib_checknumber(L, narg) lj_lib_checknum((L), (narg)) +#endif +LJ_FUNC lua_Number lj_lib_checknum(lua_State *L, int narg); +LJ_FUNC int32_t lj_lib_checkint(lua_State *L, int narg); +LJ_FUNC int32_t lj_lib_optint(lua_State *L, int narg, int32_t def); +LJ_FUNC GCfunc *lj_lib_checkfunc(lua_State *L, int narg); +LJ_FUNC GCtab *lj_lib_checktab(lua_State *L, int narg); +LJ_FUNC GCtab *lj_lib_checktabornil(lua_State *L, int narg); +LJ_FUNC int lj_lib_checkopt(lua_State *L, int narg, int def, const char *lst); + +/* Avoid including lj_frame.h. */ +#if LJ_GC64 +#define lj_lib_upvalue(L, n) \ + (&gcval(L->base-2)->fn.c.upvalue[(n)-1]) +#elif LJ_FR2 +#define lj_lib_upvalue(L, n) \ + (&gcref((L->base-2)->gcr)->fn.c.upvalue[(n)-1]) +#else +#define lj_lib_upvalue(L, n) \ + (&gcref((L->base-1)->fr.func)->fn.c.upvalue[(n)-1]) +#endif + +#if LJ_TARGET_WINDOWS +#define lj_lib_checkfpu(L) \ + do { setnumV(L->top++, (lua_Number)1437217655); \ + if (lua_tointeger(L, -1) != 1437217655) lj_err_caller(L, LJ_ERR_BADFPU); \ + L->top--; } while (0) +#else +#define lj_lib_checkfpu(L) UNUSED(L) +#endif + +LJ_FUNC GCfunc *lj_lib_pushcc(lua_State *L, lua_CFunction f, int id, int n); +#define lj_lib_pushcf(L, fn, id) (lj_lib_pushcc(L, (fn), (id), 0)) + +/* Library function declarations. Scanned by buildvm. */ +#define LJLIB_CF(name) static int lj_cf_##name(lua_State *L) +#define LJLIB_ASM(name) static int lj_ffh_##name(lua_State *L) +#define LJLIB_ASM_(name) +#define LJLIB_LUA(name) +#define LJLIB_SET(name) +#define LJLIB_PUSH(arg) +#define LJLIB_REC(handler) +#define LJLIB_NOREGUV +#define LJLIB_NOREG + +#define LJ_LIB_REG(L, regname, name) \ + lj_lib_register(L, regname, lj_lib_init_##name, lj_lib_cf_##name) + +LJ_FUNC void lj_lib_register(lua_State *L, const char *libname, + const uint8_t *init, const lua_CFunction *cf); +LJ_FUNC void lj_lib_prereg(lua_State *L, const char *name, lua_CFunction f, + GCtab *env); +LJ_FUNC int lj_lib_postreg(lua_State *L, lua_CFunction cf, int id, + const char *name); + +/* Library init data tags. */ +#define LIBINIT_LENMASK 0x3f +#define LIBINIT_TAGMASK 0xc0 +#define LIBINIT_CF 0x00 +#define LIBINIT_ASM 0x40 +#define LIBINIT_ASM_ 0x80 +#define LIBINIT_STRING 0xc0 +#define LIBINIT_MAXSTR 0x38 +#define LIBINIT_LUA 0xf9 +#define LIBINIT_SET 0xfa +#define LIBINIT_NUMBER 0xfb +#define LIBINIT_COPY 0xfc +#define LIBINIT_LASTCL 0xfd +#define LIBINIT_FFID 0xfe +#define LIBINIT_END 0xff + +/* Exported library functions. */ + +typedef struct RandomState RandomState; +LJ_FUNC uint64_t LJ_FASTCALL lj_math_random_step(RandomState *rs); + +#endif diff --git a/lib/LuaJIT/lj_lib.o b/lib/LuaJIT/lj_lib.o new file mode 100644 index 0000000000000000000000000000000000000000..0df116a11f8cc6c9813fcf0002d2199cd7102042 GIT binary patch literal 7248 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 +#include + +#define lj_load_c +#define LUA_CORE + +#include "lua.h" +#include "lauxlib.h" + +#include "lj_obj.h" +#include "lj_gc.h" +#include "lj_err.h" +#include "lj_buf.h" +#include "lj_func.h" +#include "lj_frame.h" +#include "lj_vm.h" +#include "lj_lex.h" +#include "lj_bcdump.h" +#include "lj_parse.h" + +/* -- Load Lua source code and bytecode ----------------------------------- */ + +static TValue *cpparser(lua_State *L, lua_CFunction dummy, void *ud) +{ + LexState *ls = (LexState *)ud; + GCproto *pt; + GCfunc *fn; + int bc; + UNUSED(dummy); + cframe_errfunc(L->cframe) = -1; /* Inherit error function. */ + bc = lj_lex_setup(L, ls); + if (ls->mode && !strchr(ls->mode, bc ? 'b' : 't')) { + setstrV(L, L->top++, lj_err_str(L, LJ_ERR_XMODE)); + lj_err_throw(L, LUA_ERRSYNTAX); + } + pt = bc ? lj_bcread(ls) : lj_parse(ls); + fn = lj_func_newL_empty(L, pt, tabref(L->env)); + /* Don't combine above/below into one statement. */ + setfuncV(L, L->top++, fn); + return NULL; +} + +LUA_API int lua_loadx(lua_State *L, lua_Reader reader, void *data, + const char *chunkname, const char *mode) +{ + LexState ls; + int status; + ls.rfunc = reader; + ls.rdata = data; + ls.chunkarg = chunkname ? chunkname : "?"; + ls.mode = mode; + lj_buf_init(L, &ls.sb); + status = lj_vm_cpcall(L, NULL, &ls, cpparser); + lj_lex_cleanup(L, &ls); + lj_gc_check(L); + return status; +} + +LUA_API int lua_load(lua_State *L, lua_Reader reader, void *data, + const char *chunkname) +{ + return lua_loadx(L, reader, data, chunkname, NULL); +} + +typedef struct FileReaderCtx { + FILE *fp; + char buf[LUAL_BUFFERSIZE]; +} FileReaderCtx; + +static const char *reader_file(lua_State *L, void *ud, size_t *size) +{ + FileReaderCtx *ctx = (FileReaderCtx *)ud; + UNUSED(L); + if (feof(ctx->fp)) return NULL; + *size = fread(ctx->buf, 1, sizeof(ctx->buf), ctx->fp); + return *size > 0 ? ctx->buf : NULL; +} + +LUALIB_API int luaL_loadfilex(lua_State *L, const char *filename, + const char *mode) +{ + FileReaderCtx ctx; + int status; + const char *chunkname; + if (filename) { + ctx.fp = fopen(filename, "rb"); + if (ctx.fp == NULL) { + lua_pushfstring(L, "cannot open %s: %s", filename, strerror(errno)); + return LUA_ERRFILE; + } + chunkname = lua_pushfstring(L, "@%s", filename); + } else { + ctx.fp = stdin; + chunkname = "=stdin"; + } + status = lua_loadx(L, reader_file, &ctx, chunkname, mode); + if (ferror(ctx.fp)) { + L->top -= filename ? 2 : 1; + lua_pushfstring(L, "cannot read %s: %s", chunkname+1, strerror(errno)); + if (filename) + fclose(ctx.fp); + return LUA_ERRFILE; + } + if (filename) { + L->top--; + copyTV(L, L->top-1, L->top); + fclose(ctx.fp); + } + return status; +} + +LUALIB_API int luaL_loadfile(lua_State *L, const char *filename) +{ + return luaL_loadfilex(L, filename, NULL); +} + +typedef struct StringReaderCtx { + const char *str; + size_t size; +} StringReaderCtx; + +static const char *reader_string(lua_State *L, void *ud, size_t *size) +{ + StringReaderCtx *ctx = (StringReaderCtx *)ud; + UNUSED(L); + if (ctx->size == 0) return NULL; + *size = ctx->size; + ctx->size = 0; + return ctx->str; +} + +LUALIB_API int luaL_loadbufferx(lua_State *L, const char *buf, size_t size, + const char *name, const char *mode) +{ + StringReaderCtx ctx; + ctx.str = buf; + ctx.size = size; + return lua_loadx(L, reader_string, &ctx, name, mode); +} + +LUALIB_API int luaL_loadbuffer(lua_State *L, const char *buf, size_t size, + const char *name) +{ + return luaL_loadbufferx(L, buf, size, name, NULL); +} + +LUALIB_API int luaL_loadstring(lua_State *L, const char *s) +{ + return luaL_loadbuffer(L, s, strlen(s), s); +} + +/* -- Dump bytecode ------------------------------------------------------- */ + +LUA_API int lua_dump(lua_State *L, lua_Writer writer, void *data) +{ + cTValue *o = L->top-1; + api_check(L, L->top > L->base); + if (tvisfunc(o) && isluafunc(funcV(o))) + return lj_bcwrite(L, funcproto(funcV(o)), writer, data, 0); + else + return 1; +} + diff --git a/lib/LuaJIT/lj_load.o b/lib/LuaJIT/lj_load.o new file mode 100644 index 0000000000000000000000000000000000000000..60e8e3c6a1d0b2485c2d17dd3c4f1f55efb2119d GIT binary patch literal 5552 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 literal 0 HcmV?d00001 diff --git a/lib/LuaJIT/lj_load_dyn.o b/lib/LuaJIT/lj_load_dyn.o new file mode 100644 index 0000000000000000000000000000000000000000..a9803ca0d598624b6f990c073fc39c276cfda609 GIT binary patch literal 5552 zcmbW4ZERcB8Gw)Nw3wv5HV9T1HsTU@WDLBzq^Ru*vr{`c217F%ORJjBb)0LG&0pBp zt(n+Jmj-dpH5DwSw-8GObzAqp})h8`%cNn*fU*F0Syo3trwF-F7;9%NoeLXpn3^ z6>9MLHS5UyQvI<-3y|a(VlOrVkM6zAVMabN!|sIk~M zjO=&&dGkRo{w~u4k9YHCU#gY!{+IE16;^`037zhhqxDb6U(uSw!yI#Ob5n=Gc5-e z1q(U5DoAT1IAIWI2L$3xSoNMzhhWxAe6ASq#(uIMv}J#Dr>)kyRXLCaCjY7$$@y8i zo>V&}$bh9L@woa3?5zAr4CE(R1xmKkuJ&ZLqi`6!46$l}xv!r^-jK_a*XsjkF2KbQ z%+QY5Wf&vu8#py*?d<1P0`#=vdAM$U$CKhR7nfj{{}a!zHUuZDT)YPhdx(pl#U|9I zfcYH_%r!1ws^8d*emL7$&#aBF-~oVLHH&i;Z*o5uzkx7Z{4F`R8cm5`O54w|>Lkl$ zOQo_EELY4@aBFQh{8$uz57exwnUe0Un%ODc%SQL^-5nepeKh^4;I42aybWWZ++r8Z z^_9n%tKoOuzTpP%Nl3&G{Nt)`K$U|~YQx_n8#eqW+@nU|8PBK@bRX_D0(%Yru;G2U zj}1Zh1nPi1j1Q?BL*2L$oa?EBa!e}6`r@*hFub6K2NKT&$^t&XuH{Y|ffJsX5qzfS zA!Etqp6WG%U<6$dY@}}=X<^=Pq8$kOL)i&GxgJoqaFV6Z;j2F5#^BsKa2$1iqt_UO z^dL>!ccKsdh0q4%2D^}Rwo|b!$iQR*vPa!9#dYiuuqnmvMO|>*o--KNLxvwmj_Ytj z>Fq+j{~@T_!~VL~NzK#oree$kf1Shwo}AJLz)k$uJ zU1B%L({pu336%PJ_S!rwtJnLvPjPo!Rn~CL8N?09vi*2KR@YtK+q!31aXn7WM-{(5 zGL8%1bJt1=nto?8zz!h4R#mpjUHB2?9sE4qg@3aP|4tYFLKprLa`-vrM*uqUp8~%b z_Q_$j?hDHBb=2>W|65)7xi0)7aP$wGQHtIf|59AXkmnkh7gRgtiTkn>4|d^qb>ZPI ze5eagbm5Nz$8jyFBdOml6N08%XbXe;Cyr%?Er#I!L&+cu+C+z znQ{q7WvgA|v>jryo(DgWqmF5Vc3TC5qF|O}4pa4_%zH9-v^ry%4BtB_xUBdu34Fsy znL-GH9Pb#-pFjwL9KX#p$M*{g@{hq!^Oq5VIQ=Z!Sn^F!*ZO}T9N)p3uY-Mtf_^r` zPxCtnzm4$E5`H`3rsB9|@vhQ-UMBjVAbf@JI|#o(ct7FW)mgw|1L3MxZp3gMVA0WIyIQ3s6 zocjNo;^?2|-z542#Q!P6>3Y3JIGukNPA~`-_zu_k_mFj=`rjvhwh;Yy2**94{cI-w zKTUX=@Xrt)ggOd_S1HK$kbJiJHq}1t;QBl8IS1F@32!^N4syZ4^_?`R&Z*8v-!>jA0 z&yQ*!0bBIB9ez}F{QD_{x{j~QsA76QYxQ5L{P9kuqV05?e+R$w=*^%_N>}avnmg~YjG}|_BR4^ et{oueMFWr@IP!Ni8K&9MUQPV8^wNGf?f(nGGbu>` literal 0 HcmV?d00001 diff --git a/lib/LuaJIT/lj_mcode.c b/lib/LuaJIT/lj_mcode.c new file mode 100644 index 0000000..64b0ca9 --- /dev/null +++ b/lib/LuaJIT/lj_mcode.c @@ -0,0 +1,381 @@ +/* +** Machine code management. +** Copyright (C) 2005-2017 Mike Pall. See Copyright Notice in luajit.h +*/ + +#define lj_mcode_c +#define LUA_CORE + +#include "lj_obj.h" +#if LJ_HASJIT +#include "lj_gc.h" +#include "lj_err.h" +#include "lj_jit.h" +#include "lj_mcode.h" +#include "lj_trace.h" +#include "lj_dispatch.h" +#endif +#if LJ_HASJIT || LJ_HASFFI +#include "lj_vm.h" +#endif + +/* -- OS-specific functions ----------------------------------------------- */ + +#if LJ_HASJIT || LJ_HASFFI + +/* Define this if you want to run LuaJIT with Valgrind. */ +#ifdef LUAJIT_USE_VALGRIND +#include +#endif + +#if LJ_TARGET_IOS +void sys_icache_invalidate(void *start, size_t len); +#endif + +/* Synchronize data/instruction cache. */ +void lj_mcode_sync(void *start, void *end) +{ +#ifdef LUAJIT_USE_VALGRIND + VALGRIND_DISCARD_TRANSLATIONS(start, (char *)end-(char *)start); +#endif +#if LJ_TARGET_X86ORX64 + UNUSED(start); UNUSED(end); +#elif LJ_TARGET_IOS + sys_icache_invalidate(start, (char *)end-(char *)start); +#elif LJ_TARGET_PPC + lj_vm_cachesync(start, end); +#elif defined(__GNUC__) + __clear_cache(start, end); +#else +#error "Missing builtin to flush instruction cache" +#endif +} + +#endif + +#if LJ_HASJIT + +#if LJ_TARGET_WINDOWS + +#define WIN32_LEAN_AND_MEAN +#include + +#define MCPROT_RW PAGE_READWRITE +#define MCPROT_RX PAGE_EXECUTE_READ +#define MCPROT_RWX PAGE_EXECUTE_READWRITE + +static void *mcode_alloc_at(jit_State *J, uintptr_t hint, size_t sz, DWORD prot) +{ + void *p = LJ_WIN_VALLOC((void *)hint, sz, + MEM_RESERVE|MEM_COMMIT|MEM_TOP_DOWN, prot); + if (!p && !hint) + lj_trace_err(J, LJ_TRERR_MCODEAL); + return p; +} + +static void mcode_free(jit_State *J, void *p, size_t sz) +{ + UNUSED(J); UNUSED(sz); + VirtualFree(p, 0, MEM_RELEASE); +} + +static int mcode_setprot(void *p, size_t sz, DWORD prot) +{ + DWORD oprot; + return !LJ_WIN_VPROTECT(p, sz, prot, &oprot); +} + +#elif LJ_TARGET_POSIX + +#include + +#ifndef MAP_ANONYMOUS +#define MAP_ANONYMOUS MAP_ANON +#endif + +#define MCPROT_RW (PROT_READ|PROT_WRITE) +#define MCPROT_RX (PROT_READ|PROT_EXEC) +#define MCPROT_RWX (PROT_READ|PROT_WRITE|PROT_EXEC) + +static void *mcode_alloc_at(jit_State *J, uintptr_t hint, size_t sz, int prot) +{ + void *p = mmap((void *)hint, sz, prot, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); + if (p == MAP_FAILED) { + if (!hint) lj_trace_err(J, LJ_TRERR_MCODEAL); + p = NULL; + } + return p; +} + +static void mcode_free(jit_State *J, void *p, size_t sz) +{ + UNUSED(J); + munmap(p, sz); +} + +static int mcode_setprot(void *p, size_t sz, int prot) +{ + return mprotect(p, sz, prot); +} + +#elif LJ_64 + +#error "Missing OS support for explicit placement of executable memory" + +#else + +/* Fallback allocator. This will fail if memory is not executable by default. */ +#define LUAJIT_UNPROTECT_MCODE +#define MCPROT_RW 0 +#define MCPROT_RX 0 +#define MCPROT_RWX 0 + +static void *mcode_alloc_at(jit_State *J, uintptr_t hint, size_t sz, int prot) +{ + UNUSED(hint); UNUSED(prot); + return lj_mem_new(J->L, sz); +} + +static void mcode_free(jit_State *J, void *p, size_t sz) +{ + lj_mem_free(J2G(J), p, sz); +} + +#endif + +/* -- MCode area protection ----------------------------------------------- */ + +/* Define this ONLY if page protection twiddling becomes a bottleneck. */ +#ifdef LUAJIT_UNPROTECT_MCODE + +/* It's generally considered to be a potential security risk to have +** pages with simultaneous write *and* execute access in a process. +** +** Do not even think about using this mode for server processes or +** apps handling untrusted external data (such as a browser). +** +** The security risk is not in LuaJIT itself -- but if an adversary finds +** any *other* flaw in your C application logic, then any RWX memory page +** simplifies writing an exploit considerably. +*/ +#define MCPROT_GEN MCPROT_RWX +#define MCPROT_RUN MCPROT_RWX + +static void mcode_protect(jit_State *J, int prot) +{ + UNUSED(J); UNUSED(prot); +} + +#else + +/* This is the default behaviour and much safer: +** +** Most of the time the memory pages holding machine code are executable, +** but NONE of them is writable. +** +** The current memory area is marked read-write (but NOT executable) only +** during the short time window while the assembler generates machine code. +*/ +#define MCPROT_GEN MCPROT_RW +#define MCPROT_RUN MCPROT_RX + +/* Protection twiddling failed. Probably due to kernel security. */ +static LJ_NOINLINE void mcode_protfail(jit_State *J) +{ + lua_CFunction panic = J2G(J)->panic; + if (panic) { + lua_State *L = J->L; + setstrV(L, L->top++, lj_err_str(L, LJ_ERR_JITPROT)); + panic(L); + } +} + +/* Change protection of MCode area. */ +static void mcode_protect(jit_State *J, int prot) +{ + if (J->mcprot != prot) { + if (LJ_UNLIKELY(mcode_setprot(J->mcarea, J->szmcarea, prot))) + mcode_protfail(J); + J->mcprot = prot; + } +} + +#endif + +/* -- MCode area allocation ----------------------------------------------- */ + +#if LJ_64 +#define mcode_validptr(p) (p) +#else +#define mcode_validptr(p) ((p) && (uintptr_t)(p) < 0xffff0000) +#endif + +#ifdef LJ_TARGET_JUMPRANGE + +/* Get memory within relative jump distance of our code in 64 bit mode. */ +static void *mcode_alloc(jit_State *J, size_t sz) +{ + /* Target an address in the static assembler code (64K aligned). + ** Try addresses within a distance of target-range/2+1MB..target+range/2-1MB. + ** Use half the jump range so every address in the range can reach any other. + */ +#if LJ_TARGET_MIPS + /* Use the middle of the 256MB-aligned region. */ + uintptr_t target = ((uintptr_t)(void *)lj_vm_exit_handler & + ~(uintptr_t)0x0fffffffu) + 0x08000000u; +#else + uintptr_t target = (uintptr_t)(void *)lj_vm_exit_handler & ~(uintptr_t)0xffff; +#endif + const uintptr_t range = (1u << (LJ_TARGET_JUMPRANGE-1)) - (1u << 21); + /* First try a contiguous area below the last one. */ + uintptr_t hint = J->mcarea ? (uintptr_t)J->mcarea - sz : 0; + int i; + /* Limit probing iterations, depending on the available pool size. */ + for (i = 0; i < LJ_TARGET_JUMPRANGE; i++) { + if (mcode_validptr(hint)) { + void *p = mcode_alloc_at(J, hint, sz, MCPROT_GEN); + + if (mcode_validptr(p) && + ((uintptr_t)p + sz - target < range || target - (uintptr_t)p < range)) + return p; + if (p) mcode_free(J, p, sz); /* Free badly placed area. */ + } + /* Next try probing 64K-aligned pseudo-random addresses. */ + do { + hint = LJ_PRNG_BITS(J, LJ_TARGET_JUMPRANGE-16) << 16; + } while (!(hint + sz < range+range)); + hint = target + hint - range; + } + lj_trace_err(J, LJ_TRERR_MCODEAL); /* Give up. OS probably ignores hints? */ + return NULL; +} + +#else + +/* All memory addresses are reachable by relative jumps. */ +static void *mcode_alloc(jit_State *J, size_t sz) +{ +#if defined(__OpenBSD__) || LJ_TARGET_UWP + /* Allow better executable memory allocation for OpenBSD W^X mode. */ + void *p = mcode_alloc_at(J, 0, sz, MCPROT_RUN); + if (p && mcode_setprot(p, sz, MCPROT_GEN)) { + mcode_free(J, p, sz); + return NULL; + } + return p; +#else + return mcode_alloc_at(J, 0, sz, MCPROT_GEN); +#endif +} + +#endif + +/* -- MCode area management ----------------------------------------------- */ + +/* Allocate a new MCode area. */ +static void mcode_allocarea(jit_State *J) +{ + MCode *oldarea = J->mcarea; + size_t sz = (size_t)J->param[JIT_P_sizemcode] << 10; + sz = (sz + LJ_PAGESIZE-1) & ~(size_t)(LJ_PAGESIZE - 1); + J->mcarea = (MCode *)mcode_alloc(J, sz); + J->szmcarea = sz; + J->mcprot = MCPROT_GEN; + J->mctop = (MCode *)((char *)J->mcarea + J->szmcarea); + J->mcbot = (MCode *)((char *)J->mcarea + sizeof(MCLink)); + ((MCLink *)J->mcarea)->next = oldarea; + ((MCLink *)J->mcarea)->size = sz; + J->szallmcarea += sz; +} + +/* Free all MCode areas. */ +void lj_mcode_free(jit_State *J) +{ + MCode *mc = J->mcarea; + J->mcarea = NULL; + J->szallmcarea = 0; + while (mc) { + MCode *next = ((MCLink *)mc)->next; + mcode_free(J, mc, ((MCLink *)mc)->size); + mc = next; + } +} + +/* -- MCode transactions -------------------------------------------------- */ + +/* Reserve the remainder of the current MCode area. */ +MCode *lj_mcode_reserve(jit_State *J, MCode **lim) +{ + if (!J->mcarea) + mcode_allocarea(J); + else + mcode_protect(J, MCPROT_GEN); + *lim = J->mcbot; + return J->mctop; +} + +/* Commit the top part of the current MCode area. */ +void lj_mcode_commit(jit_State *J, MCode *top) +{ + J->mctop = top; + mcode_protect(J, MCPROT_RUN); +} + +/* Abort the reservation. */ +void lj_mcode_abort(jit_State *J) +{ + if (J->mcarea) + mcode_protect(J, MCPROT_RUN); +} + +/* Set/reset protection to allow patching of MCode areas. */ +MCode *lj_mcode_patch(jit_State *J, MCode *ptr, int finish) +{ +#ifdef LUAJIT_UNPROTECT_MCODE + UNUSED(J); UNUSED(ptr); UNUSED(finish); + return NULL; +#else + if (finish) { + if (J->mcarea == ptr) + mcode_protect(J, MCPROT_RUN); + else if (LJ_UNLIKELY(mcode_setprot(ptr, ((MCLink *)ptr)->size, MCPROT_RUN))) + mcode_protfail(J); + return NULL; + } else { + MCode *mc = J->mcarea; + /* Try current area first to use the protection cache. */ + if (ptr >= mc && ptr < (MCode *)((char *)mc + J->szmcarea)) { + mcode_protect(J, MCPROT_GEN); + return mc; + } + /* Otherwise search through the list of MCode areas. */ + for (;;) { + mc = ((MCLink *)mc)->next; + lua_assert(mc != NULL); + if (ptr >= mc && ptr < (MCode *)((char *)mc + ((MCLink *)mc)->size)) { + if (LJ_UNLIKELY(mcode_setprot(mc, ((MCLink *)mc)->size, MCPROT_GEN))) + mcode_protfail(J); + return mc; + } + } + } +#endif +} + +/* Limit of MCode reservation reached. */ +void lj_mcode_limiterr(jit_State *J, size_t need) +{ + size_t sizemcode, maxmcode; + lj_mcode_abort(J); + sizemcode = (size_t)J->param[JIT_P_sizemcode] << 10; + sizemcode = (sizemcode + LJ_PAGESIZE-1) & ~(size_t)(LJ_PAGESIZE - 1); + maxmcode = (size_t)J->param[JIT_P_maxmcode] << 10; + if ((size_t)need > sizemcode) + lj_trace_err(J, LJ_TRERR_MCODEOV); /* Too long for any area. */ + if (J->szallmcarea + sizemcode > maxmcode) + lj_trace_err(J, LJ_TRERR_MCODEAL); + mcode_allocarea(J); + lj_trace_err(J, LJ_TRERR_MCODELM); /* Retry with new area. */ +} + +#endif diff --git a/lib/LuaJIT/lj_mcode.h b/lib/LuaJIT/lj_mcode.h new file mode 100644 index 0000000..f0847e9 --- /dev/null +++ b/lib/LuaJIT/lj_mcode.h @@ -0,0 +1,30 @@ +/* +** Machine code management. +** Copyright (C) 2005-2017 Mike Pall. See Copyright Notice in luajit.h +*/ + +#ifndef _LJ_MCODE_H +#define _LJ_MCODE_H + +#include "lj_obj.h" + +#if LJ_HASJIT || LJ_HASFFI +LJ_FUNC void lj_mcode_sync(void *start, void *end); +#endif + +#if LJ_HASJIT + +#include "lj_jit.h" + +LJ_FUNC void lj_mcode_free(jit_State *J); +LJ_FUNC MCode *lj_mcode_reserve(jit_State *J, MCode **lim); +LJ_FUNC void lj_mcode_commit(jit_State *J, MCode *m); +LJ_FUNC void lj_mcode_abort(jit_State *J); +LJ_FUNC MCode *lj_mcode_patch(jit_State *J, MCode *ptr, int finish); +LJ_FUNC_NORET void lj_mcode_limiterr(jit_State *J, size_t need); + +#define lj_mcode_commitbot(J, m) (J->mcbot = (m)) + +#endif + +#endif diff --git a/lib/LuaJIT/lj_mcode.o b/lib/LuaJIT/lj_mcode.o new file mode 100644 index 0000000000000000000000000000000000000000..f67efd2c3a9e1a4e5e39cd8a0324c8263437517c GIT binary patch literal 3960 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[] is a GC root. */ + setgcref(g->gcroot[GCROOT_MMNAME+mm], obj2gco(s)); + } +} + +/* Negative caching of a few fast metamethods. See the lj_meta_fast() macro. */ +cTValue *lj_meta_cache(GCtab *mt, MMS mm, GCstr *name) +{ + cTValue *mo = lj_tab_getstr(mt, name); + lua_assert(mm <= MM_FAST); + if (!mo || tvisnil(mo)) { /* No metamethod? */ + mt->nomm |= (uint8_t)(1u<metatable); + else if (tvisudata(o)) + mt = tabref(udataV(o)->metatable); + else + mt = tabref(basemt_obj(G(L), o)); + if (mt) { + cTValue *mo = lj_tab_getstr(mt, mmname_str(G(L), mm)); + if (mo) + return mo; + } + return niltv(L); +} + +#if LJ_HASFFI +/* Tailcall from C function. */ +int lj_meta_tailcall(lua_State *L, cTValue *tv) +{ + TValue *base = L->base; + TValue *top = L->top; + const BCIns *pc = frame_pc(base-1); /* Preserve old PC from frame. */ + copyTV(L, base-1-LJ_FR2, tv); /* Replace frame with new object. */ + if (LJ_FR2) + (top++)->u64 = LJ_CONT_TAILCALL; + else + top->u32.lo = LJ_CONT_TAILCALL; + setframe_pc(top++, pc); + if (LJ_FR2) top++; + setframe_gc(top, obj2gco(L), LJ_TTHREAD); /* Dummy frame object. */ + setframe_ftsz(top, ((char *)(top+1) - (char *)base) + FRAME_CONT); + L->base = L->top = top+1; + /* + ** before: [old_mo|PC] [... ...] + ** ^base ^top + ** after: [new_mo|itype] [... ...] [NULL|PC] [dummy|delta] + ** ^base/top + ** tailcall: [new_mo|PC] [... ...] + ** ^base ^top + */ + return 0; +} +#endif + +/* Setup call to metamethod to be run by Assembler VM. */ +static TValue *mmcall(lua_State *L, ASMFunction cont, cTValue *mo, + cTValue *a, cTValue *b) +{ + /* + ** |-- framesize -> top top+1 top+2 top+3 + ** before: [func slots ...] + ** mm setup: [func slots ...] [cont|?] [mo|tmtype] [a] [b] + ** in asm: [func slots ...] [cont|PC] [mo|delta] [a] [b] + ** ^-- func base ^-- mm base + ** after mm: [func slots ...] [result] + ** ^-- copy to base[PC_RA] --/ for lj_cont_ra + ** istruecond + branch for lj_cont_cond* + ** ignore for lj_cont_nop + ** next PC: [func slots ...] + */ + TValue *top = L->top; + if (curr_funcisL(L)) top = curr_topL(L); + setcont(top++, cont); /* Assembler VM stores PC in upper word or FR2. */ + if (LJ_FR2) setnilV(top++); + copyTV(L, top++, mo); /* Store metamethod and two arguments. */ + if (LJ_FR2) setnilV(top++); + copyTV(L, top, a); + copyTV(L, top+1, b); + return top; /* Return new base. */ +} + +/* -- C helpers for some instructions, called from assembler VM ----------- */ + +/* Helper for TGET*. __index chain and metamethod. */ +cTValue *lj_meta_tget(lua_State *L, cTValue *o, cTValue *k) +{ + int loop; + for (loop = 0; loop < LJ_MAX_IDXCHAIN; loop++) { + cTValue *mo; + if (LJ_LIKELY(tvistab(o))) { + GCtab *t = tabV(o); + cTValue *tv = lj_tab_get(L, t, k); + if (!tvisnil(tv) || + !(mo = lj_meta_fast(L, tabref(t->metatable), MM_index))) + return tv; + } else if (tvisnil(mo = lj_meta_lookup(L, o, MM_index))) { + lj_err_optype(L, o, LJ_ERR_OPINDEX); + return NULL; /* unreachable */ + } + if (tvisfunc(mo)) { + L->top = mmcall(L, lj_cont_ra, mo, o, k); + return NULL; /* Trigger metamethod call. */ + } + o = mo; + } + lj_err_msg(L, LJ_ERR_GETLOOP); + return NULL; /* unreachable */ +} + +/* Helper for TSET*. __newindex chain and metamethod. */ +TValue *lj_meta_tset(lua_State *L, cTValue *o, cTValue *k) +{ + TValue tmp; + int loop; + for (loop = 0; loop < LJ_MAX_IDXCHAIN; loop++) { + cTValue *mo; + if (LJ_LIKELY(tvistab(o))) { + GCtab *t = tabV(o); + cTValue *tv = lj_tab_get(L, t, k); + if (LJ_LIKELY(!tvisnil(tv))) { + t->nomm = 0; /* Invalidate negative metamethod cache. */ + lj_gc_anybarriert(L, t); + return (TValue *)tv; + } else if (!(mo = lj_meta_fast(L, tabref(t->metatable), MM_newindex))) { + t->nomm = 0; /* Invalidate negative metamethod cache. */ + lj_gc_anybarriert(L, t); + if (tv != niltv(L)) + return (TValue *)tv; + if (tvisnil(k)) lj_err_msg(L, LJ_ERR_NILIDX); + else if (tvisint(k)) { setnumV(&tmp, (lua_Number)intV(k)); k = &tmp; } + else if (tvisnum(k) && tvisnan(k)) lj_err_msg(L, LJ_ERR_NANIDX); + return lj_tab_newkey(L, t, k); + } + } else if (tvisnil(mo = lj_meta_lookup(L, o, MM_newindex))) { + lj_err_optype(L, o, LJ_ERR_OPINDEX); + return NULL; /* unreachable */ + } + if (tvisfunc(mo)) { + L->top = mmcall(L, lj_cont_nop, mo, o, k); + /* L->top+2 = v filled in by caller. */ + return NULL; /* Trigger metamethod call. */ + } + copyTV(L, &tmp, mo); + o = &tmp; + } + lj_err_msg(L, LJ_ERR_SETLOOP); + return NULL; /* unreachable */ +} + +static cTValue *str2num(cTValue *o, TValue *n) +{ + if (tvisnum(o)) + return o; + else if (tvisint(o)) + return (setnumV(n, (lua_Number)intV(o)), n); + else if (tvisstr(o) && lj_strscan_num(strV(o), n)) + return n; + else + return NULL; +} + +/* Helper for arithmetic instructions. Coercion, metamethod. */ +TValue *lj_meta_arith(lua_State *L, TValue *ra, cTValue *rb, cTValue *rc, + BCReg op) +{ + MMS mm = bcmode_mm(op); + TValue tempb, tempc; + cTValue *b, *c; + if ((b = str2num(rb, &tempb)) != NULL && + (c = str2num(rc, &tempc)) != NULL) { /* Try coercion first. */ + setnumV(ra, lj_vm_foldarith(numV(b), numV(c), (int)mm-MM_add)); + return NULL; + } else { + cTValue *mo = lj_meta_lookup(L, rb, mm); + if (tvisnil(mo)) { + mo = lj_meta_lookup(L, rc, mm); + if (tvisnil(mo)) { + if (str2num(rb, &tempb) == NULL) rc = rb; + lj_err_optype(L, rc, LJ_ERR_OPARITH); + return NULL; /* unreachable */ + } + } + return mmcall(L, lj_cont_ra, mo, rb, rc); + } +} + +/* Helper for CAT. Coercion, iterative concat, __concat metamethod. */ +TValue *lj_meta_cat(lua_State *L, TValue *top, int left) +{ + int fromc = 0; + if (left < 0) { left = -left; fromc = 1; } + do { + if (!(tvisstr(top) || tvisnumber(top)) || + !(tvisstr(top-1) || tvisnumber(top-1))) { + cTValue *mo = lj_meta_lookup(L, top-1, MM_concat); + if (tvisnil(mo)) { + mo = lj_meta_lookup(L, top, MM_concat); + if (tvisnil(mo)) { + if (tvisstr(top-1) || tvisnumber(top-1)) top++; + lj_err_optype(L, top-1, LJ_ERR_OPCAT); + return NULL; /* unreachable */ + } + } + /* One of the top two elements is not a string, call __cat metamethod: + ** + ** before: [...][CAT stack .........................] + ** top-1 top top+1 top+2 + ** pick two: [...][CAT stack ...] [o1] [o2] + ** setup mm: [...][CAT stack ...] [cont|?] [mo|tmtype] [o1] [o2] + ** in asm: [...][CAT stack ...] [cont|PC] [mo|delta] [o1] [o2] + ** ^-- func base ^-- mm base + ** after mm: [...][CAT stack ...] <--push-- [result] + ** next step: [...][CAT stack .............] + */ + copyTV(L, top+2*LJ_FR2+2, top); /* Carefully ordered stack copies! */ + copyTV(L, top+2*LJ_FR2+1, top-1); + copyTV(L, top+LJ_FR2, mo); + setcont(top-1, lj_cont_cat); + if (LJ_FR2) { setnilV(top); setnilV(top+2); top += 2; } + return top+1; /* Trigger metamethod call. */ + } else { + /* Pick as many strings as possible from the top and concatenate them: + ** + ** before: [...][CAT stack ...........................] + ** pick str: [...][CAT stack ...] [...... strings ......] + ** concat: [...][CAT stack ...] [result] + ** next step: [...][CAT stack ............] + */ + TValue *e, *o = top; + uint64_t tlen = tvisstr(o) ? strV(o)->len : STRFMT_MAXBUF_NUM; + SBuf *sb; + do { + o--; tlen += tvisstr(o) ? strV(o)->len : STRFMT_MAXBUF_NUM; + } while (--left > 0 && (tvisstr(o-1) || tvisnumber(o-1))); + if (tlen >= LJ_MAX_STR) lj_err_msg(L, LJ_ERR_STROV); + sb = lj_buf_tmp_(L); + lj_buf_more(sb, (MSize)tlen); + for (e = top, top = o; o <= e; o++) { + if (tvisstr(o)) { + GCstr *s = strV(o); + MSize len = s->len; + lj_buf_putmem(sb, strdata(s), len); + } else if (tvisint(o)) { + lj_strfmt_putint(sb, intV(o)); + } else { + lj_strfmt_putfnum(sb, STRFMT_G14, numV(o)); + } + } + setstrV(L, top, lj_buf_str(L, sb)); + } + } while (left >= 1); + if (LJ_UNLIKELY(G(L)->gc.total >= G(L)->gc.threshold)) { + if (!fromc) L->top = curr_topL(L); + lj_gc_step(L); + } + return NULL; +} + +/* Helper for LEN. __len metamethod. */ +TValue * LJ_FASTCALL lj_meta_len(lua_State *L, cTValue *o) +{ + cTValue *mo = lj_meta_lookup(L, o, MM_len); + if (tvisnil(mo)) { + if (LJ_52 && tvistab(o)) + tabref(tabV(o)->metatable)->nomm |= (uint8_t)(1u<gch.metatable), MM_eq); + if (mo) { + TValue *top; + uint32_t it; + if (tabref(o1->gch.metatable) != tabref(o2->gch.metatable)) { + cTValue *mo2 = lj_meta_fast(L, tabref(o2->gch.metatable), MM_eq); + if (mo2 == NULL || !lj_obj_equal(mo, mo2)) + return (TValue *)(intptr_t)ne; + } + top = curr_top(L); + setcont(top++, ne ? lj_cont_condf : lj_cont_condt); + if (LJ_FR2) setnilV(top++); + copyTV(L, top++, mo); + if (LJ_FR2) setnilV(top++); + it = ~(uint32_t)o1->gch.gct; + setgcV(L, top, o1, it); + setgcV(L, top+1, o2, it); + return top; /* Trigger metamethod call. */ + } + return (TValue *)(intptr_t)ne; +} + +#if LJ_HASFFI +TValue * LJ_FASTCALL lj_meta_equal_cd(lua_State *L, BCIns ins) +{ + ASMFunction cont = (bc_op(ins) & 1) ? lj_cont_condf : lj_cont_condt; + int op = (int)bc_op(ins) & ~1; + TValue tv; + cTValue *mo, *o2, *o1 = &L->base[bc_a(ins)]; + cTValue *o1mm = o1; + if (op == BC_ISEQV) { + o2 = &L->base[bc_d(ins)]; + if (!tviscdata(o1mm)) o1mm = o2; + } else if (op == BC_ISEQS) { + setstrV(L, &tv, gco2str(proto_kgc(curr_proto(L), ~(ptrdiff_t)bc_d(ins)))); + o2 = &tv; + } else if (op == BC_ISEQN) { + o2 = &mref(curr_proto(L)->k, cTValue)[bc_d(ins)]; + } else { + lua_assert(op == BC_ISEQP); + setpriV(&tv, ~bc_d(ins)); + o2 = &tv; + } + mo = lj_meta_lookup(L, o1mm, MM_eq); + if (LJ_LIKELY(!tvisnil(mo))) + return mmcall(L, cont, mo, o1, o2); + else + return (TValue *)(intptr_t)(bc_op(ins) & 1); +} +#endif + +/* Helper for ordered comparisons. String compare, __lt/__le metamethods. */ +TValue *lj_meta_comp(lua_State *L, cTValue *o1, cTValue *o2, int op) +{ + if (LJ_HASFFI && (tviscdata(o1) || tviscdata(o2))) { + ASMFunction cont = (op & 1) ? lj_cont_condf : lj_cont_condt; + MMS mm = (op & 2) ? MM_le : MM_lt; + cTValue *mo = lj_meta_lookup(L, tviscdata(o1) ? o1 : o2, mm); + if (LJ_UNLIKELY(tvisnil(mo))) goto err; + return mmcall(L, cont, mo, o1, o2); + } else if (LJ_52 || itype(o1) == itype(o2)) { + /* Never called with two numbers. */ + if (tvisstr(o1) && tvisstr(o2)) { + int32_t res = lj_str_cmp(strV(o1), strV(o2)); + return (TValue *)(intptr_t)(((op&2) ? res <= 0 : res < 0) ^ (op&1)); + } else { + trymt: + while (1) { + ASMFunction cont = (op & 1) ? lj_cont_condf : lj_cont_condt; + MMS mm = (op & 2) ? MM_le : MM_lt; + cTValue *mo = lj_meta_lookup(L, o1, mm); +#if LJ_52 + if (tvisnil(mo) && tvisnil((mo = lj_meta_lookup(L, o2, mm)))) +#else + cTValue *mo2 = lj_meta_lookup(L, o2, mm); + if (tvisnil(mo) || !lj_obj_equal(mo, mo2)) +#endif + { + if (op & 2) { /* MM_le not found: retry with MM_lt. */ + cTValue *ot = o1; o1 = o2; o2 = ot; /* Swap operands. */ + op ^= 3; /* Use LT and flip condition. */ + continue; + } + goto err; + } + return mmcall(L, cont, mo, o1, o2); + } + } + } else if (tvisbool(o1) && tvisbool(o2)) { + goto trymt; + } else { + err: + lj_err_comp(L, o1, o2); + return NULL; + } +} + +/* Helper for ISTYPE and ISNUM. Implicit coercion or error. */ +void lj_meta_istype(lua_State *L, BCReg ra, BCReg tp) +{ + L->top = curr_topL(L); + ra++; tp--; + lua_assert(LJ_DUALNUM || tp != ~LJ_TNUMX); /* ISTYPE -> ISNUM broken. */ + if (LJ_DUALNUM && tp == ~LJ_TNUMX) lj_lib_checkint(L, ra); + else if (tp == ~LJ_TNUMX+1) lj_lib_checknum(L, ra); + else if (tp == ~LJ_TSTR) lj_lib_checkstr(L, ra); + else lj_err_argtype(L, ra, lj_obj_itypename[tp]); +} + +/* Helper for calls. __call metamethod. */ +void lj_meta_call(lua_State *L, TValue *func, TValue *top) +{ + cTValue *mo = lj_meta_lookup(L, func, MM_call); + TValue *p; + if (!tvisfunc(mo)) + lj_err_optype_call(L, func); + for (p = top; p > func+2*LJ_FR2; p--) copyTV(L, p, p-1); + if (LJ_FR2) copyTV(L, func+2, func); + copyTV(L, func, mo); +} + +/* Helper for FORI. Coercion. */ +void LJ_FASTCALL lj_meta_for(lua_State *L, TValue *o) +{ + if (!lj_strscan_numberobj(o)) lj_err_msg(L, LJ_ERR_FORINIT); + if (!lj_strscan_numberobj(o+1)) lj_err_msg(L, LJ_ERR_FORLIM); + if (!lj_strscan_numberobj(o+2)) lj_err_msg(L, LJ_ERR_FORSTEP); + if (LJ_DUALNUM) { + /* Ensure all slots are integers or all slots are numbers. */ + int32_t k[3]; + int nint = 0; + ptrdiff_t i; + for (i = 0; i <= 2; i++) { + if (tvisint(o+i)) { + k[i] = intV(o+i); nint++; + } else { + k[i] = lj_num2int(numV(o+i)); nint += ((lua_Number)k[i] == numV(o+i)); + } + } + if (nint == 3) { /* Narrow to integers. */ + setintV(o, k[0]); + setintV(o+1, k[1]); + setintV(o+2, k[2]); + } else if (nint != 0) { /* Widen to numbers. */ + if (tvisint(o)) setnumV(o, (lua_Number)intV(o)); + if (tvisint(o+1)) setnumV(o+1, (lua_Number)intV(o+1)); + if (tvisint(o+2)) setnumV(o+2, (lua_Number)intV(o+2)); + } + } +} + diff --git a/lib/LuaJIT/lj_meta.h b/lib/LuaJIT/lj_meta.h new file mode 100644 index 0000000..73b4572 --- /dev/null +++ b/lib/LuaJIT/lj_meta.h @@ -0,0 +1,38 @@ +/* +** Metamethod handling. +** Copyright (C) 2005-2017 Mike Pall. See Copyright Notice in luajit.h +*/ + +#ifndef _LJ_META_H +#define _LJ_META_H + +#include "lj_obj.h" + +/* Metamethod handling */ +LJ_FUNC void lj_meta_init(lua_State *L); +LJ_FUNC cTValue *lj_meta_cache(GCtab *mt, MMS mm, GCstr *name); +LJ_FUNC cTValue *lj_meta_lookup(lua_State *L, cTValue *o, MMS mm); +#if LJ_HASFFI +LJ_FUNC int lj_meta_tailcall(lua_State *L, cTValue *tv); +#endif + +#define lj_meta_fastg(g, mt, mm) \ + ((mt) == NULL ? NULL : ((mt)->nomm & (1u<<(mm))) ? NULL : \ + lj_meta_cache(mt, mm, mmname_str(g, mm))) +#define lj_meta_fast(L, mt, mm) lj_meta_fastg(G(L), mt, mm) + +/* C helpers for some instructions, called from assembler VM. */ +LJ_FUNCA cTValue *lj_meta_tget(lua_State *L, cTValue *o, cTValue *k); +LJ_FUNCA TValue *lj_meta_tset(lua_State *L, cTValue *o, cTValue *k); +LJ_FUNCA TValue *lj_meta_arith(lua_State *L, TValue *ra, cTValue *rb, + cTValue *rc, BCReg op); +LJ_FUNCA TValue *lj_meta_cat(lua_State *L, TValue *top, int left); +LJ_FUNCA TValue * LJ_FASTCALL lj_meta_len(lua_State *L, cTValue *o); +LJ_FUNCA TValue *lj_meta_equal(lua_State *L, GCobj *o1, GCobj *o2, int ne); +LJ_FUNCA TValue * LJ_FASTCALL lj_meta_equal_cd(lua_State *L, BCIns ins); +LJ_FUNCA TValue *lj_meta_comp(lua_State *L, cTValue *o1, cTValue *o2, int op); +LJ_FUNCA void lj_meta_istype(lua_State *L, BCReg ra, BCReg tp); +LJ_FUNCA void lj_meta_call(lua_State *L, TValue *func, TValue *top); +LJ_FUNCA void LJ_FASTCALL lj_meta_for(lua_State *L, TValue *o); + +#endif diff --git a/lib/LuaJIT/lj_meta.o b/lib/LuaJIT/lj_meta.o new file mode 100644 index 0000000000000000000000000000000000000000..ae3486f1ed4b7012fa26f8baa928e8af511e7d72 GIT binary patch literal 10112 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 E0RI4ErvLx| literal 0 HcmV?d00001 diff --git a/lib/LuaJIT/lj_meta_dyn.o b/lib/LuaJIT/lj_meta_dyn.o new file mode 100644 index 0000000000000000000000000000000000000000..ae3486f1ed4b7012fa26f8baa928e8af511e7d72 GIT binary patch literal 10112 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 E0RI4ErvLx| literal 0 HcmV?d00001 diff --git a/lib/LuaJIT/lj_obj.c b/lib/LuaJIT/lj_obj.c new file mode 100644 index 0000000..ee33aeb --- /dev/null +++ b/lib/LuaJIT/lj_obj.c @@ -0,0 +1,50 @@ +/* +** Miscellaneous object handling. +** Copyright (C) 2005-2017 Mike Pall. See Copyright Notice in luajit.h +*/ + +#define lj_obj_c +#define LUA_CORE + +#include "lj_obj.h" + +/* Object type names. */ +LJ_DATADEF const char *const lj_obj_typename[] = { /* ORDER LUA_T */ + "no value", "nil", "boolean", "userdata", "number", "string", + "table", "function", "userdata", "thread", "proto", "cdata" +}; + +LJ_DATADEF const char *const lj_obj_itypename[] = { /* ORDER LJ_T */ + "nil", "boolean", "boolean", "userdata", "string", "upval", "thread", + "proto", "function", "trace", "cdata", "table", "userdata", "number" +}; + +/* Compare two objects without calling metamethods. */ +int LJ_FASTCALL lj_obj_equal(cTValue *o1, cTValue *o2) +{ + if (itype(o1) == itype(o2)) { + if (tvispri(o1)) + return 1; + if (!tvisnum(o1)) + return gcrefeq(o1->gcr, o2->gcr); + } else if (!tvisnumber(o1) || !tvisnumber(o2)) { + return 0; + } + return numberVnum(o1) == numberVnum(o2); +} + +/* Return pointer to object or its object data. */ +const void * LJ_FASTCALL lj_obj_ptr(cTValue *o) +{ + if (tvisudata(o)) + return uddata(udataV(o)); + else if (tvislightud(o)) + return lightudV(o); + else if (LJ_HASFFI && tviscdata(o)) + return cdataptr(cdataV(o)); + else if (tvisgcv(o)) + return gcV(o); + else + return NULL; +} + diff --git a/lib/LuaJIT/lj_obj.h b/lib/LuaJIT/lj_obj.h new file mode 100644 index 0000000..72b7ace --- /dev/null +++ b/lib/LuaJIT/lj_obj.h @@ -0,0 +1,991 @@ +/* +** LuaJIT VM tags, values and objects. +** Copyright (C) 2005-2017 Mike Pall. See Copyright Notice in luajit.h +** +** Portions taken verbatim or adapted from the Lua interpreter. +** Copyright (C) 1994-2008 Lua.org, PUC-Rio. See Copyright Notice in lua.h +*/ + +#ifndef _LJ_OBJ_H +#define _LJ_OBJ_H + +#include "lua.h" +#include "lj_def.h" +#include "lj_arch.h" + +/* -- Memory references (32 bit address space) ---------------------------- */ + +/* Memory and GC object sizes. */ +typedef uint32_t MSize; +#if LJ_GC64 +typedef uint64_t GCSize; +#else +typedef uint32_t GCSize; +#endif + +/* Memory reference */ +typedef struct MRef { +#if LJ_GC64 + uint64_t ptr64; /* True 64 bit pointer. */ +#else + uint32_t ptr32; /* Pseudo 32 bit pointer. */ +#endif +} MRef; + +#if LJ_GC64 +#define mref(r, t) ((t *)(void *)(r).ptr64) + +#define setmref(r, p) ((r).ptr64 = (uint64_t)(void *)(p)) +#define setmrefr(r, v) ((r).ptr64 = (v).ptr64) +#else +#define mref(r, t) ((t *)(void *)(uintptr_t)(r).ptr32) + +#define setmref(r, p) ((r).ptr32 = (uint32_t)(uintptr_t)(void *)(p)) +#define setmrefr(r, v) ((r).ptr32 = (v).ptr32) +#endif + +/* -- GC object references (32 bit address space) ------------------------- */ + +/* GCobj reference */ +typedef struct GCRef { +#if LJ_GC64 + uint64_t gcptr64; /* True 64 bit pointer. */ +#else + uint32_t gcptr32; /* Pseudo 32 bit pointer. */ +#endif +} GCRef; + +/* Common GC header for all collectable objects. */ +#define GCHeader GCRef nextgc; uint8_t marked; uint8_t gct +/* This occupies 6 bytes, so use the next 2 bytes for non-32 bit fields. */ + +#if LJ_GC64 +#define gcref(r) ((GCobj *)(r).gcptr64) +#define gcrefp(r, t) ((t *)(void *)(r).gcptr64) +#define gcrefu(r) ((r).gcptr64) +#define gcrefeq(r1, r2) ((r1).gcptr64 == (r2).gcptr64) + +#define setgcref(r, gc) ((r).gcptr64 = (uint64_t)&(gc)->gch) +#define setgcreft(r, gc, it) \ + (r).gcptr64 = (uint64_t)&(gc)->gch | (((uint64_t)(it)) << 47) +#define setgcrefp(r, p) ((r).gcptr64 = (uint64_t)(p)) +#define setgcrefnull(r) ((r).gcptr64 = 0) +#define setgcrefr(r, v) ((r).gcptr64 = (v).gcptr64) +#else +#define gcref(r) ((GCobj *)(uintptr_t)(r).gcptr32) +#define gcrefp(r, t) ((t *)(void *)(uintptr_t)(r).gcptr32) +#define gcrefu(r) ((r).gcptr32) +#define gcrefeq(r1, r2) ((r1).gcptr32 == (r2).gcptr32) + +#define setgcref(r, gc) ((r).gcptr32 = (uint32_t)(uintptr_t)&(gc)->gch) +#define setgcrefp(r, p) ((r).gcptr32 = (uint32_t)(uintptr_t)(p)) +#define setgcrefnull(r) ((r).gcptr32 = 0) +#define setgcrefr(r, v) ((r).gcptr32 = (v).gcptr32) +#endif + +#define gcnext(gc) (gcref((gc)->gch.nextgc)) + +/* IMPORTANT NOTE: +** +** All uses of the setgcref* macros MUST be accompanied with a write barrier. +** +** This is to ensure the integrity of the incremental GC. The invariant +** to preserve is that a black object never points to a white object. +** I.e. never store a white object into a field of a black object. +** +** It's ok to LEAVE OUT the write barrier ONLY in the following cases: +** - The source is not a GC object (NULL). +** - The target is a GC root. I.e. everything in global_State. +** - The target is a lua_State field (threads are never black). +** - The target is a stack slot, see setgcV et al. +** - The target is an open upvalue, i.e. pointing to a stack slot. +** - The target is a newly created object (i.e. marked white). But make +** sure nothing invokes the GC inbetween. +** - The target and the source are the same object (self-reference). +** - The target already contains the object (e.g. moving elements around). +** +** The most common case is a store to a stack slot. All other cases where +** a barrier has been omitted are annotated with a NOBARRIER comment. +** +** The same logic applies for stores to table slots (array part or hash +** part). ALL uses of lj_tab_set* require a barrier for the stored value +** *and* the stored key, based on the above rules. In practice this means +** a barrier is needed if *either* of the key or value are a GC object. +** +** It's ok to LEAVE OUT the write barrier in the following special cases: +** - The stored value is nil. The key doesn't matter because it's either +** not resurrected or lj_tab_newkey() will take care of the key barrier. +** - The key doesn't matter if the *previously* stored value is guaranteed +** to be non-nil (because the key is kept alive in the table). +** - The key doesn't matter if it's guaranteed not to be part of the table, +** since lj_tab_newkey() takes care of the key barrier. This applies +** trivially to new tables, but watch out for resurrected keys. Storing +** a nil value leaves the key in the table! +** +** In case of doubt use lj_gc_anybarriert() as it's rather cheap. It's used +** by the interpreter for all table stores. +** +** Note: In contrast to Lua's GC, LuaJIT's GC does *not* specially mark +** dead keys in tables. The reference is left in, but it's guaranteed to +** be never dereferenced as long as the value is nil. It's ok if the key is +** freed or if any object subsequently gets the same address. +** +** Not destroying dead keys helps to keep key hash slots stable. This avoids +** specialization back-off for HREFK when a value flips between nil and +** non-nil and the GC gets in the way. It also allows safely hoisting +** HREF/HREFK across GC steps. Dead keys are only removed if a table is +** resized (i.e. by NEWREF) and xREF must not be CSEd across a resize. +** +** The trade-off is that a write barrier for tables must take the key into +** account, too. Implicitly resurrecting the key by storing a non-nil value +** may invalidate the incremental GC invariant. +*/ + +/* -- Common type definitions --------------------------------------------- */ + +/* Types for handling bytecodes. Need this here, details in lj_bc.h. */ +typedef uint32_t BCIns; /* Bytecode instruction. */ +typedef uint32_t BCPos; /* Bytecode position. */ +typedef uint32_t BCReg; /* Bytecode register. */ +typedef int32_t BCLine; /* Bytecode line number. */ + +/* Internal assembler functions. Never call these directly from C. */ +typedef void (*ASMFunction)(void); + +/* Resizable string buffer. Need this here, details in lj_buf.h. */ +typedef struct SBuf { + MRef p; /* String buffer pointer. */ + MRef e; /* String buffer end pointer. */ + MRef b; /* String buffer base. */ + MRef L; /* lua_State, used for buffer resizing. */ +} SBuf; + +/* -- Tags and values ----------------------------------------------------- */ + +/* Frame link. */ +typedef union { + int32_t ftsz; /* Frame type and size of previous frame. */ + MRef pcr; /* Or PC for Lua frames. */ +} FrameLink; + +/* Tagged value. */ +typedef LJ_ALIGN(8) union TValue { + uint64_t u64; /* 64 bit pattern overlaps number. */ + lua_Number n; /* Number object overlaps split tag/value object. */ +#if LJ_GC64 + GCRef gcr; /* GCobj reference with tag. */ + int64_t it64; + struct { + LJ_ENDIAN_LOHI( + int32_t i; /* Integer value. */ + , uint32_t it; /* Internal object tag. Must overlap MSW of number. */ + ) + }; +#else + struct { + LJ_ENDIAN_LOHI( + union { + GCRef gcr; /* GCobj reference (if any). */ + int32_t i; /* Integer value. */ + }; + , uint32_t it; /* Internal object tag. Must overlap MSW of number. */ + ) + }; +#endif +#if LJ_FR2 + int64_t ftsz; /* Frame type and size of previous frame, or PC. */ +#else + struct { + LJ_ENDIAN_LOHI( + GCRef func; /* Function for next frame (or dummy L). */ + , FrameLink tp; /* Link to previous frame. */ + ) + } fr; +#endif + struct { + LJ_ENDIAN_LOHI( + uint32_t lo; /* Lower 32 bits of number. */ + , uint32_t hi; /* Upper 32 bits of number. */ + ) + } u32; +} TValue; + +typedef const TValue cTValue; + +#define tvref(r) (mref(r, TValue)) + +/* More external and GCobj tags for internal objects. */ +#define LAST_TT LUA_TTHREAD +#define LUA_TPROTO (LAST_TT+1) +#define LUA_TCDATA (LAST_TT+2) + +/* Internal object tags. +** +** Format for 32 bit GC references (!LJ_GC64): +** +** Internal tags overlap the MSW of a number object (must be a double). +** Interpreted as a double these are special NaNs. The FPU only generates +** one type of NaN (0xfff8_0000_0000_0000). So MSWs > 0xfff80000 are available +** for use as internal tags. Small negative numbers are used to shorten the +** encoding of type comparisons (reg/mem against sign-ext. 8 bit immediate). +** +** ---MSW---.---LSW--- +** primitive types | itype | | +** lightuserdata | itype | void * | (32 bit platforms) +** lightuserdata |ffff| void * | (64 bit platforms, 47 bit pointers) +** GC objects | itype | GCRef | +** int (LJ_DUALNUM)| itype | int | +** number -------double------ +** +** Format for 64 bit GC references (LJ_GC64): +** +** The upper 13 bits must be 1 (0xfff8...) for a special NaN. The next +** 4 bits hold the internal tag. The lowest 47 bits either hold a pointer, +** a zero-extended 32 bit integer or all bits set to 1 for primitive types. +** +** ------MSW------.------LSW------ +** primitive types |1..1|itype|1..................1| +** GC objects/lightud |1..1|itype|-------GCRef--------| +** int (LJ_DUALNUM) |1..1|itype|0..0|-----int-------| +** number ------------double------------- +** +** ORDER LJ_T +** Primitive types nil/false/true must be first, lightuserdata next. +** GC objects are at the end, table/userdata must be lowest. +** Also check lj_ir.h for similar ordering constraints. +*/ +#define LJ_TNIL (~0u) +#define LJ_TFALSE (~1u) +#define LJ_TTRUE (~2u) +#define LJ_TLIGHTUD (~3u) +#define LJ_TSTR (~4u) +#define LJ_TUPVAL (~5u) +#define LJ_TTHREAD (~6u) +#define LJ_TPROTO (~7u) +#define LJ_TFUNC (~8u) +#define LJ_TTRACE (~9u) +#define LJ_TCDATA (~10u) +#define LJ_TTAB (~11u) +#define LJ_TUDATA (~12u) +/* This is just the canonical number type used in some places. */ +#define LJ_TNUMX (~13u) + +/* Integers have itype == LJ_TISNUM doubles have itype < LJ_TISNUM */ +#if LJ_64 && !LJ_GC64 +#define LJ_TISNUM 0xfffeffffu +#else +#define LJ_TISNUM LJ_TNUMX +#endif +#define LJ_TISTRUECOND LJ_TFALSE +#define LJ_TISPRI LJ_TTRUE +#define LJ_TISGCV (LJ_TSTR+1) +#define LJ_TISTABUD LJ_TTAB + +#if LJ_GC64 +#define LJ_GCVMASK (((uint64_t)1 << 47) - 1) +#endif + +/* -- String object ------------------------------------------------------- */ + +/* String object header. String payload follows. */ +typedef struct GCstr { + GCHeader; + uint8_t reserved; /* Used by lexer for fast lookup of reserved words. */ + uint8_t unused; + MSize hash; /* Hash of string. */ + MSize len; /* Size of string. */ +} GCstr; + +#define strref(r) (&gcref((r))->str) +#define strdata(s) ((const char *)((s)+1)) +#define strdatawr(s) ((char *)((s)+1)) +#define strVdata(o) strdata(strV(o)) +#define sizestring(s) (sizeof(struct GCstr)+(s)->len+1) + +/* -- Userdata object ----------------------------------------------------- */ + +/* Userdata object. Payload follows. */ +typedef struct GCudata { + GCHeader; + uint8_t udtype; /* Userdata type. */ + uint8_t unused2; + GCRef env; /* Should be at same offset in GCfunc. */ + MSize len; /* Size of payload. */ + GCRef metatable; /* Must be at same offset in GCtab. */ + uint32_t align1; /* To force 8 byte alignment of the payload. */ +} GCudata; + +/* Userdata types. */ +enum { + UDTYPE_USERDATA, /* Regular userdata. */ + UDTYPE_IO_FILE, /* I/O library FILE. */ + UDTYPE_FFI_CLIB, /* FFI C library namespace. */ + UDTYPE__MAX +}; + +#define uddata(u) ((void *)((u)+1)) +#define sizeudata(u) (sizeof(struct GCudata)+(u)->len) + +/* -- C data object ------------------------------------------------------- */ + +/* C data object. Payload follows. */ +typedef struct GCcdata { + GCHeader; + uint16_t ctypeid; /* C type ID. */ +} GCcdata; + +/* Prepended to variable-sized or realigned C data objects. */ +typedef struct GCcdataVar { + uint16_t offset; /* Offset to allocated memory (relative to GCcdata). */ + uint16_t extra; /* Extra space allocated (incl. GCcdata + GCcdatav). */ + MSize len; /* Size of payload. */ +} GCcdataVar; + +#define cdataptr(cd) ((void *)((cd)+1)) +#define cdataisv(cd) ((cd)->marked & 0x80) +#define cdatav(cd) ((GCcdataVar *)((char *)(cd) - sizeof(GCcdataVar))) +#define cdatavlen(cd) check_exp(cdataisv(cd), cdatav(cd)->len) +#define sizecdatav(cd) (cdatavlen(cd) + cdatav(cd)->extra) +#define memcdatav(cd) ((void *)((char *)(cd) - cdatav(cd)->offset)) + +/* -- Prototype object ---------------------------------------------------- */ + +#define SCALE_NUM_GCO ((int32_t)sizeof(lua_Number)/sizeof(GCRef)) +#define round_nkgc(n) (((n) + SCALE_NUM_GCO-1) & ~(SCALE_NUM_GCO-1)) + +typedef struct GCproto { + GCHeader; + uint8_t numparams; /* Number of parameters. */ + uint8_t framesize; /* Fixed frame size. */ + MSize sizebc; /* Number of bytecode instructions. */ +#if LJ_GC64 + uint32_t unused_gc64; +#endif + GCRef gclist; + MRef k; /* Split constant array (points to the middle). */ + MRef uv; /* Upvalue list. local slot|0x8000 or parent uv idx. */ + MSize sizekgc; /* Number of collectable constants. */ + MSize sizekn; /* Number of lua_Number constants. */ + MSize sizept; /* Total size including colocated arrays. */ + uint8_t sizeuv; /* Number of upvalues. */ + uint8_t flags; /* Miscellaneous flags (see below). */ + uint16_t trace; /* Anchor for chain of root traces. */ + /* ------ The following fields are for debugging/tracebacks only ------ */ + GCRef chunkname; /* Name of the chunk this function was defined in. */ + BCLine firstline; /* First line of the function definition. */ + BCLine numline; /* Number of lines for the function definition. */ + MRef lineinfo; /* Compressed map from bytecode ins. to source line. */ + MRef uvinfo; /* Upvalue names. */ + MRef varinfo; /* Names and compressed extents of local variables. */ +} GCproto; + +/* Flags for prototype. */ +#define PROTO_CHILD 0x01 /* Has child prototypes. */ +#define PROTO_VARARG 0x02 /* Vararg function. */ +#define PROTO_FFI 0x04 /* Uses BC_KCDATA for FFI datatypes. */ +#define PROTO_NOJIT 0x08 /* JIT disabled for this function. */ +#define PROTO_ILOOP 0x10 /* Patched bytecode with ILOOP etc. */ +/* Only used during parsing. */ +#define PROTO_HAS_RETURN 0x20 /* Already emitted a return. */ +#define PROTO_FIXUP_RETURN 0x40 /* Need to fixup emitted returns. */ +/* Top bits used for counting created closures. */ +#define PROTO_CLCOUNT 0x20 /* Base of saturating 3 bit counter. */ +#define PROTO_CLC_BITS 3 +#define PROTO_CLC_POLY (3*PROTO_CLCOUNT) /* Polymorphic threshold. */ + +#define PROTO_UV_LOCAL 0x8000 /* Upvalue for local slot. */ +#define PROTO_UV_IMMUTABLE 0x4000 /* Immutable upvalue. */ + +#define proto_kgc(pt, idx) \ + check_exp((uintptr_t)(intptr_t)(idx) >= (uintptr_t)-(intptr_t)(pt)->sizekgc, \ + gcref(mref((pt)->k, GCRef)[(idx)])) +#define proto_knumtv(pt, idx) \ + check_exp((uintptr_t)(idx) < (pt)->sizekn, &mref((pt)->k, TValue)[(idx)]) +#define proto_bc(pt) ((BCIns *)((char *)(pt) + sizeof(GCproto))) +#define proto_bcpos(pt, pc) ((BCPos)((pc) - proto_bc(pt))) +#define proto_uv(pt) (mref((pt)->uv, uint16_t)) + +#define proto_chunkname(pt) (strref((pt)->chunkname)) +#define proto_chunknamestr(pt) (strdata(proto_chunkname((pt)))) +#define proto_lineinfo(pt) (mref((pt)->lineinfo, const void)) +#define proto_uvinfo(pt) (mref((pt)->uvinfo, const uint8_t)) +#define proto_varinfo(pt) (mref((pt)->varinfo, const uint8_t)) + +/* -- Upvalue object ------------------------------------------------------ */ + +typedef struct GCupval { + GCHeader; + uint8_t closed; /* Set if closed (i.e. uv->v == &uv->u.value). */ + uint8_t immutable; /* Immutable value. */ + union { + TValue tv; /* If closed: the value itself. */ + struct { /* If open: double linked list, anchored at thread. */ + GCRef prev; + GCRef next; + }; + }; + MRef v; /* Points to stack slot (open) or above (closed). */ + uint32_t dhash; /* Disambiguation hash: dh1 != dh2 => cannot alias. */ +} GCupval; + +#define uvprev(uv_) (&gcref((uv_)->prev)->uv) +#define uvnext(uv_) (&gcref((uv_)->next)->uv) +#define uvval(uv_) (mref((uv_)->v, TValue)) + +/* -- Function object (closures) ------------------------------------------ */ + +/* Common header for functions. env should be at same offset in GCudata. */ +#define GCfuncHeader \ + GCHeader; uint8_t ffid; uint8_t nupvalues; \ + GCRef env; GCRef gclist; MRef pc + +typedef struct GCfuncC { + GCfuncHeader; + lua_CFunction f; /* C function to be called. */ + TValue upvalue[1]; /* Array of upvalues (TValue). */ +} GCfuncC; + +typedef struct GCfuncL { + GCfuncHeader; + GCRef uvptr[1]; /* Array of _pointers_ to upvalue objects (GCupval). */ +} GCfuncL; + +typedef union GCfunc { + GCfuncC c; + GCfuncL l; +} GCfunc; + +#define FF_LUA 0 +#define FF_C 1 +#define isluafunc(fn) ((fn)->c.ffid == FF_LUA) +#define iscfunc(fn) ((fn)->c.ffid == FF_C) +#define isffunc(fn) ((fn)->c.ffid > FF_C) +#define funcproto(fn) \ + check_exp(isluafunc(fn), (GCproto *)(mref((fn)->l.pc, char)-sizeof(GCproto))) +#define sizeCfunc(n) (sizeof(GCfuncC)-sizeof(TValue)+sizeof(TValue)*(n)) +#define sizeLfunc(n) (sizeof(GCfuncL)-sizeof(GCRef)+sizeof(GCRef)*(n)) + +/* -- Table object -------------------------------------------------------- */ + +/* Hash node. */ +typedef struct Node { + TValue val; /* Value object. Must be first field. */ + TValue key; /* Key object. */ + MRef next; /* Hash chain. */ +#if !LJ_GC64 + MRef freetop; /* Top of free elements (stored in t->node[0]). */ +#endif +} Node; + +LJ_STATIC_ASSERT(offsetof(Node, val) == 0); + +typedef struct GCtab { + GCHeader; + uint8_t nomm; /* Negative cache for fast metamethods. */ + int8_t colo; /* Array colocation. */ + MRef array; /* Array part. */ + GCRef gclist; + GCRef metatable; /* Must be at same offset in GCudata. */ + MRef node; /* Hash part. */ + uint32_t asize; /* Size of array part (keys [0, asize-1]). */ + uint32_t hmask; /* Hash part mask (size of hash part - 1). */ +#if LJ_GC64 + MRef freetop; /* Top of free elements. */ +#endif +} GCtab; + +#define sizetabcolo(n) ((n)*sizeof(TValue) + sizeof(GCtab)) +#define tabref(r) (&gcref((r))->tab) +#define noderef(r) (mref((r), Node)) +#define nextnode(n) (mref((n)->next, Node)) +#if LJ_GC64 +#define getfreetop(t, n) (noderef((t)->freetop)) +#define setfreetop(t, n, v) (setmref((t)->freetop, (v))) +#else +#define getfreetop(t, n) (noderef((n)->freetop)) +#define setfreetop(t, n, v) (setmref((n)->freetop, (v))) +#endif + +/* -- State objects ------------------------------------------------------- */ + +/* VM states. */ +enum { + LJ_VMST_INTERP, /* Interpreter. */ + LJ_VMST_C, /* C function. */ + LJ_VMST_GC, /* Garbage collector. */ + LJ_VMST_EXIT, /* Trace exit handler. */ + LJ_VMST_RECORD, /* Trace recorder. */ + LJ_VMST_OPT, /* Optimizer. */ + LJ_VMST_ASM, /* Assembler. */ + LJ_VMST__MAX +}; + +#define setvmstate(g, st) ((g)->vmstate = ~LJ_VMST_##st) + +/* Metamethods. ORDER MM */ +#ifdef LJ_HASFFI +#define MMDEF_FFI(_) _(new) +#else +#define MMDEF_FFI(_) +#endif + +#if LJ_52 || LJ_HASFFI +#define MMDEF_PAIRS(_) _(pairs) _(ipairs) +#else +#define MMDEF_PAIRS(_) +#define MM_pairs 255 +#define MM_ipairs 255 +#endif + +#define MMDEF(_) \ + _(index) _(newindex) _(gc) _(mode) _(eq) _(len) \ + /* Only the above (fast) metamethods are negative cached (max. 8). */ \ + _(lt) _(le) _(concat) _(call) \ + /* The following must be in ORDER ARITH. */ \ + _(add) _(sub) _(mul) _(div) _(mod) _(pow) _(unm) \ + /* The following are used in the standard libraries. */ \ + _(metatable) _(tostring) MMDEF_FFI(_) MMDEF_PAIRS(_) + +typedef enum { +#define MMENUM(name) MM_##name, +MMDEF(MMENUM) +#undef MMENUM + MM__MAX, + MM____ = MM__MAX, + MM_FAST = MM_len +} MMS; + +/* GC root IDs. */ +typedef enum { + GCROOT_MMNAME, /* Metamethod names. */ + GCROOT_MMNAME_LAST = GCROOT_MMNAME + MM__MAX-1, + GCROOT_BASEMT, /* Metatables for base types. */ + GCROOT_BASEMT_NUM = GCROOT_BASEMT + ~LJ_TNUMX, + GCROOT_IO_INPUT, /* Userdata for default I/O input file. */ + GCROOT_IO_OUTPUT, /* Userdata for default I/O output file. */ + GCROOT_MAX +} GCRootID; + +#define basemt_it(g, it) ((g)->gcroot[GCROOT_BASEMT+~(it)]) +#define basemt_obj(g, o) ((g)->gcroot[GCROOT_BASEMT+itypemap(o)]) +#define mmname_str(g, mm) (strref((g)->gcroot[GCROOT_MMNAME+(mm)])) + +typedef struct GCState { + GCSize total; /* Memory currently allocated. */ + GCSize threshold; /* Memory threshold. */ + uint8_t currentwhite; /* Current white color. */ + uint8_t state; /* GC state. */ + uint8_t nocdatafin; /* No cdata finalizer called. */ + uint8_t unused2; + MSize sweepstr; /* Sweep position in string table. */ + GCRef root; /* List of all collectable objects. */ + MRef sweep; /* Sweep position in root list. */ + GCRef gray; /* List of gray objects. */ + GCRef grayagain; /* List of objects for atomic traversal. */ + GCRef weak; /* List of weak tables (to be cleared). */ + GCRef mmudata; /* List of userdata (to be finalized). */ + GCSize debt; /* Debt (how much GC is behind schedule). */ + GCSize estimate; /* Estimate of memory actually in use. */ + MSize stepmul; /* Incremental GC step granularity. */ + MSize pause; /* Pause between successive GC cycles. */ +} GCState; + +/* Global state, shared by all threads of a Lua universe. */ +typedef struct global_State { + GCRef *strhash; /* String hash table (hash chain anchors). */ + MSize strmask; /* String hash mask (size of hash table - 1). */ + MSize strnum; /* Number of strings in hash table. */ + lua_Alloc allocf; /* Memory allocator. */ + void *allocd; /* Memory allocator data. */ + GCState gc; /* Garbage collector. */ + volatile int32_t vmstate; /* VM state or current JIT code trace number. */ + SBuf tmpbuf; /* Temporary string buffer. */ + GCstr strempty; /* Empty string. */ + uint8_t stremptyz; /* Zero terminator of empty string. */ + uint8_t hookmask; /* Hook mask. */ + uint8_t dispatchmode; /* Dispatch mode. */ + uint8_t vmevmask; /* VM event mask. */ + GCRef mainthref; /* Link to main thread. */ + TValue registrytv; /* Anchor for registry. */ + TValue tmptv, tmptv2; /* Temporary TValues. */ + Node nilnode; /* Fallback 1-element hash part (nil key and value). */ + GCupval uvhead; /* Head of double-linked list of all open upvalues. */ + int32_t hookcount; /* Instruction hook countdown. */ + int32_t hookcstart; /* Start count for instruction hook counter. */ + lua_Hook hookf; /* Hook function. */ + lua_CFunction wrapf; /* Wrapper for C function calls. */ + lua_CFunction panic; /* Called as a last resort for errors. */ + BCIns bc_cfunc_int; /* Bytecode for internal C function calls. */ + BCIns bc_cfunc_ext; /* Bytecode for external C function calls. */ + GCRef cur_L; /* Currently executing lua_State. */ + MRef jit_base; /* Current JIT code L->base or NULL. */ + MRef ctype_state; /* Pointer to C type state. */ + GCRef gcroot[GCROOT_MAX]; /* GC roots. */ +} global_State; + +#define mainthread(g) (&gcref(g->mainthref)->th) +#define niltv(L) \ + check_exp(tvisnil(&G(L)->nilnode.val), &G(L)->nilnode.val) +#define niltvg(g) \ + check_exp(tvisnil(&(g)->nilnode.val), &(g)->nilnode.val) + +/* Hook management. Hook event masks are defined in lua.h. */ +#define HOOK_EVENTMASK 0x0f +#define HOOK_ACTIVE 0x10 +#define HOOK_ACTIVE_SHIFT 4 +#define HOOK_VMEVENT 0x20 +#define HOOK_GC 0x40 +#define HOOK_PROFILE 0x80 +#define hook_active(g) ((g)->hookmask & HOOK_ACTIVE) +#define hook_enter(g) ((g)->hookmask |= HOOK_ACTIVE) +#define hook_entergc(g) ((g)->hookmask |= (HOOK_ACTIVE|HOOK_GC)) +#define hook_vmevent(g) ((g)->hookmask |= (HOOK_ACTIVE|HOOK_VMEVENT)) +#define hook_leave(g) ((g)->hookmask &= ~HOOK_ACTIVE) +#define hook_save(g) ((g)->hookmask & ~HOOK_EVENTMASK) +#define hook_restore(g, h) \ + ((g)->hookmask = ((g)->hookmask & HOOK_EVENTMASK) | (h)) + +/* Per-thread state object. */ +struct lua_State { + GCHeader; + uint8_t dummy_ffid; /* Fake FF_C for curr_funcisL() on dummy frames. */ + uint8_t status; /* Thread status. */ + MRef glref; /* Link to global state. */ + GCRef gclist; /* GC chain. */ + TValue *base; /* Base of currently executing function. */ + TValue *top; /* First free slot in the stack. */ + MRef maxstack; /* Last free slot in the stack. */ + MRef stack; /* Stack base. */ + GCRef openupval; /* List of open upvalues in the stack. */ + GCRef env; /* Thread environment (table of globals). */ + void *cframe; /* End of C stack frame chain. */ + MSize stacksize; /* True stack size (incl. LJ_STACK_EXTRA). */ +}; + +#define G(L) (mref(L->glref, global_State)) +#define registry(L) (&G(L)->registrytv) + +/* Macros to access the currently executing (Lua) function. */ +#if LJ_GC64 +#define curr_func(L) (&gcval(L->base-2)->fn) +#elif LJ_FR2 +#define curr_func(L) (&gcref((L->base-2)->gcr)->fn) +#else +#define curr_func(L) (&gcref((L->base-1)->fr.func)->fn) +#endif +#define curr_funcisL(L) (isluafunc(curr_func(L))) +#define curr_proto(L) (funcproto(curr_func(L))) +#define curr_topL(L) (L->base + curr_proto(L)->framesize) +#define curr_top(L) (curr_funcisL(L) ? curr_topL(L) : L->top) + +/* -- GC object definition and conversions -------------------------------- */ + +/* GC header for generic access to common fields of GC objects. */ +typedef struct GChead { + GCHeader; + uint8_t unused1; + uint8_t unused2; + GCRef env; + GCRef gclist; + GCRef metatable; +} GChead; + +/* The env field SHOULD be at the same offset for all GC objects. */ +LJ_STATIC_ASSERT(offsetof(GChead, env) == offsetof(GCfuncL, env)); +LJ_STATIC_ASSERT(offsetof(GChead, env) == offsetof(GCudata, env)); + +/* The metatable field MUST be at the same offset for all GC objects. */ +LJ_STATIC_ASSERT(offsetof(GChead, metatable) == offsetof(GCtab, metatable)); +LJ_STATIC_ASSERT(offsetof(GChead, metatable) == offsetof(GCudata, metatable)); + +/* The gclist field MUST be at the same offset for all GC objects. */ +LJ_STATIC_ASSERT(offsetof(GChead, gclist) == offsetof(lua_State, gclist)); +LJ_STATIC_ASSERT(offsetof(GChead, gclist) == offsetof(GCproto, gclist)); +LJ_STATIC_ASSERT(offsetof(GChead, gclist) == offsetof(GCfuncL, gclist)); +LJ_STATIC_ASSERT(offsetof(GChead, gclist) == offsetof(GCtab, gclist)); + +typedef union GCobj { + GChead gch; + GCstr str; + GCupval uv; + lua_State th; + GCproto pt; + GCfunc fn; + GCcdata cd; + GCtab tab; + GCudata ud; +} GCobj; + +/* Macros to convert a GCobj pointer into a specific value. */ +#define gco2str(o) check_exp((o)->gch.gct == ~LJ_TSTR, &(o)->str) +#define gco2uv(o) check_exp((o)->gch.gct == ~LJ_TUPVAL, &(o)->uv) +#define gco2th(o) check_exp((o)->gch.gct == ~LJ_TTHREAD, &(o)->th) +#define gco2pt(o) check_exp((o)->gch.gct == ~LJ_TPROTO, &(o)->pt) +#define gco2func(o) check_exp((o)->gch.gct == ~LJ_TFUNC, &(o)->fn) +#define gco2cd(o) check_exp((o)->gch.gct == ~LJ_TCDATA, &(o)->cd) +#define gco2tab(o) check_exp((o)->gch.gct == ~LJ_TTAB, &(o)->tab) +#define gco2ud(o) check_exp((o)->gch.gct == ~LJ_TUDATA, &(o)->ud) + +/* Macro to convert any collectable object into a GCobj pointer. */ +#define obj2gco(v) ((GCobj *)(v)) + +/* -- TValue getters/setters ---------------------------------------------- */ + +#ifdef LUA_USE_ASSERT +#include "lj_gc.h" +#endif + +/* Macros to test types. */ +#if LJ_GC64 +#define itype(o) ((uint32_t)((o)->it64 >> 47)) +#define tvisnil(o) ((o)->it64 == -1) +#else +#define itype(o) ((o)->it) +#define tvisnil(o) (itype(o) == LJ_TNIL) +#endif +#define tvisfalse(o) (itype(o) == LJ_TFALSE) +#define tvistrue(o) (itype(o) == LJ_TTRUE) +#define tvisbool(o) (tvisfalse(o) || tvistrue(o)) +#if LJ_64 && !LJ_GC64 +#define tvislightud(o) (((int32_t)itype(o) >> 15) == -2) +#else +#define tvislightud(o) (itype(o) == LJ_TLIGHTUD) +#endif +#define tvisstr(o) (itype(o) == LJ_TSTR) +#define tvisfunc(o) (itype(o) == LJ_TFUNC) +#define tvisthread(o) (itype(o) == LJ_TTHREAD) +#define tvisproto(o) (itype(o) == LJ_TPROTO) +#define tviscdata(o) (itype(o) == LJ_TCDATA) +#define tvistab(o) (itype(o) == LJ_TTAB) +#define tvisudata(o) (itype(o) == LJ_TUDATA) +#define tvisnumber(o) (itype(o) <= LJ_TISNUM) +#define tvisint(o) (LJ_DUALNUM && itype(o) == LJ_TISNUM) +#define tvisnum(o) (itype(o) < LJ_TISNUM) + +#define tvistruecond(o) (itype(o) < LJ_TISTRUECOND) +#define tvispri(o) (itype(o) >= LJ_TISPRI) +#define tvistabud(o) (itype(o) <= LJ_TISTABUD) /* && !tvisnum() */ +#define tvisgcv(o) ((itype(o) - LJ_TISGCV) > (LJ_TNUMX - LJ_TISGCV)) + +/* Special macros to test numbers for NaN, +0, -0, +1 and raw equality. */ +#define tvisnan(o) ((o)->n != (o)->n) +#if LJ_64 +#define tviszero(o) (((o)->u64 << 1) == 0) +#else +#define tviszero(o) (((o)->u32.lo | ((o)->u32.hi << 1)) == 0) +#endif +#define tvispzero(o) ((o)->u64 == 0) +#define tvismzero(o) ((o)->u64 == U64x(80000000,00000000)) +#define tvispone(o) ((o)->u64 == U64x(3ff00000,00000000)) +#define rawnumequal(o1, o2) ((o1)->u64 == (o2)->u64) + +/* Macros to convert type ids. */ +#if LJ_64 && !LJ_GC64 +#define itypemap(o) \ + (tvisnumber(o) ? ~LJ_TNUMX : tvislightud(o) ? ~LJ_TLIGHTUD : ~itype(o)) +#else +#define itypemap(o) (tvisnumber(o) ? ~LJ_TNUMX : ~itype(o)) +#endif + +/* Macros to get tagged values. */ +#if LJ_GC64 +#define gcval(o) ((GCobj *)(gcrefu((o)->gcr) & LJ_GCVMASK)) +#else +#define gcval(o) (gcref((o)->gcr)) +#endif +#define boolV(o) check_exp(tvisbool(o), (LJ_TFALSE - itype(o))) +#if LJ_64 +#define lightudV(o) \ + check_exp(tvislightud(o), (void *)((o)->u64 & U64x(00007fff,ffffffff))) +#else +#define lightudV(o) check_exp(tvislightud(o), gcrefp((o)->gcr, void)) +#endif +#define gcV(o) check_exp(tvisgcv(o), gcval(o)) +#define strV(o) check_exp(tvisstr(o), &gcval(o)->str) +#define funcV(o) check_exp(tvisfunc(o), &gcval(o)->fn) +#define threadV(o) check_exp(tvisthread(o), &gcval(o)->th) +#define protoV(o) check_exp(tvisproto(o), &gcval(o)->pt) +#define cdataV(o) check_exp(tviscdata(o), &gcval(o)->cd) +#define tabV(o) check_exp(tvistab(o), &gcval(o)->tab) +#define udataV(o) check_exp(tvisudata(o), &gcval(o)->ud) +#define numV(o) check_exp(tvisnum(o), (o)->n) +#define intV(o) check_exp(tvisint(o), (int32_t)(o)->i) + +/* Macros to set tagged values. */ +#if LJ_GC64 +#define setitype(o, i) ((o)->it = ((i) << 15)) +#define setnilV(o) ((o)->it64 = -1) +#define setpriV(o, x) ((o)->it64 = (int64_t)~((uint64_t)~(x)<<47)) +#define setboolV(o, x) ((o)->it64 = (int64_t)~((uint64_t)((x)+1)<<47)) +#else +#define setitype(o, i) ((o)->it = (i)) +#define setnilV(o) ((o)->it = LJ_TNIL) +#define setboolV(o, x) ((o)->it = LJ_TFALSE-(uint32_t)(x)) +#define setpriV(o, i) (setitype((o), (i))) +#endif + +static LJ_AINLINE void setlightudV(TValue *o, void *p) +{ +#if LJ_GC64 + o->u64 = (uint64_t)p | (((uint64_t)LJ_TLIGHTUD) << 47); +#elif LJ_64 + o->u64 = (uint64_t)p | (((uint64_t)0xffff) << 48); +#else + setgcrefp(o->gcr, p); setitype(o, LJ_TLIGHTUD); +#endif +} + +#if LJ_64 +#define checklightudptr(L, p) \ + (((uint64_t)(p) >> 47) ? (lj_err_msg(L, LJ_ERR_BADLU), NULL) : (p)) +#else +#define checklightudptr(L, p) (p) +#endif + +#if LJ_FR2 +#define contptr(f) ((void *)(f)) +#define setcont(o, f) ((o)->u64 = (uint64_t)(uintptr_t)contptr(f)) +#elif LJ_64 +#define contptr(f) \ + ((void *)(uintptr_t)(uint32_t)((intptr_t)(f) - (intptr_t)lj_vm_asm_begin)) +#define setcont(o, f) \ + ((o)->u64 = (uint64_t)(void *)(f) - (uint64_t)lj_vm_asm_begin) +#else +#define contptr(f) ((void *)(f)) +#define setcont(o, f) setlightudV((o), contptr(f)) +#endif + +#define tvchecklive(L, o) \ + UNUSED(L), lua_assert(!tvisgcv(o) || \ + ((~itype(o) == gcval(o)->gch.gct) && !isdead(G(L), gcval(o)))) + +static LJ_AINLINE void setgcVraw(TValue *o, GCobj *v, uint32_t itype) +{ +#if LJ_GC64 + setgcreft(o->gcr, v, itype); +#else + setgcref(o->gcr, v); setitype(o, itype); +#endif +} + +static LJ_AINLINE void setgcV(lua_State *L, TValue *o, GCobj *v, uint32_t it) +{ + setgcVraw(o, v, it); tvchecklive(L, o); +} + +#define define_setV(name, type, tag) \ +static LJ_AINLINE void name(lua_State *L, TValue *o, type *v) \ +{ \ + setgcV(L, o, obj2gco(v), tag); \ +} +define_setV(setstrV, GCstr, LJ_TSTR) +define_setV(setthreadV, lua_State, LJ_TTHREAD) +define_setV(setprotoV, GCproto, LJ_TPROTO) +define_setV(setfuncV, GCfunc, LJ_TFUNC) +define_setV(setcdataV, GCcdata, LJ_TCDATA) +define_setV(settabV, GCtab, LJ_TTAB) +define_setV(setudataV, GCudata, LJ_TUDATA) + +#define setnumV(o, x) ((o)->n = (x)) +#define setnanV(o) ((o)->u64 = U64x(fff80000,00000000)) +#define setpinfV(o) ((o)->u64 = U64x(7ff00000,00000000)) +#define setminfV(o) ((o)->u64 = U64x(fff00000,00000000)) + +static LJ_AINLINE void setintV(TValue *o, int32_t i) +{ +#if LJ_DUALNUM + o->i = (uint32_t)i; setitype(o, LJ_TISNUM); +#else + o->n = (lua_Number)i; +#endif +} + +static LJ_AINLINE void setint64V(TValue *o, int64_t i) +{ + if (LJ_DUALNUM && LJ_LIKELY(i == (int64_t)(int32_t)i)) + setintV(o, (int32_t)i); + else + setnumV(o, (lua_Number)i); +} + +#if LJ_64 +#define setintptrV(o, i) setint64V((o), (i)) +#else +#define setintptrV(o, i) setintV((o), (i)) +#endif + +/* Copy tagged values. */ +static LJ_AINLINE void copyTV(lua_State *L, TValue *o1, const TValue *o2) +{ + *o1 = *o2; tvchecklive(L, o1); +} + +/* -- Number to integer conversion ---------------------------------------- */ + +#if LJ_SOFTFP +LJ_ASMF int32_t lj_vm_tobit(double x); +#if LJ_TARGET_MIPS64 +LJ_ASMF int32_t lj_vm_tointg(double x); +#endif +#endif + +static LJ_AINLINE int32_t lj_num2bit(lua_Number n) +{ +#if LJ_SOFTFP + return lj_vm_tobit(n); +#else + TValue o; + o.n = n + 6755399441055744.0; /* 2^52 + 2^51 */ + return (int32_t)o.u32.lo; +#endif +} + +#define lj_num2int(n) ((int32_t)(n)) + +/* +** This must match the JIT backend behavior. In particular for archs +** that don't have a common hardware instruction for this conversion. +** Note that signed FP to unsigned int conversions have an undefined +** result and should never be relied upon in portable FFI code. +** See also: C99 or C11 standard, 6.3.1.4, footnote of (1). +*/ +static LJ_AINLINE uint64_t lj_num2u64(lua_Number n) +{ +#if LJ_TARGET_X86ORX64 || LJ_TARGET_MIPS + int64_t i = (int64_t)n; + if (i < 0) i = (int64_t)(n - 18446744073709551616.0); + return (uint64_t)i; +#else + return (uint64_t)n; +#endif +} + +static LJ_AINLINE int32_t numberVint(cTValue *o) +{ + if (LJ_LIKELY(tvisint(o))) + return intV(o); + else + return lj_num2int(numV(o)); +} + +static LJ_AINLINE lua_Number numberVnum(cTValue *o) +{ + if (LJ_UNLIKELY(tvisint(o))) + return (lua_Number)intV(o); + else + return numV(o); +} + +/* -- Miscellaneous object handling --------------------------------------- */ + +/* Names and maps for internal and external object tags. */ +LJ_DATA const char *const lj_obj_typename[1+LUA_TCDATA+1]; +LJ_DATA const char *const lj_obj_itypename[~LJ_TNUMX+1]; + +#define lj_typename(o) (lj_obj_itypename[itypemap(o)]) + +/* Compare two objects without calling metamethods. */ +LJ_FUNC int LJ_FASTCALL lj_obj_equal(cTValue *o1, cTValue *o2); +LJ_FUNC const void * LJ_FASTCALL lj_obj_ptr(cTValue *o); + +#endif diff --git a/lib/LuaJIT/lj_obj.o b/lib/LuaJIT/lj_obj.o new file mode 100644 index 0000000000000000000000000000000000000000..73b4a1e68c64259689a21fcda20ee4cda6567822 GIT binary patch literal 2768 zcmd^=O-vI}5XYxbDJohZ8c8sr>H$b}1vN%AkP<+|5Q7*EF(&P{ECRN>W&6s99>DY> z;ljZKi5HF@J#s91&?E8W(UURpq((!Cq0Ve)V0~#X#+#G8%=~}v&HLK--YyOdU#|)T zKr8?~u&;yyjD+^%s24`zC>(*6!QjfhVAqzq++8g07Ux=@?s&dsptJBTTpyYU8#No$ zzBl}$&>tQs^e-3Qgj)csDO&B_pwaT%zve!5?_vkDZ?R^jrYqDNeqQ+Ueii%AZ+};9 z%daAhyeI|D3JvUkMF+eJ;^3sD|=|rV{BFUAw1;Hi~460K;dz zJbNky3CBrUrVY8Q<&K-mgsgI>?1zxc%$g}sldffsL&kNK0~0wrsiqtolxrp}NP5kc znMhe+=h6ww1>1>WR?ec?{0BDJ*LOA2HhAl9d!)Z{A1T!3n*D%Tu&bp;-*zzV~TQ`DD2MBJ(BIarTg!jyZ{GBgsL`n#tkWF40&>zzxGMzV-@E)&Y9ntu4)@>S>GbHCm`lo#%yHav{ z{kOA@YWzIw8yc7V5smk-9@98JHJ-)U%Y0;AlN!ItdP(EASwGV_OIX#o?DLw&W!!a* z%eXH!F8lmSEse`}`Ji#x=LmDU!BoU9aUeV{6-IU;jqfoS z_=cDKq(sV^S73NAwvotY!EhZC)Uwm)L}{f=ubLvPG*V6y?}(9f(rL@aRNGOOfluAJ ztTL02IKi5XO}KPl{)shXFLZWlwBDkuH#J_b=~JjFUw_$rzxY2gsIA{xQPg7j+x)Yb zOMPCz;x`%pz8_*N~A{C`F=*R-gbtW-h7Nfl#+ZV^B0uDpxpY``9R6K zX#Mnel6+Y|%|{;_5v(yB;R(|{r+=27Pkrj&4iS82*hj)=`uZENu3Y{W&;N-pmqI=N EH#iF8=Kufz literal 0 HcmV?d00001 diff --git a/lib/LuaJIT/lj_obj_dyn.o b/lib/LuaJIT/lj_obj_dyn.o new file mode 100644 index 0000000000000000000000000000000000000000..73b4a1e68c64259689a21fcda20ee4cda6567822 GIT binary patch literal 2768 zcmd^=O-vI}5XYxbDJohZ8c8sr>H$b}1vN%AkP<+|5Q7*EF(&P{ECRN>W&6s99>DY> z;ljZKi5HF@J#s91&?E8W(UURpq((!Cq0Ve)V0~#X#+#G8%=~}v&HLK--YyOdU#|)T zKr8?~u&;yyjD+^%s24`zC>(*6!QjfhVAqzq++8g07Ux=@?s&dsptJBTTpyYU8#No$ zzBl}$&>tQs^e-3Qgj)csDO&B_pwaT%zve!5?_vkDZ?R^jrYqDNeqQ+Ueii%AZ+};9 z%daAhyeI|D3JvUkMF+eJ;^3sD|=|rV{BFUAw1;Hi~460K;dz zJbNky3CBrUrVY8Q<&K-mgsgI>?1zxc%$g}sldffsL&kNK0~0wrsiqtolxrp}NP5kc znMhe+=h6ww1>1>WR?ec?{0BDJ*LOA2HhAl9d!)Z{A1T!3n*D%Tu&bp;-*zzV~TQ`DD2MBJ(BIarTg!jyZ{GBgsL`n#tkWF40&>zzxGMzV-@E)&Y9ntu4)@>S>GbHCm`lo#%yHav{ z{kOA@YWzIw8yc7V5smk-9@98JHJ-)U%Y0;AlN!ItdP(EASwGV_OIX#o?DLw&W!!a* z%eXH!F8lmSEse`}`Ji#x=LmDU!BoU9aUeV{6-IU;jqfoS z_=cDKq(sV^S73NAwvotY!EhZC)Uwm)L}{f=ubLvPG*V6y?}(9f(rL@aRNGOOfluAJ ztTL02IKi5XO}KPl{)shXFLZWlwBDkuH#J_b=~JjFUw_$rzxY2gsIA{xQPg7j+x)Yb zOMPCz;x`%pz8_*N~A{C`F=*R-gbtW-h7Nfl#+ZV^B0uDpxpY``9R6K zX#Mnel6+Y|%|{;_5v(yB;R(|{r+=27Pkrj&4iS82*hj)=`uZENu3Y{W&;N-pmqI=N EH#iF8=Kufz literal 0 HcmV?d00001 diff --git a/lib/LuaJIT/lj_opt_dce.c b/lib/LuaJIT/lj_opt_dce.c new file mode 100644 index 0000000..2417f32 --- /dev/null +++ b/lib/LuaJIT/lj_opt_dce.c @@ -0,0 +1,78 @@ +/* +** DCE: Dead Code Elimination. Pre-LOOP only -- ASM already performs DCE. +** Copyright (C) 2005-2017 Mike Pall. See Copyright Notice in luajit.h +*/ + +#define lj_opt_dce_c +#define LUA_CORE + +#include "lj_obj.h" + +#if LJ_HASJIT + +#include "lj_ir.h" +#include "lj_jit.h" +#include "lj_iropt.h" + +/* Some local macros to save typing. Undef'd at the end. */ +#define IR(ref) (&J->cur.ir[(ref)]) + +/* Scan through all snapshots and mark all referenced instructions. */ +static void dce_marksnap(jit_State *J) +{ + SnapNo i, nsnap = J->cur.nsnap; + for (i = 0; i < nsnap; i++) { + SnapShot *snap = &J->cur.snap[i]; + SnapEntry *map = &J->cur.snapmap[snap->mapofs]; + MSize n, nent = snap->nent; + for (n = 0; n < nent; n++) { + IRRef ref = snap_ref(map[n]); + if (ref >= REF_FIRST) + irt_setmark(IR(ref)->t); + } + } +} + +/* Backwards propagate marks. Replace unused instructions with NOPs. */ +static void dce_propagate(jit_State *J) +{ + IRRef1 *pchain[IR__MAX]; + IRRef ins; + uint32_t i; + for (i = 0; i < IR__MAX; i++) pchain[i] = &J->chain[i]; + for (ins = J->cur.nins-1; ins >= REF_FIRST; ins--) { + IRIns *ir = IR(ins); + if (irt_ismarked(ir->t)) { + irt_clearmark(ir->t); + pchain[ir->o] = &ir->prev; + } else if (!ir_sideeff(ir)) { + *pchain[ir->o] = ir->prev; /* Reroute original instruction chain. */ + ir->t.irt = IRT_NIL; + ir->o = IR_NOP; /* Replace instruction with NOP. */ + ir->op1 = ir->op2 = 0; + ir->prev = 0; + continue; + } + if (ir->op1 >= REF_FIRST) irt_setmark(IR(ir->op1)->t); + if (ir->op2 >= REF_FIRST) irt_setmark(IR(ir->op2)->t); + } +} + +/* Dead Code Elimination. +** +** First backpropagate marks for all used instructions. Then replace +** the unused ones with a NOP. Note that compressing the IR to eliminate +** the NOPs does not pay off. +*/ +void lj_opt_dce(jit_State *J) +{ + if ((J->flags & JIT_F_OPT_DCE)) { + dce_marksnap(J); + dce_propagate(J); + memset(J->bpropcache, 0, sizeof(J->bpropcache)); /* Invalidate cache. */ + } +} + +#undef IR + +#endif diff --git a/lib/LuaJIT/lj_opt_dce.o b/lib/LuaJIT/lj_opt_dce.o new file mode 100644 index 0000000000000000000000000000000000000000..925ac0f8a0429111d8ba0d91d7dcefb51dc34a09 GIT binary patch literal 1888 zcmbtU-)j?D6h4_u+fhR&D@6o_Mk%{3=wSM?MpROpnwt{WBFeH)F>Mp8YfMciT19Bm z+1122P<`=V5CrkXM0Ovxwc1^a55)(IPYWVgsSk=!BuL^plY4O-;){NPoO8bOHr24yrdIQMZK_^p zb1&SiXL*p=ttBt2A5fV z?SU`*&wyI?DEUP!TMunpG^(+wN=U6JtaS8A!T*?YlA+>y|I-2gNZl zEHVilj4EdDu>3>1^?Y(>?aB@8SkAW1CmZ)Q-Y9A%+!`rX|D4s&veK`q+laY0uXg@HqX4xcS;IQFE>g;PbI}Fa zhqIJfU^)Ae6&Is{=ema(bDq_5qh7SQyfRj+*Zs2*^RLb803!cRdr=TjEpU|?uRiqk zRQt@UGsw)ncE4x0nekAWhj4%Y9$$C(mt#G?faceBHBzC2OV`CC;F|Qhws_i<8GP~0 z$ZzGo8p&|`{T6AhT|t$6yNN?A(bv&-uk^R&BC9EA_hfiiZ{~D2-An*Cewx9Jm7Hit zLbBDddgeiHXmy5Lwj0{M!dvV5g^y<8#no`)CnPpXcssfv!kPyur%%Pw<9cj#B%zG} ze#gc$>GAl9xSk-_$xJMj9<@{+MLCF}hQPZ3(3^h5eJ1#9yuFRRPM-Gx+QfyF9v?=j zXBu)$Xqm+6xTYs2bxfEgxDzpVbL2!AWuMF)dfo4dhXm1IE{}FZKog3Ed zjVaI34(vdVYJO|JpV!=l(hhO{MmveRIA6#;HqIWp-M$<)hsYDuVV>^{@8*QqLlH0L zC+tB~n$3TS4@BIH=1;#ekuT;?{W#A?3MM(J@P^kTM>U-J#5re=6y!MBONI^3`~QHx PX8Gc;Mp8YfMciT19Bm z+1122P<`=V5CrkXM0Ovxwc1^a55)(IPYWVgsSk=!BuL^plY4O-;){NPoO8bOHr24yrdIQMZK_^p zb1&SiXL*p=ttBt2A5fV z?SU`*&wyI?DEUP!TMunpG^(+wN=U6JtaS8A!T*?YlA+>y|I-2gNZl zEHVilj4EdDu>3>1^?Y(>?aB@8SkAW1CmZ)Q-Y9A%+!`rX|D4s&veK`q+laY0uXg@HqX4xcS;IQFE>g;PbI}Fa zhqIJfU^)Ae6&Is{=ema(bDq_5qh7SQyfRj+*Zs2*^RLb803!cRdr=TjEpU|?uRiqk zRQt@UGsw)ncE4x0nekAWhj4%Y9$$C(mt#G?faceBHBzC2OV`CC;F|Qhws_i<8GP~0 z$ZzGo8p&|`{T6AhT|t$6yNN?A(bv&-uk^R&BC9EA_hfiiZ{~D2-An*Cewx9Jm7Hit zLbBDddgeiHXmy5Lwj0{M!dvV5g^y<8#no`)CnPpXcssfv!kPyur%%Pw<9cj#B%zG} ze#gc$>GAl9xSk-_$xJMj9<@{+MLCF}hQPZ3(3^h5eJ1#9yuFRRPM-Gx+QfyF9v?=j zXBu)$Xqm+6xTYs2bxfEgxDzpVbL2!AWuMF)dfo4dhXm1IE{}FZKog3Ed zjVaI34(vdVYJO|JpV!=l(hhO{MmveRIA6#;HqIWp-M$<)hsYDuVV>^{@8*QqLlH0L zC+tB~n$3TS4@BIH=1;#ekuT;?{W#A?3MM(J@P^kTM>U-J#5re=6y!MBONI^3`~QHx PX8Gc; + +#include "lj_obj.h" + +#if LJ_HASJIT + +#include "lj_buf.h" +#include "lj_str.h" +#include "lj_tab.h" +#include "lj_ir.h" +#include "lj_jit.h" +#include "lj_ircall.h" +#include "lj_iropt.h" +#include "lj_trace.h" +#if LJ_HASFFI +#include "lj_ctype.h" +#include "lj_carith.h" +#endif +#include "lj_vm.h" +#include "lj_strscan.h" +#include "lj_strfmt.h" + +/* Here's a short description how the FOLD engine processes instructions: +** +** The FOLD engine receives a single instruction stored in fins (J->fold.ins). +** The instruction and its operands are used to select matching fold rules. +** These are applied iteratively until a fixed point is reached. +** +** The 8 bit opcode of the instruction itself plus the opcodes of the +** two instructions referenced by its operands form a 24 bit key +** 'ins left right' (unused operands -> 0, literals -> lowest 8 bits). +** +** This key is used for partial matching against the fold rules. The +** left/right operand fields of the key are successively masked with +** the 'any' wildcard, from most specific to least specific: +** +** ins left right +** ins any right +** ins left any +** ins any any +** +** The masked key is used to lookup a matching fold rule in a semi-perfect +** hash table. If a matching rule is found, the related fold function is run. +** Multiple rules can share the same fold function. A fold rule may return +** one of several special values: +** +** - NEXTFOLD means no folding was applied, because an additional test +** inside the fold function failed. Matching continues against less +** specific fold rules. Finally the instruction is passed on to CSE. +** +** - RETRYFOLD means the instruction was modified in-place. Folding is +** retried as if this instruction had just been received. +** +** All other return values are terminal actions -- no further folding is +** applied: +** +** - INTFOLD(i) returns a reference to the integer constant i. +** +** - LEFTFOLD and RIGHTFOLD return the left/right operand reference +** without emitting an instruction. +** +** - CSEFOLD and EMITFOLD pass the instruction directly to CSE or emit +** it without passing through any further optimizations. +** +** - FAILFOLD, DROPFOLD and CONDFOLD only apply to instructions which have +** no result (e.g. guarded assertions): FAILFOLD means the guard would +** always fail, i.e. the current trace is pointless. DROPFOLD means +** the guard is always true and has been eliminated. CONDFOLD is a +** shortcut for FAILFOLD + cond (i.e. drop if true, otherwise fail). +** +** - Any other return value is interpreted as an IRRef or TRef. This +** can be a reference to an existing or a newly created instruction. +** Only the least-significant 16 bits (IRRef1) are used to form a TRef +** which is finally returned to the caller. +** +** The FOLD engine receives instructions both from the trace recorder and +** substituted instructions from LOOP unrolling. This means all types +** of instructions may end up here, even though the recorder bypasses +** FOLD in some cases. Thus all loads, stores and allocations must have +** an any/any rule to avoid being passed on to CSE. +** +** Carefully read the following requirements before adding or modifying +** any fold rules: +** +** Requirement #1: All fold rules must preserve their destination type. +** +** Consistently use INTFOLD() (KINT result) or lj_ir_knum() (KNUM result). +** Never use lj_ir_knumint() which can have either a KINT or KNUM result. +** +** Requirement #2: Fold rules should not create *new* instructions which +** reference operands *across* PHIs. +** +** E.g. a RETRYFOLD with 'fins->op1 = fleft->op1' is invalid if the +** left operand is a PHI. Then fleft->op1 would point across the PHI +** frontier to an invariant instruction. Adding a PHI for this instruction +** would be counterproductive. The solution is to add a barrier which +** prevents folding across PHIs, i.e. 'PHIBARRIER(fleft)' in this case. +** The only exception is for recurrences with high latencies like +** repeated int->num->int conversions. +** +** One could relax this condition a bit if the referenced instruction is +** a PHI, too. But this often leads to worse code due to excessive +** register shuffling. +** +** Note: returning *existing* instructions (e.g. LEFTFOLD) is ok, though. +** Even returning fleft->op1 would be ok, because a new PHI will added, +** if needed. But again, this leads to excessive register shuffling and +** should be avoided. +** +** Requirement #3: The set of all fold rules must be monotonic to guarantee +** termination. +** +** The goal is optimization, so one primarily wants to add strength-reducing +** rules. This means eliminating an instruction or replacing an instruction +** with one or more simpler instructions. Don't add fold rules which point +** into the other direction. +** +** Some rules (like commutativity) do not directly reduce the strength of +** an instruction, but enable other fold rules (e.g. by moving constants +** to the right operand). These rules must be made unidirectional to avoid +** cycles. +** +** Rule of thumb: the trace recorder expands the IR and FOLD shrinks it. +*/ + +/* Some local macros to save typing. Undef'd at the end. */ +#define IR(ref) (&J->cur.ir[(ref)]) +#define fins (&J->fold.ins) +#define fleft (J->fold.left) +#define fright (J->fold.right) +#define knumleft (ir_knum(fleft)->n) +#define knumright (ir_knum(fright)->n) + +/* Pass IR on to next optimization in chain (FOLD). */ +#define emitir(ot, a, b) (lj_ir_set(J, (ot), (a), (b)), lj_opt_fold(J)) + +/* Fold function type. Fastcall on x86 significantly reduces their size. */ +typedef IRRef (LJ_FASTCALL *FoldFunc)(jit_State *J); + +/* Macros for the fold specs, so buildvm can recognize them. */ +#define LJFOLD(x) +#define LJFOLDX(x) +#define LJFOLDF(name) static TRef LJ_FASTCALL fold_##name(jit_State *J) +/* Note: They must be at the start of a line or buildvm ignores them! */ + +/* Barrier to prevent using operands across PHIs. */ +#define PHIBARRIER(ir) if (irt_isphi((ir)->t)) return NEXTFOLD + +/* Barrier to prevent folding across a GC step. +** GC steps can only happen at the head of a trace and at LOOP. +** And the GC is only driven forward if there's at least one allocation. +*/ +#define gcstep_barrier(J, ref) \ + ((ref) < J->chain[IR_LOOP] && \ + (J->chain[IR_SNEW] || J->chain[IR_XSNEW] || \ + J->chain[IR_TNEW] || J->chain[IR_TDUP] || \ + J->chain[IR_CNEW] || J->chain[IR_CNEWI] || \ + J->chain[IR_BUFSTR] || J->chain[IR_TOSTR] || J->chain[IR_CALLA])) + +/* -- Constant folding for FP numbers ------------------------------------- */ + +LJFOLD(ADD KNUM KNUM) +LJFOLD(SUB KNUM KNUM) +LJFOLD(MUL KNUM KNUM) +LJFOLD(DIV KNUM KNUM) +LJFOLD(ATAN2 KNUM KNUM) +LJFOLD(LDEXP KNUM KNUM) +LJFOLD(MIN KNUM KNUM) +LJFOLD(MAX KNUM KNUM) +LJFOLDF(kfold_numarith) +{ + lua_Number a = knumleft; + lua_Number b = knumright; + lua_Number y = lj_vm_foldarith(a, b, fins->o - IR_ADD); + return lj_ir_knum(J, y); +} + +LJFOLD(NEG KNUM FLOAD) +LJFOLD(ABS KNUM FLOAD) +LJFOLDF(kfold_numabsneg) +{ + lua_Number a = knumleft; + lua_Number y = lj_vm_foldarith(a, a, fins->o - IR_ADD); + return lj_ir_knum(J, y); +} + +LJFOLD(LDEXP KNUM KINT) +LJFOLDF(kfold_ldexp) +{ +#if LJ_TARGET_X86ORX64 + UNUSED(J); + return NEXTFOLD; +#else + return lj_ir_knum(J, ldexp(knumleft, fright->i)); +#endif +} + +LJFOLD(FPMATH KNUM any) +LJFOLDF(kfold_fpmath) +{ + lua_Number a = knumleft; + lua_Number y = lj_vm_foldfpm(a, fins->op2); + return lj_ir_knum(J, y); +} + +LJFOLD(POW KNUM KINT) +LJFOLDF(kfold_numpow) +{ + lua_Number a = knumleft; + lua_Number b = (lua_Number)fright->i; + lua_Number y = lj_vm_foldarith(a, b, IR_POW - IR_ADD); + return lj_ir_knum(J, y); +} + +/* Must not use kfold_kref for numbers (could be NaN). */ +LJFOLD(EQ KNUM KNUM) +LJFOLD(NE KNUM KNUM) +LJFOLD(LT KNUM KNUM) +LJFOLD(GE KNUM KNUM) +LJFOLD(LE KNUM KNUM) +LJFOLD(GT KNUM KNUM) +LJFOLD(ULT KNUM KNUM) +LJFOLD(UGE KNUM KNUM) +LJFOLD(ULE KNUM KNUM) +LJFOLD(UGT KNUM KNUM) +LJFOLDF(kfold_numcomp) +{ + return CONDFOLD(lj_ir_numcmp(knumleft, knumright, (IROp)fins->o)); +} + +/* -- Constant folding for 32 bit integers -------------------------------- */ + +static int32_t kfold_intop(int32_t k1, int32_t k2, IROp op) +{ + switch (op) { + case IR_ADD: k1 += k2; break; + case IR_SUB: k1 -= k2; break; + case IR_MUL: k1 *= k2; break; + case IR_MOD: k1 = lj_vm_modi(k1, k2); break; + case IR_NEG: k1 = -k1; break; + case IR_BAND: k1 &= k2; break; + case IR_BOR: k1 |= k2; break; + case IR_BXOR: k1 ^= k2; break; + case IR_BSHL: k1 <<= (k2 & 31); break; + case IR_BSHR: k1 = (int32_t)((uint32_t)k1 >> (k2 & 31)); break; + case IR_BSAR: k1 >>= (k2 & 31); break; + case IR_BROL: k1 = (int32_t)lj_rol((uint32_t)k1, (k2 & 31)); break; + case IR_BROR: k1 = (int32_t)lj_ror((uint32_t)k1, (k2 & 31)); break; + case IR_MIN: k1 = k1 < k2 ? k1 : k2; break; + case IR_MAX: k1 = k1 > k2 ? k1 : k2; break; + default: lua_assert(0); break; + } + return k1; +} + +LJFOLD(ADD KINT KINT) +LJFOLD(SUB KINT KINT) +LJFOLD(MUL KINT KINT) +LJFOLD(MOD KINT KINT) +LJFOLD(NEG KINT KINT) +LJFOLD(BAND KINT KINT) +LJFOLD(BOR KINT KINT) +LJFOLD(BXOR KINT KINT) +LJFOLD(BSHL KINT KINT) +LJFOLD(BSHR KINT KINT) +LJFOLD(BSAR KINT KINT) +LJFOLD(BROL KINT KINT) +LJFOLD(BROR KINT KINT) +LJFOLD(MIN KINT KINT) +LJFOLD(MAX KINT KINT) +LJFOLDF(kfold_intarith) +{ + return INTFOLD(kfold_intop(fleft->i, fright->i, (IROp)fins->o)); +} + +LJFOLD(ADDOV KINT KINT) +LJFOLD(SUBOV KINT KINT) +LJFOLD(MULOV KINT KINT) +LJFOLDF(kfold_intovarith) +{ + lua_Number n = lj_vm_foldarith((lua_Number)fleft->i, (lua_Number)fright->i, + fins->o - IR_ADDOV); + int32_t k = lj_num2int(n); + if (n != (lua_Number)k) + return FAILFOLD; + return INTFOLD(k); +} + +LJFOLD(BNOT KINT) +LJFOLDF(kfold_bnot) +{ + return INTFOLD(~fleft->i); +} + +LJFOLD(BSWAP KINT) +LJFOLDF(kfold_bswap) +{ + return INTFOLD((int32_t)lj_bswap((uint32_t)fleft->i)); +} + +LJFOLD(LT KINT KINT) +LJFOLD(GE KINT KINT) +LJFOLD(LE KINT KINT) +LJFOLD(GT KINT KINT) +LJFOLD(ULT KINT KINT) +LJFOLD(UGE KINT KINT) +LJFOLD(ULE KINT KINT) +LJFOLD(UGT KINT KINT) +LJFOLD(ABC KINT KINT) +LJFOLDF(kfold_intcomp) +{ + int32_t a = fleft->i, b = fright->i; + switch ((IROp)fins->o) { + case IR_LT: return CONDFOLD(a < b); + case IR_GE: return CONDFOLD(a >= b); + case IR_LE: return CONDFOLD(a <= b); + case IR_GT: return CONDFOLD(a > b); + case IR_ULT: return CONDFOLD((uint32_t)a < (uint32_t)b); + case IR_UGE: return CONDFOLD((uint32_t)a >= (uint32_t)b); + case IR_ULE: return CONDFOLD((uint32_t)a <= (uint32_t)b); + case IR_ABC: + case IR_UGT: return CONDFOLD((uint32_t)a > (uint32_t)b); + default: lua_assert(0); return FAILFOLD; + } +} + +LJFOLD(UGE any KINT) +LJFOLDF(kfold_intcomp0) +{ + if (fright->i == 0) + return DROPFOLD; + return NEXTFOLD; +} + +/* -- Constant folding for 64 bit integers -------------------------------- */ + +static uint64_t kfold_int64arith(uint64_t k1, uint64_t k2, IROp op) +{ + switch (op) { +#if LJ_HASFFI + case IR_ADD: k1 += k2; break; + case IR_SUB: k1 -= k2; break; + case IR_MUL: k1 *= k2; break; + case IR_BAND: k1 &= k2; break; + case IR_BOR: k1 |= k2; break; + case IR_BXOR: k1 ^= k2; break; + case IR_BSHL: k1 <<= (k2 & 63); break; + case IR_BSHR: k1 = (int32_t)((uint32_t)k1 >> (k2 & 63)); break; + case IR_BSAR: k1 >>= (k2 & 63); break; + case IR_BROL: k1 = (int32_t)lj_rol((uint32_t)k1, (k2 & 63)); break; + case IR_BROR: k1 = (int32_t)lj_ror((uint32_t)k1, (k2 & 63)); break; +#endif + default: UNUSED(k2); lua_assert(0); break; + } + return k1; +} + +LJFOLD(ADD KINT64 KINT64) +LJFOLD(SUB KINT64 KINT64) +LJFOLD(MUL KINT64 KINT64) +LJFOLD(BAND KINT64 KINT64) +LJFOLD(BOR KINT64 KINT64) +LJFOLD(BXOR KINT64 KINT64) +LJFOLDF(kfold_int64arith) +{ + return INT64FOLD(kfold_int64arith(ir_k64(fleft)->u64, + ir_k64(fright)->u64, (IROp)fins->o)); +} + +LJFOLD(DIV KINT64 KINT64) +LJFOLD(MOD KINT64 KINT64) +LJFOLD(POW KINT64 KINT64) +LJFOLDF(kfold_int64arith2) +{ +#if LJ_HASFFI + uint64_t k1 = ir_k64(fleft)->u64, k2 = ir_k64(fright)->u64; + if (irt_isi64(fins->t)) { + k1 = fins->o == IR_DIV ? lj_carith_divi64((int64_t)k1, (int64_t)k2) : + fins->o == IR_MOD ? lj_carith_modi64((int64_t)k1, (int64_t)k2) : + lj_carith_powi64((int64_t)k1, (int64_t)k2); + } else { + k1 = fins->o == IR_DIV ? lj_carith_divu64(k1, k2) : + fins->o == IR_MOD ? lj_carith_modu64(k1, k2) : + lj_carith_powu64(k1, k2); + } + return INT64FOLD(k1); +#else + UNUSED(J); lua_assert(0); return FAILFOLD; +#endif +} + +LJFOLD(BSHL KINT64 KINT) +LJFOLD(BSHR KINT64 KINT) +LJFOLD(BSAR KINT64 KINT) +LJFOLD(BROL KINT64 KINT) +LJFOLD(BROR KINT64 KINT) +LJFOLDF(kfold_int64shift) +{ +#if LJ_HASFFI + uint64_t k = ir_k64(fleft)->u64; + int32_t sh = (fright->i & 63); + return INT64FOLD(lj_carith_shift64(k, sh, fins->o - IR_BSHL)); +#else + UNUSED(J); lua_assert(0); return FAILFOLD; +#endif +} + +LJFOLD(BNOT KINT64) +LJFOLDF(kfold_bnot64) +{ +#if LJ_HASFFI + return INT64FOLD(~ir_k64(fleft)->u64); +#else + UNUSED(J); lua_assert(0); return FAILFOLD; +#endif +} + +LJFOLD(BSWAP KINT64) +LJFOLDF(kfold_bswap64) +{ +#if LJ_HASFFI + return INT64FOLD(lj_bswap64(ir_k64(fleft)->u64)); +#else + UNUSED(J); lua_assert(0); return FAILFOLD; +#endif +} + +LJFOLD(LT KINT64 KINT64) +LJFOLD(GE KINT64 KINT64) +LJFOLD(LE KINT64 KINT64) +LJFOLD(GT KINT64 KINT64) +LJFOLD(ULT KINT64 KINT64) +LJFOLD(UGE KINT64 KINT64) +LJFOLD(ULE KINT64 KINT64) +LJFOLD(UGT KINT64 KINT64) +LJFOLDF(kfold_int64comp) +{ +#if LJ_HASFFI + uint64_t a = ir_k64(fleft)->u64, b = ir_k64(fright)->u64; + switch ((IROp)fins->o) { + case IR_LT: return CONDFOLD((int64_t)a < (int64_t)b); + case IR_GE: return CONDFOLD((int64_t)a >= (int64_t)b); + case IR_LE: return CONDFOLD((int64_t)a <= (int64_t)b); + case IR_GT: return CONDFOLD((int64_t)a > (int64_t)b); + case IR_ULT: return CONDFOLD(a < b); + case IR_UGE: return CONDFOLD(a >= b); + case IR_ULE: return CONDFOLD(a <= b); + case IR_UGT: return CONDFOLD(a > b); + default: lua_assert(0); return FAILFOLD; + } +#else + UNUSED(J); lua_assert(0); return FAILFOLD; +#endif +} + +LJFOLD(UGE any KINT64) +LJFOLDF(kfold_int64comp0) +{ +#if LJ_HASFFI + if (ir_k64(fright)->u64 == 0) + return DROPFOLD; + return NEXTFOLD; +#else + UNUSED(J); lua_assert(0); return FAILFOLD; +#endif +} + +/* -- Constant folding for strings ---------------------------------------- */ + +LJFOLD(SNEW KKPTR KINT) +LJFOLDF(kfold_snew_kptr) +{ + GCstr *s = lj_str_new(J->L, (const char *)ir_kptr(fleft), (size_t)fright->i); + return lj_ir_kstr(J, s); +} + +LJFOLD(SNEW any KINT) +LJFOLDF(kfold_snew_empty) +{ + if (fright->i == 0) + return lj_ir_kstr(J, &J2G(J)->strempty); + return NEXTFOLD; +} + +LJFOLD(STRREF KGC KINT) +LJFOLDF(kfold_strref) +{ + GCstr *str = ir_kstr(fleft); + lua_assert((MSize)fright->i <= str->len); + return lj_ir_kkptr(J, (char *)strdata(str) + fright->i); +} + +LJFOLD(STRREF SNEW any) +LJFOLDF(kfold_strref_snew) +{ + PHIBARRIER(fleft); + if (irref_isk(fins->op2) && fright->i == 0) { + return fleft->op1; /* strref(snew(ptr, len), 0) ==> ptr */ + } else { + /* Reassociate: strref(snew(strref(str, a), len), b) ==> strref(str, a+b) */ + IRIns *ir = IR(fleft->op1); + if (ir->o == IR_STRREF) { + IRRef1 str = ir->op1; /* IRIns * is not valid across emitir. */ + PHIBARRIER(ir); + fins->op2 = emitir(IRTI(IR_ADD), ir->op2, fins->op2); /* Clobbers fins! */ + fins->op1 = str; + fins->ot = IRT(IR_STRREF, IRT_PGC); + return RETRYFOLD; + } + } + return NEXTFOLD; +} + +LJFOLD(CALLN CARG IRCALL_lj_str_cmp) +LJFOLDF(kfold_strcmp) +{ + if (irref_isk(fleft->op1) && irref_isk(fleft->op2)) { + GCstr *a = ir_kstr(IR(fleft->op1)); + GCstr *b = ir_kstr(IR(fleft->op2)); + return INTFOLD(lj_str_cmp(a, b)); + } + return NEXTFOLD; +} + +/* -- Constant folding and forwarding for buffers ------------------------- */ + +/* +** Buffer ops perform stores, but their effect is limited to the buffer +** itself. Also, buffer ops are chained: a use of an op implies a use of +** all other ops up the chain. Conversely, if an op is unused, all ops +** up the chain can go unsed. This largely eliminates the need to treat +** them as stores. +** +** Alas, treating them as normal (IRM_N) ops doesn't work, because they +** cannot be CSEd in isolation. CSE for IRM_N is implicitly done in LOOP +** or if FOLD is disabled. +** +** The compromise is to declare them as loads, emit them like stores and +** CSE whole chains manually when the BUFSTR is to be emitted. Any chain +** fragments left over from CSE are eliminated by DCE. +*/ + +/* BUFHDR is emitted like a store, see below. */ + +LJFOLD(BUFPUT BUFHDR BUFSTR) +LJFOLDF(bufput_append) +{ + /* New buffer, no other buffer op inbetween and same buffer? */ + if ((J->flags & JIT_F_OPT_FWD) && + !(fleft->op2 & IRBUFHDR_APPEND) && + fleft->prev == fright->op2 && + fleft->op1 == IR(fright->op2)->op1) { + IRRef ref = fins->op1; + IR(ref)->op2 = (fleft->op2 | IRBUFHDR_APPEND); /* Modify BUFHDR. */ + IR(ref)->op1 = fright->op1; + return ref; + } + return EMITFOLD; /* Always emit, CSE later. */ +} + +LJFOLD(BUFPUT any any) +LJFOLDF(bufput_kgc) +{ + if (LJ_LIKELY(J->flags & JIT_F_OPT_FOLD) && fright->o == IR_KGC) { + GCstr *s2 = ir_kstr(fright); + if (s2->len == 0) { /* Empty string? */ + return LEFTFOLD; + } else { + if (fleft->o == IR_BUFPUT && irref_isk(fleft->op2) && + !irt_isphi(fleft->t)) { /* Join two constant string puts in a row. */ + GCstr *s1 = ir_kstr(IR(fleft->op2)); + IRRef kref = lj_ir_kstr(J, lj_buf_cat2str(J->L, s1, s2)); + /* lj_ir_kstr() may realloc the IR and invalidates any IRIns *. */ + IR(fins->op1)->op2 = kref; /* Modify previous BUFPUT. */ + return fins->op1; + } + } + } + return EMITFOLD; /* Always emit, CSE later. */ +} + +LJFOLD(BUFSTR any any) +LJFOLDF(bufstr_kfold_cse) +{ + lua_assert(fleft->o == IR_BUFHDR || fleft->o == IR_BUFPUT || + fleft->o == IR_CALLL); + if (LJ_LIKELY(J->flags & JIT_F_OPT_FOLD)) { + if (fleft->o == IR_BUFHDR) { /* No put operations? */ + if (!(fleft->op2 & IRBUFHDR_APPEND)) /* Empty buffer? */ + return lj_ir_kstr(J, &J2G(J)->strempty); + fins->op1 = fleft->op1; + fins->op2 = fleft->prev; /* Relies on checks in bufput_append. */ + return CSEFOLD; + } else if (fleft->o == IR_BUFPUT) { + IRIns *irb = IR(fleft->op1); + if (irb->o == IR_BUFHDR && !(irb->op2 & IRBUFHDR_APPEND)) + return fleft->op2; /* Shortcut for a single put operation. */ + } + } + /* Try to CSE the whole chain. */ + if (LJ_LIKELY(J->flags & JIT_F_OPT_CSE)) { + IRRef ref = J->chain[IR_BUFSTR]; + while (ref) { + IRIns *irs = IR(ref), *ira = fleft, *irb = IR(irs->op1); + while (ira->o == irb->o && ira->op2 == irb->op2) { + lua_assert(ira->o == IR_BUFHDR || ira->o == IR_BUFPUT || + ira->o == IR_CALLL || ira->o == IR_CARG); + if (ira->o == IR_BUFHDR && !(ira->op2 & IRBUFHDR_APPEND)) + return ref; /* CSE succeeded. */ + if (ira->o == IR_CALLL && ira->op2 == IRCALL_lj_buf_puttab) + break; + ira = IR(ira->op1); + irb = IR(irb->op1); + } + ref = irs->prev; + } + } + return EMITFOLD; /* No CSE possible. */ +} + +LJFOLD(CALLL CARG IRCALL_lj_buf_putstr_reverse) +LJFOLD(CALLL CARG IRCALL_lj_buf_putstr_upper) +LJFOLD(CALLL CARG IRCALL_lj_buf_putstr_lower) +LJFOLD(CALLL CARG IRCALL_lj_strfmt_putquoted) +LJFOLDF(bufput_kfold_op) +{ + if (irref_isk(fleft->op2)) { + const CCallInfo *ci = &lj_ir_callinfo[fins->op2]; + SBuf *sb = lj_buf_tmp_(J->L); + sb = ((SBuf * (LJ_FASTCALL *)(SBuf *, GCstr *))ci->func)(sb, + ir_kstr(IR(fleft->op2))); + fins->o = IR_BUFPUT; + fins->op1 = fleft->op1; + fins->op2 = lj_ir_kstr(J, lj_buf_tostr(sb)); + return RETRYFOLD; + } + return EMITFOLD; /* Always emit, CSE later. */ +} + +LJFOLD(CALLL CARG IRCALL_lj_buf_putstr_rep) +LJFOLDF(bufput_kfold_rep) +{ + if (irref_isk(fleft->op2)) { + IRIns *irc = IR(fleft->op1); + if (irref_isk(irc->op2)) { + SBuf *sb = lj_buf_tmp_(J->L); + sb = lj_buf_putstr_rep(sb, ir_kstr(IR(irc->op2)), IR(fleft->op2)->i); + fins->o = IR_BUFPUT; + fins->op1 = irc->op1; + fins->op2 = lj_ir_kstr(J, lj_buf_tostr(sb)); + return RETRYFOLD; + } + } + return EMITFOLD; /* Always emit, CSE later. */ +} + +LJFOLD(CALLL CARG IRCALL_lj_strfmt_putfxint) +LJFOLD(CALLL CARG IRCALL_lj_strfmt_putfnum_int) +LJFOLD(CALLL CARG IRCALL_lj_strfmt_putfnum_uint) +LJFOLD(CALLL CARG IRCALL_lj_strfmt_putfnum) +LJFOLD(CALLL CARG IRCALL_lj_strfmt_putfstr) +LJFOLD(CALLL CARG IRCALL_lj_strfmt_putfchar) +LJFOLDF(bufput_kfold_fmt) +{ + IRIns *irc = IR(fleft->op1); + lua_assert(irref_isk(irc->op2)); /* SFormat must be const. */ + if (irref_isk(fleft->op2)) { + SFormat sf = (SFormat)IR(irc->op2)->i; + IRIns *ira = IR(fleft->op2); + SBuf *sb = lj_buf_tmp_(J->L); + switch (fins->op2) { + case IRCALL_lj_strfmt_putfxint: + sb = lj_strfmt_putfxint(sb, sf, ir_k64(ira)->u64); + break; + case IRCALL_lj_strfmt_putfstr: + sb = lj_strfmt_putfstr(sb, sf, ir_kstr(ira)); + break; + case IRCALL_lj_strfmt_putfchar: + sb = lj_strfmt_putfchar(sb, sf, ira->i); + break; + case IRCALL_lj_strfmt_putfnum_int: + case IRCALL_lj_strfmt_putfnum_uint: + case IRCALL_lj_strfmt_putfnum: + default: { + const CCallInfo *ci = &lj_ir_callinfo[fins->op2]; + sb = ((SBuf * (*)(SBuf *, SFormat, lua_Number))ci->func)(sb, sf, + ir_knum(ira)->n); + break; + } + } + fins->o = IR_BUFPUT; + fins->op1 = irc->op1; + fins->op2 = lj_ir_kstr(J, lj_buf_tostr(sb)); + return RETRYFOLD; + } + return EMITFOLD; /* Always emit, CSE later. */ +} + +/* -- Constant folding of pointer arithmetic ------------------------------ */ + +LJFOLD(ADD KGC KINT) +LJFOLD(ADD KGC KINT64) +LJFOLDF(kfold_add_kgc) +{ + GCobj *o = ir_kgc(fleft); +#if LJ_64 + ptrdiff_t ofs = (ptrdiff_t)ir_kint64(fright)->u64; +#else + ptrdiff_t ofs = fright->i; +#endif +#if LJ_HASFFI + if (irt_iscdata(fleft->t)) { + CType *ct = ctype_raw(ctype_ctsG(J2G(J)), gco2cd(o)->ctypeid); + if (ctype_isnum(ct->info) || ctype_isenum(ct->info) || + ctype_isptr(ct->info) || ctype_isfunc(ct->info) || + ctype_iscomplex(ct->info) || ctype_isvector(ct->info)) + return lj_ir_kkptr(J, (char *)o + ofs); + } +#endif + return lj_ir_kptr(J, (char *)o + ofs); +} + +LJFOLD(ADD KPTR KINT) +LJFOLD(ADD KPTR KINT64) +LJFOLD(ADD KKPTR KINT) +LJFOLD(ADD KKPTR KINT64) +LJFOLDF(kfold_add_kptr) +{ + void *p = ir_kptr(fleft); +#if LJ_64 + ptrdiff_t ofs = (ptrdiff_t)ir_kint64(fright)->u64; +#else + ptrdiff_t ofs = fright->i; +#endif + return lj_ir_kptr_(J, fleft->o, (char *)p + ofs); +} + +LJFOLD(ADD any KGC) +LJFOLD(ADD any KPTR) +LJFOLD(ADD any KKPTR) +LJFOLDF(kfold_add_kright) +{ + if (fleft->o == IR_KINT || fleft->o == IR_KINT64) { + IRRef1 tmp = fins->op1; fins->op1 = fins->op2; fins->op2 = tmp; + return RETRYFOLD; + } + return NEXTFOLD; +} + +/* -- Constant folding of conversions ------------------------------------- */ + +LJFOLD(TOBIT KNUM KNUM) +LJFOLDF(kfold_tobit) +{ + return INTFOLD(lj_num2bit(knumleft)); +} + +LJFOLD(CONV KINT IRCONV_NUM_INT) +LJFOLDF(kfold_conv_kint_num) +{ + return lj_ir_knum(J, (lua_Number)fleft->i); +} + +LJFOLD(CONV KINT IRCONV_NUM_U32) +LJFOLDF(kfold_conv_kintu32_num) +{ + return lj_ir_knum(J, (lua_Number)(uint32_t)fleft->i); +} + +LJFOLD(CONV KINT IRCONV_INT_I8) +LJFOLD(CONV KINT IRCONV_INT_U8) +LJFOLD(CONV KINT IRCONV_INT_I16) +LJFOLD(CONV KINT IRCONV_INT_U16) +LJFOLDF(kfold_conv_kint_ext) +{ + int32_t k = fleft->i; + if ((fins->op2 & IRCONV_SRCMASK) == IRT_I8) k = (int8_t)k; + else if ((fins->op2 & IRCONV_SRCMASK) == IRT_U8) k = (uint8_t)k; + else if ((fins->op2 & IRCONV_SRCMASK) == IRT_I16) k = (int16_t)k; + else k = (uint16_t)k; + return INTFOLD(k); +} + +LJFOLD(CONV KINT IRCONV_I64_INT) +LJFOLD(CONV KINT IRCONV_U64_INT) +LJFOLD(CONV KINT IRCONV_I64_U32) +LJFOLD(CONV KINT IRCONV_U64_U32) +LJFOLDF(kfold_conv_kint_i64) +{ + if ((fins->op2 & IRCONV_SEXT)) + return INT64FOLD((uint64_t)(int64_t)fleft->i); + else + return INT64FOLD((uint64_t)(int64_t)(uint32_t)fleft->i); +} + +LJFOLD(CONV KINT64 IRCONV_NUM_I64) +LJFOLDF(kfold_conv_kint64_num_i64) +{ + return lj_ir_knum(J, (lua_Number)(int64_t)ir_kint64(fleft)->u64); +} + +LJFOLD(CONV KINT64 IRCONV_NUM_U64) +LJFOLDF(kfold_conv_kint64_num_u64) +{ + return lj_ir_knum(J, (lua_Number)ir_kint64(fleft)->u64); +} + +LJFOLD(CONV KINT64 IRCONV_INT_I64) +LJFOLD(CONV KINT64 IRCONV_U32_I64) +LJFOLDF(kfold_conv_kint64_int_i64) +{ + return INTFOLD((int32_t)ir_kint64(fleft)->u64); +} + +LJFOLD(CONV KNUM IRCONV_INT_NUM) +LJFOLDF(kfold_conv_knum_int_num) +{ + lua_Number n = knumleft; + int32_t k = lj_num2int(n); + if (irt_isguard(fins->t) && n != (lua_Number)k) { + /* We're about to create a guard which always fails, like CONV +1.5. + ** Some pathological loops cause this during LICM, e.g.: + ** local x,k,t = 0,1.5,{1,[1.5]=2} + ** for i=1,200 do x = x+ t[k]; k = k == 1 and 1.5 or 1 end + ** assert(x == 300) + */ + return FAILFOLD; + } + return INTFOLD(k); +} + +LJFOLD(CONV KNUM IRCONV_U32_NUM) +LJFOLDF(kfold_conv_knum_u32_num) +{ +#ifdef _MSC_VER + { /* Workaround for MSVC bug. */ + volatile uint32_t u = (uint32_t)knumleft; + return INTFOLD((int32_t)u); + } +#else + return INTFOLD((int32_t)(uint32_t)knumleft); +#endif +} + +LJFOLD(CONV KNUM IRCONV_I64_NUM) +LJFOLDF(kfold_conv_knum_i64_num) +{ + return INT64FOLD((uint64_t)(int64_t)knumleft); +} + +LJFOLD(CONV KNUM IRCONV_U64_NUM) +LJFOLDF(kfold_conv_knum_u64_num) +{ + return INT64FOLD(lj_num2u64(knumleft)); +} + +LJFOLD(TOSTR KNUM any) +LJFOLDF(kfold_tostr_knum) +{ + return lj_ir_kstr(J, lj_strfmt_num(J->L, ir_knum(fleft))); +} + +LJFOLD(TOSTR KINT any) +LJFOLDF(kfold_tostr_kint) +{ + return lj_ir_kstr(J, fins->op2 == IRTOSTR_INT ? + lj_strfmt_int(J->L, fleft->i) : + lj_strfmt_char(J->L, fleft->i)); +} + +LJFOLD(STRTO KGC) +LJFOLDF(kfold_strto) +{ + TValue n; + if (lj_strscan_num(ir_kstr(fleft), &n)) + return lj_ir_knum(J, numV(&n)); + return FAILFOLD; +} + +/* -- Constant folding of equality checks --------------------------------- */ + +/* Don't constant-fold away FLOAD checks against KNULL. */ +LJFOLD(EQ FLOAD KNULL) +LJFOLD(NE FLOAD KNULL) +LJFOLDX(lj_opt_cse) + +/* But fold all other KNULL compares, since only KNULL is equal to KNULL. */ +LJFOLD(EQ any KNULL) +LJFOLD(NE any KNULL) +LJFOLD(EQ KNULL any) +LJFOLD(NE KNULL any) +LJFOLD(EQ KINT KINT) /* Constants are unique, so same refs <==> same value. */ +LJFOLD(NE KINT KINT) +LJFOLD(EQ KINT64 KINT64) +LJFOLD(NE KINT64 KINT64) +LJFOLD(EQ KGC KGC) +LJFOLD(NE KGC KGC) +LJFOLDF(kfold_kref) +{ + return CONDFOLD((fins->op1 == fins->op2) ^ (fins->o == IR_NE)); +} + +/* -- Algebraic shortcuts ------------------------------------------------- */ + +LJFOLD(FPMATH FPMATH IRFPM_FLOOR) +LJFOLD(FPMATH FPMATH IRFPM_CEIL) +LJFOLD(FPMATH FPMATH IRFPM_TRUNC) +LJFOLDF(shortcut_round) +{ + IRFPMathOp op = (IRFPMathOp)fleft->op2; + if (op == IRFPM_FLOOR || op == IRFPM_CEIL || op == IRFPM_TRUNC) + return LEFTFOLD; /* round(round_left(x)) = round_left(x) */ + return NEXTFOLD; +} + +LJFOLD(ABS ABS FLOAD) +LJFOLDF(shortcut_left) +{ + return LEFTFOLD; /* f(g(x)) ==> g(x) */ +} + +LJFOLD(ABS NEG FLOAD) +LJFOLDF(shortcut_dropleft) +{ + PHIBARRIER(fleft); + fins->op1 = fleft->op1; /* abs(neg(x)) ==> abs(x) */ + return RETRYFOLD; +} + +/* Note: no safe shortcuts with STRTO and TOSTR ("1e2" ==> +100 ==> "100"). */ +LJFOLD(NEG NEG any) +LJFOLD(BNOT BNOT) +LJFOLD(BSWAP BSWAP) +LJFOLDF(shortcut_leftleft) +{ + PHIBARRIER(fleft); /* See above. Fold would be ok, but not beneficial. */ + return fleft->op1; /* f(g(x)) ==> x */ +} + +/* -- FP algebraic simplifications ---------------------------------------- */ + +/* FP arithmetic is tricky -- there's not much to simplify. +** Please note the following common pitfalls before sending "improvements": +** x+0 ==> x is INVALID for x=-0 +** 0-x ==> -x is INVALID for x=+0 +** x*0 ==> 0 is INVALID for x=-0, x=+-Inf or x=NaN +*/ + +LJFOLD(ADD NEG any) +LJFOLDF(simplify_numadd_negx) +{ + PHIBARRIER(fleft); + fins->o = IR_SUB; /* (-a) + b ==> b - a */ + fins->op1 = fins->op2; + fins->op2 = fleft->op1; + return RETRYFOLD; +} + +LJFOLD(ADD any NEG) +LJFOLDF(simplify_numadd_xneg) +{ + PHIBARRIER(fright); + fins->o = IR_SUB; /* a + (-b) ==> a - b */ + fins->op2 = fright->op1; + return RETRYFOLD; +} + +LJFOLD(SUB any KNUM) +LJFOLDF(simplify_numsub_k) +{ + lua_Number n = knumright; + if (n == 0.0) /* x - (+-0) ==> x */ + return LEFTFOLD; + return NEXTFOLD; +} + +LJFOLD(SUB NEG KNUM) +LJFOLDF(simplify_numsub_negk) +{ + PHIBARRIER(fleft); + fins->op2 = fleft->op1; /* (-x) - k ==> (-k) - x */ + fins->op1 = (IRRef1)lj_ir_knum(J, -knumright); + return RETRYFOLD; +} + +LJFOLD(SUB any NEG) +LJFOLDF(simplify_numsub_xneg) +{ + PHIBARRIER(fright); + fins->o = IR_ADD; /* a - (-b) ==> a + b */ + fins->op2 = fright->op1; + return RETRYFOLD; +} + +LJFOLD(MUL any KNUM) +LJFOLD(DIV any KNUM) +LJFOLDF(simplify_nummuldiv_k) +{ + lua_Number n = knumright; + if (n == 1.0) { /* x o 1 ==> x */ + return LEFTFOLD; + } else if (n == -1.0) { /* x o -1 ==> -x */ + IRRef op1 = fins->op1; + fins->op2 = (IRRef1)lj_ir_ksimd(J, LJ_KSIMD_NEG); /* Modifies fins. */ + fins->op1 = op1; + fins->o = IR_NEG; + return RETRYFOLD; + } else if (fins->o == IR_MUL && n == 2.0) { /* x * 2 ==> x + x */ + fins->o = IR_ADD; + fins->op2 = fins->op1; + return RETRYFOLD; + } else if (fins->o == IR_DIV) { /* x / 2^k ==> x * 2^-k */ + uint64_t u = ir_knum(fright)->u64; + uint32_t ex = ((uint32_t)(u >> 52) & 0x7ff); + if ((u & U64x(000fffff,ffffffff)) == 0 && ex - 1 < 0x7fd) { + u = (u & ((uint64_t)1 << 63)) | ((uint64_t)(0x7fe - ex) << 52); + fins->o = IR_MUL; /* Multiply by exact reciprocal. */ + fins->op2 = lj_ir_knum_u64(J, u); + return RETRYFOLD; + } + } + return NEXTFOLD; +} + +LJFOLD(MUL NEG KNUM) +LJFOLD(DIV NEG KNUM) +LJFOLDF(simplify_nummuldiv_negk) +{ + PHIBARRIER(fleft); + fins->op1 = fleft->op1; /* (-a) o k ==> a o (-k) */ + fins->op2 = (IRRef1)lj_ir_knum(J, -knumright); + return RETRYFOLD; +} + +LJFOLD(MUL NEG NEG) +LJFOLD(DIV NEG NEG) +LJFOLDF(simplify_nummuldiv_negneg) +{ + PHIBARRIER(fleft); + PHIBARRIER(fright); + fins->op1 = fleft->op1; /* (-a) o (-b) ==> a o b */ + fins->op2 = fright->op1; + return RETRYFOLD; +} + +LJFOLD(POW any KINT) +LJFOLDF(simplify_numpow_xk) +{ + int32_t k = fright->i; + TRef ref = fins->op1; + if (k == 0) /* x ^ 0 ==> 1 */ + return lj_ir_knum_one(J); /* Result must be a number, not an int. */ + if (k == 1) /* x ^ 1 ==> x */ + return LEFTFOLD; + if ((uint32_t)(k+65536) > 2*65536u) /* Limit code explosion. */ + return NEXTFOLD; + if (k < 0) { /* x ^ (-k) ==> (1/x) ^ k. */ + ref = emitir(IRTN(IR_DIV), lj_ir_knum_one(J), ref); + k = -k; + } + /* Unroll x^k for 1 <= k <= 65536. */ + for (; (k & 1) == 0; k >>= 1) /* Handle leading zeros. */ + ref = emitir(IRTN(IR_MUL), ref, ref); + if ((k >>= 1) != 0) { /* Handle trailing bits. */ + TRef tmp = emitir(IRTN(IR_MUL), ref, ref); + for (; k != 1; k >>= 1) { + if (k & 1) + ref = emitir(IRTN(IR_MUL), ref, tmp); + tmp = emitir(IRTN(IR_MUL), tmp, tmp); + } + ref = emitir(IRTN(IR_MUL), ref, tmp); + } + return ref; +} + +LJFOLD(POW KNUM any) +LJFOLDF(simplify_numpow_kx) +{ + lua_Number n = knumleft; + if (n == 2.0) { /* 2.0 ^ i ==> ldexp(1.0, tonum(i)) */ + fins->o = IR_CONV; +#if LJ_TARGET_X86ORX64 + fins->op1 = fins->op2; + fins->op2 = IRCONV_NUM_INT; + fins->op2 = (IRRef1)lj_opt_fold(J); +#endif + fins->op1 = (IRRef1)lj_ir_knum_one(J); + fins->o = IR_LDEXP; + return RETRYFOLD; + } + return NEXTFOLD; +} + +/* -- Simplify conversions ------------------------------------------------ */ + +LJFOLD(CONV CONV IRCONV_NUM_INT) /* _NUM */ +LJFOLDF(shortcut_conv_num_int) +{ + PHIBARRIER(fleft); + /* Only safe with a guarded conversion to int. */ + if ((fleft->op2 & IRCONV_SRCMASK) == IRT_NUM && irt_isguard(fleft->t)) + return fleft->op1; /* f(g(x)) ==> x */ + return NEXTFOLD; +} + +LJFOLD(CONV CONV IRCONV_INT_NUM) /* _INT */ +LJFOLD(CONV CONV IRCONV_U32_NUM) /* _U32*/ +LJFOLDF(simplify_conv_int_num) +{ + /* Fold even across PHI to avoid expensive num->int conversions in loop. */ + if ((fleft->op2 & IRCONV_SRCMASK) == + ((fins->op2 & IRCONV_DSTMASK) >> IRCONV_DSH)) + return fleft->op1; + return NEXTFOLD; +} + +LJFOLD(CONV CONV IRCONV_I64_NUM) /* _INT or _U32 */ +LJFOLD(CONV CONV IRCONV_U64_NUM) /* _INT or _U32 */ +LJFOLDF(simplify_conv_i64_num) +{ + PHIBARRIER(fleft); + if ((fleft->op2 & IRCONV_SRCMASK) == IRT_INT) { + /* Reduce to a sign-extension. */ + fins->op1 = fleft->op1; + fins->op2 = ((IRT_I64<<5)|IRT_INT|IRCONV_SEXT); + return RETRYFOLD; + } else if ((fleft->op2 & IRCONV_SRCMASK) == IRT_U32) { +#if LJ_TARGET_X64 + return fleft->op1; +#else + /* Reduce to a zero-extension. */ + fins->op1 = fleft->op1; + fins->op2 = (IRT_I64<<5)|IRT_U32; + return RETRYFOLD; +#endif + } + return NEXTFOLD; +} + +LJFOLD(CONV CONV IRCONV_INT_I64) /* _INT or _U32 */ +LJFOLD(CONV CONV IRCONV_INT_U64) /* _INT or _U32 */ +LJFOLD(CONV CONV IRCONV_U32_I64) /* _INT or _U32 */ +LJFOLD(CONV CONV IRCONV_U32_U64) /* _INT or _U32 */ +LJFOLDF(simplify_conv_int_i64) +{ + int src; + PHIBARRIER(fleft); + src = (fleft->op2 & IRCONV_SRCMASK); + if (src == IRT_INT || src == IRT_U32) { + if (src == ((fins->op2 & IRCONV_DSTMASK) >> IRCONV_DSH)) { + return fleft->op1; + } else { + fins->op2 = ((fins->op2 & IRCONV_DSTMASK) | src); + fins->op1 = fleft->op1; + return RETRYFOLD; + } + } + return NEXTFOLD; +} + +LJFOLD(CONV CONV IRCONV_FLOAT_NUM) /* _FLOAT */ +LJFOLDF(simplify_conv_flt_num) +{ + PHIBARRIER(fleft); + if ((fleft->op2 & IRCONV_SRCMASK) == IRT_FLOAT) + return fleft->op1; + return NEXTFOLD; +} + +/* Shortcut TOBIT + IRT_NUM <- IRT_INT/IRT_U32 conversion. */ +LJFOLD(TOBIT CONV KNUM) +LJFOLDF(simplify_tobit_conv) +{ + /* Fold even across PHI to avoid expensive num->int conversions in loop. */ + if ((fleft->op2 & IRCONV_SRCMASK) == IRT_INT) { + lua_assert(irt_isnum(fleft->t)); + return fleft->op1; + } else if ((fleft->op2 & IRCONV_SRCMASK) == IRT_U32) { + lua_assert(irt_isnum(fleft->t)); + fins->o = IR_CONV; + fins->op1 = fleft->op1; + fins->op2 = (IRT_INT<<5)|IRT_U32; + return RETRYFOLD; + } + return NEXTFOLD; +} + +/* Shortcut floor/ceil/round + IRT_NUM <- IRT_INT/IRT_U32 conversion. */ +LJFOLD(FPMATH CONV IRFPM_FLOOR) +LJFOLD(FPMATH CONV IRFPM_CEIL) +LJFOLD(FPMATH CONV IRFPM_TRUNC) +LJFOLDF(simplify_floor_conv) +{ + if ((fleft->op2 & IRCONV_SRCMASK) == IRT_INT || + (fleft->op2 & IRCONV_SRCMASK) == IRT_U32) + return LEFTFOLD; + return NEXTFOLD; +} + +/* Strength reduction of widening. */ +LJFOLD(CONV any IRCONV_I64_INT) +LJFOLD(CONV any IRCONV_U64_INT) +LJFOLDF(simplify_conv_sext) +{ + IRRef ref = fins->op1; + int64_t ofs = 0; + if (!(fins->op2 & IRCONV_SEXT)) + return NEXTFOLD; + PHIBARRIER(fleft); + if (fleft->o == IR_XLOAD && (irt_isu8(fleft->t) || irt_isu16(fleft->t))) + goto ok_reduce; + if (fleft->o == IR_ADD && irref_isk(fleft->op2)) { + ofs = (int64_t)IR(fleft->op2)->i; + ref = fleft->op1; + } + /* Use scalar evolution analysis results to strength-reduce sign-extension. */ + if (ref == J->scev.idx) { + IRRef lo = J->scev.dir ? J->scev.start : J->scev.stop; + lua_assert(irt_isint(J->scev.t)); + if (lo && IR(lo)->o == IR_KINT && IR(lo)->i + ofs >= 0) { + ok_reduce: +#if LJ_TARGET_X64 + /* Eliminate widening. All 32 bit ops do an implicit zero-extension. */ + return LEFTFOLD; +#else + /* Reduce to a (cheaper) zero-extension. */ + fins->op2 &= ~IRCONV_SEXT; + return RETRYFOLD; +#endif + } + } + return NEXTFOLD; +} + +/* Strength reduction of narrowing. */ +LJFOLD(CONV ADD IRCONV_INT_I64) +LJFOLD(CONV SUB IRCONV_INT_I64) +LJFOLD(CONV MUL IRCONV_INT_I64) +LJFOLD(CONV ADD IRCONV_INT_U64) +LJFOLD(CONV SUB IRCONV_INT_U64) +LJFOLD(CONV MUL IRCONV_INT_U64) +LJFOLD(CONV ADD IRCONV_U32_I64) +LJFOLD(CONV SUB IRCONV_U32_I64) +LJFOLD(CONV MUL IRCONV_U32_I64) +LJFOLD(CONV ADD IRCONV_U32_U64) +LJFOLD(CONV SUB IRCONV_U32_U64) +LJFOLD(CONV MUL IRCONV_U32_U64) +LJFOLDF(simplify_conv_narrow) +{ + IROp op = (IROp)fleft->o; + IRType t = irt_type(fins->t); + IRRef op1 = fleft->op1, op2 = fleft->op2, mode = fins->op2; + PHIBARRIER(fleft); + op1 = emitir(IRTI(IR_CONV), op1, mode); + op2 = emitir(IRTI(IR_CONV), op2, mode); + fins->ot = IRT(op, t); + fins->op1 = op1; + fins->op2 = op2; + return RETRYFOLD; +} + +/* Special CSE rule for CONV. */ +LJFOLD(CONV any any) +LJFOLDF(cse_conv) +{ + if (LJ_LIKELY(J->flags & JIT_F_OPT_CSE)) { + IRRef op1 = fins->op1, op2 = (fins->op2 & IRCONV_MODEMASK); + uint8_t guard = irt_isguard(fins->t); + IRRef ref = J->chain[IR_CONV]; + while (ref > op1) { + IRIns *ir = IR(ref); + /* Commoning with stronger checks is ok. */ + if (ir->op1 == op1 && (ir->op2 & IRCONV_MODEMASK) == op2 && + irt_isguard(ir->t) >= guard) + return ref; + ref = ir->prev; + } + } + return EMITFOLD; /* No fallthrough to regular CSE. */ +} + +/* FP conversion narrowing. */ +LJFOLD(TOBIT ADD KNUM) +LJFOLD(TOBIT SUB KNUM) +LJFOLD(CONV ADD IRCONV_INT_NUM) +LJFOLD(CONV SUB IRCONV_INT_NUM) +LJFOLD(CONV ADD IRCONV_I64_NUM) +LJFOLD(CONV SUB IRCONV_I64_NUM) +LJFOLDF(narrow_convert) +{ + PHIBARRIER(fleft); + /* Narrowing ignores PHIs and repeating it inside the loop is not useful. */ + if (J->chain[IR_LOOP]) + return NEXTFOLD; + lua_assert(fins->o != IR_CONV || (fins->op2&IRCONV_CONVMASK) != IRCONV_TOBIT); + return lj_opt_narrow_convert(J); +} + +/* -- Integer algebraic simplifications ----------------------------------- */ + +LJFOLD(ADD any KINT) +LJFOLD(ADDOV any KINT) +LJFOLD(SUBOV any KINT) +LJFOLDF(simplify_intadd_k) +{ + if (fright->i == 0) /* i o 0 ==> i */ + return LEFTFOLD; + return NEXTFOLD; +} + +LJFOLD(MULOV any KINT) +LJFOLDF(simplify_intmul_k) +{ + if (fright->i == 0) /* i * 0 ==> 0 */ + return RIGHTFOLD; + if (fright->i == 1) /* i * 1 ==> i */ + return LEFTFOLD; + if (fright->i == 2) { /* i * 2 ==> i + i */ + fins->o = IR_ADDOV; + fins->op2 = fins->op1; + return RETRYFOLD; + } + return NEXTFOLD; +} + +LJFOLD(SUB any KINT) +LJFOLDF(simplify_intsub_k) +{ + if (fright->i == 0) /* i - 0 ==> i */ + return LEFTFOLD; + fins->o = IR_ADD; /* i - k ==> i + (-k) */ + fins->op2 = (IRRef1)lj_ir_kint(J, -fright->i); /* Overflow for -2^31 ok. */ + return RETRYFOLD; +} + +LJFOLD(SUB KINT any) +LJFOLD(SUB KINT64 any) +LJFOLDF(simplify_intsub_kleft) +{ + if (fleft->o == IR_KINT ? (fleft->i == 0) : (ir_kint64(fleft)->u64 == 0)) { + fins->o = IR_NEG; /* 0 - i ==> -i */ + fins->op1 = fins->op2; + return RETRYFOLD; + } + return NEXTFOLD; +} + +LJFOLD(ADD any KINT64) +LJFOLDF(simplify_intadd_k64) +{ + if (ir_kint64(fright)->u64 == 0) /* i + 0 ==> i */ + return LEFTFOLD; + return NEXTFOLD; +} + +LJFOLD(SUB any KINT64) +LJFOLDF(simplify_intsub_k64) +{ + uint64_t k = ir_kint64(fright)->u64; + if (k == 0) /* i - 0 ==> i */ + return LEFTFOLD; + fins->o = IR_ADD; /* i - k ==> i + (-k) */ + fins->op2 = (IRRef1)lj_ir_kint64(J, (uint64_t)-(int64_t)k); + return RETRYFOLD; +} + +static TRef simplify_intmul_k(jit_State *J, int32_t k) +{ + /* Note: many more simplifications are possible, e.g. 2^k1 +- 2^k2. + ** But this is mainly intended for simple address arithmetic. + ** Also it's easier for the backend to optimize the original multiplies. + */ + if (k == 0) { /* i * 0 ==> 0 */ + return RIGHTFOLD; + } else if (k == 1) { /* i * 1 ==> i */ + return LEFTFOLD; + } else if ((k & (k-1)) == 0) { /* i * 2^k ==> i << k */ + fins->o = IR_BSHL; + fins->op2 = lj_ir_kint(J, lj_fls((uint32_t)k)); + return RETRYFOLD; + } + return NEXTFOLD; +} + +LJFOLD(MUL any KINT) +LJFOLDF(simplify_intmul_k32) +{ + if (fright->i >= 0) + return simplify_intmul_k(J, fright->i); + return NEXTFOLD; +} + +LJFOLD(MUL any KINT64) +LJFOLDF(simplify_intmul_k64) +{ +#if LJ_HASFFI + if (ir_kint64(fright)->u64 < 0x80000000u) + return simplify_intmul_k(J, (int32_t)ir_kint64(fright)->u64); + return NEXTFOLD; +#else + UNUSED(J); lua_assert(0); return FAILFOLD; +#endif +} + +LJFOLD(MOD any KINT) +LJFOLDF(simplify_intmod_k) +{ + int32_t k = fright->i; + lua_assert(k != 0); + if (k > 0 && (k & (k-1)) == 0) { /* i % (2^k) ==> i & (2^k-1) */ + fins->o = IR_BAND; + fins->op2 = lj_ir_kint(J, k-1); + return RETRYFOLD; + } + return NEXTFOLD; +} + +LJFOLD(MOD KINT any) +LJFOLDF(simplify_intmod_kleft) +{ + if (fleft->i == 0) + return INTFOLD(0); + return NEXTFOLD; +} + +LJFOLD(SUB any any) +LJFOLD(SUBOV any any) +LJFOLDF(simplify_intsub) +{ + if (fins->op1 == fins->op2 && !irt_isnum(fins->t)) /* i - i ==> 0 */ + return irt_is64(fins->t) ? INT64FOLD(0) : INTFOLD(0); + return NEXTFOLD; +} + +LJFOLD(SUB ADD any) +LJFOLDF(simplify_intsubadd_leftcancel) +{ + if (!irt_isnum(fins->t)) { + PHIBARRIER(fleft); + if (fins->op2 == fleft->op1) /* (i + j) - i ==> j */ + return fleft->op2; + if (fins->op2 == fleft->op2) /* (i + j) - j ==> i */ + return fleft->op1; + } + return NEXTFOLD; +} + +LJFOLD(SUB SUB any) +LJFOLDF(simplify_intsubsub_leftcancel) +{ + if (!irt_isnum(fins->t)) { + PHIBARRIER(fleft); + if (fins->op2 == fleft->op1) { /* (i - j) - i ==> 0 - j */ + fins->op1 = (IRRef1)lj_ir_kint(J, 0); + fins->op2 = fleft->op2; + return RETRYFOLD; + } + } + return NEXTFOLD; +} + +LJFOLD(SUB any SUB) +LJFOLDF(simplify_intsubsub_rightcancel) +{ + if (!irt_isnum(fins->t)) { + PHIBARRIER(fright); + if (fins->op1 == fright->op1) /* i - (i - j) ==> j */ + return fright->op2; + } + return NEXTFOLD; +} + +LJFOLD(SUB any ADD) +LJFOLDF(simplify_intsubadd_rightcancel) +{ + if (!irt_isnum(fins->t)) { + PHIBARRIER(fright); + if (fins->op1 == fright->op1) { /* i - (i + j) ==> 0 - j */ + fins->op2 = fright->op2; + fins->op1 = (IRRef1)lj_ir_kint(J, 0); + return RETRYFOLD; + } + if (fins->op1 == fright->op2) { /* i - (j + i) ==> 0 - j */ + fins->op2 = fright->op1; + fins->op1 = (IRRef1)lj_ir_kint(J, 0); + return RETRYFOLD; + } + } + return NEXTFOLD; +} + +LJFOLD(SUB ADD ADD) +LJFOLDF(simplify_intsubaddadd_cancel) +{ + if (!irt_isnum(fins->t)) { + PHIBARRIER(fleft); + PHIBARRIER(fright); + if (fleft->op1 == fright->op1) { /* (i + j1) - (i + j2) ==> j1 - j2 */ + fins->op1 = fleft->op2; + fins->op2 = fright->op2; + return RETRYFOLD; + } + if (fleft->op1 == fright->op2) { /* (i + j1) - (j2 + i) ==> j1 - j2 */ + fins->op1 = fleft->op2; + fins->op2 = fright->op1; + return RETRYFOLD; + } + if (fleft->op2 == fright->op1) { /* (j1 + i) - (i + j2) ==> j1 - j2 */ + fins->op1 = fleft->op1; + fins->op2 = fright->op2; + return RETRYFOLD; + } + if (fleft->op2 == fright->op2) { /* (j1 + i) - (j2 + i) ==> j1 - j2 */ + fins->op1 = fleft->op1; + fins->op2 = fright->op1; + return RETRYFOLD; + } + } + return NEXTFOLD; +} + +LJFOLD(BAND any KINT) +LJFOLD(BAND any KINT64) +LJFOLDF(simplify_band_k) +{ + int64_t k = fright->o == IR_KINT ? (int64_t)fright->i : + (int64_t)ir_k64(fright)->u64; + if (k == 0) /* i & 0 ==> 0 */ + return RIGHTFOLD; + if (k == -1) /* i & -1 ==> i */ + return LEFTFOLD; + return NEXTFOLD; +} + +LJFOLD(BOR any KINT) +LJFOLD(BOR any KINT64) +LJFOLDF(simplify_bor_k) +{ + int64_t k = fright->o == IR_KINT ? (int64_t)fright->i : + (int64_t)ir_k64(fright)->u64; + if (k == 0) /* i | 0 ==> i */ + return LEFTFOLD; + if (k == -1) /* i | -1 ==> -1 */ + return RIGHTFOLD; + return NEXTFOLD; +} + +LJFOLD(BXOR any KINT) +LJFOLD(BXOR any KINT64) +LJFOLDF(simplify_bxor_k) +{ + int64_t k = fright->o == IR_KINT ? (int64_t)fright->i : + (int64_t)ir_k64(fright)->u64; + if (k == 0) /* i xor 0 ==> i */ + return LEFTFOLD; + if (k == -1) { /* i xor -1 ==> ~i */ + fins->o = IR_BNOT; + fins->op2 = 0; + return RETRYFOLD; + } + return NEXTFOLD; +} + +LJFOLD(BSHL any KINT) +LJFOLD(BSHR any KINT) +LJFOLD(BSAR any KINT) +LJFOLD(BROL any KINT) +LJFOLD(BROR any KINT) +LJFOLDF(simplify_shift_ik) +{ + int32_t mask = irt_is64(fins->t) ? 63 : 31; + int32_t k = (fright->i & mask); + if (k == 0) /* i o 0 ==> i */ + return LEFTFOLD; + if (k == 1 && fins->o == IR_BSHL) { /* i << 1 ==> i + i */ + fins->o = IR_ADD; + fins->op2 = fins->op1; + return RETRYFOLD; + } + if (k != fright->i) { /* i o k ==> i o (k & mask) */ + fins->op2 = (IRRef1)lj_ir_kint(J, k); + return RETRYFOLD; + } +#ifndef LJ_TARGET_UNIFYROT + if (fins->o == IR_BROR) { /* bror(i, k) ==> brol(i, (-k)&mask) */ + fins->o = IR_BROL; + fins->op2 = (IRRef1)lj_ir_kint(J, (-k)&mask); + return RETRYFOLD; + } +#endif + return NEXTFOLD; +} + +LJFOLD(BSHL any BAND) +LJFOLD(BSHR any BAND) +LJFOLD(BSAR any BAND) +LJFOLD(BROL any BAND) +LJFOLD(BROR any BAND) +LJFOLDF(simplify_shift_andk) +{ + IRIns *irk = IR(fright->op2); + PHIBARRIER(fright); + if ((fins->o < IR_BROL ? LJ_TARGET_MASKSHIFT : LJ_TARGET_MASKROT) && + irk->o == IR_KINT) { /* i o (j & mask) ==> i o j */ + int32_t mask = irt_is64(fins->t) ? 63 : 31; + int32_t k = irk->i & mask; + if (k == mask) { + fins->op2 = fright->op1; + return RETRYFOLD; + } + } + return NEXTFOLD; +} + +LJFOLD(BSHL KINT any) +LJFOLD(BSHR KINT any) +LJFOLD(BSHL KINT64 any) +LJFOLD(BSHR KINT64 any) +LJFOLDF(simplify_shift1_ki) +{ + int64_t k = fleft->o == IR_KINT ? (int64_t)fleft->i : + (int64_t)ir_k64(fleft)->u64; + if (k == 0) /* 0 o i ==> 0 */ + return LEFTFOLD; + return NEXTFOLD; +} + +LJFOLD(BSAR KINT any) +LJFOLD(BROL KINT any) +LJFOLD(BROR KINT any) +LJFOLD(BSAR KINT64 any) +LJFOLD(BROL KINT64 any) +LJFOLD(BROR KINT64 any) +LJFOLDF(simplify_shift2_ki) +{ + int64_t k = fleft->o == IR_KINT ? (int64_t)fleft->i : + (int64_t)ir_k64(fleft)->u64; + if (k == 0 || k == -1) /* 0 o i ==> 0; -1 o i ==> -1 */ + return LEFTFOLD; + return NEXTFOLD; +} + +LJFOLD(BSHL BAND KINT) +LJFOLD(BSHR BAND KINT) +LJFOLD(BROL BAND KINT) +LJFOLD(BROR BAND KINT) +LJFOLDF(simplify_shiftk_andk) +{ + IRIns *irk = IR(fleft->op2); + PHIBARRIER(fleft); + if (irk->o == IR_KINT) { /* (i & k1) o k2 ==> (i o k2) & (k1 o k2) */ + int32_t k = kfold_intop(irk->i, fright->i, (IROp)fins->o); + fins->op1 = fleft->op1; + fins->op1 = (IRRef1)lj_opt_fold(J); + fins->op2 = (IRRef1)lj_ir_kint(J, k); + fins->ot = IRTI(IR_BAND); + return RETRYFOLD; + } else if (irk->o == IR_KINT64) { + uint64_t k = kfold_int64arith(ir_k64(irk)->u64, fright->i, (IROp)fins->o); + IROpT ot = fleft->ot; + fins->op1 = fleft->op1; + fins->op1 = (IRRef1)lj_opt_fold(J); + fins->op2 = (IRRef1)lj_ir_kint64(J, k); + fins->ot = ot; + return RETRYFOLD; + } + return NEXTFOLD; +} + +LJFOLD(BAND BSHL KINT) +LJFOLD(BAND BSHR KINT) +LJFOLDF(simplify_andk_shiftk) +{ + IRIns *irk = IR(fleft->op2); + if (irk->o == IR_KINT && + kfold_intop(-1, irk->i, (IROp)fleft->o) == fright->i) + return LEFTFOLD; /* (i o k1) & k2 ==> i, if (-1 o k1) == k2 */ + return NEXTFOLD; +} + +LJFOLD(BAND BOR KINT) +LJFOLD(BOR BAND KINT) +LJFOLDF(simplify_andor_k) +{ + IRIns *irk = IR(fleft->op2); + PHIBARRIER(fleft); + if (irk->o == IR_KINT) { + int32_t k = kfold_intop(irk->i, fright->i, (IROp)fins->o); + /* (i | k1) & k2 ==> i & k2, if (k1 & k2) == 0. */ + /* (i & k1) | k2 ==> i | k2, if (k1 | k2) == -1. */ + if (k == (fins->o == IR_BAND ? 0 : -1)) { + fins->op1 = fleft->op1; + return RETRYFOLD; + } + } + return NEXTFOLD; +} + +LJFOLD(BAND BOR KINT64) +LJFOLD(BOR BAND KINT64) +LJFOLDF(simplify_andor_k64) +{ +#if LJ_HASFFI + IRIns *irk = IR(fleft->op2); + PHIBARRIER(fleft); + if (irk->o == IR_KINT64) { + uint64_t k = kfold_int64arith(ir_k64(irk)->u64, + ir_k64(fright)->u64, (IROp)fins->o); + /* (i | k1) & k2 ==> i & k2, if (k1 & k2) == 0. */ + /* (i & k1) | k2 ==> i | k2, if (k1 | k2) == -1. */ + if (k == (fins->o == IR_BAND ? (uint64_t)0 : ~(uint64_t)0)) { + fins->op1 = fleft->op1; + return RETRYFOLD; + } + } + return NEXTFOLD; +#else + UNUSED(J); lua_assert(0); return FAILFOLD; +#endif +} + +/* -- Reassociation ------------------------------------------------------- */ + +LJFOLD(ADD ADD KINT) +LJFOLD(MUL MUL KINT) +LJFOLD(BAND BAND KINT) +LJFOLD(BOR BOR KINT) +LJFOLD(BXOR BXOR KINT) +LJFOLDF(reassoc_intarith_k) +{ + IRIns *irk = IR(fleft->op2); + if (irk->o == IR_KINT) { + int32_t k = kfold_intop(irk->i, fright->i, (IROp)fins->o); + if (k == irk->i) /* (i o k1) o k2 ==> i o k1, if (k1 o k2) == k1. */ + return LEFTFOLD; + PHIBARRIER(fleft); + fins->op1 = fleft->op1; + fins->op2 = (IRRef1)lj_ir_kint(J, k); + return RETRYFOLD; /* (i o k1) o k2 ==> i o (k1 o k2) */ + } + return NEXTFOLD; +} + +LJFOLD(ADD ADD KINT64) +LJFOLD(MUL MUL KINT64) +LJFOLD(BAND BAND KINT64) +LJFOLD(BOR BOR KINT64) +LJFOLD(BXOR BXOR KINT64) +LJFOLDF(reassoc_intarith_k64) +{ +#if LJ_HASFFI + IRIns *irk = IR(fleft->op2); + if (irk->o == IR_KINT64) { + uint64_t k = kfold_int64arith(ir_k64(irk)->u64, + ir_k64(fright)->u64, (IROp)fins->o); + PHIBARRIER(fleft); + fins->op1 = fleft->op1; + fins->op2 = (IRRef1)lj_ir_kint64(J, k); + return RETRYFOLD; /* (i o k1) o k2 ==> i o (k1 o k2) */ + } + return NEXTFOLD; +#else + UNUSED(J); lua_assert(0); return FAILFOLD; +#endif +} + +LJFOLD(MIN MIN any) +LJFOLD(MAX MAX any) +LJFOLD(BAND BAND any) +LJFOLD(BOR BOR any) +LJFOLDF(reassoc_dup) +{ + if (fins->op2 == fleft->op1 || fins->op2 == fleft->op2) + return LEFTFOLD; /* (a o b) o a ==> a o b; (a o b) o b ==> a o b */ + return NEXTFOLD; +} + +LJFOLD(BXOR BXOR any) +LJFOLDF(reassoc_bxor) +{ + PHIBARRIER(fleft); + if (fins->op2 == fleft->op1) /* (a xor b) xor a ==> b */ + return fleft->op2; + if (fins->op2 == fleft->op2) /* (a xor b) xor b ==> a */ + return fleft->op1; + return NEXTFOLD; +} + +LJFOLD(BSHL BSHL KINT) +LJFOLD(BSHR BSHR KINT) +LJFOLD(BSAR BSAR KINT) +LJFOLD(BROL BROL KINT) +LJFOLD(BROR BROR KINT) +LJFOLDF(reassoc_shift) +{ + IRIns *irk = IR(fleft->op2); + PHIBARRIER(fleft); /* The (shift any KINT) rule covers k2 == 0 and more. */ + if (irk->o == IR_KINT) { /* (i o k1) o k2 ==> i o (k1 + k2) */ + int32_t mask = irt_is64(fins->t) ? 63 : 31; + int32_t k = (irk->i & mask) + (fright->i & mask); + if (k > mask) { /* Combined shift too wide? */ + if (fins->o == IR_BSHL || fins->o == IR_BSHR) + return mask == 31 ? INTFOLD(0) : INT64FOLD(0); + else if (fins->o == IR_BSAR) + k = mask; + else + k &= mask; + } + fins->op1 = fleft->op1; + fins->op2 = (IRRef1)lj_ir_kint(J, k); + return RETRYFOLD; + } + return NEXTFOLD; +} + +LJFOLD(MIN MIN KNUM) +LJFOLD(MAX MAX KNUM) +LJFOLD(MIN MIN KINT) +LJFOLD(MAX MAX KINT) +LJFOLDF(reassoc_minmax_k) +{ + IRIns *irk = IR(fleft->op2); + if (irk->o == IR_KNUM) { + lua_Number a = ir_knum(irk)->n; + lua_Number y = lj_vm_foldarith(a, knumright, fins->o - IR_ADD); + if (a == y) /* (x o k1) o k2 ==> x o k1, if (k1 o k2) == k1. */ + return LEFTFOLD; + PHIBARRIER(fleft); + fins->op1 = fleft->op1; + fins->op2 = (IRRef1)lj_ir_knum(J, y); + return RETRYFOLD; /* (x o k1) o k2 ==> x o (k1 o k2) */ + } else if (irk->o == IR_KINT) { + int32_t a = irk->i; + int32_t y = kfold_intop(a, fright->i, fins->o); + if (a == y) /* (x o k1) o k2 ==> x o k1, if (k1 o k2) == k1. */ + return LEFTFOLD; + PHIBARRIER(fleft); + fins->op1 = fleft->op1; + fins->op2 = (IRRef1)lj_ir_kint(J, y); + return RETRYFOLD; /* (x o k1) o k2 ==> x o (k1 o k2) */ + } + return NEXTFOLD; +} + +LJFOLD(MIN MAX any) +LJFOLD(MAX MIN any) +LJFOLDF(reassoc_minmax_left) +{ + if (fins->op2 == fleft->op1 || fins->op2 == fleft->op2) + return RIGHTFOLD; /* (b o1 a) o2 b ==> b; (a o1 b) o2 b ==> b */ + return NEXTFOLD; +} + +LJFOLD(MIN any MAX) +LJFOLD(MAX any MIN) +LJFOLDF(reassoc_minmax_right) +{ + if (fins->op1 == fright->op1 || fins->op1 == fright->op2) + return LEFTFOLD; /* a o2 (a o1 b) ==> a; a o2 (b o1 a) ==> a */ + return NEXTFOLD; +} + +/* -- Array bounds check elimination -------------------------------------- */ + +/* Eliminate ABC across PHIs to handle t[i-1] forwarding case. +** ABC(asize, (i+k)+(-k)) ==> ABC(asize, i), but only if it already exists. +** Could be generalized to (i+k1)+k2 ==> i+(k1+k2), but needs better disambig. +*/ +LJFOLD(ABC any ADD) +LJFOLDF(abc_fwd) +{ + if (LJ_LIKELY(J->flags & JIT_F_OPT_ABC)) { + if (irref_isk(fright->op2)) { + IRIns *add2 = IR(fright->op1); + if (add2->o == IR_ADD && irref_isk(add2->op2) && + IR(fright->op2)->i == -IR(add2->op2)->i) { + IRRef ref = J->chain[IR_ABC]; + IRRef lim = add2->op1; + if (fins->op1 > lim) lim = fins->op1; + while (ref > lim) { + IRIns *ir = IR(ref); + if (ir->op1 == fins->op1 && ir->op2 == add2->op1) + return DROPFOLD; + ref = ir->prev; + } + } + } + } + return NEXTFOLD; +} + +/* Eliminate ABC for constants. +** ABC(asize, k1), ABC(asize k2) ==> ABC(asize, max(k1, k2)) +** Drop second ABC if k2 is lower. Otherwise patch first ABC with k2. +*/ +LJFOLD(ABC any KINT) +LJFOLDF(abc_k) +{ + if (LJ_LIKELY(J->flags & JIT_F_OPT_ABC)) { + IRRef ref = J->chain[IR_ABC]; + IRRef asize = fins->op1; + while (ref > asize) { + IRIns *ir = IR(ref); + if (ir->op1 == asize && irref_isk(ir->op2)) { + int32_t k = IR(ir->op2)->i; + if (fright->i > k) + ir->op2 = fins->op2; + return DROPFOLD; + } + ref = ir->prev; + } + return EMITFOLD; /* Already performed CSE. */ + } + return NEXTFOLD; +} + +/* Eliminate invariant ABC inside loop. */ +LJFOLD(ABC any any) +LJFOLDF(abc_invar) +{ + /* Invariant ABC marked as PTR. Drop if op1 is invariant, too. */ + if (!irt_isint(fins->t) && fins->op1 < J->chain[IR_LOOP] && + !irt_isphi(IR(fins->op1)->t)) + return DROPFOLD; + return NEXTFOLD; +} + +/* -- Commutativity ------------------------------------------------------- */ + +/* The refs of commutative ops are canonicalized. Lower refs go to the right. +** Rationale behind this: +** - It (also) moves constants to the right. +** - It reduces the number of FOLD rules (e.g. (BOR any KINT) suffices). +** - It helps CSE to find more matches. +** - The assembler generates better code with constants at the right. +*/ + +LJFOLD(ADD any any) +LJFOLD(MUL any any) +LJFOLD(ADDOV any any) +LJFOLD(MULOV any any) +LJFOLDF(comm_swap) +{ + if (fins->op1 < fins->op2) { /* Move lower ref to the right. */ + IRRef1 tmp = fins->op1; + fins->op1 = fins->op2; + fins->op2 = tmp; + return RETRYFOLD; + } + return NEXTFOLD; +} + +LJFOLD(EQ any any) +LJFOLD(NE any any) +LJFOLDF(comm_equal) +{ + /* For non-numbers only: x == x ==> drop; x ~= x ==> fail */ + if (fins->op1 == fins->op2 && !irt_isnum(fins->t)) + return CONDFOLD(fins->o == IR_EQ); + return fold_comm_swap(J); +} + +LJFOLD(LT any any) +LJFOLD(GE any any) +LJFOLD(LE any any) +LJFOLD(GT any any) +LJFOLD(ULT any any) +LJFOLD(UGE any any) +LJFOLD(ULE any any) +LJFOLD(UGT any any) +LJFOLDF(comm_comp) +{ + /* For non-numbers only: x <=> x ==> drop; x <> x ==> fail */ + if (fins->op1 == fins->op2 && !irt_isnum(fins->t)) + return CONDFOLD((fins->o ^ (fins->o >> 1)) & 1); + if (fins->op1 < fins->op2) { /* Move lower ref to the right. */ + IRRef1 tmp = fins->op1; + fins->op1 = fins->op2; + fins->op2 = tmp; + fins->o ^= 3; /* GT <-> LT, GE <-> LE, does not affect U */ + return RETRYFOLD; + } + return NEXTFOLD; +} + +LJFOLD(BAND any any) +LJFOLD(BOR any any) +LJFOLD(MIN any any) +LJFOLD(MAX any any) +LJFOLDF(comm_dup) +{ + if (fins->op1 == fins->op2) /* x o x ==> x */ + return LEFTFOLD; + return fold_comm_swap(J); +} + +LJFOLD(BXOR any any) +LJFOLDF(comm_bxor) +{ + if (fins->op1 == fins->op2) /* i xor i ==> 0 */ + return irt_is64(fins->t) ? INT64FOLD(0) : INTFOLD(0); + return fold_comm_swap(J); +} + +/* -- Simplification of compound expressions ------------------------------ */ + +static TRef kfold_xload(jit_State *J, IRIns *ir, const void *p) +{ + int32_t k; + switch (irt_type(ir->t)) { + case IRT_NUM: return lj_ir_knum_u64(J, *(uint64_t *)p); + case IRT_I8: k = (int32_t)*(int8_t *)p; break; + case IRT_U8: k = (int32_t)*(uint8_t *)p; break; + case IRT_I16: k = (int32_t)(int16_t)lj_getu16(p); break; + case IRT_U16: k = (int32_t)(uint16_t)lj_getu16(p); break; + case IRT_INT: case IRT_U32: k = (int32_t)lj_getu32(p); break; + case IRT_I64: case IRT_U64: return lj_ir_kint64(J, *(uint64_t *)p); + default: return 0; + } + return lj_ir_kint(J, k); +} + +/* Turn: string.sub(str, a, b) == kstr +** into: string.byte(str, a) == string.byte(kstr, 1) etc. +** Note: this creates unaligned XLOADs on x86/x64. +*/ +LJFOLD(EQ SNEW KGC) +LJFOLD(NE SNEW KGC) +LJFOLDF(merge_eqne_snew_kgc) +{ + GCstr *kstr = ir_kstr(fright); + int32_t len = (int32_t)kstr->len; + lua_assert(irt_isstr(fins->t)); + +#if LJ_TARGET_UNALIGNED +#define FOLD_SNEW_MAX_LEN 4 /* Handle string lengths 0, 1, 2, 3, 4. */ +#define FOLD_SNEW_TYPE8 IRT_I8 /* Creates shorter immediates. */ +#else +#define FOLD_SNEW_MAX_LEN 1 /* Handle string lengths 0 or 1. */ +#define FOLD_SNEW_TYPE8 IRT_U8 /* Prefer unsigned loads. */ +#endif + + PHIBARRIER(fleft); + if (len <= FOLD_SNEW_MAX_LEN) { + IROp op = (IROp)fins->o; + IRRef strref = fleft->op1; + if (IR(strref)->o != IR_STRREF) + return NEXTFOLD; + if (op == IR_EQ) { + emitir(IRTGI(IR_EQ), fleft->op2, lj_ir_kint(J, len)); + /* Caveat: fins/fleft/fright is no longer valid after emitir. */ + } else { + /* NE is not expanded since this would need an OR of two conds. */ + if (!irref_isk(fleft->op2)) /* Only handle the constant length case. */ + return NEXTFOLD; + if (IR(fleft->op2)->i != len) + return DROPFOLD; + } + if (len > 0) { + /* A 4 byte load for length 3 is ok -- all strings have an extra NUL. */ + uint16_t ot = (uint16_t)(len == 1 ? IRT(IR_XLOAD, FOLD_SNEW_TYPE8) : + len == 2 ? IRT(IR_XLOAD, IRT_U16) : + IRTI(IR_XLOAD)); + TRef tmp = emitir(ot, strref, + IRXLOAD_READONLY | (len > 1 ? IRXLOAD_UNALIGNED : 0)); + TRef val = kfold_xload(J, IR(tref_ref(tmp)), strdata(kstr)); + if (len == 3) + tmp = emitir(IRTI(IR_BAND), tmp, + lj_ir_kint(J, LJ_ENDIAN_SELECT(0x00ffffff, 0xffffff00))); + fins->op1 = (IRRef1)tmp; + fins->op2 = (IRRef1)val; + fins->ot = (IROpT)IRTGI(op); + return RETRYFOLD; + } else { + return DROPFOLD; + } + } + return NEXTFOLD; +} + +/* -- Loads --------------------------------------------------------------- */ + +/* Loads cannot be folded or passed on to CSE in general. +** Alias analysis is needed to check for forwarding opportunities. +** +** Caveat: *all* loads must be listed here or they end up at CSE! +*/ + +LJFOLD(ALOAD any) +LJFOLDX(lj_opt_fwd_aload) + +/* From HREF fwd (see below). Must eliminate, not supported by fwd/backend. */ +LJFOLD(HLOAD KKPTR) +LJFOLDF(kfold_hload_kkptr) +{ + UNUSED(J); + lua_assert(ir_kptr(fleft) == niltvg(J2G(J))); + return TREF_NIL; +} + +LJFOLD(HLOAD any) +LJFOLDX(lj_opt_fwd_hload) + +LJFOLD(ULOAD any) +LJFOLDX(lj_opt_fwd_uload) + +LJFOLD(CALLL any IRCALL_lj_tab_len) +LJFOLDX(lj_opt_fwd_tab_len) + +/* Upvalue refs are really loads, but there are no corresponding stores. +** So CSE is ok for them, except for UREFO across a GC step (see below). +** If the referenced function is const, its upvalue addresses are const, too. +** This can be used to improve CSE by looking for the same address, +** even if the upvalues originate from a different function. +*/ +LJFOLD(UREFO KGC any) +LJFOLD(UREFC KGC any) +LJFOLDF(cse_uref) +{ + if (LJ_LIKELY(J->flags & JIT_F_OPT_CSE)) { + IRRef ref = J->chain[fins->o]; + GCfunc *fn = ir_kfunc(fleft); + GCupval *uv = gco2uv(gcref(fn->l.uvptr[(fins->op2 >> 8)])); + while (ref > 0) { + IRIns *ir = IR(ref); + if (irref_isk(ir->op1)) { + GCfunc *fn2 = ir_kfunc(IR(ir->op1)); + if (gco2uv(gcref(fn2->l.uvptr[(ir->op2 >> 8)])) == uv) { + if (fins->o == IR_UREFO && gcstep_barrier(J, ref)) + break; + return ref; + } + } + ref = ir->prev; + } + } + return EMITFOLD; +} + +LJFOLD(HREFK any any) +LJFOLDX(lj_opt_fwd_hrefk) + +LJFOLD(HREF TNEW any) +LJFOLDF(fwd_href_tnew) +{ + if (lj_opt_fwd_href_nokey(J)) + return lj_ir_kkptr(J, niltvg(J2G(J))); + return NEXTFOLD; +} + +LJFOLD(HREF TDUP KPRI) +LJFOLD(HREF TDUP KGC) +LJFOLD(HREF TDUP KNUM) +LJFOLDF(fwd_href_tdup) +{ + TValue keyv; + lj_ir_kvalue(J->L, &keyv, fright); + if (lj_tab_get(J->L, ir_ktab(IR(fleft->op1)), &keyv) == niltvg(J2G(J)) && + lj_opt_fwd_href_nokey(J)) + return lj_ir_kkptr(J, niltvg(J2G(J))); + return NEXTFOLD; +} + +/* We can safely FOLD/CSE array/hash refs and field loads, since there +** are no corresponding stores. But we need to check for any NEWREF with +** an aliased table, as it may invalidate all of the pointers and fields. +** Only HREF needs the NEWREF check -- AREF and HREFK already depend on +** FLOADs. And NEWREF itself is treated like a store (see below). +** LREF is constant (per trace) since coroutine switches are not inlined. +*/ +LJFOLD(FLOAD TNEW IRFL_TAB_ASIZE) +LJFOLDF(fload_tab_tnew_asize) +{ + if (LJ_LIKELY(J->flags & JIT_F_OPT_FOLD) && lj_opt_fwd_tptr(J, fins->op1)) + return INTFOLD(fleft->op1); + return NEXTFOLD; +} + +LJFOLD(FLOAD TNEW IRFL_TAB_HMASK) +LJFOLDF(fload_tab_tnew_hmask) +{ + if (LJ_LIKELY(J->flags & JIT_F_OPT_FOLD) && lj_opt_fwd_tptr(J, fins->op1)) + return INTFOLD((1 << fleft->op2)-1); + return NEXTFOLD; +} + +LJFOLD(FLOAD TDUP IRFL_TAB_ASIZE) +LJFOLDF(fload_tab_tdup_asize) +{ + if (LJ_LIKELY(J->flags & JIT_F_OPT_FOLD) && lj_opt_fwd_tptr(J, fins->op1)) + return INTFOLD((int32_t)ir_ktab(IR(fleft->op1))->asize); + return NEXTFOLD; +} + +LJFOLD(FLOAD TDUP IRFL_TAB_HMASK) +LJFOLDF(fload_tab_tdup_hmask) +{ + if (LJ_LIKELY(J->flags & JIT_F_OPT_FOLD) && lj_opt_fwd_tptr(J, fins->op1)) + return INTFOLD((int32_t)ir_ktab(IR(fleft->op1))->hmask); + return NEXTFOLD; +} + +LJFOLD(HREF any any) +LJFOLD(FLOAD any IRFL_TAB_ARRAY) +LJFOLD(FLOAD any IRFL_TAB_NODE) +LJFOLD(FLOAD any IRFL_TAB_ASIZE) +LJFOLD(FLOAD any IRFL_TAB_HMASK) +LJFOLDF(fload_tab_ah) +{ + TRef tr = lj_opt_cse(J); + return lj_opt_fwd_tptr(J, tref_ref(tr)) ? tr : EMITFOLD; +} + +/* Strings are immutable, so we can safely FOLD/CSE the related FLOAD. */ +LJFOLD(FLOAD KGC IRFL_STR_LEN) +LJFOLDF(fload_str_len_kgc) +{ + if (LJ_LIKELY(J->flags & JIT_F_OPT_FOLD)) + return INTFOLD((int32_t)ir_kstr(fleft)->len); + return NEXTFOLD; +} + +LJFOLD(FLOAD SNEW IRFL_STR_LEN) +LJFOLDF(fload_str_len_snew) +{ + if (LJ_LIKELY(J->flags & JIT_F_OPT_FOLD)) { + PHIBARRIER(fleft); + return fleft->op2; + } + return NEXTFOLD; +} + +LJFOLD(FLOAD TOSTR IRFL_STR_LEN) +LJFOLDF(fload_str_len_tostr) +{ + if (LJ_LIKELY(J->flags & JIT_F_OPT_FOLD) && fleft->op2 == IRTOSTR_CHAR) + return INTFOLD(1); + return NEXTFOLD; +} + +/* The C type ID of cdata objects is immutable. */ +LJFOLD(FLOAD KGC IRFL_CDATA_CTYPEID) +LJFOLDF(fload_cdata_typeid_kgc) +{ + if (LJ_LIKELY(J->flags & JIT_F_OPT_FOLD)) + return INTFOLD((int32_t)ir_kcdata(fleft)->ctypeid); + return NEXTFOLD; +} + +/* Get the contents of immutable cdata objects. */ +LJFOLD(FLOAD KGC IRFL_CDATA_PTR) +LJFOLD(FLOAD KGC IRFL_CDATA_INT) +LJFOLD(FLOAD KGC IRFL_CDATA_INT64) +LJFOLDF(fload_cdata_int64_kgc) +{ + if (LJ_LIKELY(J->flags & JIT_F_OPT_FOLD)) { + void *p = cdataptr(ir_kcdata(fleft)); + if (irt_is64(fins->t)) + return INT64FOLD(*(uint64_t *)p); + else + return INTFOLD(*(int32_t *)p); + } + return NEXTFOLD; +} + +LJFOLD(FLOAD CNEW IRFL_CDATA_CTYPEID) +LJFOLD(FLOAD CNEWI IRFL_CDATA_CTYPEID) +LJFOLDF(fload_cdata_typeid_cnew) +{ + if (LJ_LIKELY(J->flags & JIT_F_OPT_FOLD)) + return fleft->op1; /* No PHI barrier needed. CNEW/CNEWI op1 is const. */ + return NEXTFOLD; +} + +/* Pointer, int and int64 cdata objects are immutable. */ +LJFOLD(FLOAD CNEWI IRFL_CDATA_PTR) +LJFOLD(FLOAD CNEWI IRFL_CDATA_INT) +LJFOLD(FLOAD CNEWI IRFL_CDATA_INT64) +LJFOLDF(fload_cdata_ptr_int64_cnew) +{ + if (LJ_LIKELY(J->flags & JIT_F_OPT_FOLD)) + return fleft->op2; /* Fold even across PHI to avoid allocations. */ + return NEXTFOLD; +} + +LJFOLD(FLOAD any IRFL_STR_LEN) +LJFOLD(FLOAD any IRFL_FUNC_ENV) +LJFOLD(FLOAD any IRFL_THREAD_ENV) +LJFOLD(FLOAD any IRFL_CDATA_CTYPEID) +LJFOLD(FLOAD any IRFL_CDATA_PTR) +LJFOLD(FLOAD any IRFL_CDATA_INT) +LJFOLD(FLOAD any IRFL_CDATA_INT64) +LJFOLD(VLOAD any any) /* Vararg loads have no corresponding stores. */ +LJFOLDX(lj_opt_cse) + +/* All other field loads need alias analysis. */ +LJFOLD(FLOAD any any) +LJFOLDX(lj_opt_fwd_fload) + +/* This is for LOOP only. Recording handles SLOADs internally. */ +LJFOLD(SLOAD any any) +LJFOLDF(fwd_sload) +{ + if ((fins->op2 & IRSLOAD_FRAME)) { + TRef tr = lj_opt_cse(J); + return tref_ref(tr) < J->chain[IR_RETF] ? EMITFOLD : tr; + } else { + lua_assert(J->slot[fins->op1] != 0); + return J->slot[fins->op1]; + } +} + +/* Only fold for KKPTR. The pointer _and_ the contents must be const. */ +LJFOLD(XLOAD KKPTR any) +LJFOLDF(xload_kptr) +{ + TRef tr = kfold_xload(J, fins, ir_kptr(fleft)); + return tr ? tr : NEXTFOLD; +} + +LJFOLD(XLOAD any any) +LJFOLDX(lj_opt_fwd_xload) + +/* -- Write barriers ------------------------------------------------------ */ + +/* Write barriers are amenable to CSE, but not across any incremental +** GC steps. +** +** The same logic applies to open upvalue references, because a stack +** may be resized during a GC step (not the current stack, but maybe that +** of a coroutine). +*/ +LJFOLD(TBAR any) +LJFOLD(OBAR any any) +LJFOLD(UREFO any any) +LJFOLDF(barrier_tab) +{ + TRef tr = lj_opt_cse(J); + if (gcstep_barrier(J, tref_ref(tr))) /* CSE across GC step? */ + return EMITFOLD; /* Raw emit. Assumes fins is left intact by CSE. */ + return tr; +} + +LJFOLD(TBAR TNEW) +LJFOLD(TBAR TDUP) +LJFOLDF(barrier_tnew_tdup) +{ + /* New tables are always white and never need a barrier. */ + if (fins->op1 < J->chain[IR_LOOP]) /* Except across a GC step. */ + return NEXTFOLD; + return DROPFOLD; +} + +/* -- Profiling ----------------------------------------------------------- */ + +LJFOLD(PROF any any) +LJFOLDF(prof) +{ + IRRef ref = J->chain[IR_PROF]; + if (ref+1 == J->cur.nins) /* Drop neighbouring IR_PROF. */ + return ref; + return EMITFOLD; +} + +/* -- Stores and allocations ---------------------------------------------- */ + +/* Stores and allocations cannot be folded or passed on to CSE in general. +** But some stores can be eliminated with dead-store elimination (DSE). +** +** Caveat: *all* stores and allocs must be listed here or they end up at CSE! +*/ + +LJFOLD(ASTORE any any) +LJFOLD(HSTORE any any) +LJFOLDX(lj_opt_dse_ahstore) + +LJFOLD(USTORE any any) +LJFOLDX(lj_opt_dse_ustore) + +LJFOLD(FSTORE any any) +LJFOLDX(lj_opt_dse_fstore) + +LJFOLD(XSTORE any any) +LJFOLDX(lj_opt_dse_xstore) + +LJFOLD(NEWREF any any) /* Treated like a store. */ +LJFOLD(CALLA any any) +LJFOLD(CALLL any any) /* Safeguard fallback. */ +LJFOLD(CALLS any any) +LJFOLD(CALLXS any any) +LJFOLD(XBAR) +LJFOLD(RETF any any) /* Modifies BASE. */ +LJFOLD(TNEW any any) +LJFOLD(TDUP any) +LJFOLD(CNEW any any) +LJFOLD(XSNEW any any) +LJFOLD(BUFHDR any any) +LJFOLDX(lj_ir_emit) + +/* ------------------------------------------------------------------------ */ + +/* Every entry in the generated hash table is a 32 bit pattern: +** +** xxxxxxxx iiiiiii lllllll rrrrrrrrrr +** +** xxxxxxxx = 8 bit index into fold function table +** iiiiiii = 7 bit folded instruction opcode +** lllllll = 7 bit left instruction opcode +** rrrrrrrrrr = 8 bit right instruction opcode or 10 bits from literal field +*/ + +#include "lj_folddef.h" + +/* ------------------------------------------------------------------------ */ + +/* Fold IR instruction. */ +TRef LJ_FASTCALL lj_opt_fold(jit_State *J) +{ + uint32_t key, any; + IRRef ref; + + if (LJ_UNLIKELY((J->flags & JIT_F_OPT_MASK) != JIT_F_OPT_DEFAULT)) { + lua_assert(((JIT_F_OPT_FOLD|JIT_F_OPT_FWD|JIT_F_OPT_CSE|JIT_F_OPT_DSE) | + JIT_F_OPT_DEFAULT) == JIT_F_OPT_DEFAULT); + /* Folding disabled? Chain to CSE, but not for loads/stores/allocs. */ + if (!(J->flags & JIT_F_OPT_FOLD) && irm_kind(lj_ir_mode[fins->o]) == IRM_N) + return lj_opt_cse(J); + + /* No FOLD, forwarding or CSE? Emit raw IR for loads, except for SLOAD. */ + if ((J->flags & (JIT_F_OPT_FOLD|JIT_F_OPT_FWD|JIT_F_OPT_CSE)) != + (JIT_F_OPT_FOLD|JIT_F_OPT_FWD|JIT_F_OPT_CSE) && + irm_kind(lj_ir_mode[fins->o]) == IRM_L && fins->o != IR_SLOAD) + return lj_ir_emit(J); + + /* No FOLD or DSE? Emit raw IR for stores. */ + if ((J->flags & (JIT_F_OPT_FOLD|JIT_F_OPT_DSE)) != + (JIT_F_OPT_FOLD|JIT_F_OPT_DSE) && + irm_kind(lj_ir_mode[fins->o]) == IRM_S) + return lj_ir_emit(J); + } + + /* Fold engine start/retry point. */ +retry: + /* Construct key from opcode and operand opcodes (unless literal/none). */ + key = ((uint32_t)fins->o << 17); + if (fins->op1 >= J->cur.nk) { + key += (uint32_t)IR(fins->op1)->o << 10; + *fleft = *IR(fins->op1); + if (fins->op1 < REF_TRUE) + fleft[1] = IR(fins->op1)[1]; + } + if (fins->op2 >= J->cur.nk) { + key += (uint32_t)IR(fins->op2)->o; + *fright = *IR(fins->op2); + if (fins->op2 < REF_TRUE) + fright[1] = IR(fins->op2)[1]; + } else { + key += (fins->op2 & 0x3ffu); /* Literal mask. Must include IRCONV_*MASK. */ + } + + /* Check for a match in order from most specific to least specific. */ + any = 0; + for (;;) { + uint32_t k = key | (any & 0x1ffff); + uint32_t h = fold_hashkey(k); + uint32_t fh = fold_hash[h]; /* Lookup key in semi-perfect hash table. */ + if ((fh & 0xffffff) == k || (fh = fold_hash[h+1], (fh & 0xffffff) == k)) { + ref = (IRRef)tref_ref(fold_func[fh >> 24](J)); + if (ref != NEXTFOLD) + break; + } + if (any == 0xfffff) /* Exhausted folding. Pass on to CSE. */ + return lj_opt_cse(J); + any = (any | (any >> 10)) ^ 0xffc00; + } + + /* Return value processing, ordered by frequency. */ + if (LJ_LIKELY(ref >= MAX_FOLD)) + return TREF(ref, irt_t(IR(ref)->t)); + if (ref == RETRYFOLD) + goto retry; + if (ref == KINTFOLD) + return lj_ir_kint(J, fins->i); + if (ref == FAILFOLD) + lj_trace_err(J, LJ_TRERR_GFAIL); + lua_assert(ref == DROPFOLD); + return REF_DROP; +} + +/* -- Common-Subexpression Elimination ------------------------------------ */ + +/* CSE an IR instruction. This is very fast due to the skip-list chains. */ +TRef LJ_FASTCALL lj_opt_cse(jit_State *J) +{ + /* Avoid narrow to wide store-to-load forwarding stall */ + IRRef2 op12 = (IRRef2)fins->op1 + ((IRRef2)fins->op2 << 16); + IROp op = fins->o; + if (LJ_LIKELY(J->flags & JIT_F_OPT_CSE)) { + /* Limited search for same operands in per-opcode chain. */ + IRRef ref = J->chain[op]; + IRRef lim = fins->op1; + if (fins->op2 > lim) lim = fins->op2; /* Relies on lit < REF_BIAS. */ + while (ref > lim) { + if (IR(ref)->op12 == op12) + return TREF(ref, irt_t(IR(ref)->t)); /* Common subexpression found. */ + ref = IR(ref)->prev; + } + } + /* Otherwise emit IR (inlined for speed). */ + { + IRRef ref = lj_ir_nextins(J); + IRIns *ir = IR(ref); + ir->prev = J->chain[op]; + ir->op12 = op12; + J->chain[op] = (IRRef1)ref; + ir->o = fins->o; + J->guardemit.irt |= fins->t.irt; + return TREF(ref, irt_t((ir->t = fins->t))); + } +} + +/* CSE with explicit search limit. */ +TRef LJ_FASTCALL lj_opt_cselim(jit_State *J, IRRef lim) +{ + IRRef ref = J->chain[fins->o]; + IRRef2 op12 = (IRRef2)fins->op1 + ((IRRef2)fins->op2 << 16); + while (ref > lim) { + if (IR(ref)->op12 == op12) + return ref; + ref = IR(ref)->prev; + } + return lj_ir_emit(J); +} + +/* ------------------------------------------------------------------------ */ + +#undef IR +#undef fins +#undef fleft +#undef fright +#undef knumleft +#undef knumright +#undef emitir + +#endif diff --git a/lib/LuaJIT/lj_opt_fold.o b/lib/LuaJIT/lj_opt_fold.o new file mode 100644 index 0000000000000000000000000000000000000000..09bc6b45d43ff8ef20e4e12efeb11374f4dea5ab GIT binary patch literal 47336 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 Md+0je>Gk)207IqF*#H0l literal 0 HcmV?d00001 diff --git a/lib/LuaJIT/lj_opt_fold_dyn.o b/lib/LuaJIT/lj_opt_fold_dyn.o new file mode 100644 index 0000000000000000000000000000000000000000..09bc6b45d43ff8ef20e4e12efeb11374f4dea5ab GIT binary patch literal 47336 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 Md+0je>Gk)207IqF*#H0l literal 0 HcmV?d00001 diff --git a/lib/LuaJIT/lj_opt_loop.c b/lib/LuaJIT/lj_opt_loop.c new file mode 100644 index 0000000..441b8ad --- /dev/null +++ b/lib/LuaJIT/lj_opt_loop.c @@ -0,0 +1,449 @@ +/* +** LOOP: Loop Optimizations. +** Copyright (C) 2005-2017 Mike Pall. See Copyright Notice in luajit.h +*/ + +#define lj_opt_loop_c +#define LUA_CORE + +#include "lj_obj.h" + +#if LJ_HASJIT + +#include "lj_err.h" +#include "lj_buf.h" +#include "lj_ir.h" +#include "lj_jit.h" +#include "lj_iropt.h" +#include "lj_trace.h" +#include "lj_snap.h" +#include "lj_vm.h" + +/* Loop optimization: +** +** Traditional Loop-Invariant Code Motion (LICM) splits the instructions +** of a loop into invariant and variant instructions. The invariant +** instructions are hoisted out of the loop and only the variant +** instructions remain inside the loop body. +** +** Unfortunately LICM is mostly useless for compiling dynamic languages. +** The IR has many guards and most of the subsequent instructions are +** control-dependent on them. The first non-hoistable guard would +** effectively prevent hoisting of all subsequent instructions. +** +** That's why we use a special form of unrolling using copy-substitution, +** combined with redundancy elimination: +** +** The recorded instruction stream is re-emitted to the compiler pipeline +** with substituted operands. The substitution table is filled with the +** refs returned by re-emitting each instruction. This can be done +** on-the-fly, because the IR is in strict SSA form, where every ref is +** defined before its use. +** +** This aproach generates two code sections, separated by the LOOP +** instruction: +** +** 1. The recorded instructions form a kind of pre-roll for the loop. It +** contains a mix of invariant and variant instructions and performs +** exactly one loop iteration (but not necessarily the 1st iteration). +** +** 2. The loop body contains only the variant instructions and performs +** all remaining loop iterations. +** +** On first sight that looks like a waste of space, because the variant +** instructions are present twice. But the key insight is that the +** pre-roll honors the control-dependencies for *both* the pre-roll itself +** *and* the loop body! +** +** It also means one doesn't have to explicitly model control-dependencies +** (which, BTW, wouldn't help LICM much). And it's much easier to +** integrate sparse snapshotting with this approach. +** +** One of the nicest aspects of this approach is that all of the +** optimizations of the compiler pipeline (FOLD, CSE, FWD, etc.) can be +** reused with only minor restrictions (e.g. one should not fold +** instructions across loop-carried dependencies). +** +** But in general all optimizations can be applied which only need to look +** backwards into the generated instruction stream. At any point in time +** during the copy-substitution process this contains both a static loop +** iteration (the pre-roll) and a dynamic one (from the to-be-copied +** instruction up to the end of the partial loop body). +** +** Since control-dependencies are implicitly kept, CSE also applies to all +** kinds of guards. The major advantage is that all invariant guards can +** be hoisted, too. +** +** Load/store forwarding works across loop iterations, too. This is +** important if loop-carried dependencies are kept in upvalues or tables. +** E.g. 'self.idx = self.idx + 1' deep down in some OO-style method may +** become a forwarded loop-recurrence after inlining. +** +** Since the IR is in SSA form, loop-carried dependencies have to be +** modeled with PHI instructions. The potential candidates for PHIs are +** collected on-the-fly during copy-substitution. After eliminating the +** redundant ones, PHI instructions are emitted *below* the loop body. +** +** Note that this departure from traditional SSA form doesn't change the +** semantics of the PHI instructions themselves. But it greatly simplifies +** on-the-fly generation of the IR and the machine code. +*/ + +/* Some local macros to save typing. Undef'd at the end. */ +#define IR(ref) (&J->cur.ir[(ref)]) + +/* Pass IR on to next optimization in chain (FOLD). */ +#define emitir(ot, a, b) (lj_ir_set(J, (ot), (a), (b)), lj_opt_fold(J)) + +/* Emit raw IR without passing through optimizations. */ +#define emitir_raw(ot, a, b) (lj_ir_set(J, (ot), (a), (b)), lj_ir_emit(J)) + +/* -- PHI elimination ----------------------------------------------------- */ + +/* Emit or eliminate collected PHIs. */ +static void loop_emit_phi(jit_State *J, IRRef1 *subst, IRRef1 *phi, IRRef nphi, + SnapNo onsnap) +{ + int passx = 0; + IRRef i, j, nslots; + IRRef invar = J->chain[IR_LOOP]; + /* Pass #1: mark redundant and potentially redundant PHIs. */ + for (i = 0, j = 0; i < nphi; i++) { + IRRef lref = phi[i]; + IRRef rref = subst[lref]; + if (lref == rref || rref == REF_DROP) { /* Invariants are redundant. */ + irt_clearphi(IR(lref)->t); + } else { + phi[j++] = (IRRef1)lref; + if (!(IR(rref)->op1 == lref || IR(rref)->op2 == lref)) { + /* Quick check for simple recurrences failed, need pass2. */ + irt_setmark(IR(lref)->t); + passx = 1; + } + } + } + nphi = j; + /* Pass #2: traverse variant part and clear marks of non-redundant PHIs. */ + if (passx) { + SnapNo s; + for (i = J->cur.nins-1; i > invar; i--) { + IRIns *ir = IR(i); + if (!irref_isk(ir->op2)) irt_clearmark(IR(ir->op2)->t); + if (!irref_isk(ir->op1)) { + irt_clearmark(IR(ir->op1)->t); + if (ir->op1 < invar && + ir->o >= IR_CALLN && ir->o <= IR_CARG) { /* ORDER IR */ + ir = IR(ir->op1); + while (ir->o == IR_CARG) { + if (!irref_isk(ir->op2)) irt_clearmark(IR(ir->op2)->t); + if (irref_isk(ir->op1)) break; + ir = IR(ir->op1); + irt_clearmark(ir->t); + } + } + } + } + for (s = J->cur.nsnap-1; s >= onsnap; s--) { + SnapShot *snap = &J->cur.snap[s]; + SnapEntry *map = &J->cur.snapmap[snap->mapofs]; + MSize n, nent = snap->nent; + for (n = 0; n < nent; n++) { + IRRef ref = snap_ref(map[n]); + if (!irref_isk(ref)) irt_clearmark(IR(ref)->t); + } + } + } + /* Pass #3: add PHIs for variant slots without a corresponding SLOAD. */ + nslots = J->baseslot+J->maxslot; + for (i = 1; i < nslots; i++) { + IRRef ref = tref_ref(J->slot[i]); + while (!irref_isk(ref) && ref != subst[ref]) { + IRIns *ir = IR(ref); + irt_clearmark(ir->t); /* Unmark potential uses, too. */ + if (irt_isphi(ir->t) || irt_ispri(ir->t)) + break; + irt_setphi(ir->t); + if (nphi >= LJ_MAX_PHI) + lj_trace_err(J, LJ_TRERR_PHIOV); + phi[nphi++] = (IRRef1)ref; + ref = subst[ref]; + if (ref > invar) + break; + } + } + /* Pass #4: propagate non-redundant PHIs. */ + while (passx) { + passx = 0; + for (i = 0; i < nphi; i++) { + IRRef lref = phi[i]; + IRIns *ir = IR(lref); + if (!irt_ismarked(ir->t)) { /* Propagate only from unmarked PHIs. */ + IRIns *irr = IR(subst[lref]); + if (irt_ismarked(irr->t)) { /* Right ref points to other PHI? */ + irt_clearmark(irr->t); /* Mark that PHI as non-redundant. */ + passx = 1; /* Retry. */ + } + } + } + } + /* Pass #5: emit PHI instructions or eliminate PHIs. */ + for (i = 0; i < nphi; i++) { + IRRef lref = phi[i]; + IRIns *ir = IR(lref); + if (!irt_ismarked(ir->t)) { /* Emit PHI if not marked. */ + IRRef rref = subst[lref]; + if (rref > invar) + irt_setphi(IR(rref)->t); + emitir_raw(IRT(IR_PHI, irt_type(ir->t)), lref, rref); + } else { /* Otherwise eliminate PHI. */ + irt_clearmark(ir->t); + irt_clearphi(ir->t); + } + } +} + +/* -- Loop unrolling using copy-substitution ------------------------------ */ + +/* Copy-substitute snapshot. */ +static void loop_subst_snap(jit_State *J, SnapShot *osnap, + SnapEntry *loopmap, IRRef1 *subst) +{ + SnapEntry *nmap, *omap = &J->cur.snapmap[osnap->mapofs]; + SnapEntry *nextmap = &J->cur.snapmap[snap_nextofs(&J->cur, osnap)]; + MSize nmapofs; + MSize on, ln, nn, onent = osnap->nent; + BCReg nslots = osnap->nslots; + SnapShot *snap = &J->cur.snap[J->cur.nsnap]; + if (irt_isguard(J->guardemit)) { /* Guard inbetween? */ + nmapofs = J->cur.nsnapmap; + J->cur.nsnap++; /* Add new snapshot. */ + } else { /* Otherwise overwrite previous snapshot. */ + snap--; + nmapofs = snap->mapofs; + } + J->guardemit.irt = 0; + /* Setup new snapshot. */ + snap->mapofs = (uint32_t)nmapofs; + snap->ref = (IRRef1)J->cur.nins; + snap->nslots = nslots; + snap->topslot = osnap->topslot; + snap->count = 0; + nmap = &J->cur.snapmap[nmapofs]; + /* Substitute snapshot slots. */ + on = ln = nn = 0; + while (on < onent) { + SnapEntry osn = omap[on], lsn = loopmap[ln]; + if (snap_slot(lsn) < snap_slot(osn)) { /* Copy slot from loop map. */ + nmap[nn++] = lsn; + ln++; + } else { /* Copy substituted slot from snapshot map. */ + if (snap_slot(lsn) == snap_slot(osn)) ln++; /* Shadowed loop slot. */ + if (!irref_isk(snap_ref(osn))) + osn = snap_setref(osn, subst[snap_ref(osn)]); + nmap[nn++] = osn; + on++; + } + } + while (snap_slot(loopmap[ln]) < nslots) /* Copy remaining loop slots. */ + nmap[nn++] = loopmap[ln++]; + snap->nent = (uint8_t)nn; + omap += onent; + nmap += nn; + while (omap < nextmap) /* Copy PC + frame links. */ + *nmap++ = *omap++; + J->cur.nsnapmap = (uint32_t)(nmap - J->cur.snapmap); +} + +typedef struct LoopState { + jit_State *J; + IRRef1 *subst; + MSize sizesubst; +} LoopState; + +/* Unroll loop. */ +static void loop_unroll(LoopState *lps) +{ + jit_State *J = lps->J; + IRRef1 phi[LJ_MAX_PHI]; + uint32_t nphi = 0; + IRRef1 *subst; + SnapNo onsnap; + SnapShot *osnap, *loopsnap; + SnapEntry *loopmap, *psentinel; + IRRef ins, invar; + + /* Allocate substitution table. + ** Only non-constant refs in [REF_BIAS,invar) are valid indexes. + */ + invar = J->cur.nins; + lps->sizesubst = invar - REF_BIAS; + lps->subst = lj_mem_newvec(J->L, lps->sizesubst, IRRef1); + subst = lps->subst - REF_BIAS; + subst[REF_BASE] = REF_BASE; + + /* LOOP separates the pre-roll from the loop body. */ + emitir_raw(IRTG(IR_LOOP, IRT_NIL), 0, 0); + + /* Grow snapshot buffer and map for copy-substituted snapshots. + ** Need up to twice the number of snapshots minus #0 and loop snapshot. + ** Need up to twice the number of entries plus fallback substitutions + ** from the loop snapshot entries for each new snapshot. + ** Caveat: both calls may reallocate J->cur.snap and J->cur.snapmap! + */ + onsnap = J->cur.nsnap; + lj_snap_grow_buf(J, 2*onsnap-2); + lj_snap_grow_map(J, J->cur.nsnapmap*2+(onsnap-2)*J->cur.snap[onsnap-1].nent); + + /* The loop snapshot is used for fallback substitutions. */ + loopsnap = &J->cur.snap[onsnap-1]; + loopmap = &J->cur.snapmap[loopsnap->mapofs]; + /* The PC of snapshot #0 and the loop snapshot must match. */ + psentinel = &loopmap[loopsnap->nent]; + lua_assert(*psentinel == J->cur.snapmap[J->cur.snap[0].nent]); + *psentinel = SNAP(255, 0, 0); /* Replace PC with temporary sentinel. */ + + /* Start substitution with snapshot #1 (#0 is empty for root traces). */ + osnap = &J->cur.snap[1]; + + /* Copy and substitute all recorded instructions and snapshots. */ + for (ins = REF_FIRST; ins < invar; ins++) { + IRIns *ir; + IRRef op1, op2; + + if (ins >= osnap->ref) /* Instruction belongs to next snapshot? */ + loop_subst_snap(J, osnap++, loopmap, subst); /* Copy-substitute it. */ + + /* Substitute instruction operands. */ + ir = IR(ins); + op1 = ir->op1; + if (!irref_isk(op1)) op1 = subst[op1]; + op2 = ir->op2; + if (!irref_isk(op2)) op2 = subst[op2]; + if (irm_kind(lj_ir_mode[ir->o]) == IRM_N && + op1 == ir->op1 && op2 == ir->op2) { /* Regular invariant ins? */ + subst[ins] = (IRRef1)ins; /* Shortcut. */ + } else { + /* Re-emit substituted instruction to the FOLD/CSE/etc. pipeline. */ + IRType1 t = ir->t; /* Get this first, since emitir may invalidate ir. */ + IRRef ref = tref_ref(emitir(ir->ot & ~IRT_ISPHI, op1, op2)); + subst[ins] = (IRRef1)ref; + if (ref != ins) { + IRIns *irr = IR(ref); + if (ref < invar) { /* Loop-carried dependency? */ + /* Potential PHI? */ + if (!irref_isk(ref) && !irt_isphi(irr->t) && !irt_ispri(irr->t)) { + irt_setphi(irr->t); + if (nphi >= LJ_MAX_PHI) + lj_trace_err(J, LJ_TRERR_PHIOV); + phi[nphi++] = (IRRef1)ref; + } + /* Check all loop-carried dependencies for type instability. */ + if (!irt_sametype(t, irr->t)) { + if (irt_isinteger(t) && irt_isinteger(irr->t)) + continue; + else if (irt_isnum(t) && irt_isinteger(irr->t)) /* Fix int->num. */ + ref = tref_ref(emitir(IRTN(IR_CONV), ref, IRCONV_NUM_INT)); + else if (irt_isnum(irr->t) && irt_isinteger(t)) /* Fix num->int. */ + ref = tref_ref(emitir(IRTGI(IR_CONV), ref, + IRCONV_INT_NUM|IRCONV_CHECK)); + else + lj_trace_err(J, LJ_TRERR_TYPEINS); + subst[ins] = (IRRef1)ref; + irr = IR(ref); + goto phiconv; + } + } else if (ref != REF_DROP && irr->o == IR_CONV && + ref > invar && irr->op1 < invar) { + /* May need an extra PHI for a CONV. */ + ref = irr->op1; + irr = IR(ref); + phiconv: + if (ref < invar && !irref_isk(ref) && !irt_isphi(irr->t)) { + irt_setphi(irr->t); + if (nphi >= LJ_MAX_PHI) + lj_trace_err(J, LJ_TRERR_PHIOV); + phi[nphi++] = (IRRef1)ref; + } + } + } + } + } + if (!irt_isguard(J->guardemit)) /* Drop redundant snapshot. */ + J->cur.nsnapmap = (uint32_t)J->cur.snap[--J->cur.nsnap].mapofs; + lua_assert(J->cur.nsnapmap <= J->sizesnapmap); + *psentinel = J->cur.snapmap[J->cur.snap[0].nent]; /* Restore PC. */ + + loop_emit_phi(J, subst, phi, nphi, onsnap); +} + +/* Undo any partial changes made by the loop optimization. */ +static void loop_undo(jit_State *J, IRRef ins, SnapNo nsnap, MSize nsnapmap) +{ + ptrdiff_t i; + SnapShot *snap = &J->cur.snap[nsnap-1]; + SnapEntry *map = J->cur.snapmap; + map[snap->mapofs + snap->nent] = map[J->cur.snap[0].nent]; /* Restore PC. */ + J->cur.nsnapmap = (uint32_t)nsnapmap; + J->cur.nsnap = nsnap; + J->guardemit.irt = 0; + lj_ir_rollback(J, ins); + for (i = 0; i < BPROP_SLOTS; i++) { /* Remove backprop. cache entries. */ + BPropEntry *bp = &J->bpropcache[i]; + if (bp->val >= ins) + bp->key = 0; + } + for (ins--; ins >= REF_FIRST; ins--) { /* Remove flags. */ + IRIns *ir = IR(ins); + irt_clearphi(ir->t); + irt_clearmark(ir->t); + } +} + +/* Protected callback for loop optimization. */ +static TValue *cploop_opt(lua_State *L, lua_CFunction dummy, void *ud) +{ + UNUSED(L); UNUSED(dummy); + loop_unroll((LoopState *)ud); + return NULL; +} + +/* Loop optimization. */ +int lj_opt_loop(jit_State *J) +{ + IRRef nins = J->cur.nins; + SnapNo nsnap = J->cur.nsnap; + MSize nsnapmap = J->cur.nsnapmap; + LoopState lps; + int errcode; + lps.J = J; + lps.subst = NULL; + lps.sizesubst = 0; + errcode = lj_vm_cpcall(J->L, NULL, &lps, cploop_opt); + lj_mem_freevec(J2G(J), lps.subst, lps.sizesubst, IRRef1); + if (LJ_UNLIKELY(errcode)) { + lua_State *L = J->L; + if (errcode == LUA_ERRRUN && tvisnumber(L->top-1)) { /* Trace error? */ + int32_t e = numberVint(L->top-1); + switch ((TraceError)e) { + case LJ_TRERR_TYPEINS: /* Type instability. */ + case LJ_TRERR_GFAIL: /* Guard would always fail. */ + /* Unrolling via recording fixes many cases, e.g. a flipped boolean. */ + if (--J->instunroll < 0) /* But do not unroll forever. */ + break; + L->top--; /* Remove error object. */ + loop_undo(J, nins, nsnap, nsnapmap); + return 1; /* Loop optimization failed, continue recording. */ + default: + break; + } + } + lj_err_throw(L, errcode); /* Propagate all other errors. */ + } + return 0; /* Loop optimization is ok. */ +} + +#undef IR +#undef emitir +#undef emitir_raw + +#endif diff --git a/lib/LuaJIT/lj_opt_loop.o b/lib/LuaJIT/lj_opt_loop.o new file mode 100644 index 0000000000000000000000000000000000000000..9b570d4ffce8fbfee6bd6d4b1f64c5e647d1464f GIT binary patch literal 5072 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.ir[(ref)]) +#define fins (&J->fold.ins) +#define fleft (J->fold.left) +#define fright (J->fold.right) + +/* +** Caveat #1: return value is not always a TRef -- only use with tref_ref(). +** Caveat #2: FWD relies on active CSE for xREF operands -- see lj_opt_fold(). +*/ + +/* Return values from alias analysis. */ +typedef enum { + ALIAS_NO, /* The two refs CANNOT alias (exact). */ + ALIAS_MAY, /* The two refs MAY alias (inexact). */ + ALIAS_MUST /* The two refs MUST alias (exact). */ +} AliasRet; + +/* -- ALOAD/HLOAD forwarding and ASTORE/HSTORE elimination ---------------- */ + +/* Simplified escape analysis: check for intervening stores. */ +static AliasRet aa_escape(jit_State *J, IRIns *ir, IRIns *stop) +{ + IRRef ref = (IRRef)(ir - J->cur.ir); /* The ref that might be stored. */ + for (ir++; ir < stop; ir++) + if (ir->op2 == ref && + (ir->o == IR_ASTORE || ir->o == IR_HSTORE || + ir->o == IR_USTORE || ir->o == IR_FSTORE)) + return ALIAS_MAY; /* Reference was stored and might alias. */ + return ALIAS_NO; /* Reference was not stored. */ +} + +/* Alias analysis for two different table references. */ +static AliasRet aa_table(jit_State *J, IRRef ta, IRRef tb) +{ + IRIns *taba = IR(ta), *tabb = IR(tb); + int newa, newb; + lua_assert(ta != tb); + lua_assert(irt_istab(taba->t) && irt_istab(tabb->t)); + /* Disambiguate new allocations. */ + newa = (taba->o == IR_TNEW || taba->o == IR_TDUP); + newb = (tabb->o == IR_TNEW || tabb->o == IR_TDUP); + if (newa && newb) + return ALIAS_NO; /* Two different allocations never alias. */ + if (newb) { /* At least one allocation? */ + IRIns *tmp = taba; taba = tabb; tabb = tmp; + } else if (!newa) { + return ALIAS_MAY; /* Anything else: we just don't know. */ + } + return aa_escape(J, taba, tabb); +} + +/* Alias analysis for array and hash access using key-based disambiguation. */ +static AliasRet aa_ahref(jit_State *J, IRIns *refa, IRIns *refb) +{ + IRRef ka = refa->op2; + IRRef kb = refb->op2; + IRIns *keya, *keyb; + IRRef ta, tb; + if (refa == refb) + return ALIAS_MUST; /* Shortcut for same refs. */ + keya = IR(ka); + if (keya->o == IR_KSLOT) { ka = keya->op1; keya = IR(ka); } + keyb = IR(kb); + if (keyb->o == IR_KSLOT) { kb = keyb->op1; keyb = IR(kb); } + ta = (refa->o==IR_HREFK || refa->o==IR_AREF) ? IR(refa->op1)->op1 : refa->op1; + tb = (refb->o==IR_HREFK || refb->o==IR_AREF) ? IR(refb->op1)->op1 : refb->op1; + if (ka == kb) { + /* Same key. Check for same table with different ref (NEWREF vs. HREF). */ + if (ta == tb) + return ALIAS_MUST; /* Same key, same table. */ + else + return aa_table(J, ta, tb); /* Same key, possibly different table. */ + } + if (irref_isk(ka) && irref_isk(kb)) + return ALIAS_NO; /* Different constant keys. */ + if (refa->o == IR_AREF) { + /* Disambiguate array references based on index arithmetic. */ + int32_t ofsa = 0, ofsb = 0; + IRRef basea = ka, baseb = kb; + lua_assert(refb->o == IR_AREF); + /* Gather base and offset from t[base] or t[base+-ofs]. */ + if (keya->o == IR_ADD && irref_isk(keya->op2)) { + basea = keya->op1; + ofsa = IR(keya->op2)->i; + if (basea == kb && ofsa != 0) + return ALIAS_NO; /* t[base+-ofs] vs. t[base]. */ + } + if (keyb->o == IR_ADD && irref_isk(keyb->op2)) { + baseb = keyb->op1; + ofsb = IR(keyb->op2)->i; + if (ka == baseb && ofsb != 0) + return ALIAS_NO; /* t[base] vs. t[base+-ofs]. */ + } + if (basea == baseb && ofsa != ofsb) + return ALIAS_NO; /* t[base+-o1] vs. t[base+-o2] and o1 != o2. */ + } else { + /* Disambiguate hash references based on the type of their keys. */ + lua_assert((refa->o==IR_HREF || refa->o==IR_HREFK || refa->o==IR_NEWREF) && + (refb->o==IR_HREF || refb->o==IR_HREFK || refb->o==IR_NEWREF)); + if (!irt_sametype(keya->t, keyb->t)) + return ALIAS_NO; /* Different key types. */ + } + if (ta == tb) + return ALIAS_MAY; /* Same table, cannot disambiguate keys. */ + else + return aa_table(J, ta, tb); /* Try to disambiguate tables. */ +} + +/* Array and hash load forwarding. */ +static TRef fwd_ahload(jit_State *J, IRRef xref) +{ + IRIns *xr = IR(xref); + IRRef lim = xref; /* Search limit. */ + IRRef ref; + + /* Search for conflicting stores. */ + ref = J->chain[fins->o+IRDELTA_L2S]; + while (ref > xref) { + IRIns *store = IR(ref); + switch (aa_ahref(J, xr, IR(store->op1))) { + case ALIAS_NO: break; /* Continue searching. */ + case ALIAS_MAY: lim = ref; goto cselim; /* Limit search for load. */ + case ALIAS_MUST: return store->op2; /* Store forwarding. */ + } + ref = store->prev; + } + + /* No conflicting store (yet): const-fold loads from allocations. */ + { + IRIns *ir = (xr->o == IR_HREFK || xr->o == IR_AREF) ? IR(xr->op1) : xr; + IRRef tab = ir->op1; + ir = IR(tab); + if (ir->o == IR_TNEW || (ir->o == IR_TDUP && irref_isk(xr->op2))) { + /* A NEWREF with a number key may end up pointing to the array part. + ** But it's referenced from HSTORE and not found in the ASTORE chain. + ** For now simply consider this a conflict without forwarding anything. + */ + if (xr->o == IR_AREF) { + IRRef ref2 = J->chain[IR_NEWREF]; + while (ref2 > tab) { + IRIns *newref = IR(ref2); + if (irt_isnum(IR(newref->op2)->t)) + goto cselim; + ref2 = newref->prev; + } + } + /* NEWREF inhibits CSE for HREF, and dependent FLOADs from HREFK/AREF. + ** But the above search for conflicting stores was limited by xref. + ** So continue searching, limited by the TNEW/TDUP. Store forwarding + ** is ok, too. A conflict does NOT limit the search for a matching load. + */ + while (ref > tab) { + IRIns *store = IR(ref); + switch (aa_ahref(J, xr, IR(store->op1))) { + case ALIAS_NO: break; /* Continue searching. */ + case ALIAS_MAY: goto cselim; /* Conflicting store. */ + case ALIAS_MUST: return store->op2; /* Store forwarding. */ + } + ref = store->prev; + } + lua_assert(ir->o != IR_TNEW || irt_isnil(fins->t)); + if (irt_ispri(fins->t)) { + return TREF_PRI(irt_type(fins->t)); + } else if (irt_isnum(fins->t) || (LJ_DUALNUM && irt_isint(fins->t)) || + irt_isstr(fins->t)) { + TValue keyv; + cTValue *tv; + IRIns *key = IR(xr->op2); + if (key->o == IR_KSLOT) key = IR(key->op1); + lj_ir_kvalue(J->L, &keyv, key); + tv = lj_tab_get(J->L, ir_ktab(IR(ir->op1)), &keyv); + lua_assert(itype2irt(tv) == irt_type(fins->t)); + if (irt_isnum(fins->t)) + return lj_ir_knum_u64(J, tv->u64); + else if (LJ_DUALNUM && irt_isint(fins->t)) + return lj_ir_kint(J, intV(tv)); + else + return lj_ir_kstr(J, strV(tv)); + } + /* Othwerwise: don't intern as a constant. */ + } + } + +cselim: + /* Try to find a matching load. Below the conflicting store, if any. */ + ref = J->chain[fins->o]; + while (ref > lim) { + IRIns *load = IR(ref); + if (load->op1 == xref) + return ref; /* Load forwarding. */ + ref = load->prev; + } + return 0; /* Conflict or no match. */ +} + +/* Reassociate ALOAD across PHIs to handle t[i-1] forwarding case. */ +static TRef fwd_aload_reassoc(jit_State *J) +{ + IRIns *irx = IR(fins->op1); + IRIns *key = IR(irx->op2); + if (key->o == IR_ADD && irref_isk(key->op2)) { + IRIns *add2 = IR(key->op1); + if (add2->o == IR_ADD && irref_isk(add2->op2) && + IR(key->op2)->i == -IR(add2->op2)->i) { + IRRef ref = J->chain[IR_AREF]; + IRRef lim = add2->op1; + if (irx->op1 > lim) lim = irx->op1; + while (ref > lim) { + IRIns *ir = IR(ref); + if (ir->op1 == irx->op1 && ir->op2 == add2->op1) + return fwd_ahload(J, ref); + ref = ir->prev; + } + } + } + return 0; +} + +/* ALOAD forwarding. */ +TRef LJ_FASTCALL lj_opt_fwd_aload(jit_State *J) +{ + IRRef ref; + if ((ref = fwd_ahload(J, fins->op1)) || + (ref = fwd_aload_reassoc(J))) + return ref; + return EMITFOLD; +} + +/* HLOAD forwarding. */ +TRef LJ_FASTCALL lj_opt_fwd_hload(jit_State *J) +{ + IRRef ref = fwd_ahload(J, fins->op1); + if (ref) + return ref; + return EMITFOLD; +} + +/* HREFK forwarding. */ +TRef LJ_FASTCALL lj_opt_fwd_hrefk(jit_State *J) +{ + IRRef tab = fleft->op1; + IRRef ref = J->chain[IR_NEWREF]; + while (ref > tab) { + IRIns *newref = IR(ref); + if (tab == newref->op1) { + if (fright->op1 == newref->op2) + return ref; /* Forward from NEWREF. */ + else + goto docse; + } else if (aa_table(J, tab, newref->op1) != ALIAS_NO) { + goto docse; + } + ref = newref->prev; + } + /* No conflicting NEWREF: key location unchanged for HREFK of TDUP. */ + if (IR(tab)->o == IR_TDUP) + fins->t.irt &= ~IRT_GUARD; /* Drop HREFK guard. */ +docse: + return CSEFOLD; +} + +/* Check whether HREF of TNEW/TDUP can be folded to niltv. */ +int LJ_FASTCALL lj_opt_fwd_href_nokey(jit_State *J) +{ + IRRef lim = fins->op1; /* Search limit. */ + IRRef ref; + + /* The key for an ASTORE may end up in the hash part after a NEWREF. */ + if (irt_isnum(fright->t) && J->chain[IR_NEWREF] > lim) { + ref = J->chain[IR_ASTORE]; + while (ref > lim) { + if (ref < J->chain[IR_NEWREF]) + return 0; /* Conflict. */ + ref = IR(ref)->prev; + } + } + + /* Search for conflicting stores. */ + ref = J->chain[IR_HSTORE]; + while (ref > lim) { + IRIns *store = IR(ref); + if (aa_ahref(J, fins, IR(store->op1)) != ALIAS_NO) + return 0; /* Conflict. */ + ref = store->prev; + } + + return 1; /* No conflict. Can fold to niltv. */ +} + +/* Check whether there's no aliasing table.clear. */ +static int fwd_aa_tab_clear(jit_State *J, IRRef lim, IRRef ta) +{ + IRRef ref = J->chain[IR_CALLS]; + while (ref > lim) { + IRIns *calls = IR(ref); + if (calls->op2 == IRCALL_lj_tab_clear && + (ta == calls->op1 || aa_table(J, ta, calls->op1) != ALIAS_NO)) + return 0; /* Conflict. */ + ref = calls->prev; + } + return 1; /* No conflict. Can safely FOLD/CSE. */ +} + +/* Check whether there's no aliasing NEWREF/table.clear for the left operand. */ +int LJ_FASTCALL lj_opt_fwd_tptr(jit_State *J, IRRef lim) +{ + IRRef ta = fins->op1; + IRRef ref = J->chain[IR_NEWREF]; + while (ref > lim) { + IRIns *newref = IR(ref); + if (ta == newref->op1 || aa_table(J, ta, newref->op1) != ALIAS_NO) + return 0; /* Conflict. */ + ref = newref->prev; + } + return fwd_aa_tab_clear(J, lim, ta); +} + +/* ASTORE/HSTORE elimination. */ +TRef LJ_FASTCALL lj_opt_dse_ahstore(jit_State *J) +{ + IRRef xref = fins->op1; /* xREF reference. */ + IRRef val = fins->op2; /* Stored value reference. */ + IRIns *xr = IR(xref); + IRRef1 *refp = &J->chain[fins->o]; + IRRef ref = *refp; + while (ref > xref) { /* Search for redundant or conflicting stores. */ + IRIns *store = IR(ref); + switch (aa_ahref(J, xr, IR(store->op1))) { + case ALIAS_NO: + break; /* Continue searching. */ + case ALIAS_MAY: /* Store to MAYBE the same location. */ + if (store->op2 != val) /* Conflict if the value is different. */ + goto doemit; + break; /* Otherwise continue searching. */ + case ALIAS_MUST: /* Store to the same location. */ + if (store->op2 == val) /* Same value: drop the new store. */ + return DROPFOLD; + /* Different value: try to eliminate the redundant store. */ + if (ref > J->chain[IR_LOOP]) { /* Quick check to avoid crossing LOOP. */ + IRIns *ir; + /* Check for any intervening guards (includes conflicting loads). */ + for (ir = IR(J->cur.nins-1); ir > store; ir--) + if (irt_isguard(ir->t) || ir->o == IR_CALLL) + goto doemit; /* No elimination possible. */ + /* Remove redundant store from chain and replace with NOP. */ + *refp = store->prev; + store->o = IR_NOP; + store->t.irt = IRT_NIL; + store->op1 = store->op2 = 0; + store->prev = 0; + /* Now emit the new store instead. */ + } + goto doemit; + } + ref = *(refp = &store->prev); + } +doemit: + return EMITFOLD; /* Otherwise we have a conflict or simply no match. */ +} + +/* -- ULOAD forwarding ---------------------------------------------------- */ + +/* The current alias analysis for upvalues is very simplistic. It only +** disambiguates between the unique upvalues of the same function. +** This is good enough for now, since most upvalues are read-only. +** +** A more precise analysis would be feasible with the help of the parser: +** generate a unique key for every upvalue, even across all prototypes. +** Lacking a realistic use-case, it's unclear whether this is beneficial. +*/ +static AliasRet aa_uref(IRIns *refa, IRIns *refb) +{ + if (refa->o != refb->o) + return ALIAS_NO; /* Different UREFx type. */ + if (refa->op1 == refb->op1) { /* Same function. */ + if (refa->op2 == refb->op2) + return ALIAS_MUST; /* Same function, same upvalue idx. */ + else + return ALIAS_NO; /* Same function, different upvalue idx. */ + } else { /* Different functions, check disambiguation hash values. */ + if (((refa->op2 ^ refb->op2) & 0xff)) + return ALIAS_NO; /* Upvalues with different hash values cannot alias. */ + else + return ALIAS_MAY; /* No conclusion can be drawn for same hash value. */ + } +} + +/* ULOAD forwarding. */ +TRef LJ_FASTCALL lj_opt_fwd_uload(jit_State *J) +{ + IRRef uref = fins->op1; + IRRef lim = REF_BASE; /* Search limit. */ + IRIns *xr = IR(uref); + IRRef ref; + + /* Search for conflicting stores. */ + ref = J->chain[IR_USTORE]; + while (ref > lim) { + IRIns *store = IR(ref); + switch (aa_uref(xr, IR(store->op1))) { + case ALIAS_NO: break; /* Continue searching. */ + case ALIAS_MAY: lim = ref; goto cselim; /* Limit search for load. */ + case ALIAS_MUST: return store->op2; /* Store forwarding. */ + } + ref = store->prev; + } + +cselim: + /* Try to find a matching load. Below the conflicting store, if any. */ + + ref = J->chain[IR_ULOAD]; + while (ref > lim) { + IRIns *ir = IR(ref); + if (ir->op1 == uref || + (IR(ir->op1)->op12 == IR(uref)->op12 && IR(ir->op1)->o == IR(uref)->o)) + return ref; /* Match for identical or equal UREFx (non-CSEable UREFO). */ + ref = ir->prev; + } + return lj_ir_emit(J); +} + +/* USTORE elimination. */ +TRef LJ_FASTCALL lj_opt_dse_ustore(jit_State *J) +{ + IRRef xref = fins->op1; /* xREF reference. */ + IRRef val = fins->op2; /* Stored value reference. */ + IRIns *xr = IR(xref); + IRRef1 *refp = &J->chain[IR_USTORE]; + IRRef ref = *refp; + while (ref > xref) { /* Search for redundant or conflicting stores. */ + IRIns *store = IR(ref); + switch (aa_uref(xr, IR(store->op1))) { + case ALIAS_NO: + break; /* Continue searching. */ + case ALIAS_MAY: /* Store to MAYBE the same location. */ + if (store->op2 != val) /* Conflict if the value is different. */ + goto doemit; + break; /* Otherwise continue searching. */ + case ALIAS_MUST: /* Store to the same location. */ + if (store->op2 == val) /* Same value: drop the new store. */ + return DROPFOLD; + /* Different value: try to eliminate the redundant store. */ + if (ref > J->chain[IR_LOOP]) { /* Quick check to avoid crossing LOOP. */ + IRIns *ir; + /* Check for any intervening guards (includes conflicting loads). */ + for (ir = IR(J->cur.nins-1); ir > store; ir--) + if (irt_isguard(ir->t)) + goto doemit; /* No elimination possible. */ + /* Remove redundant store from chain and replace with NOP. */ + *refp = store->prev; + store->o = IR_NOP; + store->t.irt = IRT_NIL; + store->op1 = store->op2 = 0; + store->prev = 0; + if (ref+1 < J->cur.nins && + store[1].o == IR_OBAR && store[1].op1 == xref) { + IRRef1 *bp = &J->chain[IR_OBAR]; + IRIns *obar; + for (obar = IR(*bp); *bp > ref+1; obar = IR(*bp)) + bp = &obar->prev; + /* Remove OBAR, too. */ + *bp = obar->prev; + obar->o = IR_NOP; + obar->t.irt = IRT_NIL; + obar->op1 = obar->op2 = 0; + obar->prev = 0; + } + /* Now emit the new store instead. */ + } + goto doemit; + } + ref = *(refp = &store->prev); + } +doemit: + return EMITFOLD; /* Otherwise we have a conflict or simply no match. */ +} + +/* -- FLOAD forwarding and FSTORE elimination ----------------------------- */ + +/* Alias analysis for field access. +** Field loads are cheap and field stores are rare. +** Simple disambiguation based on field types is good enough. +*/ +static AliasRet aa_fref(jit_State *J, IRIns *refa, IRIns *refb) +{ + if (refa->op2 != refb->op2) + return ALIAS_NO; /* Different fields. */ + if (refa->op1 == refb->op1) + return ALIAS_MUST; /* Same field, same object. */ + else if (refa->op2 >= IRFL_TAB_META && refa->op2 <= IRFL_TAB_NOMM) + return aa_table(J, refa->op1, refb->op1); /* Disambiguate tables. */ + else + return ALIAS_MAY; /* Same field, possibly different object. */ +} + +/* Only the loads for mutable fields end up here (see FOLD). */ +TRef LJ_FASTCALL lj_opt_fwd_fload(jit_State *J) +{ + IRRef oref = fins->op1; /* Object reference. */ + IRRef fid = fins->op2; /* Field ID. */ + IRRef lim = oref; /* Search limit. */ + IRRef ref; + + /* Search for conflicting stores. */ + ref = J->chain[IR_FSTORE]; + while (ref > oref) { + IRIns *store = IR(ref); + switch (aa_fref(J, fins, IR(store->op1))) { + case ALIAS_NO: break; /* Continue searching. */ + case ALIAS_MAY: lim = ref; goto cselim; /* Limit search for load. */ + case ALIAS_MUST: return store->op2; /* Store forwarding. */ + } + ref = store->prev; + } + + /* No conflicting store: const-fold field loads from allocations. */ + if (fid == IRFL_TAB_META) { + IRIns *ir = IR(oref); + if (ir->o == IR_TNEW || ir->o == IR_TDUP) + return lj_ir_knull(J, IRT_TAB); + } + +cselim: + /* Try to find a matching load. Below the conflicting store, if any. */ + return lj_opt_cselim(J, lim); +} + +/* FSTORE elimination. */ +TRef LJ_FASTCALL lj_opt_dse_fstore(jit_State *J) +{ + IRRef fref = fins->op1; /* FREF reference. */ + IRRef val = fins->op2; /* Stored value reference. */ + IRIns *xr = IR(fref); + IRRef1 *refp = &J->chain[IR_FSTORE]; + IRRef ref = *refp; + while (ref > fref) { /* Search for redundant or conflicting stores. */ + IRIns *store = IR(ref); + switch (aa_fref(J, xr, IR(store->op1))) { + case ALIAS_NO: + break; /* Continue searching. */ + case ALIAS_MAY: + if (store->op2 != val) /* Conflict if the value is different. */ + goto doemit; + break; /* Otherwise continue searching. */ + case ALIAS_MUST: + if (store->op2 == val) /* Same value: drop the new store. */ + return DROPFOLD; + /* Different value: try to eliminate the redundant store. */ + if (ref > J->chain[IR_LOOP]) { /* Quick check to avoid crossing LOOP. */ + IRIns *ir; + /* Check for any intervening guards or conflicting loads. */ + for (ir = IR(J->cur.nins-1); ir > store; ir--) + if (irt_isguard(ir->t) || (ir->o == IR_FLOAD && ir->op2 == xr->op2)) + goto doemit; /* No elimination possible. */ + /* Remove redundant store from chain and replace with NOP. */ + *refp = store->prev; + store->o = IR_NOP; + store->t.irt = IRT_NIL; + store->op1 = store->op2 = 0; + store->prev = 0; + /* Now emit the new store instead. */ + } + goto doemit; + } + ref = *(refp = &store->prev); + } +doemit: + return EMITFOLD; /* Otherwise we have a conflict or simply no match. */ +} + +/* -- XLOAD forwarding and XSTORE elimination ----------------------------- */ + +/* Find cdata allocation for a reference (if any). */ +static IRIns *aa_findcnew(jit_State *J, IRIns *ir) +{ + while (ir->o == IR_ADD) { + if (!irref_isk(ir->op1)) { + IRIns *ir1 = aa_findcnew(J, IR(ir->op1)); /* Left-recursion. */ + if (ir1) return ir1; + } + if (irref_isk(ir->op2)) return NULL; + ir = IR(ir->op2); /* Flatten right-recursion. */ + } + return ir->o == IR_CNEW ? ir : NULL; +} + +/* Alias analysis for two cdata allocations. */ +static AliasRet aa_cnew(jit_State *J, IRIns *refa, IRIns *refb) +{ + IRIns *cnewa = aa_findcnew(J, refa); + IRIns *cnewb = aa_findcnew(J, refb); + if (cnewa == cnewb) + return ALIAS_MAY; /* Same allocation or neither is an allocation. */ + if (cnewa && cnewb) + return ALIAS_NO; /* Two different allocations never alias. */ + if (cnewb) { cnewa = cnewb; refb = refa; } + return aa_escape(J, cnewa, refb); +} + +/* Alias analysis for XLOAD/XSTORE. */ +static AliasRet aa_xref(jit_State *J, IRIns *refa, IRIns *xa, IRIns *xb) +{ + ptrdiff_t ofsa = 0, ofsb = 0; + IRIns *refb = IR(xb->op1); + IRIns *basea = refa, *baseb = refb; + if (refa == refb && irt_sametype(xa->t, xb->t)) + return ALIAS_MUST; /* Shortcut for same refs with identical type. */ + /* Offset-based disambiguation. */ + if (refa->o == IR_ADD && irref_isk(refa->op2)) { + IRIns *irk = IR(refa->op2); + basea = IR(refa->op1); + ofsa = (LJ_64 && irk->o == IR_KINT64) ? (ptrdiff_t)ir_k64(irk)->u64 : + (ptrdiff_t)irk->i; + } + if (refb->o == IR_ADD && irref_isk(refb->op2)) { + IRIns *irk = IR(refb->op2); + baseb = IR(refb->op1); + ofsb = (LJ_64 && irk->o == IR_KINT64) ? (ptrdiff_t)ir_k64(irk)->u64 : + (ptrdiff_t)irk->i; + } + /* Treat constified pointers like base vs. base+offset. */ + if (basea->o == IR_KPTR && baseb->o == IR_KPTR) { + ofsb += (char *)ir_kptr(baseb) - (char *)ir_kptr(basea); + baseb = basea; + } + /* This implements (very) strict aliasing rules. + ** Different types do NOT alias, except for differences in signedness. + ** Type punning through unions is allowed (but forces a reload). + */ + if (basea == baseb) { + ptrdiff_t sza = irt_size(xa->t), szb = irt_size(xb->t); + if (ofsa == ofsb) { + if (sza == szb && irt_isfp(xa->t) == irt_isfp(xb->t)) + return ALIAS_MUST; /* Same-sized, same-kind. May need to convert. */ + } else if (ofsa + sza <= ofsb || ofsb + szb <= ofsa) { + return ALIAS_NO; /* Non-overlapping base+-o1 vs. base+-o2. */ + } + /* NYI: extract, extend or reinterpret bits (int <-> fp). */ + return ALIAS_MAY; /* Overlapping or type punning: force reload. */ + } + if (!irt_sametype(xa->t, xb->t) && + !(irt_typerange(xa->t, IRT_I8, IRT_U64) && + ((xa->t.irt - IRT_I8) ^ (xb->t.irt - IRT_I8)) == 1)) + return ALIAS_NO; + /* NYI: structural disambiguation. */ + return aa_cnew(J, basea, baseb); /* Try to disambiguate allocations. */ +} + +/* Return CSEd reference or 0. Caveat: swaps lower ref to the right! */ +static IRRef reassoc_trycse(jit_State *J, IROp op, IRRef op1, IRRef op2) +{ + IRRef ref = J->chain[op]; + IRRef lim = op1; + if (op2 > lim) { lim = op2; op2 = op1; op1 = lim; } + while (ref > lim) { + IRIns *ir = IR(ref); + if (ir->op1 == op1 && ir->op2 == op2) + return ref; + ref = ir->prev; + } + return 0; +} + +/* Reassociate index references. */ +static IRRef reassoc_xref(jit_State *J, IRIns *ir) +{ + ptrdiff_t ofs = 0; + if (ir->o == IR_ADD && irref_isk(ir->op2)) { /* Get constant offset. */ + IRIns *irk = IR(ir->op2); + ofs = (LJ_64 && irk->o == IR_KINT64) ? (ptrdiff_t)ir_k64(irk)->u64 : + (ptrdiff_t)irk->i; + ir = IR(ir->op1); + } + if (ir->o == IR_ADD) { /* Add of base + index. */ + /* Index ref > base ref for loop-carried dependences. Only check op1. */ + IRIns *ir2, *ir1 = IR(ir->op1); + int32_t shift = 0; + IRRef idxref; + /* Determine index shifts. Don't bother with IR_MUL here. */ + if (ir1->o == IR_BSHL && irref_isk(ir1->op2)) + shift = IR(ir1->op2)->i; + else if (ir1->o == IR_ADD && ir1->op1 == ir1->op2) + shift = 1; + else + ir1 = ir; + ir2 = IR(ir1->op1); + /* A non-reassociated add. Must be a loop-carried dependence. */ + if (ir2->o == IR_ADD && irt_isint(ir2->t) && irref_isk(ir2->op2)) + ofs += (ptrdiff_t)IR(ir2->op2)->i << shift; + else + return 0; + idxref = ir2->op1; + /* Try to CSE the reassociated chain. Give up if not found. */ + if (ir1 != ir && + !(idxref = reassoc_trycse(J, ir1->o, idxref, + ir1->o == IR_BSHL ? ir1->op2 : idxref))) + return 0; + if (!(idxref = reassoc_trycse(J, IR_ADD, idxref, ir->op2))) + return 0; + if (ofs != 0) { + IRRef refk = tref_ref(lj_ir_kintp(J, ofs)); + if (!(idxref = reassoc_trycse(J, IR_ADD, idxref, refk))) + return 0; + } + return idxref; /* Success, found a reassociated index reference. Phew. */ + } + return 0; /* Failure. */ +} + +/* XLOAD forwarding. */ +TRef LJ_FASTCALL lj_opt_fwd_xload(jit_State *J) +{ + IRRef xref = fins->op1; + IRIns *xr = IR(xref); + IRRef lim = xref; /* Search limit. */ + IRRef ref; + + if ((fins->op2 & IRXLOAD_READONLY)) + goto cselim; + if ((fins->op2 & IRXLOAD_VOLATILE)) + goto doemit; + + /* Search for conflicting stores. */ + ref = J->chain[IR_XSTORE]; +retry: + if (J->chain[IR_CALLXS] > lim) lim = J->chain[IR_CALLXS]; + if (J->chain[IR_XBAR] > lim) lim = J->chain[IR_XBAR]; + while (ref > lim) { + IRIns *store = IR(ref); + switch (aa_xref(J, xr, fins, store)) { + case ALIAS_NO: break; /* Continue searching. */ + case ALIAS_MAY: lim = ref; goto cselim; /* Limit search for load. */ + case ALIAS_MUST: + /* Emit conversion if the loaded type doesn't match the forwarded type. */ + if (!irt_sametype(fins->t, IR(store->op2)->t)) { + IRType dt = irt_type(fins->t), st = irt_type(IR(store->op2)->t); + if (dt == IRT_I8 || dt == IRT_I16) { /* Trunc + sign-extend. */ + st = dt | IRCONV_SEXT; + dt = IRT_INT; + } else if (dt == IRT_U8 || dt == IRT_U16) { /* Trunc + zero-extend. */ + st = dt; + dt = IRT_INT; + } + fins->ot = IRT(IR_CONV, dt); + fins->op1 = store->op2; + fins->op2 = (dt<<5)|st; + return RETRYFOLD; + } + return store->op2; /* Store forwarding. */ + } + ref = store->prev; + } + +cselim: + /* Try to find a matching load. Below the conflicting store, if any. */ + ref = J->chain[IR_XLOAD]; + while (ref > lim) { + /* CSE for XLOAD depends on the type, but not on the IRXLOAD_* flags. */ + if (IR(ref)->op1 == xref && irt_sametype(IR(ref)->t, fins->t)) + return ref; + ref = IR(ref)->prev; + } + + /* Reassociate XLOAD across PHIs to handle a[i-1] forwarding case. */ + if (!(fins->op2 & IRXLOAD_READONLY) && J->chain[IR_LOOP] && + xref == fins->op1 && (xref = reassoc_xref(J, xr)) != 0) { + ref = J->chain[IR_XSTORE]; + while (ref > lim) /* Skip stores that have already been checked. */ + ref = IR(ref)->prev; + lim = xref; + xr = IR(xref); + goto retry; /* Retry with the reassociated reference. */ + } +doemit: + return EMITFOLD; +} + +/* XSTORE elimination. */ +TRef LJ_FASTCALL lj_opt_dse_xstore(jit_State *J) +{ + IRRef xref = fins->op1; + IRIns *xr = IR(xref); + IRRef lim = xref; /* Search limit. */ + IRRef val = fins->op2; /* Stored value reference. */ + IRRef1 *refp = &J->chain[IR_XSTORE]; + IRRef ref = *refp; + if (J->chain[IR_CALLXS] > lim) lim = J->chain[IR_CALLXS]; + if (J->chain[IR_XBAR] > lim) lim = J->chain[IR_XBAR]; + if (J->chain[IR_XSNEW] > lim) lim = J->chain[IR_XSNEW]; + while (ref > lim) { /* Search for redundant or conflicting stores. */ + IRIns *store = IR(ref); + switch (aa_xref(J, xr, fins, store)) { + case ALIAS_NO: + break; /* Continue searching. */ + case ALIAS_MAY: + if (store->op2 != val) /* Conflict if the value is different. */ + goto doemit; + break; /* Otherwise continue searching. */ + case ALIAS_MUST: + if (store->op2 == val) /* Same value: drop the new store. */ + return DROPFOLD; + /* Different value: try to eliminate the redundant store. */ + if (ref > J->chain[IR_LOOP]) { /* Quick check to avoid crossing LOOP. */ + IRIns *ir; + /* Check for any intervening guards or any XLOADs (no AA performed). */ + for (ir = IR(J->cur.nins-1); ir > store; ir--) + if (irt_isguard(ir->t) || ir->o == IR_XLOAD) + goto doemit; /* No elimination possible. */ + /* Remove redundant store from chain and replace with NOP. */ + *refp = store->prev; + store->o = IR_NOP; + store->t.irt = IRT_NIL; + store->op1 = store->op2 = 0; + store->prev = 0; + /* Now emit the new store instead. */ + } + goto doemit; + } + ref = *(refp = &store->prev); + } +doemit: + return EMITFOLD; /* Otherwise we have a conflict or simply no match. */ +} + +/* -- Forwarding of lj_tab_len -------------------------------------------- */ + +/* This is rather simplistic right now, but better than nothing. */ +TRef LJ_FASTCALL lj_opt_fwd_tab_len(jit_State *J) +{ + IRRef tab = fins->op1; /* Table reference. */ + IRRef lim = tab; /* Search limit. */ + IRRef ref; + + /* Any ASTORE is a conflict and limits the search. */ + if (J->chain[IR_ASTORE] > lim) lim = J->chain[IR_ASTORE]; + + /* Search for conflicting HSTORE with numeric key. */ + ref = J->chain[IR_HSTORE]; + while (ref > lim) { + IRIns *store = IR(ref); + IRIns *href = IR(store->op1); + IRIns *key = IR(href->op2); + if (irt_isnum(key->o == IR_KSLOT ? IR(key->op1)->t : key->t)) { + lim = ref; /* Conflicting store found, limits search for TLEN. */ + break; + } + ref = store->prev; + } + + /* Search for aliasing table.clear. */ + if (!fwd_aa_tab_clear(J, lim, tab)) + return lj_ir_emit(J); + + /* Try to find a matching load. Below the conflicting store, if any. */ + return lj_opt_cselim(J, lim); +} + +/* -- ASTORE/HSTORE previous type analysis -------------------------------- */ + +/* Check whether the previous value for a table store is non-nil. +** This can be derived either from a previous store or from a previous +** load (because all loads from tables perform a type check). +** +** The result of the analysis can be used to avoid the metatable check +** and the guard against HREF returning niltv. Both of these are cheap, +** so let's not spend too much effort on the analysis. +** +** A result of 1 is exact: previous value CANNOT be nil. +** A result of 0 is inexact: previous value MAY be nil. +*/ +int lj_opt_fwd_wasnonnil(jit_State *J, IROpT loadop, IRRef xref) +{ + /* First check stores. */ + IRRef ref = J->chain[loadop+IRDELTA_L2S]; + while (ref > xref) { + IRIns *store = IR(ref); + if (store->op1 == xref) { /* Same xREF. */ + /* A nil store MAY alias, but a non-nil store MUST alias. */ + return !irt_isnil(store->t); + } else if (irt_isnil(store->t)) { /* Must check any nil store. */ + IRRef skref = IR(store->op1)->op2; + IRRef xkref = IR(xref)->op2; + /* Same key type MAY alias. Need ALOAD check due to multiple int types. */ + if (loadop == IR_ALOAD || irt_sametype(IR(skref)->t, IR(xkref)->t)) { + if (skref == xkref || !irref_isk(skref) || !irref_isk(xkref)) + return 0; /* A nil store with same const key or var key MAY alias. */ + /* Different const keys CANNOT alias. */ + } /* Different key types CANNOT alias. */ + } /* Other non-nil stores MAY alias. */ + ref = store->prev; + } + + /* Check loads since nothing could be derived from stores. */ + ref = J->chain[loadop]; + while (ref > xref) { + IRIns *load = IR(ref); + if (load->op1 == xref) { /* Same xREF. */ + /* A nil load MAY alias, but a non-nil load MUST alias. */ + return !irt_isnil(load->t); + } /* Other non-nil loads MAY alias. */ + ref = load->prev; + } + return 0; /* Nothing derived at all, previous value MAY be nil. */ +} + +/* ------------------------------------------------------------------------ */ + +#undef IR +#undef fins +#undef fleft +#undef fright + +#endif diff --git a/lib/LuaJIT/lj_opt_mem.o b/lib/LuaJIT/lj_opt_mem.o new file mode 100644 index 0000000000000000000000000000000000000000..02a9a8937bbaa59309d6e9e168718be07dfa7eb7 GIT binary patch literal 10968 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 literal 0 HcmV?d00001 diff --git a/lib/LuaJIT/lj_opt_mem_dyn.o b/lib/LuaJIT/lj_opt_mem_dyn.o new file mode 100644 index 0000000000000000000000000000000000000000..02a9a8937bbaa59309d6e9e168718be07dfa7eb7 GIT binary patch literal 10968 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 literal 0 HcmV?d00001 diff --git a/lib/LuaJIT/lj_opt_narrow.c b/lib/LuaJIT/lj_opt_narrow.c new file mode 100644 index 0000000..cd96ca4 --- /dev/null +++ b/lib/LuaJIT/lj_opt_narrow.c @@ -0,0 +1,654 @@ +/* +** NARROW: Narrowing of numbers to integers (double to int32_t). +** STRIPOV: Stripping of overflow checks. +** Copyright (C) 2005-2017 Mike Pall. See Copyright Notice in luajit.h +*/ + +#define lj_opt_narrow_c +#define LUA_CORE + +#include "lj_obj.h" + +#if LJ_HASJIT + +#include "lj_bc.h" +#include "lj_ir.h" +#include "lj_jit.h" +#include "lj_iropt.h" +#include "lj_trace.h" +#include "lj_vm.h" +#include "lj_strscan.h" + +/* Rationale for narrowing optimizations: +** +** Lua has only a single number type and this is a FP double by default. +** Narrowing doubles to integers does not pay off for the interpreter on a +** current-generation x86/x64 machine. Most FP operations need the same +** amount of execution resources as their integer counterparts, except +** with slightly longer latencies. Longer latencies are a non-issue for +** the interpreter, since they are usually hidden by other overhead. +** +** The total CPU execution bandwidth is the sum of the bandwidth of the FP +** and the integer units, because they execute in parallel. The FP units +** have an equal or higher bandwidth than the integer units. Not using +** them means losing execution bandwidth. Moving work away from them to +** the already quite busy integer units is a losing proposition. +** +** The situation for JIT-compiled code is a bit different: the higher code +** density makes the extra latencies much more visible. Tight loops expose +** the latencies for updating the induction variables. Array indexing +** requires narrowing conversions with high latencies and additional +** guards (to check that the index is really an integer). And many common +** optimizations only work on integers. +** +** One solution would be speculative, eager narrowing of all number loads. +** This causes many problems, like losing -0 or the need to resolve type +** mismatches between traces. It also effectively forces the integer type +** to have overflow-checking semantics. This impedes many basic +** optimizations and requires adding overflow checks to all integer +** arithmetic operations (whereas FP arithmetics can do without). +** +** Always replacing an FP op with an integer op plus an overflow check is +** counter-productive on a current-generation super-scalar CPU. Although +** the overflow check branches are highly predictable, they will clog the +** execution port for the branch unit and tie up reorder buffers. This is +** turning a pure data-flow dependency into a different data-flow +** dependency (with slightly lower latency) *plus* a control dependency. +** In general, you don't want to do this since latencies due to data-flow +** dependencies can be well hidden by out-of-order execution. +** +** A better solution is to keep all numbers as FP values and only narrow +** when it's beneficial to do so. LuaJIT uses predictive narrowing for +** induction variables and demand-driven narrowing for index expressions, +** integer arguments and bit operations. Additionally it can eliminate or +** hoist most of the resulting overflow checks. Regular arithmetic +** computations are never narrowed to integers. +** +** The integer type in the IR has convenient wrap-around semantics and +** ignores overflow. Extra operations have been added for +** overflow-checking arithmetic (ADDOV/SUBOV) instead of an extra type. +** Apart from reducing overall complexity of the compiler, this also +** nicely solves the problem where you want to apply algebraic +** simplifications to ADD, but not to ADDOV. And the x86/x64 assembler can +** use lea instead of an add for integer ADD, but not for ADDOV (lea does +** not affect the flags, but it helps to avoid register moves). +** +** +** All of the above has to be reconsidered for architectures with slow FP +** operations or without a hardware FPU. The dual-number mode of LuaJIT +** addresses this issue. Arithmetic operations are performed on integers +** as far as possible and overflow checks are added as needed. +** +** This implies that narrowing for integer arguments and bit operations +** should also strip overflow checks, e.g. replace ADDOV with ADD. The +** original overflow guards are weak and can be eliminated by DCE, if +** there's no other use. +** +** A slight twist is that it's usually beneficial to use overflow-checked +** integer arithmetics if all inputs are already integers. This is the only +** change that affects the single-number mode, too. +*/ + +/* Some local macros to save typing. Undef'd at the end. */ +#define IR(ref) (&J->cur.ir[(ref)]) +#define fins (&J->fold.ins) + +/* Pass IR on to next optimization in chain (FOLD). */ +#define emitir(ot, a, b) (lj_ir_set(J, (ot), (a), (b)), lj_opt_fold(J)) + +#define emitir_raw(ot, a, b) (lj_ir_set(J, (ot), (a), (b)), lj_ir_emit(J)) + +/* -- Elimination of narrowing type conversions --------------------------- */ + +/* Narrowing of index expressions and bit operations is demand-driven. The +** trace recorder emits a narrowing type conversion (CONV.int.num or TOBIT) +** in all of these cases (e.g. array indexing or string indexing). FOLD +** already takes care of eliminating simple redundant conversions like +** CONV.int.num(CONV.num.int(x)) ==> x. +** +** But the surrounding code is FP-heavy and arithmetic operations are +** performed on FP numbers (for the single-number mode). Consider a common +** example such as 'x=t[i+1]', with 'i' already an integer (due to induction +** variable narrowing). The index expression would be recorded as +** CONV.int.num(ADD(CONV.num.int(i), 1)) +** which is clearly suboptimal. +** +** One can do better by recursively backpropagating the narrowing type +** conversion across FP arithmetic operations. This turns FP ops into +** their corresponding integer counterparts. Depending on the semantics of +** the conversion they also need to check for overflow. Currently only ADD +** and SUB are supported. +** +** The above example can be rewritten as +** ADDOV(CONV.int.num(CONV.num.int(i)), 1) +** and then into ADDOV(i, 1) after folding of the conversions. The original +** FP ops remain in the IR and are eliminated by DCE since all references to +** them are gone. +** +** [In dual-number mode the trace recorder already emits ADDOV etc., but +** this can be further reduced. See below.] +** +** Special care has to be taken to avoid narrowing across an operation +** which is potentially operating on non-integral operands. One obvious +** case is when an expression contains a non-integral constant, but ends +** up as an integer index at runtime (like t[x+1.5] with x=0.5). +** +** Operations with two non-constant operands illustrate a similar problem +** (like t[a+b] with a=1.5 and b=2.5). Backpropagation has to stop there, +** unless it can be proven that either operand is integral (e.g. by CSEing +** a previous conversion). As a not-so-obvious corollary this logic also +** applies for a whole expression tree (e.g. t[(a+1)+(b+1)]). +** +** Correctness of the transformation is guaranteed by avoiding to expand +** the tree by adding more conversions than the one we would need to emit +** if not backpropagating. TOBIT employs a more optimistic rule, because +** the conversion has special semantics, designed to make the life of the +** compiler writer easier. ;-) +** +** Using on-the-fly backpropagation of an expression tree doesn't work +** because it's unknown whether the transform is correct until the end. +** This either requires IR rollback and cache invalidation for every +** subtree or a two-pass algorithm. The former didn't work out too well, +** so the code now combines a recursive collector with a stack-based +** emitter. +** +** [A recursive backpropagation algorithm with backtracking, employing +** skip-list lookup and round-robin caching, emitting stack operations +** on-the-fly for a stack-based interpreter -- and all of that in a meager +** kilobyte? Yep, compilers are a great treasure chest. Throw away your +** textbooks and read the codebase of a compiler today!] +** +** There's another optimization opportunity for array indexing: it's +** always accompanied by an array bounds-check. The outermost overflow +** check may be delegated to the ABC operation. This works because ABC is +** an unsigned comparison and wrap-around due to overflow creates negative +** numbers. +** +** But this optimization is only valid for constants that cannot overflow +** an int32_t into the range of valid array indexes [0..2^27+1). A check +** for +-2^30 is safe since -2^31 - 2^30 wraps to 2^30 and 2^31-1 + 2^30 +** wraps to -2^30-1. +** +** It's also good enough in practice, since e.g. t[i+1] or t[i-10] are +** quite common. So the above example finally ends up as ADD(i, 1)! +** +** Later on, the assembler is able to fuse the whole array reference and +** the ADD into the memory operands of loads and other instructions. This +** is why LuaJIT is able to generate very pretty (and fast) machine code +** for array indexing. And that, my dear, concludes another story about +** one of the hidden secrets of LuaJIT ... +*/ + +/* Maximum backpropagation depth and maximum stack size. */ +#define NARROW_MAX_BACKPROP 100 +#define NARROW_MAX_STACK 256 + +/* The stack machine has a 32 bit instruction format: [IROpT | IRRef1] +** The lower 16 bits hold a reference (or 0). The upper 16 bits hold +** the IR opcode + type or one of the following special opcodes: +*/ +enum { + NARROW_REF, /* Push ref. */ + NARROW_CONV, /* Push conversion of ref. */ + NARROW_SEXT, /* Push sign-extension of ref. */ + NARROW_INT /* Push KINT ref. The next code holds an int32_t. */ +}; + +typedef uint32_t NarrowIns; + +#define NARROWINS(op, ref) (((op) << 16) + (ref)) +#define narrow_op(ins) ((IROpT)((ins) >> 16)) +#define narrow_ref(ins) ((IRRef1)(ins)) + +/* Context used for narrowing of type conversions. */ +typedef struct NarrowConv { + jit_State *J; /* JIT compiler state. */ + NarrowIns *sp; /* Current stack pointer. */ + NarrowIns *maxsp; /* Maximum stack pointer minus redzone. */ + IRRef mode; /* Conversion mode (IRCONV_*). */ + IRType t; /* Destination type: IRT_INT or IRT_I64. */ + NarrowIns stack[NARROW_MAX_STACK]; /* Stack holding stack-machine code. */ +} NarrowConv; + +/* Lookup a reference in the backpropagation cache. */ +static BPropEntry *narrow_bpc_get(jit_State *J, IRRef1 key, IRRef mode) +{ + ptrdiff_t i; + for (i = 0; i < BPROP_SLOTS; i++) { + BPropEntry *bp = &J->bpropcache[i]; + /* Stronger checks are ok, too. */ + if (bp->key == key && bp->mode >= mode && + ((bp->mode ^ mode) & IRCONV_MODEMASK) == 0) + return bp; + } + return NULL; +} + +/* Add an entry to the backpropagation cache. */ +static void narrow_bpc_set(jit_State *J, IRRef1 key, IRRef1 val, IRRef mode) +{ + uint32_t slot = J->bpropslot; + BPropEntry *bp = &J->bpropcache[slot]; + J->bpropslot = (slot + 1) & (BPROP_SLOTS-1); + bp->key = key; + bp->val = val; + bp->mode = mode; +} + +/* Backpropagate overflow stripping. */ +static void narrow_stripov_backprop(NarrowConv *nc, IRRef ref, int depth) +{ + jit_State *J = nc->J; + IRIns *ir = IR(ref); + if (ir->o == IR_ADDOV || ir->o == IR_SUBOV || + (ir->o == IR_MULOV && (nc->mode & IRCONV_CONVMASK) == IRCONV_ANY)) { + BPropEntry *bp = narrow_bpc_get(nc->J, ref, IRCONV_TOBIT); + if (bp) { + ref = bp->val; + } else if (++depth < NARROW_MAX_BACKPROP && nc->sp < nc->maxsp) { + NarrowIns *savesp = nc->sp; + narrow_stripov_backprop(nc, ir->op1, depth); + if (nc->sp < nc->maxsp) { + narrow_stripov_backprop(nc, ir->op2, depth); + if (nc->sp < nc->maxsp) { + *nc->sp++ = NARROWINS(IRT(ir->o - IR_ADDOV + IR_ADD, IRT_INT), ref); + return; + } + } + nc->sp = savesp; /* Path too deep, need to backtrack. */ + } + } + *nc->sp++ = NARROWINS(NARROW_REF, ref); +} + +/* Backpropagate narrowing conversion. Return number of needed conversions. */ +static int narrow_conv_backprop(NarrowConv *nc, IRRef ref, int depth) +{ + jit_State *J = nc->J; + IRIns *ir = IR(ref); + IRRef cref; + + if (nc->sp >= nc->maxsp) return 10; /* Path too deep. */ + + /* Check the easy cases first. */ + if (ir->o == IR_CONV && (ir->op2 & IRCONV_SRCMASK) == IRT_INT) { + if ((nc->mode & IRCONV_CONVMASK) <= IRCONV_ANY) + narrow_stripov_backprop(nc, ir->op1, depth+1); + else + *nc->sp++ = NARROWINS(NARROW_REF, ir->op1); /* Undo conversion. */ + if (nc->t == IRT_I64) + *nc->sp++ = NARROWINS(NARROW_SEXT, 0); /* Sign-extend integer. */ + return 0; + } else if (ir->o == IR_KNUM) { /* Narrow FP constant. */ + lua_Number n = ir_knum(ir)->n; + if ((nc->mode & IRCONV_CONVMASK) == IRCONV_TOBIT) { + /* Allows a wider range of constants. */ + int64_t k64 = (int64_t)n; + if (n == (lua_Number)k64) { /* Only if const doesn't lose precision. */ + *nc->sp++ = NARROWINS(NARROW_INT, 0); + *nc->sp++ = (NarrowIns)k64; /* But always truncate to 32 bits. */ + return 0; + } + } else { + int32_t k = lj_num2int(n); + /* Only if constant is a small integer. */ + if (checki16(k) && n == (lua_Number)k) { + *nc->sp++ = NARROWINS(NARROW_INT, 0); + *nc->sp++ = (NarrowIns)k; + return 0; + } + } + return 10; /* Never narrow other FP constants (this is rare). */ + } + + /* Try to CSE the conversion. Stronger checks are ok, too. */ + cref = J->chain[fins->o]; + while (cref > ref) { + IRIns *cr = IR(cref); + if (cr->op1 == ref && + (fins->o == IR_TOBIT || + ((cr->op2 & IRCONV_MODEMASK) == (nc->mode & IRCONV_MODEMASK) && + irt_isguard(cr->t) >= irt_isguard(fins->t)))) { + *nc->sp++ = NARROWINS(NARROW_REF, cref); + return 0; /* Already there, no additional conversion needed. */ + } + cref = cr->prev; + } + + /* Backpropagate across ADD/SUB. */ + if (ir->o == IR_ADD || ir->o == IR_SUB) { + /* Try cache lookup first. */ + IRRef mode = nc->mode; + BPropEntry *bp; + /* Inner conversions need a stronger check. */ + if ((mode & IRCONV_CONVMASK) == IRCONV_INDEX && depth > 0) + mode += IRCONV_CHECK-IRCONV_INDEX; + bp = narrow_bpc_get(nc->J, (IRRef1)ref, mode); + if (bp) { + *nc->sp++ = NARROWINS(NARROW_REF, bp->val); + return 0; + } else if (nc->t == IRT_I64) { + /* Try sign-extending from an existing (checked) conversion to int. */ + mode = (IRT_INT<<5)|IRT_NUM|IRCONV_INDEX; + bp = narrow_bpc_get(nc->J, (IRRef1)ref, mode); + if (bp) { + *nc->sp++ = NARROWINS(NARROW_REF, bp->val); + *nc->sp++ = NARROWINS(NARROW_SEXT, 0); + return 0; + } + } + if (++depth < NARROW_MAX_BACKPROP && nc->sp < nc->maxsp) { + NarrowIns *savesp = nc->sp; + int count = narrow_conv_backprop(nc, ir->op1, depth); + count += narrow_conv_backprop(nc, ir->op2, depth); + if (count <= 1) { /* Limit total number of conversions. */ + *nc->sp++ = NARROWINS(IRT(ir->o, nc->t), ref); + return count; + } + nc->sp = savesp; /* Too many conversions, need to backtrack. */ + } + } + + /* Otherwise add a conversion. */ + *nc->sp++ = NARROWINS(NARROW_CONV, ref); + return 1; +} + +/* Emit the conversions collected during backpropagation. */ +static IRRef narrow_conv_emit(jit_State *J, NarrowConv *nc) +{ + /* The fins fields must be saved now -- emitir() overwrites them. */ + IROpT guardot = irt_isguard(fins->t) ? IRTG(IR_ADDOV-IR_ADD, 0) : 0; + IROpT convot = fins->ot; + IRRef1 convop2 = fins->op2; + NarrowIns *next = nc->stack; /* List of instructions from backpropagation. */ + NarrowIns *last = nc->sp; + NarrowIns *sp = nc->stack; /* Recycle the stack to store operands. */ + while (next < last) { /* Simple stack machine to process the ins. list. */ + NarrowIns ref = *next++; + IROpT op = narrow_op(ref); + if (op == NARROW_REF) { + *sp++ = ref; + } else if (op == NARROW_CONV) { + *sp++ = emitir_raw(convot, ref, convop2); /* Raw emit avoids a loop. */ + } else if (op == NARROW_SEXT) { + lua_assert(sp >= nc->stack+1); + sp[-1] = emitir(IRT(IR_CONV, IRT_I64), sp[-1], + (IRT_I64<<5)|IRT_INT|IRCONV_SEXT); + } else if (op == NARROW_INT) { + lua_assert(next < last); + *sp++ = nc->t == IRT_I64 ? + lj_ir_kint64(J, (int64_t)(int32_t)*next++) : + lj_ir_kint(J, *next++); + } else { /* Regular IROpT. Pops two operands and pushes one result. */ + IRRef mode = nc->mode; + lua_assert(sp >= nc->stack+2); + sp--; + /* Omit some overflow checks for array indexing. See comments above. */ + if ((mode & IRCONV_CONVMASK) == IRCONV_INDEX) { + if (next == last && irref_isk(narrow_ref(sp[0])) && + (uint32_t)IR(narrow_ref(sp[0]))->i + 0x40000000u < 0x80000000u) + guardot = 0; + else /* Otherwise cache a stronger check. */ + mode += IRCONV_CHECK-IRCONV_INDEX; + } + sp[-1] = emitir(op+guardot, sp[-1], sp[0]); + /* Add to cache. */ + if (narrow_ref(ref)) + narrow_bpc_set(J, narrow_ref(ref), narrow_ref(sp[-1]), mode); + } + } + lua_assert(sp == nc->stack+1); + return nc->stack[0]; +} + +/* Narrow a type conversion of an arithmetic operation. */ +TRef LJ_FASTCALL lj_opt_narrow_convert(jit_State *J) +{ + if ((J->flags & JIT_F_OPT_NARROW)) { + NarrowConv nc; + nc.J = J; + nc.sp = nc.stack; + nc.maxsp = &nc.stack[NARROW_MAX_STACK-4]; + nc.t = irt_type(fins->t); + if (fins->o == IR_TOBIT) { + nc.mode = IRCONV_TOBIT; /* Used only in the backpropagation cache. */ + } else { + nc.mode = fins->op2; + } + if (narrow_conv_backprop(&nc, fins->op1, 0) <= 1) + return narrow_conv_emit(J, &nc); + } + return NEXTFOLD; +} + +/* -- Narrowing of implicit conversions ----------------------------------- */ + +/* Recursively strip overflow checks. */ +static TRef narrow_stripov(jit_State *J, TRef tr, int lastop, IRRef mode) +{ + IRRef ref = tref_ref(tr); + IRIns *ir = IR(ref); + int op = ir->o; + if (op >= IR_ADDOV && op <= lastop) { + BPropEntry *bp = narrow_bpc_get(J, ref, mode); + if (bp) { + return TREF(bp->val, irt_t(IR(bp->val)->t)); + } else { + IRRef op1 = ir->op1, op2 = ir->op2; /* The IR may be reallocated. */ + op1 = narrow_stripov(J, op1, lastop, mode); + op2 = narrow_stripov(J, op2, lastop, mode); + tr = emitir(IRT(op - IR_ADDOV + IR_ADD, + ((mode & IRCONV_DSTMASK) >> IRCONV_DSH)), op1, op2); + narrow_bpc_set(J, ref, tref_ref(tr), mode); + } + } else if (LJ_64 && (mode & IRCONV_SEXT) && !irt_is64(ir->t)) { + tr = emitir(IRT(IR_CONV, IRT_INTP), tr, mode); + } + return tr; +} + +/* Narrow array index. */ +TRef LJ_FASTCALL lj_opt_narrow_index(jit_State *J, TRef tr) +{ + IRIns *ir; + lua_assert(tref_isnumber(tr)); + if (tref_isnum(tr)) /* Conversion may be narrowed, too. See above. */ + return emitir(IRTGI(IR_CONV), tr, IRCONV_INT_NUM|IRCONV_INDEX); + /* Omit some overflow checks for array indexing. See comments above. */ + ir = IR(tref_ref(tr)); + if ((ir->o == IR_ADDOV || ir->o == IR_SUBOV) && irref_isk(ir->op2) && + (uint32_t)IR(ir->op2)->i + 0x40000000u < 0x80000000u) + return emitir(IRTI(ir->o - IR_ADDOV + IR_ADD), ir->op1, ir->op2); + return tr; +} + +/* Narrow conversion to integer operand (overflow undefined). */ +TRef LJ_FASTCALL lj_opt_narrow_toint(jit_State *J, TRef tr) +{ + if (tref_isstr(tr)) + tr = emitir(IRTG(IR_STRTO, IRT_NUM), tr, 0); + if (tref_isnum(tr)) /* Conversion may be narrowed, too. See above. */ + return emitir(IRTI(IR_CONV), tr, IRCONV_INT_NUM|IRCONV_ANY); + if (!tref_isinteger(tr)) + lj_trace_err(J, LJ_TRERR_BADTYPE); + /* + ** Undefined overflow semantics allow stripping of ADDOV, SUBOV and MULOV. + ** Use IRCONV_TOBIT for the cache entries, since the semantics are the same. + */ + return narrow_stripov(J, tr, IR_MULOV, (IRT_INT<<5)|IRT_INT|IRCONV_TOBIT); +} + +/* Narrow conversion to bitop operand (overflow wrapped). */ +TRef LJ_FASTCALL lj_opt_narrow_tobit(jit_State *J, TRef tr) +{ + if (tref_isstr(tr)) + tr = emitir(IRTG(IR_STRTO, IRT_NUM), tr, 0); + if (tref_isnum(tr)) /* Conversion may be narrowed, too. See above. */ + return emitir(IRTI(IR_TOBIT), tr, lj_ir_knum_tobit(J)); + if (!tref_isinteger(tr)) + lj_trace_err(J, LJ_TRERR_BADTYPE); + /* + ** Wrapped overflow semantics allow stripping of ADDOV and SUBOV. + ** MULOV cannot be stripped due to precision widening. + */ + return narrow_stripov(J, tr, IR_SUBOV, (IRT_INT<<5)|IRT_INT|IRCONV_TOBIT); +} + +#if LJ_HASFFI +/* Narrow C array index (overflow undefined). */ +TRef LJ_FASTCALL lj_opt_narrow_cindex(jit_State *J, TRef tr) +{ + lua_assert(tref_isnumber(tr)); + if (tref_isnum(tr)) + return emitir(IRT(IR_CONV, IRT_INTP), tr, (IRT_INTP<<5)|IRT_NUM|IRCONV_ANY); + /* Undefined overflow semantics allow stripping of ADDOV, SUBOV and MULOV. */ + return narrow_stripov(J, tr, IR_MULOV, + LJ_64 ? ((IRT_INTP<<5)|IRT_INT|IRCONV_SEXT) : + ((IRT_INTP<<5)|IRT_INT|IRCONV_TOBIT)); +} +#endif + +/* -- Narrowing of arithmetic operators ----------------------------------- */ + +/* Check whether a number fits into an int32_t (-0 is ok, too). */ +static int numisint(lua_Number n) +{ + return (n == (lua_Number)lj_num2int(n)); +} + +/* Convert string to number. Error out for non-numeric string values. */ +static TRef conv_str_tonum(jit_State *J, TRef tr, TValue *o) +{ + if (tref_isstr(tr)) { + tr = emitir(IRTG(IR_STRTO, IRT_NUM), tr, 0); + /* Would need an inverted STRTO for this rare and useless case. */ + if (!lj_strscan_num(strV(o), o)) /* Convert in-place. Value used below. */ + lj_trace_err(J, LJ_TRERR_BADTYPE); /* Punt if non-numeric. */ + } + return tr; +} + +/* Narrowing of arithmetic operations. */ +TRef lj_opt_narrow_arith(jit_State *J, TRef rb, TRef rc, + TValue *vb, TValue *vc, IROp op) +{ + rb = conv_str_tonum(J, rb, vb); + rc = conv_str_tonum(J, rc, vc); + /* Must not narrow MUL in non-DUALNUM variant, because it loses -0. */ + if ((op >= IR_ADD && op <= (LJ_DUALNUM ? IR_MUL : IR_SUB)) && + tref_isinteger(rb) && tref_isinteger(rc) && + numisint(lj_vm_foldarith(numberVnum(vb), numberVnum(vc), + (int)op - (int)IR_ADD))) + return emitir(IRTGI((int)op - (int)IR_ADD + (int)IR_ADDOV), rb, rc); + if (!tref_isnum(rb)) rb = emitir(IRTN(IR_CONV), rb, IRCONV_NUM_INT); + if (!tref_isnum(rc)) rc = emitir(IRTN(IR_CONV), rc, IRCONV_NUM_INT); + return emitir(IRTN(op), rb, rc); +} + +/* Narrowing of unary minus operator. */ +TRef lj_opt_narrow_unm(jit_State *J, TRef rc, TValue *vc) +{ + rc = conv_str_tonum(J, rc, vc); + if (tref_isinteger(rc)) { + if ((uint32_t)numberVint(vc) != 0x80000000u) + return emitir(IRTGI(IR_SUBOV), lj_ir_kint(J, 0), rc); + rc = emitir(IRTN(IR_CONV), rc, IRCONV_NUM_INT); + } + return emitir(IRTN(IR_NEG), rc, lj_ir_ksimd(J, LJ_KSIMD_NEG)); +} + +/* Narrowing of modulo operator. */ +TRef lj_opt_narrow_mod(jit_State *J, TRef rb, TRef rc, TValue *vb, TValue *vc) +{ + TRef tmp; + rb = conv_str_tonum(J, rb, vb); + rc = conv_str_tonum(J, rc, vc); + if ((LJ_DUALNUM || (J->flags & JIT_F_OPT_NARROW)) && + tref_isinteger(rb) && tref_isinteger(rc) && + (tvisint(vc) ? intV(vc) != 0 : !tviszero(vc))) { + emitir(IRTGI(IR_NE), rc, lj_ir_kint(J, 0)); + return emitir(IRTI(IR_MOD), rb, rc); + } + /* b % c ==> b - floor(b/c)*c */ + rb = lj_ir_tonum(J, rb); + rc = lj_ir_tonum(J, rc); + tmp = emitir(IRTN(IR_DIV), rb, rc); + tmp = emitir(IRTN(IR_FPMATH), tmp, IRFPM_FLOOR); + tmp = emitir(IRTN(IR_MUL), tmp, rc); + return emitir(IRTN(IR_SUB), rb, tmp); +} + +/* Narrowing of power operator or math.pow. */ +TRef lj_opt_narrow_pow(jit_State *J, TRef rb, TRef rc, TValue *vb, TValue *vc) +{ + rb = conv_str_tonum(J, rb, vb); + rb = lj_ir_tonum(J, rb); /* Left arg is always treated as an FP number. */ + rc = conv_str_tonum(J, rc, vc); + /* Narrowing must be unconditional to preserve (-x)^i semantics. */ + if (tvisint(vc) || numisint(numV(vc))) { + int checkrange = 0; + /* Split pow is faster for bigger exponents. But do this only for (+k)^i. */ + if (tref_isk(rb) && (int32_t)ir_knum(IR(tref_ref(rb)))->u32.hi >= 0) { + int32_t k = numberVint(vc); + if (!(k >= -65536 && k <= 65536)) goto split_pow; + checkrange = 1; + } + if (!tref_isinteger(rc)) { + /* Guarded conversion to integer! */ + rc = emitir(IRTGI(IR_CONV), rc, IRCONV_INT_NUM|IRCONV_CHECK); + } + if (checkrange && !tref_isk(rc)) { /* Range guard: -65536 <= i <= 65536 */ + TRef tmp = emitir(IRTI(IR_ADD), rc, lj_ir_kint(J, 65536)); + emitir(IRTGI(IR_ULE), tmp, lj_ir_kint(J, 2*65536)); + } + return emitir(IRTN(IR_POW), rb, rc); + } +split_pow: + /* FOLD covers most cases, but some are easier to do here. */ + if (tref_isk(rb) && tvispone(ir_knum(IR(tref_ref(rb))))) + return rb; /* 1 ^ x ==> 1 */ + rc = lj_ir_tonum(J, rc); + if (tref_isk(rc) && ir_knum(IR(tref_ref(rc)))->n == 0.5) + return emitir(IRTN(IR_FPMATH), rb, IRFPM_SQRT); /* x ^ 0.5 ==> sqrt(x) */ + /* Split up b^c into exp2(c*log2(b)). Assembler may rejoin later. */ + rb = emitir(IRTN(IR_FPMATH), rb, IRFPM_LOG2); + rc = emitir(IRTN(IR_MUL), rb, rc); + return emitir(IRTN(IR_FPMATH), rc, IRFPM_EXP2); +} + +/* -- Predictive narrowing of induction variables ------------------------- */ + +/* Narrow a single runtime value. */ +static int narrow_forl(jit_State *J, cTValue *o) +{ + if (tvisint(o)) return 1; + if (LJ_DUALNUM || (J->flags & JIT_F_OPT_NARROW)) return numisint(numV(o)); + return 0; +} + +/* Narrow the FORL index type by looking at the runtime values. */ +IRType lj_opt_narrow_forl(jit_State *J, cTValue *tv) +{ + lua_assert(tvisnumber(&tv[FORL_IDX]) && + tvisnumber(&tv[FORL_STOP]) && + tvisnumber(&tv[FORL_STEP])); + /* Narrow only if the runtime values of start/stop/step are all integers. */ + if (narrow_forl(J, &tv[FORL_IDX]) && + narrow_forl(J, &tv[FORL_STOP]) && + narrow_forl(J, &tv[FORL_STEP])) { + /* And if the loop index can't possibly overflow. */ + lua_Number step = numberVnum(&tv[FORL_STEP]); + lua_Number sum = numberVnum(&tv[FORL_STOP]) + step; + if (0 <= step ? (sum <= 2147483647.0) : (sum >= -2147483648.0)) + return IRT_INT; + } + return IRT_NUM; +} + +#undef IR +#undef fins +#undef emitir +#undef emitir_raw + +#endif diff --git a/lib/LuaJIT/lj_opt_narrow.o b/lib/LuaJIT/lj_opt_narrow.o new file mode 100644 index 0000000000000000000000000000000000000000..9ae6e964b41ad3bc95e49727d782bfb33525bc4f GIT binary patch literal 9808 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 literal 0 HcmV?d00001 diff --git a/lib/LuaJIT/lj_opt_narrow_dyn.o b/lib/LuaJIT/lj_opt_narrow_dyn.o new file mode 100644 index 0000000000000000000000000000000000000000..9ae6e964b41ad3bc95e49727d782bfb33525bc4f GIT binary patch literal 9808 zcmbtZ4Rlo1oxd}gz(`=;lzK4ThbazW|EzSU9>MBhU&Hn!P-8b_x z$?Vy)d(X+d`+oQTegFUey$>Gn2X1yc9LyRHb|Xu@Stw)P@1*ACVp`5-u<1-)!OyA7 zS1i-{f9at!d3spU!;Nk|ta<~H!Vy*J^Jhnz4wbim#*~&}rT?+mOw3RJouTAwjA@az4mEV4_JMA_tkJuFf*IYAV^Jhi^ktuI*VRYD8II^5DDi{Z&_?ofP2Y&dTQ zW4X*Rz`vci5(FuG)!X@qy26am03zfKm=Rin2rY^DqI;&nZGAx_BtL?gU+KHkm#wyc zwMyxngMGm4Ql)bzRuIaL-QwB5UEPYMD!#w$aPuXJ{Qy(?lzx3m*4Qa|D}}Esuf0`vsO3#V`Rt9)m8%aK zO3~}eFRsU;(p8LK$aN3-!Jow_3m|@l>+};!K9rxH2WK6d?;_P50}NS66nntL%w)0K zfN=C{K4)k+-w2K7DxGUUHhF}UtzUjKHg@`-rQI45Oyg0RV+9Dp%&xc9NIRvQ@Cu;abk#uU=Nb84M$)F7B?_pS{$Ur@^0{OAh z&Hbz-n%Dy)l>Bn`xvmsOMRauK>eneeTP|st1KLzrh{Xk#F0hF7Nc>zdx5EGydf?L8 zO6QB1X_22Iv%juYb{@hq)Xw*5{FIs3-CxVR1|0Mul~1XBFy3ULnqwq1>QHt*BpCaa z`?B@$ZSL4d?wpmE>XCb0v5&kuZ*@D4@;QkyFkycXHlZR_oJ#jYu)<0d-U2U$ab0f; zHK-R^AVW^ZoGj4?Sk!V_T<250OpI&CH}zLAw)y>za}9XP=)RGRE}}}gWG2y&-z8Km zgNo38hZ%#lU&$CGYN5gEFX~FAe^9+0CCT4am#h4X6*~WV89!_`uLrz(WY&<&;Xl3yS3`7oQ&zAX<%7^FSJ$leV(SzdQ?FXp^l^t&VkCG2MPKfe4ic~mA%}D3_ zl|F6AWp+Gt3u=0fBkEmhwN+2&hTJW=8XrzIMX}nW^euMnaaY{Uj>h?ZtcN~wwEWwi z*{h&iP3Tidx4EE! z#ST5P{9P70q3{R+pm~x)X7H^P8vZDSZM?|?Myj7t@2ukQ&@LIgzZY_cwvj@{cB~O< z)bK)IfS*+PLGjuZ4PK=2xOuGX%K;s%6)Wa>5Z=}U>`KN8(6qK;s;(5-AdaxF^o}#7 zNF_be=_|l95LxFdrffnvXublaZyR|R;Ol&Oa2QZwNwobn>dLEwQ)lo$bFe?uXP1*MpX$gnO%|JNl%QMv2*9z@X zfDdDv{@6ZaN!iKPIf-EqsJsyFk5+{axT?wqmG0jW>)b%3&Baf}PI-gJ`TL>s4yDtD z25E%OJC)8Kn6oUU^EuGcUeB$HXubuBf` zg})*)!psDCVeo&U)X?}=N{Ylas9P`6`U~CXE1a#@Ho5f3dQ|`M;9XLg%SSQL zc|7q2rK(l$q!5x5$AyS;26@vB{uG%C?yAT}pUbpAC5E3NhGztWnaZbolk!A_z&RwG zeqq||Rq&srC4Ig?+)aX8V{A#;H^E%BBTDB>;#GmRgPIv#(YAOXe2aHcc=}r%F(Bn_ z$NVLqCVmAPTmJJu@e!r}J?sZ#`Pd4^Ji%ppxD}0R`g=;>kFvE$#UZtQ)Ui1mDO65d zraBEjq>ogD=MQ!a3dtXbPR$5A(BMOda{0DiUmn+l61*VB>dE9QIvMo$XahKs| zO=EAs`)M2PpFM_x=16KR+89b12=vgxPAx$N0@xJ1*ei30{W&Z5;wbqV*qjcvi{eA3}37 ziN8(~ztoEFA$SpIVc{f=sE&7eHsQryymPLsT zM4pdB0H^U;*rxLU+8THH;y=O;>0x>cqm%N3iDyyDIzJSkmZWCaZl*Q+v(MmPpMl(+79snJ<_EhC-!+jOrC~@NA!7dJ<>A_R7CS-&$T4= zaS~_nM`+6+oKzvr;j`R1vzLf_U;LU;It&jaO3gTuzv3!3&ffr(r(ABEn5%w3=eiz- zqJrS;^jzqE7P!VwS}`y;3QElkNO_D++T$q{HVe}=F7{iHf!PcQvi|!rV$$bpz#&ON zmGZk6Y6Zz&&((rtHzBE@|4(t5kh%*IA3dC#yTs2REqR`a*nCKldiWe|%)!KN%E@FuLH@?u#L9t08&<@AS;;A`Wy{8I z)<0lpNE~d#<>Vmt#@=Hd$0}+ui@tzxmp_P4#v}4OF}7t>%e^?cK2;vfCmG=-IODXK zTML)FaGog-UIesbKYbW>oTux4>yIbafwC>f3D5mqA1e<&1p2+6qExJsJ0Wh%3EGYx zQrLE>9$rDp_Ph0$$!^9*Zqm!VO6TPeVM`<&+RB5_&2TT$!+tM3$tG%@A4M0eRU&xntheCr=$S=dx2dH?fe+f7h7M0+eorHa3;rrl^MNcA4#& z4}&5AV`F?UaVu@Obzc(RihS6LRVvgaH3D~ zWduwUvfxdEKJ(CT;5E)nhgm24a_5O~$Y2@f*MmKgO>!qP$t>R2c1;oS-Ayb87l|zb zDVmOmal&o)Cv!X?63)DZPe$8~qGt~^yK~z_XK8T`^k9oUNLHK|g)t;5KHc%fzP9CV z;J3ObEL62rV&QhLT-Yx;j%+DzDKeC6XG1p9|Bge=bFI_?8dG*f7dqxdL&IN%qPYXu z3XL&g$AHm;b^Oy3GHzG&^oKMNWB4X(##j`AJ+4YT#wr&r`a!|m%3D{=D<~@}DJqtP zv*gpU<;Tp?=5<^$eX6?$*-aO=OXb?dcxM$JZ^Ws2^0sHGo`PX{qLuJn{QP4mnJ zpnCE^P~makf={bEKu&)&9?ZfImUa+tB&)(xu$_3jvsF)VXbJ>4`=@z|fj~%wB}C70 z1}CB#M6(|J(Z6!g*e&ha>GXT@c986d%kP=HJzMh>cTdqgrJ<=edzL!urg=)C4LbZn zhb9cwa{@^moJrjkIjxi8x8*n=wdC08VaHtj>0}GWV)_xWg2e4v8pR4RlF?Cf?!Q2$@FRigTH(8Uw^NIAT+gm%J%GtYe&{j zCq5_GJ5v7rnA0f0UtD68!0cvE!M)Qw?y4N;B0T&QSMp&x*@JPAepHXQ+quM3U`+FP zmw4Q8A93`mL{2(NCGGV@i=pE$AhG937hqF#aBDDmf{C{b&`mPq_=^jjJt-_0qk5vQJfOFgpK)YOY@ zmc`t*H*;Dm9uwuPmv}lyy~HWj_R%a}vRR%Tl=Cbmjkm3}V(yiBV}n^J15R}`8T}K$ zXF471IjKU_IIx#8&_^@ihcn>EfRp^Yao3|^)%Y3A2_KfYsBa{hs_cS#( zHd%OWW5YyH5;L)gm+D|+LrZ;8Q%!Ra2WnAZk@zX0*>&{uJtnmpjT_g|96Xz9YZ|J- zMX$l;n%cVRy5?q!P8vn>>zadvZfLH)XG4QEt*hT)O$ohzej4M3hIMsqX;`pP@~7N~ z-D^{VU`%z(1W~n<$*O7I5L{34H`iB_Psw+i?3M=M8w~%+PmGO?HNtV*dVQlnur8vP zW^GerYZ_uSHg9C?Vp&hrj;NPi!0m>{-(|)af9?NY^_* z1OBuPzZ`s493GSPO>*q{6|{Gu(j))(m))Ezj-rS7pen8#CldBQay7`&A%NGj8_l27MRMAdLR3``8`=F)ZA=kI}hn z$F2Jq{hOE_x9%aw(s1kka3&46?hn@8z|w2o9~KgTVd2(2VQCs}-4h;5!#bRH-Nu@tU|m~~6|JiY*07?rn>Mkc z=00r%j!6D=xt9Ibvr_V>v9pi< zm;O_Ik}R3FNilKB-;@>@%WjKsB_@;Y-yMH#QY@8*(*6;z#czGTOs1PS@1RQtMw`@6 vZH-z_IzP3|G!ua^cI4aQ!CsU4%{S9mYfev+IT`;yU13rg<|Fx=CXW3#UrHw) literal 0 HcmV?d00001 diff --git a/lib/LuaJIT/lj_opt_sink.c b/lib/LuaJIT/lj_opt_sink.c new file mode 100644 index 0000000..c16363e --- /dev/null +++ b/lib/LuaJIT/lj_opt_sink.c @@ -0,0 +1,251 @@ +/* +** SINK: Allocation Sinking and Store Sinking. +** Copyright (C) 2005-2017 Mike Pall. See Copyright Notice in luajit.h +*/ + +#define lj_opt_sink_c +#define LUA_CORE + +#include "lj_obj.h" + +#if LJ_HASJIT + +#include "lj_ir.h" +#include "lj_jit.h" +#include "lj_iropt.h" +#include "lj_target.h" + +/* Some local macros to save typing. Undef'd at the end. */ +#define IR(ref) (&J->cur.ir[(ref)]) + +/* Check whether the store ref points to an eligible allocation. */ +static IRIns *sink_checkalloc(jit_State *J, IRIns *irs) +{ + IRIns *ir = IR(irs->op1); + if (!irref_isk(ir->op2)) + return NULL; /* Non-constant key. */ + if (ir->o == IR_HREFK || ir->o == IR_AREF) + ir = IR(ir->op1); + else if (!(ir->o == IR_HREF || ir->o == IR_NEWREF || + ir->o == IR_FREF || ir->o == IR_ADD)) + return NULL; /* Unhandled reference type (for XSTORE). */ + ir = IR(ir->op1); + if (!(ir->o == IR_TNEW || ir->o == IR_TDUP || ir->o == IR_CNEW)) + return NULL; /* Not an allocation. */ + return ir; /* Return allocation. */ +} + +/* Recursively check whether a value depends on a PHI. */ +static int sink_phidep(jit_State *J, IRRef ref) +{ + IRIns *ir = IR(ref); + if (irt_isphi(ir->t)) return 1; + if (ir->op1 >= REF_FIRST && sink_phidep(J, ir->op1)) return 1; + if (ir->op2 >= REF_FIRST && sink_phidep(J, ir->op2)) return 1; + return 0; +} + +/* Check whether a value is a sinkable PHI or loop-invariant. */ +static int sink_checkphi(jit_State *J, IRIns *ira, IRRef ref) +{ + if (ref >= REF_FIRST) { + IRIns *ir = IR(ref); + if (irt_isphi(ir->t) || (ir->o == IR_CONV && ir->op2 == IRCONV_NUM_INT && + irt_isphi(IR(ir->op1)->t))) { + ira->prev++; + return 1; /* Sinkable PHI. */ + } + /* Otherwise the value must be loop-invariant. */ + return ref < J->loopref && !sink_phidep(J, ref); + } + return 1; /* Constant (non-PHI). */ +} + +/* Mark non-sinkable allocations using single-pass backward propagation. +** +** Roots for the marking process are: +** - Some PHIs or snapshots (see below). +** - Non-PHI, non-constant values stored to PHI allocations. +** - All guards. +** - Any remaining loads not eliminated by store-to-load forwarding. +** - Stores with non-constant keys. +** - All stored values. +*/ +static void sink_mark_ins(jit_State *J) +{ + IRIns *ir, *irlast = IR(J->cur.nins-1); + for (ir = irlast ; ; ir--) { + switch (ir->o) { + case IR_BASE: + return; /* Finished. */ + case IR_CALLL: /* IRCALL_lj_tab_len */ + case IR_ALOAD: case IR_HLOAD: case IR_XLOAD: case IR_TBAR: + irt_setmark(IR(ir->op1)->t); /* Mark ref for remaining loads. */ + break; + case IR_FLOAD: + if (irt_ismarked(ir->t) || ir->op2 == IRFL_TAB_META) + irt_setmark(IR(ir->op1)->t); /* Mark table for remaining loads. */ + break; + case IR_ASTORE: case IR_HSTORE: case IR_FSTORE: case IR_XSTORE: { + IRIns *ira = sink_checkalloc(J, ir); + if (!ira || (irt_isphi(ira->t) && !sink_checkphi(J, ira, ir->op2))) + irt_setmark(IR(ir->op1)->t); /* Mark ineligible ref. */ + irt_setmark(IR(ir->op2)->t); /* Mark stored value. */ + break; + } +#if LJ_HASFFI + case IR_CNEWI: + if (irt_isphi(ir->t) && + (!sink_checkphi(J, ir, ir->op2) || + (LJ_32 && ir+1 < irlast && (ir+1)->o == IR_HIOP && + !sink_checkphi(J, ir, (ir+1)->op2)))) + irt_setmark(ir->t); /* Mark ineligible allocation. */ +#endif + /* fallthrough */ + case IR_USTORE: + irt_setmark(IR(ir->op2)->t); /* Mark stored value. */ + break; +#if LJ_HASFFI + case IR_CALLXS: +#endif + case IR_CALLS: + irt_setmark(IR(ir->op1)->t); /* Mark (potentially) stored values. */ + break; + case IR_PHI: { + IRIns *irl = IR(ir->op1), *irr = IR(ir->op2); + irl->prev = irr->prev = 0; /* Clear PHI value counts. */ + if (irl->o == irr->o && + (irl->o == IR_TNEW || irl->o == IR_TDUP || + (LJ_HASFFI && (irl->o == IR_CNEW || irl->o == IR_CNEWI)))) + break; + irt_setmark(irl->t); + irt_setmark(irr->t); + break; + } + default: + if (irt_ismarked(ir->t) || irt_isguard(ir->t)) { /* Propagate mark. */ + if (ir->op1 >= REF_FIRST) irt_setmark(IR(ir->op1)->t); + if (ir->op2 >= REF_FIRST) irt_setmark(IR(ir->op2)->t); + } + break; + } + } +} + +/* Mark all instructions referenced by a snapshot. */ +static void sink_mark_snap(jit_State *J, SnapShot *snap) +{ + SnapEntry *map = &J->cur.snapmap[snap->mapofs]; + MSize n, nent = snap->nent; + for (n = 0; n < nent; n++) { + IRRef ref = snap_ref(map[n]); + if (!irref_isk(ref)) + irt_setmark(IR(ref)->t); + } +} + +/* Iteratively remark PHI refs with differing marks or PHI value counts. */ +static void sink_remark_phi(jit_State *J) +{ + IRIns *ir; + int remark; + do { + remark = 0; + for (ir = IR(J->cur.nins-1); ir->o == IR_PHI; ir--) { + IRIns *irl = IR(ir->op1), *irr = IR(ir->op2); + if (!((irl->t.irt ^ irr->t.irt) & IRT_MARK) && irl->prev == irr->prev) + continue; + remark |= (~(irl->t.irt & irr->t.irt) & IRT_MARK); + irt_setmark(IR(ir->op1)->t); + irt_setmark(IR(ir->op2)->t); + } + } while (remark); +} + +/* Sweep instructions and tag sunken allocations and stores. */ +static void sink_sweep_ins(jit_State *J) +{ + IRIns *ir, *irbase = IR(REF_BASE); + for (ir = IR(J->cur.nins-1) ; ir >= irbase; ir--) { + switch (ir->o) { + case IR_ASTORE: case IR_HSTORE: case IR_FSTORE: case IR_XSTORE: { + IRIns *ira = sink_checkalloc(J, ir); + if (ira && !irt_ismarked(ira->t)) { + int delta = (int)(ir - ira); + ir->prev = REGSP(RID_SINK, delta > 255 ? 255 : delta); + } else { + ir->prev = REGSP_INIT; + } + break; + } + case IR_NEWREF: + if (!irt_ismarked(IR(ir->op1)->t)) { + ir->prev = REGSP(RID_SINK, 0); + } else { + irt_clearmark(ir->t); + ir->prev = REGSP_INIT; + } + break; +#if LJ_HASFFI + case IR_CNEW: case IR_CNEWI: +#endif + case IR_TNEW: case IR_TDUP: + if (!irt_ismarked(ir->t)) { + ir->t.irt &= ~IRT_GUARD; + ir->prev = REGSP(RID_SINK, 0); + J->cur.sinktags = 1; /* Signal present SINK tags to assembler. */ + } else { + irt_clearmark(ir->t); + ir->prev = REGSP_INIT; + } + break; + case IR_PHI: { + IRIns *ira = IR(ir->op2); + if (!irt_ismarked(ira->t) && + (ira->o == IR_TNEW || ira->o == IR_TDUP || + (LJ_HASFFI && (ira->o == IR_CNEW || ira->o == IR_CNEWI)))) { + ir->prev = REGSP(RID_SINK, 0); + } else { + ir->prev = REGSP_INIT; + } + break; + } + default: + irt_clearmark(ir->t); + ir->prev = REGSP_INIT; + break; + } + } + for (ir = IR(J->cur.nk); ir < irbase; ir++) { + irt_clearmark(ir->t); + ir->prev = REGSP_INIT; + /* The false-positive of irt_is64() for ASMREF_L (REF_NIL) is OK here. */ + if (irt_is64(ir->t) && ir->o != IR_KNULL) + ir++; + } +} + +/* Allocation sinking and store sinking. +** +** 1. Mark all non-sinkable allocations. +** 2. Then sink all remaining allocations and the related stores. +*/ +void lj_opt_sink(jit_State *J) +{ + const uint32_t need = (JIT_F_OPT_SINK|JIT_F_OPT_FWD| + JIT_F_OPT_DCE|JIT_F_OPT_CSE|JIT_F_OPT_FOLD); + if ((J->flags & need) == need && + (J->chain[IR_TNEW] || J->chain[IR_TDUP] || + (LJ_HASFFI && (J->chain[IR_CNEW] || J->chain[IR_CNEWI])))) { + if (!J->loopref) + sink_mark_snap(J, &J->cur.snap[J->cur.nsnap-1]); + sink_mark_ins(J); + if (J->loopref) + sink_remark_phi(J); + sink_sweep_ins(J); + } +} + +#undef IR + +#endif diff --git a/lib/LuaJIT/lj_opt_sink.o b/lib/LuaJIT/lj_opt_sink.o new file mode 100644 index 0000000000000000000000000000000000000000..1f93540aa9aafaf324390d0e75c9c440207ed161 GIT binary patch literal 5696 zcmeI0Z){W76~M3UXJcsU=ap#71`}_r>r#n&NkLQ7nbt2c;~C8AHNaSbku-@FMj%%5 zoMa5CuX{0x6|6-Q6KFyc+WLVH{eadsNdYlK5CMeNv{Zv_)yOtJI1SR6hEhr?-Z{_b z2KSJC*|cxF==t9JyXTyH{@iQ7@XDI_x7{w6kj*8Y6AxyR3Xy;8!8qa!BO)Lk75c{P ztbPhLHZ>YUYlB8^>8;rYC6ZpEj;=JapC(_uFRb1#s-xZRHMKWNv*pq3)Yk3#wsCct zD4Z-7ixwSlqfjlh@Usp93XS8J#t9l^-STy>8K0ua2zq=JbiM ztLORJy!G+(y-ypb9p~KQu%Z6?ciqd?QMZv(Ce#tboBQr@>#pYnt{ZB@S=c=)L~Xv* zJ93p1Gw&pmA$3{5V?%zUj~YzVOz4%z}C_ zZ`kMj>Igbj@?mIT7##ST5UOi6ewb2k47Y~OMJ#*Q$P8$1A<|z;+P{4Q1|l7VQ8kXt z$)po!qds&3_l*s$3ucPxgszU(`(i_Tt09|_*igM@gq^? z*DgF7-qc$OaTzU&ZMd!#r=&exSB`0F|4NS#;FKr1T39^`V?!?nt@v3*1JvMl*i&2TOU+gs9J43U7|q@u;_8UM^rlEV;U5Nxb0jDK1z!l2P@K%V=TgFMzy&3pn` zvf@`2ZRRr8+Yv#d5%Y~$uG<&SUM<`KRXCzv-CFNB)aaf$B>l$?L1(?Yb{4wDkj+Ur zZx?DWCbNbQk7qL5<$Kofs{L?ag|$x6EJM+zw?pZiy68hcE0qP@gcN4pQ!}53GC4U^ zCgQoD3ZaM%b!XIpUqNd1lTdImv;IN+VM84_3<}}JYJUp*u$qZO56+0$=mg{@vl^zS z?}J`a&!&9pG7E+xW*7Wmw)X`nK*Sqf*E15{pzaTVs9i{eJ?g*$$H+(oi<9X=KJ^MJ z>4TAqF|rm3=;!NSa)F(mF|rFmRN;k-0n<zl;P0?ezCorF86WdE7HbY;f=>-Fp( z1`~??7uN|4D49+8j9kKRF0%T(OH2jE!C2k9y_dF*V^Y>+E~@<>!VRIU=peON&0Ghv z1`=J$BgAXuR!uAjCdiuk6t zJ6pi_lRb6asX3%wfu$|J>3UdtQ2+kCnhnDFoX+1vr$F8QvD>R=?!k2WLx)+gfv~3& z*2f-o{|)46P@sCj`8IMXU!f9Qq`T7Sh8vP8fFL;UvEp!uoC`923=rD-fBPR~U~NOg zvs(4q?`(Qfs|(eJYG{i}>Ro$Z6t2FYYe~fd|1jLfRlt|hS_=F6K=6P&7O2|q(F2;T z>|PY8(gVTzfIs?3i!)pw@Pk+oo{cCEt+~_*+@qbweb~EO1HnYVuffoQ2ivd$A7DRd zyFDZBMFGG5h{ONg2e=4dT>qH&ym;n)!w|v)|K@8Ru*91Rz+Gm?$pNq>#N}MZ_%!h; z=&?C*mWHsamqwrMJOVTJaPx?T4?`dxvEU)T7RY06muSL-Ky&hlgN`5W=uDYAx_9o` z6>1UK?MSunY;8-G#x3n_ExVdKIyzfIJG;A@;XF7Z4hA_1%dDVLctgM+eQ4OB_i$yM zh722Wd?J);nOVuBIDjT2m)sy;QCgm}6DN-63Y(Nq5SQmy@{Po+O5$_!RpgI6(^7sL z$!ko$iTLA;w-T>ryq)+8#yg1DFrFfg_Y50O@iB=%gAkhJeZ<2hJjeGDk1%c#k22m* z++ci=c%1PA#1o7U6PI@#r}!KszJGk%WvAmbN^A7K14@nOa%i63PAed321zefBB z;~x+|%J>xV*BHM+{21c};;%D)llXDQZxMf!@!Q157{5dO6yx`Z=NXq@_2(E@=zHJ- z<9_0o8LuEd$#{_X`;6}(evR=K;vX=+l=u|mO~h|7-buW`cs22xjISVmi}7LNw;4Z3 z{0`&O#P2bFgt+{v#B$(sl(<5_OOi*4`x!TgS1=wY9%MX0ypr*a#H$!D5MRppHsTuN zGXIY=-b(V-jK4vA1>+sWYZy-vU&Xjd{29jkh=&>9M?Au~MLf!QKXLq{5Sy&aAaVTL zuAE;bo?!e>#5Xd2ow!yqGiRTsVhiIbns+chMRWfBDc^HMsQX8|&E}V(H@ixGyBu}3 zbu@>}wmwsYTAR&g5qi11TZA0i)mb7fox69p?E!&3on~7I-in{>DkT>lpjzlwiGU&?MHeIEZ&(myH%sh9ps{5=@V7yq5ll_Z)- z5aY*`NPSs<^qudGDiIQt-!m1!@ZtJslh+=Q=%f5M;-swcT%nuu>2IOLC(Cqk$o2mP DHmdDt literal 0 HcmV?d00001 diff --git a/lib/LuaJIT/lj_opt_sink_dyn.o b/lib/LuaJIT/lj_opt_sink_dyn.o new file mode 100644 index 0000000000000000000000000000000000000000..1f93540aa9aafaf324390d0e75c9c440207ed161 GIT binary patch literal 5696 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 DHmdDt literal 0 HcmV?d00001 diff --git a/lib/LuaJIT/lj_opt_split.c b/lib/LuaJIT/lj_opt_split.c new file mode 100644 index 0000000..79ac3cc --- /dev/null +++ b/lib/LuaJIT/lj_opt_split.c @@ -0,0 +1,870 @@ +/* +** SPLIT: Split 64 bit IR instructions into 32 bit IR instructions. +** Copyright (C) 2005-2017 Mike Pall. See Copyright Notice in luajit.h +*/ + +#define lj_opt_split_c +#define LUA_CORE + +#include "lj_obj.h" + +#if LJ_HASJIT && (LJ_SOFTFP32 || (LJ_32 && LJ_HASFFI)) + +#include "lj_err.h" +#include "lj_buf.h" +#include "lj_ir.h" +#include "lj_jit.h" +#include "lj_ircall.h" +#include "lj_iropt.h" +#include "lj_dispatch.h" +#include "lj_vm.h" + +/* SPLIT pass: +** +** This pass splits up 64 bit IR instructions into multiple 32 bit IR +** instructions. It's only active for soft-float targets or for 32 bit CPUs +** which lack native 64 bit integer operations (the FFI is currently the +** only emitter for 64 bit integer instructions). +** +** Splitting the IR in a separate pass keeps each 32 bit IR assembler +** backend simple. Only a small amount of extra functionality needs to be +** implemented. This is much easier than adding support for allocating +** register pairs to each backend (believe me, I tried). A few simple, but +** important optimizations can be performed by the SPLIT pass, which would +** be tedious to do in the backend. +** +** The basic idea is to replace each 64 bit IR instruction with its 32 bit +** equivalent plus an extra HIOP instruction. The splitted IR is not passed +** through FOLD or any other optimizations, so each HIOP is guaranteed to +** immediately follow it's counterpart. The actual functionality of HIOP is +** inferred from the previous instruction. +** +** The operands of HIOP hold the hiword input references. The output of HIOP +** is the hiword output reference, which is also used to hold the hiword +** register or spill slot information. The register allocator treats this +** instruction independently of any other instruction, which improves code +** quality compared to using fixed register pairs. +** +** It's easier to split up some instructions into two regular 32 bit +** instructions. E.g. XLOAD is split up into two XLOADs with two different +** addresses. Obviously 64 bit constants need to be split up into two 32 bit +** constants, too. Some hiword instructions can be entirely omitted, e.g. +** when zero-extending a 32 bit value to 64 bits. 64 bit arguments for calls +** are split up into two 32 bit arguments each. +** +** On soft-float targets, floating-point instructions are directly converted +** to soft-float calls by the SPLIT pass (except for comparisons and MIN/MAX). +** HIOP for number results has the type IRT_SOFTFP ("sfp" in -jdump). +** +** Here's the IR and x64 machine code for 'x.b = x.a + 1' for a struct with +** two int64_t fields: +** +** 0100 p32 ADD base +8 +** 0101 i64 XLOAD 0100 +** 0102 i64 ADD 0101 +1 +** 0103 p32 ADD base +16 +** 0104 i64 XSTORE 0103 0102 +** +** mov rax, [esi+0x8] +** add rax, +0x01 +** mov [esi+0x10], rax +** +** Here's the transformed IR and the x86 machine code after the SPLIT pass: +** +** 0100 p32 ADD base +8 +** 0101 int XLOAD 0100 +** 0102 p32 ADD base +12 +** 0103 int XLOAD 0102 +** 0104 int ADD 0101 +1 +** 0105 int HIOP 0103 +0 +** 0106 p32 ADD base +16 +** 0107 int XSTORE 0106 0104 +** 0108 int HIOP 0106 0105 +** +** mov eax, [esi+0x8] +** mov ecx, [esi+0xc] +** add eax, +0x01 +** adc ecx, +0x00 +** mov [esi+0x10], eax +** mov [esi+0x14], ecx +** +** You may notice the reassociated hiword address computation, which is +** later fused into the mov operands by the assembler. +*/ + +/* Some local macros to save typing. Undef'd at the end. */ +#define IR(ref) (&J->cur.ir[(ref)]) + +/* Directly emit the transformed IR without updating chains etc. */ +static IRRef split_emit(jit_State *J, uint16_t ot, IRRef1 op1, IRRef1 op2) +{ + IRRef nref = lj_ir_nextins(J); + IRIns *ir = IR(nref); + ir->ot = ot; + ir->op1 = op1; + ir->op2 = op2; + return nref; +} + +#if LJ_SOFTFP +/* Emit a (checked) number to integer conversion. */ +static IRRef split_num2int(jit_State *J, IRRef lo, IRRef hi, int check) +{ + IRRef tmp, res; +#if LJ_LE + tmp = split_emit(J, IRT(IR_CARG, IRT_NIL), lo, hi); +#else + tmp = split_emit(J, IRT(IR_CARG, IRT_NIL), hi, lo); +#endif + res = split_emit(J, IRTI(IR_CALLN), tmp, IRCALL_softfp_d2i); + if (check) { + tmp = split_emit(J, IRTI(IR_CALLN), res, IRCALL_softfp_i2d); + split_emit(J, IRT(IR_HIOP, IRT_SOFTFP), tmp, tmp); + split_emit(J, IRTGI(IR_EQ), tmp, lo); + split_emit(J, IRTG(IR_HIOP, IRT_SOFTFP), tmp+1, hi); + } + return res; +} + +/* Emit a CALLN with one split 64 bit argument. */ +static IRRef split_call_l(jit_State *J, IRRef1 *hisubst, IRIns *oir, + IRIns *ir, IRCallID id) +{ + IRRef tmp, op1 = ir->op1; + J->cur.nins--; +#if LJ_LE + tmp = split_emit(J, IRT(IR_CARG, IRT_NIL), oir[op1].prev, hisubst[op1]); +#else + tmp = split_emit(J, IRT(IR_CARG, IRT_NIL), hisubst[op1], oir[op1].prev); +#endif + ir->prev = tmp = split_emit(J, IRTI(IR_CALLN), tmp, id); + return split_emit(J, IRT(IR_HIOP, IRT_SOFTFP), tmp, tmp); +} +#endif + +/* Emit a CALLN with one split 64 bit argument and a 32 bit argument. */ +static IRRef split_call_li(jit_State *J, IRRef1 *hisubst, IRIns *oir, + IRIns *ir, IRCallID id) +{ + IRRef tmp, op1 = ir->op1, op2 = ir->op2; + J->cur.nins--; +#if LJ_LE + tmp = split_emit(J, IRT(IR_CARG, IRT_NIL), oir[op1].prev, hisubst[op1]); +#else + tmp = split_emit(J, IRT(IR_CARG, IRT_NIL), hisubst[op1], oir[op1].prev); +#endif + tmp = split_emit(J, IRT(IR_CARG, IRT_NIL), tmp, oir[op2].prev); + ir->prev = tmp = split_emit(J, IRTI(IR_CALLN), tmp, id); + return split_emit(J, IRT(IR_HIOP, IRT_SOFTFP), tmp, tmp); +} + +/* Emit a CALLN with two split 64 bit arguments. */ +static IRRef split_call_ll(jit_State *J, IRRef1 *hisubst, IRIns *oir, + IRIns *ir, IRCallID id) +{ + IRRef tmp, op1 = ir->op1, op2 = ir->op2; + J->cur.nins--; +#if LJ_LE + tmp = split_emit(J, IRT(IR_CARG, IRT_NIL), oir[op1].prev, hisubst[op1]); + tmp = split_emit(J, IRT(IR_CARG, IRT_NIL), tmp, oir[op2].prev); + tmp = split_emit(J, IRT(IR_CARG, IRT_NIL), tmp, hisubst[op2]); +#else + tmp = split_emit(J, IRT(IR_CARG, IRT_NIL), hisubst[op1], oir[op1].prev); + tmp = split_emit(J, IRT(IR_CARG, IRT_NIL), tmp, hisubst[op2]); + tmp = split_emit(J, IRT(IR_CARG, IRT_NIL), tmp, oir[op2].prev); +#endif + ir->prev = tmp = split_emit(J, IRTI(IR_CALLN), tmp, id); + return split_emit(J, + IRT(IR_HIOP, (LJ_SOFTFP && irt_isnum(ir->t)) ? IRT_SOFTFP : IRT_INT), + tmp, tmp); +} + +/* Get a pointer to the other 32 bit word (LE: hiword, BE: loword). */ +static IRRef split_ptr(jit_State *J, IRIns *oir, IRRef ref) +{ + IRRef nref = oir[ref].prev; + IRIns *ir = IR(nref); + int32_t ofs = 4; + if (ir->o == IR_KPTR) + return lj_ir_kptr(J, (char *)ir_kptr(ir) + ofs); + if (ir->o == IR_ADD && irref_isk(ir->op2) && !irt_isphi(oir[ref].t)) { + /* Reassociate address. */ + ofs += IR(ir->op2)->i; + nref = ir->op1; + if (ofs == 0) return nref; + } + return split_emit(J, IRT(IR_ADD, IRT_PTR), nref, lj_ir_kint(J, ofs)); +} + +#if LJ_HASFFI +static IRRef split_bitshift(jit_State *J, IRRef1 *hisubst, + IRIns *oir, IRIns *nir, IRIns *ir) +{ + IROp op = ir->o; + IRRef kref = nir->op2; + if (irref_isk(kref)) { /* Optimize constant shifts. */ + int32_t k = (IR(kref)->i & 63); + IRRef lo = nir->op1, hi = hisubst[ir->op1]; + if (op == IR_BROL || op == IR_BROR) { + if (op == IR_BROR) k = (-k & 63); + if (k >= 32) { IRRef t = lo; lo = hi; hi = t; k -= 32; } + if (k == 0) { + passthrough: + J->cur.nins--; + ir->prev = lo; + return hi; + } else { + TRef k1, k2; + IRRef t1, t2, t3, t4; + J->cur.nins--; + k1 = lj_ir_kint(J, k); + k2 = lj_ir_kint(J, (-k & 31)); + t1 = split_emit(J, IRTI(IR_BSHL), lo, k1); + t2 = split_emit(J, IRTI(IR_BSHL), hi, k1); + t3 = split_emit(J, IRTI(IR_BSHR), lo, k2); + t4 = split_emit(J, IRTI(IR_BSHR), hi, k2); + ir->prev = split_emit(J, IRTI(IR_BOR), t1, t4); + return split_emit(J, IRTI(IR_BOR), t2, t3); + } + } else if (k == 0) { + goto passthrough; + } else if (k < 32) { + if (op == IR_BSHL) { + IRRef t1 = split_emit(J, IRTI(IR_BSHL), hi, kref); + IRRef t2 = split_emit(J, IRTI(IR_BSHR), lo, lj_ir_kint(J, (-k&31))); + return split_emit(J, IRTI(IR_BOR), t1, t2); + } else { + IRRef t1 = ir->prev, t2; + lua_assert(op == IR_BSHR || op == IR_BSAR); + nir->o = IR_BSHR; + t2 = split_emit(J, IRTI(IR_BSHL), hi, lj_ir_kint(J, (-k&31))); + ir->prev = split_emit(J, IRTI(IR_BOR), t1, t2); + return split_emit(J, IRTI(op), hi, kref); + } + } else { + if (op == IR_BSHL) { + if (k == 32) + J->cur.nins--; + else + lo = ir->prev; + ir->prev = lj_ir_kint(J, 0); + return lo; + } else { + lua_assert(op == IR_BSHR || op == IR_BSAR); + if (k == 32) { + J->cur.nins--; + ir->prev = hi; + } else { + nir->op1 = hi; + } + if (op == IR_BSHR) + return lj_ir_kint(J, 0); + else + return split_emit(J, IRTI(IR_BSAR), hi, lj_ir_kint(J, 31)); + } + } + } + return split_call_li(J, hisubst, oir, ir, + op - IR_BSHL + IRCALL_lj_carith_shl64); +} + +static IRRef split_bitop(jit_State *J, IRRef1 *hisubst, + IRIns *nir, IRIns *ir) +{ + IROp op = ir->o; + IRRef hi, kref = nir->op2; + if (irref_isk(kref)) { /* Optimize bit operations with lo constant. */ + int32_t k = IR(kref)->i; + if (k == 0 || k == -1) { + if (op == IR_BAND) k = ~k; + if (k == 0) { + J->cur.nins--; + ir->prev = nir->op1; + } else if (op == IR_BXOR) { + nir->o = IR_BNOT; + nir->op2 = 0; + } else { + J->cur.nins--; + ir->prev = kref; + } + } + } + hi = hisubst[ir->op1]; + kref = hisubst[ir->op2]; + if (irref_isk(kref)) { /* Optimize bit operations with hi constant. */ + int32_t k = IR(kref)->i; + if (k == 0 || k == -1) { + if (op == IR_BAND) k = ~k; + if (k == 0) { + return hi; + } else if (op == IR_BXOR) { + return split_emit(J, IRTI(IR_BNOT), hi, 0); + } else { + return kref; + } + } + } + return split_emit(J, IRTI(op), hi, kref); +} +#endif + +/* Substitute references of a snapshot. */ +static void split_subst_snap(jit_State *J, SnapShot *snap, IRIns *oir) +{ + SnapEntry *map = &J->cur.snapmap[snap->mapofs]; + MSize n, nent = snap->nent; + for (n = 0; n < nent; n++) { + SnapEntry sn = map[n]; + IRIns *ir = &oir[snap_ref(sn)]; + if (!(LJ_SOFTFP && (sn & SNAP_SOFTFPNUM) && irref_isk(snap_ref(sn)))) + map[n] = ((sn & 0xffff0000) | ir->prev); + } +} + +/* Transform the old IR to the new IR. */ +static void split_ir(jit_State *J) +{ + IRRef nins = J->cur.nins, nk = J->cur.nk; + MSize irlen = nins - nk; + MSize need = (irlen+1)*(sizeof(IRIns) + sizeof(IRRef1)); + IRIns *oir = (IRIns *)lj_buf_tmp(J->L, need); + IRRef1 *hisubst; + IRRef ref, snref; + SnapShot *snap; + + /* Copy old IR to buffer. */ + memcpy(oir, IR(nk), irlen*sizeof(IRIns)); + /* Bias hiword substitution table and old IR. Loword kept in field prev. */ + hisubst = (IRRef1 *)&oir[irlen] - nk; + oir -= nk; + + /* Remove all IR instructions, but retain IR constants. */ + J->cur.nins = REF_FIRST; + J->loopref = 0; + + /* Process constants and fixed references. */ + for (ref = nk; ref <= REF_BASE; ref++) { + IRIns *ir = &oir[ref]; + if ((LJ_SOFTFP && ir->o == IR_KNUM) || ir->o == IR_KINT64) { + /* Split up 64 bit constant. */ + TValue tv = *ir_k64(ir); + ir->prev = lj_ir_kint(J, (int32_t)tv.u32.lo); + hisubst[ref] = lj_ir_kint(J, (int32_t)tv.u32.hi); + } else { + ir->prev = ref; /* Identity substitution for loword. */ + hisubst[ref] = 0; + } + if (irt_is64(ir->t) && ir->o != IR_KNULL) + ref++; + } + + /* Process old IR instructions. */ + snap = J->cur.snap; + snref = snap->ref; + for (ref = REF_FIRST; ref < nins; ref++) { + IRIns *ir = &oir[ref]; + IRRef nref = lj_ir_nextins(J); + IRIns *nir = IR(nref); + IRRef hi = 0; + + if (ref >= snref) { + snap->ref = nref; + split_subst_snap(J, snap++, oir); + snref = snap < &J->cur.snap[J->cur.nsnap] ? snap->ref : ~(IRRef)0; + } + + /* Copy-substitute old instruction to new instruction. */ + nir->op1 = ir->op1 < nk ? ir->op1 : oir[ir->op1].prev; + nir->op2 = ir->op2 < nk ? ir->op2 : oir[ir->op2].prev; + ir->prev = nref; /* Loword substitution. */ + nir->o = ir->o; + nir->t.irt = ir->t.irt & ~(IRT_MARK|IRT_ISPHI); + hisubst[ref] = 0; + + /* Split 64 bit instructions. */ +#if LJ_SOFTFP + if (irt_isnum(ir->t)) { + nir->t.irt = IRT_INT | (nir->t.irt & IRT_GUARD); /* Turn into INT op. */ + /* Note: hi ref = lo ref + 1! Required for SNAP_SOFTFPNUM logic. */ + switch (ir->o) { + case IR_ADD: + hi = split_call_ll(J, hisubst, oir, ir, IRCALL_softfp_add); + break; + case IR_SUB: + hi = split_call_ll(J, hisubst, oir, ir, IRCALL_softfp_sub); + break; + case IR_MUL: + hi = split_call_ll(J, hisubst, oir, ir, IRCALL_softfp_mul); + break; + case IR_DIV: + hi = split_call_ll(J, hisubst, oir, ir, IRCALL_softfp_div); + break; + case IR_POW: + hi = split_call_li(J, hisubst, oir, ir, IRCALL_lj_vm_powi); + break; + case IR_FPMATH: + /* Try to rejoin pow from EXP2, MUL and LOG2. */ + if (nir->op2 == IRFPM_EXP2 && nir->op1 > J->loopref) { + IRIns *irp = IR(nir->op1); + if (irp->o == IR_CALLN && irp->op2 == IRCALL_softfp_mul) { + IRIns *irm4 = IR(irp->op1); + IRIns *irm3 = IR(irm4->op1); + IRIns *irm12 = IR(irm3->op1); + IRIns *irl1 = IR(irm12->op1); + if (irm12->op1 > J->loopref && irl1->o == IR_CALLN && + irl1->op2 == IRCALL_lj_vm_log2) { + IRRef tmp = irl1->op1; /* Recycle first two args from LOG2. */ + IRRef arg3 = irm3->op2, arg4 = irm4->op2; + J->cur.nins--; + tmp = split_emit(J, IRT(IR_CARG, IRT_NIL), tmp, arg3); + tmp = split_emit(J, IRT(IR_CARG, IRT_NIL), tmp, arg4); + ir->prev = tmp = split_emit(J, IRTI(IR_CALLN), tmp, IRCALL_pow); + hi = split_emit(J, IRT(IR_HIOP, IRT_SOFTFP), tmp, tmp); + break; + } + } + } + hi = split_call_l(J, hisubst, oir, ir, IRCALL_lj_vm_floor + ir->op2); + break; + case IR_ATAN2: + hi = split_call_ll(J, hisubst, oir, ir, IRCALL_atan2); + break; + case IR_LDEXP: + hi = split_call_li(J, hisubst, oir, ir, IRCALL_ldexp); + break; + case IR_NEG: case IR_ABS: + nir->o = IR_CONV; /* Pass through loword. */ + nir->op2 = (IRT_INT << 5) | IRT_INT; + hi = split_emit(J, IRT(ir->o == IR_NEG ? IR_BXOR : IR_BAND, IRT_SOFTFP), + hisubst[ir->op1], + lj_ir_kint(J, (int32_t)(0x7fffffffu + (ir->o == IR_NEG)))); + break; + case IR_SLOAD: + if ((nir->op2 & IRSLOAD_CONVERT)) { /* Convert from int to number. */ + nir->op2 &= ~IRSLOAD_CONVERT; + ir->prev = nref = split_emit(J, IRTI(IR_CALLN), nref, + IRCALL_softfp_i2d); + hi = split_emit(J, IRT(IR_HIOP, IRT_SOFTFP), nref, nref); + break; + } + /* fallthrough */ + case IR_ALOAD: case IR_HLOAD: case IR_ULOAD: case IR_VLOAD: + case IR_STRTO: + hi = split_emit(J, IRT(IR_HIOP, IRT_SOFTFP), nref, nref); + break; + case IR_FLOAD: + lua_assert(ir->op1 == REF_NIL); + hi = lj_ir_kint(J, *(int32_t*)((char*)J2GG(J) + ir->op2 + LJ_LE*4)); + nir->op2 += LJ_BE*4; + break; + case IR_XLOAD: { + IRIns inslo = *nir; /* Save/undo the emit of the lo XLOAD. */ + J->cur.nins--; + hi = split_ptr(J, oir, ir->op1); /* Insert the hiref ADD. */ +#if LJ_BE + hi = split_emit(J, IRT(IR_XLOAD, IRT_INT), hi, ir->op2); + inslo.t.irt = IRT_SOFTFP | (inslo.t.irt & IRT_GUARD); +#endif + nref = lj_ir_nextins(J); + nir = IR(nref); + *nir = inslo; /* Re-emit lo XLOAD. */ +#if LJ_LE + hi = split_emit(J, IRT(IR_XLOAD, IRT_SOFTFP), hi, ir->op2); + ir->prev = nref; +#else + ir->prev = hi; hi = nref; +#endif + break; + } + case IR_ASTORE: case IR_HSTORE: case IR_USTORE: case IR_XSTORE: + split_emit(J, IRT(IR_HIOP, IRT_SOFTFP), nir->op1, hisubst[ir->op2]); + break; + case IR_CONV: { /* Conversion to number. Others handled below. */ + IRType st = (IRType)(ir->op2 & IRCONV_SRCMASK); + UNUSED(st); +#if LJ_32 && LJ_HASFFI + if (st == IRT_I64 || st == IRT_U64) { + hi = split_call_l(J, hisubst, oir, ir, + st == IRT_I64 ? IRCALL_fp64_l2d : IRCALL_fp64_ul2d); + break; + } +#endif + lua_assert(st == IRT_INT || + (LJ_32 && LJ_HASFFI && (st == IRT_U32 || st == IRT_FLOAT))); + nir->o = IR_CALLN; +#if LJ_32 && LJ_HASFFI + nir->op2 = st == IRT_INT ? IRCALL_softfp_i2d : + st == IRT_FLOAT ? IRCALL_softfp_f2d : + IRCALL_softfp_ui2d; +#else + nir->op2 = IRCALL_softfp_i2d; +#endif + hi = split_emit(J, IRT(IR_HIOP, IRT_SOFTFP), nref, nref); + break; + } + case IR_CALLN: + case IR_CALLL: + case IR_CALLS: + case IR_CALLXS: + goto split_call; + case IR_PHI: + if (nir->op1 == nir->op2) + J->cur.nins--; /* Drop useless PHIs. */ + if (hisubst[ir->op1] != hisubst[ir->op2]) + split_emit(J, IRT(IR_PHI, IRT_SOFTFP), + hisubst[ir->op1], hisubst[ir->op2]); + break; + case IR_HIOP: + J->cur.nins--; /* Drop joining HIOP. */ + ir->prev = nir->op1; + hi = nir->op2; + break; + default: + lua_assert(ir->o <= IR_NE || ir->o == IR_MIN || ir->o == IR_MAX); + hi = split_emit(J, IRTG(IR_HIOP, IRT_SOFTFP), + hisubst[ir->op1], hisubst[ir->op2]); + break; + } + } else +#endif +#if LJ_32 && LJ_HASFFI + if (irt_isint64(ir->t)) { + IRRef hiref = hisubst[ir->op1]; + nir->t.irt = IRT_INT | (nir->t.irt & IRT_GUARD); /* Turn into INT op. */ + switch (ir->o) { + case IR_ADD: + case IR_SUB: + /* Use plain op for hiword if loword cannot produce a carry/borrow. */ + if (irref_isk(nir->op2) && IR(nir->op2)->i == 0) { + ir->prev = nir->op1; /* Pass through loword. */ + nir->op1 = hiref; nir->op2 = hisubst[ir->op2]; + hi = nref; + break; + } + /* fallthrough */ + case IR_NEG: + hi = split_emit(J, IRTI(IR_HIOP), hiref, hisubst[ir->op2]); + break; + case IR_MUL: + hi = split_call_ll(J, hisubst, oir, ir, IRCALL_lj_carith_mul64); + break; + case IR_DIV: + hi = split_call_ll(J, hisubst, oir, ir, + irt_isi64(ir->t) ? IRCALL_lj_carith_divi64 : + IRCALL_lj_carith_divu64); + break; + case IR_MOD: + hi = split_call_ll(J, hisubst, oir, ir, + irt_isi64(ir->t) ? IRCALL_lj_carith_modi64 : + IRCALL_lj_carith_modu64); + break; + case IR_POW: + hi = split_call_ll(J, hisubst, oir, ir, + irt_isi64(ir->t) ? IRCALL_lj_carith_powi64 : + IRCALL_lj_carith_powu64); + break; + case IR_BNOT: + hi = split_emit(J, IRTI(IR_BNOT), hiref, 0); + break; + case IR_BSWAP: + ir->prev = split_emit(J, IRTI(IR_BSWAP), hiref, 0); + hi = nref; + break; + case IR_BAND: case IR_BOR: case IR_BXOR: + hi = split_bitop(J, hisubst, nir, ir); + break; + case IR_BSHL: case IR_BSHR: case IR_BSAR: case IR_BROL: case IR_BROR: + hi = split_bitshift(J, hisubst, oir, nir, ir); + break; + case IR_FLOAD: + lua_assert(ir->op2 == IRFL_CDATA_INT64); + hi = split_emit(J, IRTI(IR_FLOAD), nir->op1, IRFL_CDATA_INT64_4); +#if LJ_BE + ir->prev = hi; hi = nref; +#endif + break; + case IR_XLOAD: + hi = split_emit(J, IRTI(IR_XLOAD), split_ptr(J, oir, ir->op1), ir->op2); +#if LJ_BE + ir->prev = hi; hi = nref; +#endif + break; + case IR_XSTORE: + split_emit(J, IRTI(IR_HIOP), nir->op1, hisubst[ir->op2]); + break; + case IR_CONV: { /* Conversion to 64 bit integer. Others handled below. */ + IRType st = (IRType)(ir->op2 & IRCONV_SRCMASK); +#if LJ_SOFTFP + if (st == IRT_NUM) { /* NUM to 64 bit int conv. */ + hi = split_call_l(J, hisubst, oir, ir, + irt_isi64(ir->t) ? IRCALL_fp64_d2l : IRCALL_fp64_d2ul); + } else if (st == IRT_FLOAT) { /* FLOAT to 64 bit int conv. */ + nir->o = IR_CALLN; + nir->op2 = irt_isi64(ir->t) ? IRCALL_fp64_f2l : IRCALL_fp64_f2ul; + hi = split_emit(J, IRTI(IR_HIOP), nref, nref); + } +#else + if (st == IRT_NUM || st == IRT_FLOAT) { /* FP to 64 bit int conv. */ + hi = split_emit(J, IRTI(IR_HIOP), nir->op1, nref); + } +#endif + else if (st == IRT_I64 || st == IRT_U64) { /* 64/64 bit cast. */ + /* Drop cast, since assembler doesn't care. But fwd both parts. */ + hi = hiref; + goto fwdlo; + } else if ((ir->op2 & IRCONV_SEXT)) { /* Sign-extend to 64 bit. */ + IRRef k31 = lj_ir_kint(J, 31); + nir = IR(nref); /* May have been reallocated. */ + ir->prev = nir->op1; /* Pass through loword. */ + nir->o = IR_BSAR; /* hi = bsar(lo, 31). */ + nir->op2 = k31; + hi = nref; + } else { /* Zero-extend to 64 bit. */ + hi = lj_ir_kint(J, 0); + goto fwdlo; + } + break; + } + case IR_CALLXS: + goto split_call; + case IR_PHI: { + IRRef hiref2; + if ((irref_isk(nir->op1) && irref_isk(nir->op2)) || + nir->op1 == nir->op2) + J->cur.nins--; /* Drop useless PHIs. */ + hiref2 = hisubst[ir->op2]; + if (!((irref_isk(hiref) && irref_isk(hiref2)) || hiref == hiref2)) + split_emit(J, IRTI(IR_PHI), hiref, hiref2); + break; + } + case IR_HIOP: + J->cur.nins--; /* Drop joining HIOP. */ + ir->prev = nir->op1; + hi = nir->op2; + break; + default: + lua_assert(ir->o <= IR_NE); /* Comparisons. */ + split_emit(J, IRTGI(IR_HIOP), hiref, hisubst[ir->op2]); + break; + } + } else +#endif +#if LJ_SOFTFP + if (ir->o == IR_SLOAD) { + if ((nir->op2 & IRSLOAD_CONVERT)) { /* Convert from number to int. */ + nir->op2 &= ~IRSLOAD_CONVERT; + if (!(nir->op2 & IRSLOAD_TYPECHECK)) + nir->t.irt = IRT_INT; /* Drop guard. */ + split_emit(J, IRT(IR_HIOP, IRT_SOFTFP), nref, nref); + ir->prev = split_num2int(J, nref, nref+1, irt_isguard(ir->t)); + } + } else if (ir->o == IR_TOBIT) { + IRRef tmp, op1 = ir->op1; + J->cur.nins--; +#if LJ_LE + tmp = split_emit(J, IRT(IR_CARG, IRT_NIL), oir[op1].prev, hisubst[op1]); +#else + tmp = split_emit(J, IRT(IR_CARG, IRT_NIL), hisubst[op1], oir[op1].prev); +#endif + ir->prev = split_emit(J, IRTI(IR_CALLN), tmp, IRCALL_lj_vm_tobit); + } else if (ir->o == IR_TOSTR) { + if (hisubst[ir->op1]) { + if (irref_isk(ir->op1)) + nir->op1 = ir->op1; + else + split_emit(J, IRT(IR_HIOP, IRT_NIL), hisubst[ir->op1], nref); + } + } else if (ir->o == IR_HREF || ir->o == IR_NEWREF) { + if (irref_isk(ir->op2) && hisubst[ir->op2]) + nir->op2 = ir->op2; + } else +#endif + if (ir->o == IR_CONV) { /* See above, too. */ + IRType st = (IRType)(ir->op2 & IRCONV_SRCMASK); +#if LJ_32 && LJ_HASFFI + if (st == IRT_I64 || st == IRT_U64) { /* Conversion from 64 bit int. */ +#if LJ_SOFTFP + if (irt_isfloat(ir->t)) { + split_call_l(J, hisubst, oir, ir, + st == IRT_I64 ? IRCALL_fp64_l2f : IRCALL_fp64_ul2f); + J->cur.nins--; /* Drop unused HIOP. */ + } +#else + if (irt_isfp(ir->t)) { /* 64 bit integer to FP conversion. */ + ir->prev = split_emit(J, IRT(IR_HIOP, irt_type(ir->t)), + hisubst[ir->op1], nref); + } +#endif + else { /* Truncate to lower 32 bits. */ + fwdlo: + ir->prev = nir->op1; /* Forward loword. */ + /* Replace with NOP to avoid messing up the snapshot logic. */ + nir->ot = IRT(IR_NOP, IRT_NIL); + nir->op1 = nir->op2 = 0; + } + } +#endif +#if LJ_SOFTFP && LJ_32 && LJ_HASFFI + else if (irt_isfloat(ir->t)) { + if (st == IRT_NUM) { + split_call_l(J, hisubst, oir, ir, IRCALL_softfp_d2f); + J->cur.nins--; /* Drop unused HIOP. */ + } else { + nir->o = IR_CALLN; + nir->op2 = st == IRT_INT ? IRCALL_softfp_i2f : IRCALL_softfp_ui2f; + } + } else if (st == IRT_FLOAT) { + nir->o = IR_CALLN; + nir->op2 = irt_isint(ir->t) ? IRCALL_softfp_f2i : IRCALL_softfp_f2ui; + } else +#endif +#if LJ_SOFTFP + if (st == IRT_NUM || (LJ_32 && LJ_HASFFI && st == IRT_FLOAT)) { + if (irt_isguard(ir->t)) { + lua_assert(st == IRT_NUM && irt_isint(ir->t)); + J->cur.nins--; + ir->prev = split_num2int(J, nir->op1, hisubst[ir->op1], 1); + } else { + split_call_l(J, hisubst, oir, ir, +#if LJ_32 && LJ_HASFFI + st == IRT_NUM ? + (irt_isint(ir->t) ? IRCALL_softfp_d2i : IRCALL_softfp_d2ui) : + (irt_isint(ir->t) ? IRCALL_softfp_f2i : IRCALL_softfp_f2ui) +#else + IRCALL_softfp_d2i +#endif + ); + J->cur.nins--; /* Drop unused HIOP. */ + } + } +#endif + } else if (ir->o == IR_CALLXS) { + IRRef hiref; + split_call: + hiref = hisubst[ir->op1]; + if (hiref) { + IROpT ot = nir->ot; + IRRef op2 = nir->op2; + nir->ot = IRT(IR_CARG, IRT_NIL); +#if LJ_LE + nir->op2 = hiref; +#else + nir->op2 = nir->op1; nir->op1 = hiref; +#endif + ir->prev = nref = split_emit(J, ot, nref, op2); + } + if (LJ_SOFTFP ? irt_is64(ir->t) : irt_isint64(ir->t)) + hi = split_emit(J, + IRT(IR_HIOP, (LJ_SOFTFP && irt_isnum(ir->t)) ? IRT_SOFTFP : IRT_INT), + nref, nref); + } else if (ir->o == IR_CARG) { + IRRef hiref = hisubst[ir->op1]; + if (hiref) { + IRRef op2 = nir->op2; +#if LJ_LE + nir->op2 = hiref; +#else + nir->op2 = nir->op1; nir->op1 = hiref; +#endif + ir->prev = nref = split_emit(J, IRT(IR_CARG, IRT_NIL), nref, op2); + nir = IR(nref); + } + hiref = hisubst[ir->op2]; + if (hiref) { +#if !LJ_TARGET_X86 + int carg = 0; + IRIns *cir; + for (cir = IR(nir->op1); cir->o == IR_CARG; cir = IR(cir->op1)) + carg++; + if ((carg & 1) == 0) { /* Align 64 bit arguments. */ + IRRef op2 = nir->op2; + nir->op2 = REF_NIL; + nref = split_emit(J, IRT(IR_CARG, IRT_NIL), nref, op2); + nir = IR(nref); + } +#endif +#if LJ_BE + { IRRef tmp = nir->op2; nir->op2 = hiref; hiref = tmp; } +#endif + ir->prev = split_emit(J, IRT(IR_CARG, IRT_NIL), nref, hiref); + } + } else if (ir->o == IR_CNEWI) { + if (hisubst[ir->op2]) + split_emit(J, IRT(IR_HIOP, IRT_NIL), nref, hisubst[ir->op2]); + } else if (ir->o == IR_LOOP) { + J->loopref = nref; /* Needed by assembler. */ + } + hisubst[ref] = hi; /* Store hiword substitution. */ + } + if (snref == nins) { /* Substitution for last snapshot. */ + snap->ref = J->cur.nins; + split_subst_snap(J, snap, oir); + } + + /* Add PHI marks. */ + for (ref = J->cur.nins-1; ref >= REF_FIRST; ref--) { + IRIns *ir = IR(ref); + if (ir->o != IR_PHI) break; + if (!irref_isk(ir->op1)) irt_setphi(IR(ir->op1)->t); + if (ir->op2 > J->loopref) irt_setphi(IR(ir->op2)->t); + } +} + +/* Protected callback for split pass. */ +static TValue *cpsplit(lua_State *L, lua_CFunction dummy, void *ud) +{ + jit_State *J = (jit_State *)ud; + split_ir(J); + UNUSED(L); UNUSED(dummy); + return NULL; +} + +#if defined(LUA_USE_ASSERT) || LJ_SOFTFP +/* Slow, but sure way to check whether a SPLIT pass is needed. */ +static int split_needsplit(jit_State *J) +{ + IRIns *ir, *irend; + IRRef ref; + for (ir = IR(REF_FIRST), irend = IR(J->cur.nins); ir < irend; ir++) + if (LJ_SOFTFP ? irt_is64orfp(ir->t) : irt_isint64(ir->t)) + return 1; + if (LJ_SOFTFP) { + for (ref = J->chain[IR_SLOAD]; ref; ref = IR(ref)->prev) + if ((IR(ref)->op2 & IRSLOAD_CONVERT)) + return 1; + if (J->chain[IR_TOBIT]) + return 1; + } + for (ref = J->chain[IR_CONV]; ref; ref = IR(ref)->prev) { + IRType st = (IR(ref)->op2 & IRCONV_SRCMASK); + if ((LJ_SOFTFP && (st == IRT_NUM || st == IRT_FLOAT)) || + st == IRT_I64 || st == IRT_U64) + return 1; + } + return 0; /* Nope. */ +} +#endif + +/* SPLIT pass. */ +void lj_opt_split(jit_State *J) +{ +#if LJ_SOFTFP + if (!J->needsplit) + J->needsplit = split_needsplit(J); +#else + lua_assert(J->needsplit >= split_needsplit(J)); /* Verify flag. */ +#endif + if (J->needsplit) { + int errcode = lj_vm_cpcall(J->L, NULL, J, cpsplit); + if (errcode) { + /* Completely reset the trace to avoid inconsistent dump on abort. */ + J->cur.nins = J->cur.nk = REF_BASE; + J->cur.nsnap = 0; + lj_err_throw(J->L, errcode); /* Propagate errors. */ + } + } +} + +#undef IR + +#endif diff --git a/lib/LuaJIT/lj_opt_split.o b/lib/LuaJIT/lj_opt_split.o new file mode 100644 index 0000000000000000000000000000000000000000..06f2f67ff833b99a9a5c5a497511679149f7b507 GIT binary patch literal 920 zcmb<-^>JfjWMqH=Mg}_u1P><4zz~5X=l~XWVBlonU|?`}cD7Q`aQ6$8btTr<+Ff%Y=Qw|ho#v#s%L!1RuJSQtYzn~<(xF9F9L@$|vL9e(n zw(>G51(}JS9vpyT zARq*x;3Uj`7;OrafwAF=fQ%qy4iXz(Er^dT)kHw`tbo$&KpKQWegy#u2n9^rn0^OK bK`j!1s)JGJ_6y?BF922O3eyOs(e(oW;-@Hz literal 0 HcmV?d00001 diff --git a/lib/LuaJIT/lj_opt_split_dyn.o b/lib/LuaJIT/lj_opt_split_dyn.o new file mode 100644 index 0000000000000000000000000000000000000000..06f2f67ff833b99a9a5c5a497511679149f7b507 GIT binary patch literal 920 zcmb<-^>JfjWMqH=Mg}_u1P><4zz~5X=l~XWVBlonU|?`}cD7Q`aQ6$8btTr<+Ff%Y=Qw|ho#v#s%L!1RuJSQtYzn~<(xF9F9L@$|vL9e(n zw(>G51(}JS9vpyT zARq*x;3Uj`7;OrafwAF=fQ%qy4iXz(Er^dT)kHw`tbo$&KpKQWegy#u2n9^rn0^OK bK`j!1s)JGJ_6y?BF922O3eyOs(e(oW;-@Hz literal 0 HcmV?d00001 diff --git a/lib/LuaJIT/lj_parse.c b/lib/LuaJIT/lj_parse.c new file mode 100644 index 0000000..c8efafa --- /dev/null +++ b/lib/LuaJIT/lj_parse.c @@ -0,0 +1,2728 @@ +/* +** Lua parser (source code -> bytecode). +** Copyright (C) 2005-2017 Mike Pall. See Copyright Notice in luajit.h +** +** Major portions taken verbatim or adapted from the Lua interpreter. +** Copyright (C) 1994-2008 Lua.org, PUC-Rio. See Copyright Notice in lua.h +*/ + +#define lj_parse_c +#define LUA_CORE + +#include "lj_obj.h" +#include "lj_gc.h" +#include "lj_err.h" +#include "lj_debug.h" +#include "lj_buf.h" +#include "lj_str.h" +#include "lj_tab.h" +#include "lj_func.h" +#include "lj_state.h" +#include "lj_bc.h" +#if LJ_HASFFI +#include "lj_ctype.h" +#endif +#include "lj_strfmt.h" +#include "lj_lex.h" +#include "lj_parse.h" +#include "lj_vm.h" +#include "lj_vmevent.h" + +/* -- Parser structures and definitions ----------------------------------- */ + +/* Expression kinds. */ +typedef enum { + /* Constant expressions must be first and in this order: */ + VKNIL, + VKFALSE, + VKTRUE, + VKSTR, /* sval = string value */ + VKNUM, /* nval = number value */ + VKLAST = VKNUM, + VKCDATA, /* nval = cdata value, not treated as a constant expression */ + /* Non-constant expressions follow: */ + VLOCAL, /* info = local register, aux = vstack index */ + VUPVAL, /* info = upvalue index, aux = vstack index */ + VGLOBAL, /* sval = string value */ + VINDEXED, /* info = table register, aux = index reg/byte/string const */ + VJMP, /* info = instruction PC */ + VRELOCABLE, /* info = instruction PC */ + VNONRELOC, /* info = result register */ + VCALL, /* info = instruction PC, aux = base */ + VVOID +} ExpKind; + +/* Expression descriptor. */ +typedef struct ExpDesc { + union { + struct { + uint32_t info; /* Primary info. */ + uint32_t aux; /* Secondary info. */ + } s; + TValue nval; /* Number value. */ + GCstr *sval; /* String value. */ + } u; + ExpKind k; + BCPos t; /* True condition jump list. */ + BCPos f; /* False condition jump list. */ +} ExpDesc; + +/* Macros for expressions. */ +#define expr_hasjump(e) ((e)->t != (e)->f) + +#define expr_isk(e) ((e)->k <= VKLAST) +#define expr_isk_nojump(e) (expr_isk(e) && !expr_hasjump(e)) +#define expr_isnumk(e) ((e)->k == VKNUM) +#define expr_isnumk_nojump(e) (expr_isnumk(e) && !expr_hasjump(e)) +#define expr_isstrk(e) ((e)->k == VKSTR) + +#define expr_numtv(e) check_exp(expr_isnumk((e)), &(e)->u.nval) +#define expr_numberV(e) numberVnum(expr_numtv((e))) + +/* Initialize expression. */ +static LJ_AINLINE void expr_init(ExpDesc *e, ExpKind k, uint32_t info) +{ + e->k = k; + e->u.s.info = info; + e->f = e->t = NO_JMP; +} + +/* Check number constant for +-0. */ +static int expr_numiszero(ExpDesc *e) +{ + TValue *o = expr_numtv(e); + return tvisint(o) ? (intV(o) == 0) : tviszero(o); +} + +/* Per-function linked list of scope blocks. */ +typedef struct FuncScope { + struct FuncScope *prev; /* Link to outer scope. */ + MSize vstart; /* Start of block-local variables. */ + uint8_t nactvar; /* Number of active vars outside the scope. */ + uint8_t flags; /* Scope flags. */ +} FuncScope; + +#define FSCOPE_LOOP 0x01 /* Scope is a (breakable) loop. */ +#define FSCOPE_BREAK 0x02 /* Break used in scope. */ +#define FSCOPE_GOLA 0x04 /* Goto or label used in scope. */ +#define FSCOPE_UPVAL 0x08 /* Upvalue in scope. */ +#define FSCOPE_NOCLOSE 0x10 /* Do not close upvalues. */ + +#define NAME_BREAK ((GCstr *)(uintptr_t)1) + +/* Index into variable stack. */ +typedef uint16_t VarIndex; +#define LJ_MAX_VSTACK (65536 - LJ_MAX_UPVAL) + +/* Variable/goto/label info. */ +#define VSTACK_VAR_RW 0x01 /* R/W variable. */ +#define VSTACK_GOTO 0x02 /* Pending goto. */ +#define VSTACK_LABEL 0x04 /* Label. */ + +/* Per-function state. */ +typedef struct FuncState { + GCtab *kt; /* Hash table for constants. */ + LexState *ls; /* Lexer state. */ + lua_State *L; /* Lua state. */ + FuncScope *bl; /* Current scope. */ + struct FuncState *prev; /* Enclosing function. */ + BCPos pc; /* Next bytecode position. */ + BCPos lasttarget; /* Bytecode position of last jump target. */ + BCPos jpc; /* Pending jump list to next bytecode. */ + BCReg freereg; /* First free register. */ + BCReg nactvar; /* Number of active local variables. */ + BCReg nkn, nkgc; /* Number of lua_Number/GCobj constants */ + BCLine linedefined; /* First line of the function definition. */ + BCInsLine *bcbase; /* Base of bytecode stack. */ + BCPos bclim; /* Limit of bytecode stack. */ + MSize vbase; /* Base of variable stack for this function. */ + uint8_t flags; /* Prototype flags. */ + uint8_t numparams; /* Number of parameters. */ + uint8_t framesize; /* Fixed frame size. */ + uint8_t nuv; /* Number of upvalues */ + VarIndex varmap[LJ_MAX_LOCVAR]; /* Map from register to variable idx. */ + VarIndex uvmap[LJ_MAX_UPVAL]; /* Map from upvalue to variable idx. */ + VarIndex uvtmp[LJ_MAX_UPVAL]; /* Temporary upvalue map. */ +} FuncState; + +/* Binary and unary operators. ORDER OPR */ +typedef enum BinOpr { + OPR_ADD, OPR_SUB, OPR_MUL, OPR_DIV, OPR_MOD, OPR_POW, /* ORDER ARITH */ + OPR_CONCAT, + OPR_NE, OPR_EQ, + OPR_LT, OPR_GE, OPR_LE, OPR_GT, + OPR_AND, OPR_OR, + OPR_NOBINOPR +} BinOpr; + +LJ_STATIC_ASSERT((int)BC_ISGE-(int)BC_ISLT == (int)OPR_GE-(int)OPR_LT); +LJ_STATIC_ASSERT((int)BC_ISLE-(int)BC_ISLT == (int)OPR_LE-(int)OPR_LT); +LJ_STATIC_ASSERT((int)BC_ISGT-(int)BC_ISLT == (int)OPR_GT-(int)OPR_LT); +LJ_STATIC_ASSERT((int)BC_SUBVV-(int)BC_ADDVV == (int)OPR_SUB-(int)OPR_ADD); +LJ_STATIC_ASSERT((int)BC_MULVV-(int)BC_ADDVV == (int)OPR_MUL-(int)OPR_ADD); +LJ_STATIC_ASSERT((int)BC_DIVVV-(int)BC_ADDVV == (int)OPR_DIV-(int)OPR_ADD); +LJ_STATIC_ASSERT((int)BC_MODVV-(int)BC_ADDVV == (int)OPR_MOD-(int)OPR_ADD); + +/* -- Error handling ------------------------------------------------------ */ + +LJ_NORET LJ_NOINLINE static void err_syntax(LexState *ls, ErrMsg em) +{ + lj_lex_error(ls, ls->tok, em); +} + +LJ_NORET LJ_NOINLINE static void err_token(LexState *ls, LexToken tok) +{ + lj_lex_error(ls, ls->tok, LJ_ERR_XTOKEN, lj_lex_token2str(ls, tok)); +} + +LJ_NORET static void err_limit(FuncState *fs, uint32_t limit, const char *what) +{ + if (fs->linedefined == 0) + lj_lex_error(fs->ls, 0, LJ_ERR_XLIMM, limit, what); + else + lj_lex_error(fs->ls, 0, LJ_ERR_XLIMF, fs->linedefined, limit, what); +} + +#define checklimit(fs, v, l, m) if ((v) >= (l)) err_limit(fs, l, m) +#define checklimitgt(fs, v, l, m) if ((v) > (l)) err_limit(fs, l, m) +#define checkcond(ls, c, em) { if (!(c)) err_syntax(ls, em); } + +/* -- Management of constants --------------------------------------------- */ + +/* Return bytecode encoding for primitive constant. */ +#define const_pri(e) check_exp((e)->k <= VKTRUE, (e)->k) + +#define tvhaskslot(o) ((o)->u32.hi == 0) +#define tvkslot(o) ((o)->u32.lo) + +/* Add a number constant. */ +static BCReg const_num(FuncState *fs, ExpDesc *e) +{ + lua_State *L = fs->L; + TValue *o; + lua_assert(expr_isnumk(e)); + o = lj_tab_set(L, fs->kt, &e->u.nval); + if (tvhaskslot(o)) + return tvkslot(o); + o->u64 = fs->nkn; + return fs->nkn++; +} + +/* Add a GC object constant. */ +static BCReg const_gc(FuncState *fs, GCobj *gc, uint32_t itype) +{ + lua_State *L = fs->L; + TValue key, *o; + setgcV(L, &key, gc, itype); + /* NOBARRIER: the key is new or kept alive. */ + o = lj_tab_set(L, fs->kt, &key); + if (tvhaskslot(o)) + return tvkslot(o); + o->u64 = fs->nkgc; + return fs->nkgc++; +} + +/* Add a string constant. */ +static BCReg const_str(FuncState *fs, ExpDesc *e) +{ + lua_assert(expr_isstrk(e) || e->k == VGLOBAL); + return const_gc(fs, obj2gco(e->u.sval), LJ_TSTR); +} + +/* Anchor string constant to avoid GC. */ +GCstr *lj_parse_keepstr(LexState *ls, const char *str, size_t len) +{ + /* NOBARRIER: the key is new or kept alive. */ + lua_State *L = ls->L; + GCstr *s = lj_str_new(L, str, len); + TValue *tv = lj_tab_setstr(L, ls->fs->kt, s); + if (tvisnil(tv)) setboolV(tv, 1); + lj_gc_check(L); + return s; +} + +#if LJ_HASFFI +/* Anchor cdata to avoid GC. */ +void lj_parse_keepcdata(LexState *ls, TValue *tv, GCcdata *cd) +{ + /* NOBARRIER: the key is new or kept alive. */ + lua_State *L = ls->L; + setcdataV(L, tv, cd); + setboolV(lj_tab_set(L, ls->fs->kt, tv), 1); +} +#endif + +/* -- Jump list handling -------------------------------------------------- */ + +/* Get next element in jump list. */ +static BCPos jmp_next(FuncState *fs, BCPos pc) +{ + ptrdiff_t delta = bc_j(fs->bcbase[pc].ins); + if ((BCPos)delta == NO_JMP) + return NO_JMP; + else + return (BCPos)(((ptrdiff_t)pc+1)+delta); +} + +/* Check if any of the instructions on the jump list produce no value. */ +static int jmp_novalue(FuncState *fs, BCPos list) +{ + for (; list != NO_JMP; list = jmp_next(fs, list)) { + BCIns p = fs->bcbase[list >= 1 ? list-1 : list].ins; + if (!(bc_op(p) == BC_ISTC || bc_op(p) == BC_ISFC || bc_a(p) == NO_REG)) + return 1; + } + return 0; +} + +/* Patch register of test instructions. */ +static int jmp_patchtestreg(FuncState *fs, BCPos pc, BCReg reg) +{ + BCInsLine *ilp = &fs->bcbase[pc >= 1 ? pc-1 : pc]; + BCOp op = bc_op(ilp->ins); + if (op == BC_ISTC || op == BC_ISFC) { + if (reg != NO_REG && reg != bc_d(ilp->ins)) { + setbc_a(&ilp->ins, reg); + } else { /* Nothing to store or already in the right register. */ + setbc_op(&ilp->ins, op+(BC_IST-BC_ISTC)); + setbc_a(&ilp->ins, 0); + } + } else if (bc_a(ilp->ins) == NO_REG) { + if (reg == NO_REG) { + ilp->ins = BCINS_AJ(BC_JMP, bc_a(fs->bcbase[pc].ins), 0); + } else { + setbc_a(&ilp->ins, reg); + if (reg >= bc_a(ilp[1].ins)) + setbc_a(&ilp[1].ins, reg+1); + } + } else { + return 0; /* Cannot patch other instructions. */ + } + return 1; +} + +/* Drop values for all instructions on jump list. */ +static void jmp_dropval(FuncState *fs, BCPos list) +{ + for (; list != NO_JMP; list = jmp_next(fs, list)) + jmp_patchtestreg(fs, list, NO_REG); +} + +/* Patch jump instruction to target. */ +static void jmp_patchins(FuncState *fs, BCPos pc, BCPos dest) +{ + BCIns *jmp = &fs->bcbase[pc].ins; + BCPos offset = dest-(pc+1)+BCBIAS_J; + lua_assert(dest != NO_JMP); + if (offset > BCMAX_D) + err_syntax(fs->ls, LJ_ERR_XJUMP); + setbc_d(jmp, offset); +} + +/* Append to jump list. */ +static void jmp_append(FuncState *fs, BCPos *l1, BCPos l2) +{ + if (l2 == NO_JMP) { + return; + } else if (*l1 == NO_JMP) { + *l1 = l2; + } else { + BCPos list = *l1; + BCPos next; + while ((next = jmp_next(fs, list)) != NO_JMP) /* Find last element. */ + list = next; + jmp_patchins(fs, list, l2); + } +} + +/* Patch jump list and preserve produced values. */ +static void jmp_patchval(FuncState *fs, BCPos list, BCPos vtarget, + BCReg reg, BCPos dtarget) +{ + while (list != NO_JMP) { + BCPos next = jmp_next(fs, list); + if (jmp_patchtestreg(fs, list, reg)) + jmp_patchins(fs, list, vtarget); /* Jump to target with value. */ + else + jmp_patchins(fs, list, dtarget); /* Jump to default target. */ + list = next; + } +} + +/* Jump to following instruction. Append to list of pending jumps. */ +static void jmp_tohere(FuncState *fs, BCPos list) +{ + fs->lasttarget = fs->pc; + jmp_append(fs, &fs->jpc, list); +} + +/* Patch jump list to target. */ +static void jmp_patch(FuncState *fs, BCPos list, BCPos target) +{ + if (target == fs->pc) { + jmp_tohere(fs, list); + } else { + lua_assert(target < fs->pc); + jmp_patchval(fs, list, target, NO_REG, target); + } +} + +/* -- Bytecode register allocator ----------------------------------------- */ + +/* Bump frame size. */ +static void bcreg_bump(FuncState *fs, BCReg n) +{ + BCReg sz = fs->freereg + n; + if (sz > fs->framesize) { + if (sz >= LJ_MAX_SLOTS) + err_syntax(fs->ls, LJ_ERR_XSLOTS); + fs->framesize = (uint8_t)sz; + } +} + +/* Reserve registers. */ +static void bcreg_reserve(FuncState *fs, BCReg n) +{ + bcreg_bump(fs, n); + fs->freereg += n; +} + +/* Free register. */ +static void bcreg_free(FuncState *fs, BCReg reg) +{ + if (reg >= fs->nactvar) { + fs->freereg--; + lua_assert(reg == fs->freereg); + } +} + +/* Free register for expression. */ +static void expr_free(FuncState *fs, ExpDesc *e) +{ + if (e->k == VNONRELOC) + bcreg_free(fs, e->u.s.info); +} + +/* -- Bytecode emitter ---------------------------------------------------- */ + +/* Emit bytecode instruction. */ +static BCPos bcemit_INS(FuncState *fs, BCIns ins) +{ + BCPos pc = fs->pc; + LexState *ls = fs->ls; + jmp_patchval(fs, fs->jpc, pc, NO_REG, pc); + fs->jpc = NO_JMP; + if (LJ_UNLIKELY(pc >= fs->bclim)) { + ptrdiff_t base = fs->bcbase - ls->bcstack; + checklimit(fs, ls->sizebcstack, LJ_MAX_BCINS, "bytecode instructions"); + lj_mem_growvec(fs->L, ls->bcstack, ls->sizebcstack, LJ_MAX_BCINS,BCInsLine); + fs->bclim = (BCPos)(ls->sizebcstack - base); + fs->bcbase = ls->bcstack + base; + } + fs->bcbase[pc].ins = ins; + fs->bcbase[pc].line = ls->lastline; + fs->pc = pc+1; + return pc; +} + +#define bcemit_ABC(fs, o, a, b, c) bcemit_INS(fs, BCINS_ABC(o, a, b, c)) +#define bcemit_AD(fs, o, a, d) bcemit_INS(fs, BCINS_AD(o, a, d)) +#define bcemit_AJ(fs, o, a, j) bcemit_INS(fs, BCINS_AJ(o, a, j)) + +#define bcptr(fs, e) (&(fs)->bcbase[(e)->u.s.info].ins) + +/* -- Bytecode emitter for expressions ------------------------------------ */ + +/* Discharge non-constant expression to any register. */ +static void expr_discharge(FuncState *fs, ExpDesc *e) +{ + BCIns ins; + if (e->k == VUPVAL) { + ins = BCINS_AD(BC_UGET, 0, e->u.s.info); + } else if (e->k == VGLOBAL) { + ins = BCINS_AD(BC_GGET, 0, const_str(fs, e)); + } else if (e->k == VINDEXED) { + BCReg rc = e->u.s.aux; + if ((int32_t)rc < 0) { + ins = BCINS_ABC(BC_TGETS, 0, e->u.s.info, ~rc); + } else if (rc > BCMAX_C) { + ins = BCINS_ABC(BC_TGETB, 0, e->u.s.info, rc-(BCMAX_C+1)); + } else { + bcreg_free(fs, rc); + ins = BCINS_ABC(BC_TGETV, 0, e->u.s.info, rc); + } + bcreg_free(fs, e->u.s.info); + } else if (e->k == VCALL) { + e->u.s.info = e->u.s.aux; + e->k = VNONRELOC; + return; + } else if (e->k == VLOCAL) { + e->k = VNONRELOC; + return; + } else { + return; + } + e->u.s.info = bcemit_INS(fs, ins); + e->k = VRELOCABLE; +} + +/* Emit bytecode to set a range of registers to nil. */ +static void bcemit_nil(FuncState *fs, BCReg from, BCReg n) +{ + if (fs->pc > fs->lasttarget) { /* No jumps to current position? */ + BCIns *ip = &fs->bcbase[fs->pc-1].ins; + BCReg pto, pfrom = bc_a(*ip); + switch (bc_op(*ip)) { /* Try to merge with the previous instruction. */ + case BC_KPRI: + if (bc_d(*ip) != ~LJ_TNIL) break; + if (from == pfrom) { + if (n == 1) return; + } else if (from == pfrom+1) { + from = pfrom; + n++; + } else { + break; + } + *ip = BCINS_AD(BC_KNIL, from, from+n-1); /* Replace KPRI. */ + return; + case BC_KNIL: + pto = bc_d(*ip); + if (pfrom <= from && from <= pto+1) { /* Can we connect both ranges? */ + if (from+n-1 > pto) + setbc_d(ip, from+n-1); /* Patch previous instruction range. */ + return; + } + break; + default: + break; + } + } + /* Emit new instruction or replace old instruction. */ + bcemit_INS(fs, n == 1 ? BCINS_AD(BC_KPRI, from, VKNIL) : + BCINS_AD(BC_KNIL, from, from+n-1)); +} + +/* Discharge an expression to a specific register. Ignore branches. */ +static void expr_toreg_nobranch(FuncState *fs, ExpDesc *e, BCReg reg) +{ + BCIns ins; + expr_discharge(fs, e); + if (e->k == VKSTR) { + ins = BCINS_AD(BC_KSTR, reg, const_str(fs, e)); + } else if (e->k == VKNUM) { +#if LJ_DUALNUM + cTValue *tv = expr_numtv(e); + if (tvisint(tv) && checki16(intV(tv))) + ins = BCINS_AD(BC_KSHORT, reg, (BCReg)(uint16_t)intV(tv)); + else +#else + lua_Number n = expr_numberV(e); + int32_t k = lj_num2int(n); + if (checki16(k) && n == (lua_Number)k) + ins = BCINS_AD(BC_KSHORT, reg, (BCReg)(uint16_t)k); + else +#endif + ins = BCINS_AD(BC_KNUM, reg, const_num(fs, e)); +#if LJ_HASFFI + } else if (e->k == VKCDATA) { + fs->flags |= PROTO_FFI; + ins = BCINS_AD(BC_KCDATA, reg, + const_gc(fs, obj2gco(cdataV(&e->u.nval)), LJ_TCDATA)); +#endif + } else if (e->k == VRELOCABLE) { + setbc_a(bcptr(fs, e), reg); + goto noins; + } else if (e->k == VNONRELOC) { + if (reg == e->u.s.info) + goto noins; + ins = BCINS_AD(BC_MOV, reg, e->u.s.info); + } else if (e->k == VKNIL) { + bcemit_nil(fs, reg, 1); + goto noins; + } else if (e->k <= VKTRUE) { + ins = BCINS_AD(BC_KPRI, reg, const_pri(e)); + } else { + lua_assert(e->k == VVOID || e->k == VJMP); + return; + } + bcemit_INS(fs, ins); +noins: + e->u.s.info = reg; + e->k = VNONRELOC; +} + +/* Forward declaration. */ +static BCPos bcemit_jmp(FuncState *fs); + +/* Discharge an expression to a specific register. */ +static void expr_toreg(FuncState *fs, ExpDesc *e, BCReg reg) +{ + expr_toreg_nobranch(fs, e, reg); + if (e->k == VJMP) + jmp_append(fs, &e->t, e->u.s.info); /* Add it to the true jump list. */ + if (expr_hasjump(e)) { /* Discharge expression with branches. */ + BCPos jend, jfalse = NO_JMP, jtrue = NO_JMP; + if (jmp_novalue(fs, e->t) || jmp_novalue(fs, e->f)) { + BCPos jval = (e->k == VJMP) ? NO_JMP : bcemit_jmp(fs); + jfalse = bcemit_AD(fs, BC_KPRI, reg, VKFALSE); + bcemit_AJ(fs, BC_JMP, fs->freereg, 1); + jtrue = bcemit_AD(fs, BC_KPRI, reg, VKTRUE); + jmp_tohere(fs, jval); + } + jend = fs->pc; + fs->lasttarget = jend; + jmp_patchval(fs, e->f, jend, reg, jfalse); + jmp_patchval(fs, e->t, jend, reg, jtrue); + } + e->f = e->t = NO_JMP; + e->u.s.info = reg; + e->k = VNONRELOC; +} + +/* Discharge an expression to the next free register. */ +static void expr_tonextreg(FuncState *fs, ExpDesc *e) +{ + expr_discharge(fs, e); + expr_free(fs, e); + bcreg_reserve(fs, 1); + expr_toreg(fs, e, fs->freereg - 1); +} + +/* Discharge an expression to any register. */ +static BCReg expr_toanyreg(FuncState *fs, ExpDesc *e) +{ + expr_discharge(fs, e); + if (e->k == VNONRELOC) { + if (!expr_hasjump(e)) return e->u.s.info; /* Already in a register. */ + if (e->u.s.info >= fs->nactvar) { + expr_toreg(fs, e, e->u.s.info); /* Discharge to temp. register. */ + return e->u.s.info; + } + } + expr_tonextreg(fs, e); /* Discharge to next register. */ + return e->u.s.info; +} + +/* Partially discharge expression to a value. */ +static void expr_toval(FuncState *fs, ExpDesc *e) +{ + if (expr_hasjump(e)) + expr_toanyreg(fs, e); + else + expr_discharge(fs, e); +} + +/* Emit store for LHS expression. */ +static void bcemit_store(FuncState *fs, ExpDesc *var, ExpDesc *e) +{ + BCIns ins; + if (var->k == VLOCAL) { + fs->ls->vstack[var->u.s.aux].info |= VSTACK_VAR_RW; + expr_free(fs, e); + expr_toreg(fs, e, var->u.s.info); + return; + } else if (var->k == VUPVAL) { + fs->ls->vstack[var->u.s.aux].info |= VSTACK_VAR_RW; + expr_toval(fs, e); + if (e->k <= VKTRUE) + ins = BCINS_AD(BC_USETP, var->u.s.info, const_pri(e)); + else if (e->k == VKSTR) + ins = BCINS_AD(BC_USETS, var->u.s.info, const_str(fs, e)); + else if (e->k == VKNUM) + ins = BCINS_AD(BC_USETN, var->u.s.info, const_num(fs, e)); + else + ins = BCINS_AD(BC_USETV, var->u.s.info, expr_toanyreg(fs, e)); + } else if (var->k == VGLOBAL) { + BCReg ra = expr_toanyreg(fs, e); + ins = BCINS_AD(BC_GSET, ra, const_str(fs, var)); + } else { + BCReg ra, rc; + lua_assert(var->k == VINDEXED); + ra = expr_toanyreg(fs, e); + rc = var->u.s.aux; + if ((int32_t)rc < 0) { + ins = BCINS_ABC(BC_TSETS, ra, var->u.s.info, ~rc); + } else if (rc > BCMAX_C) { + ins = BCINS_ABC(BC_TSETB, ra, var->u.s.info, rc-(BCMAX_C+1)); + } else { + /* Free late alloced key reg to avoid assert on free of value reg. */ + /* This can only happen when called from expr_table(). */ + lua_assert(e->k != VNONRELOC || ra < fs->nactvar || + rc < ra || (bcreg_free(fs, rc),1)); + ins = BCINS_ABC(BC_TSETV, ra, var->u.s.info, rc); + } + } + bcemit_INS(fs, ins); + expr_free(fs, e); +} + +/* Emit method lookup expression. */ +static void bcemit_method(FuncState *fs, ExpDesc *e, ExpDesc *key) +{ + BCReg idx, func, obj = expr_toanyreg(fs, e); + expr_free(fs, e); + func = fs->freereg; + bcemit_AD(fs, BC_MOV, func+1+LJ_FR2, obj); /* Copy object to 1st argument. */ + lua_assert(expr_isstrk(key)); + idx = const_str(fs, key); + if (idx <= BCMAX_C) { + bcreg_reserve(fs, 2+LJ_FR2); + bcemit_ABC(fs, BC_TGETS, func, obj, idx); + } else { + bcreg_reserve(fs, 3+LJ_FR2); + bcemit_AD(fs, BC_KSTR, func+2+LJ_FR2, idx); + bcemit_ABC(fs, BC_TGETV, func, obj, func+2+LJ_FR2); + fs->freereg--; + } + e->u.s.info = func; + e->k = VNONRELOC; +} + +/* -- Bytecode emitter for branches --------------------------------------- */ + +/* Emit unconditional branch. */ +static BCPos bcemit_jmp(FuncState *fs) +{ + BCPos jpc = fs->jpc; + BCPos j = fs->pc - 1; + BCIns *ip = &fs->bcbase[j].ins; + fs->jpc = NO_JMP; + if ((int32_t)j >= (int32_t)fs->lasttarget && bc_op(*ip) == BC_UCLO) { + setbc_j(ip, NO_JMP); + fs->lasttarget = j+1; + } else { + j = bcemit_AJ(fs, BC_JMP, fs->freereg, NO_JMP); + } + jmp_append(fs, &j, jpc); + return j; +} + +/* Invert branch condition of bytecode instruction. */ +static void invertcond(FuncState *fs, ExpDesc *e) +{ + BCIns *ip = &fs->bcbase[e->u.s.info - 1].ins; + setbc_op(ip, bc_op(*ip)^1); +} + +/* Emit conditional branch. */ +static BCPos bcemit_branch(FuncState *fs, ExpDesc *e, int cond) +{ + BCPos pc; + if (e->k == VRELOCABLE) { + BCIns *ip = bcptr(fs, e); + if (bc_op(*ip) == BC_NOT) { + *ip = BCINS_AD(cond ? BC_ISF : BC_IST, 0, bc_d(*ip)); + return bcemit_jmp(fs); + } + } + if (e->k != VNONRELOC) { + bcreg_reserve(fs, 1); + expr_toreg_nobranch(fs, e, fs->freereg-1); + } + bcemit_AD(fs, cond ? BC_ISTC : BC_ISFC, NO_REG, e->u.s.info); + pc = bcemit_jmp(fs); + expr_free(fs, e); + return pc; +} + +/* Emit branch on true condition. */ +static void bcemit_branch_t(FuncState *fs, ExpDesc *e) +{ + BCPos pc; + expr_discharge(fs, e); + if (e->k == VKSTR || e->k == VKNUM || e->k == VKTRUE) + pc = NO_JMP; /* Never jump. */ + else if (e->k == VJMP) + invertcond(fs, e), pc = e->u.s.info; + else if (e->k == VKFALSE || e->k == VKNIL) + expr_toreg_nobranch(fs, e, NO_REG), pc = bcemit_jmp(fs); + else + pc = bcemit_branch(fs, e, 0); + jmp_append(fs, &e->f, pc); + jmp_tohere(fs, e->t); + e->t = NO_JMP; +} + +/* Emit branch on false condition. */ +static void bcemit_branch_f(FuncState *fs, ExpDesc *e) +{ + BCPos pc; + expr_discharge(fs, e); + if (e->k == VKNIL || e->k == VKFALSE) + pc = NO_JMP; /* Never jump. */ + else if (e->k == VJMP) + pc = e->u.s.info; + else if (e->k == VKSTR || e->k == VKNUM || e->k == VKTRUE) + expr_toreg_nobranch(fs, e, NO_REG), pc = bcemit_jmp(fs); + else + pc = bcemit_branch(fs, e, 1); + jmp_append(fs, &e->t, pc); + jmp_tohere(fs, e->f); + e->f = NO_JMP; +} + +/* -- Bytecode emitter for operators -------------------------------------- */ + +/* Try constant-folding of arithmetic operators. */ +static int foldarith(BinOpr opr, ExpDesc *e1, ExpDesc *e2) +{ + TValue o; + lua_Number n; + if (!expr_isnumk_nojump(e1) || !expr_isnumk_nojump(e2)) return 0; + n = lj_vm_foldarith(expr_numberV(e1), expr_numberV(e2), (int)opr-OPR_ADD); + setnumV(&o, n); + if (tvisnan(&o) || tvismzero(&o)) return 0; /* Avoid NaN and -0 as consts. */ + if (LJ_DUALNUM) { + int32_t k = lj_num2int(n); + if ((lua_Number)k == n) { + setintV(&e1->u.nval, k); + return 1; + } + } + setnumV(&e1->u.nval, n); + return 1; +} + +/* Emit arithmetic operator. */ +static void bcemit_arith(FuncState *fs, BinOpr opr, ExpDesc *e1, ExpDesc *e2) +{ + BCReg rb, rc, t; + uint32_t op; + if (foldarith(opr, e1, e2)) + return; + if (opr == OPR_POW) { + op = BC_POW; + rc = expr_toanyreg(fs, e2); + rb = expr_toanyreg(fs, e1); + } else { + op = opr-OPR_ADD+BC_ADDVV; + /* Must discharge 2nd operand first since VINDEXED might free regs. */ + expr_toval(fs, e2); + if (expr_isnumk(e2) && (rc = const_num(fs, e2)) <= BCMAX_C) + op -= BC_ADDVV-BC_ADDVN; + else + rc = expr_toanyreg(fs, e2); + /* 1st operand discharged by bcemit_binop_left, but need KNUM/KSHORT. */ + lua_assert(expr_isnumk(e1) || e1->k == VNONRELOC); + expr_toval(fs, e1); + /* Avoid two consts to satisfy bytecode constraints. */ + if (expr_isnumk(e1) && !expr_isnumk(e2) && + (t = const_num(fs, e1)) <= BCMAX_B) { + rb = rc; rc = t; op -= BC_ADDVV-BC_ADDNV; + } else { + rb = expr_toanyreg(fs, e1); + } + } + /* Using expr_free might cause asserts if the order is wrong. */ + if (e1->k == VNONRELOC && e1->u.s.info >= fs->nactvar) fs->freereg--; + if (e2->k == VNONRELOC && e2->u.s.info >= fs->nactvar) fs->freereg--; + e1->u.s.info = bcemit_ABC(fs, op, 0, rb, rc); + e1->k = VRELOCABLE; +} + +/* Emit comparison operator. */ +static void bcemit_comp(FuncState *fs, BinOpr opr, ExpDesc *e1, ExpDesc *e2) +{ + ExpDesc *eret = e1; + BCIns ins; + expr_toval(fs, e1); + if (opr == OPR_EQ || opr == OPR_NE) { + BCOp op = opr == OPR_EQ ? BC_ISEQV : BC_ISNEV; + BCReg ra; + if (expr_isk(e1)) { e1 = e2; e2 = eret; } /* Need constant in 2nd arg. */ + ra = expr_toanyreg(fs, e1); /* First arg must be in a reg. */ + expr_toval(fs, e2); + switch (e2->k) { + case VKNIL: case VKFALSE: case VKTRUE: + ins = BCINS_AD(op+(BC_ISEQP-BC_ISEQV), ra, const_pri(e2)); + break; + case VKSTR: + ins = BCINS_AD(op+(BC_ISEQS-BC_ISEQV), ra, const_str(fs, e2)); + break; + case VKNUM: + ins = BCINS_AD(op+(BC_ISEQN-BC_ISEQV), ra, const_num(fs, e2)); + break; + default: + ins = BCINS_AD(op, ra, expr_toanyreg(fs, e2)); + break; + } + } else { + uint32_t op = opr-OPR_LT+BC_ISLT; + BCReg ra, rd; + if ((op-BC_ISLT) & 1) { /* GT -> LT, GE -> LE */ + e1 = e2; e2 = eret; /* Swap operands. */ + op = ((op-BC_ISLT)^3)+BC_ISLT; + expr_toval(fs, e1); + } + rd = expr_toanyreg(fs, e2); + ra = expr_toanyreg(fs, e1); + ins = BCINS_AD(op, ra, rd); + } + /* Using expr_free might cause asserts if the order is wrong. */ + if (e1->k == VNONRELOC && e1->u.s.info >= fs->nactvar) fs->freereg--; + if (e2->k == VNONRELOC && e2->u.s.info >= fs->nactvar) fs->freereg--; + bcemit_INS(fs, ins); + eret->u.s.info = bcemit_jmp(fs); + eret->k = VJMP; +} + +/* Fixup left side of binary operator. */ +static void bcemit_binop_left(FuncState *fs, BinOpr op, ExpDesc *e) +{ + if (op == OPR_AND) { + bcemit_branch_t(fs, e); + } else if (op == OPR_OR) { + bcemit_branch_f(fs, e); + } else if (op == OPR_CONCAT) { + expr_tonextreg(fs, e); + } else if (op == OPR_EQ || op == OPR_NE) { + if (!expr_isk_nojump(e)) expr_toanyreg(fs, e); + } else { + if (!expr_isnumk_nojump(e)) expr_toanyreg(fs, e); + } +} + +/* Emit binary operator. */ +static void bcemit_binop(FuncState *fs, BinOpr op, ExpDesc *e1, ExpDesc *e2) +{ + if (op <= OPR_POW) { + bcemit_arith(fs, op, e1, e2); + } else if (op == OPR_AND) { + lua_assert(e1->t == NO_JMP); /* List must be closed. */ + expr_discharge(fs, e2); + jmp_append(fs, &e2->f, e1->f); + *e1 = *e2; + } else if (op == OPR_OR) { + lua_assert(e1->f == NO_JMP); /* List must be closed. */ + expr_discharge(fs, e2); + jmp_append(fs, &e2->t, e1->t); + *e1 = *e2; + } else if (op == OPR_CONCAT) { + expr_toval(fs, e2); + if (e2->k == VRELOCABLE && bc_op(*bcptr(fs, e2)) == BC_CAT) { + lua_assert(e1->u.s.info == bc_b(*bcptr(fs, e2))-1); + expr_free(fs, e1); + setbc_b(bcptr(fs, e2), e1->u.s.info); + e1->u.s.info = e2->u.s.info; + } else { + expr_tonextreg(fs, e2); + expr_free(fs, e2); + expr_free(fs, e1); + e1->u.s.info = bcemit_ABC(fs, BC_CAT, 0, e1->u.s.info, e2->u.s.info); + } + e1->k = VRELOCABLE; + } else { + lua_assert(op == OPR_NE || op == OPR_EQ || + op == OPR_LT || op == OPR_GE || op == OPR_LE || op == OPR_GT); + bcemit_comp(fs, op, e1, e2); + } +} + +/* Emit unary operator. */ +static void bcemit_unop(FuncState *fs, BCOp op, ExpDesc *e) +{ + if (op == BC_NOT) { + /* Swap true and false lists. */ + { BCPos temp = e->f; e->f = e->t; e->t = temp; } + jmp_dropval(fs, e->f); + jmp_dropval(fs, e->t); + expr_discharge(fs, e); + if (e->k == VKNIL || e->k == VKFALSE) { + e->k = VKTRUE; + return; + } else if (expr_isk(e) || (LJ_HASFFI && e->k == VKCDATA)) { + e->k = VKFALSE; + return; + } else if (e->k == VJMP) { + invertcond(fs, e); + return; + } else if (e->k == VRELOCABLE) { + bcreg_reserve(fs, 1); + setbc_a(bcptr(fs, e), fs->freereg-1); + e->u.s.info = fs->freereg-1; + e->k = VNONRELOC; + } else { + lua_assert(e->k == VNONRELOC); + } + } else { + lua_assert(op == BC_UNM || op == BC_LEN); + if (op == BC_UNM && !expr_hasjump(e)) { /* Constant-fold negations. */ +#if LJ_HASFFI + if (e->k == VKCDATA) { /* Fold in-place since cdata is not interned. */ + GCcdata *cd = cdataV(&e->u.nval); + int64_t *p = (int64_t *)cdataptr(cd); + if (cd->ctypeid == CTID_COMPLEX_DOUBLE) + p[1] ^= (int64_t)U64x(80000000,00000000); + else + *p = -*p; + return; + } else +#endif + if (expr_isnumk(e) && !expr_numiszero(e)) { /* Avoid folding to -0. */ + TValue *o = expr_numtv(e); + if (tvisint(o)) { + int32_t k = intV(o); + if (k == -k) + setnumV(o, -(lua_Number)k); + else + setintV(o, -k); + return; + } else { + o->u64 ^= U64x(80000000,00000000); + return; + } + } + } + expr_toanyreg(fs, e); + } + expr_free(fs, e); + e->u.s.info = bcemit_AD(fs, op, 0, e->u.s.info); + e->k = VRELOCABLE; +} + +/* -- Lexer support ------------------------------------------------------- */ + +/* Check and consume optional token. */ +static int lex_opt(LexState *ls, LexToken tok) +{ + if (ls->tok == tok) { + lj_lex_next(ls); + return 1; + } + return 0; +} + +/* Check and consume token. */ +static void lex_check(LexState *ls, LexToken tok) +{ + if (ls->tok != tok) + err_token(ls, tok); + lj_lex_next(ls); +} + +/* Check for matching token. */ +static void lex_match(LexState *ls, LexToken what, LexToken who, BCLine line) +{ + if (!lex_opt(ls, what)) { + if (line == ls->linenumber) { + err_token(ls, what); + } else { + const char *swhat = lj_lex_token2str(ls, what); + const char *swho = lj_lex_token2str(ls, who); + lj_lex_error(ls, ls->tok, LJ_ERR_XMATCH, swhat, swho, line); + } + } +} + +/* Check for string token. */ +static GCstr *lex_str(LexState *ls) +{ + GCstr *s; + if (ls->tok != TK_name && (LJ_52 || ls->tok != TK_goto)) + err_token(ls, TK_name); + s = strV(&ls->tokval); + lj_lex_next(ls); + return s; +} + +/* -- Variable handling --------------------------------------------------- */ + +#define var_get(ls, fs, i) ((ls)->vstack[(fs)->varmap[(i)]]) + +/* Define a new local variable. */ +static void var_new(LexState *ls, BCReg n, GCstr *name) +{ + FuncState *fs = ls->fs; + MSize vtop = ls->vtop; + checklimit(fs, fs->nactvar+n, LJ_MAX_LOCVAR, "local variables"); + if (LJ_UNLIKELY(vtop >= ls->sizevstack)) { + if (ls->sizevstack >= LJ_MAX_VSTACK) + lj_lex_error(ls, 0, LJ_ERR_XLIMC, LJ_MAX_VSTACK); + lj_mem_growvec(ls->L, ls->vstack, ls->sizevstack, LJ_MAX_VSTACK, VarInfo); + } + lua_assert((uintptr_t)name < VARNAME__MAX || + lj_tab_getstr(fs->kt, name) != NULL); + /* NOBARRIER: name is anchored in fs->kt and ls->vstack is not a GCobj. */ + setgcref(ls->vstack[vtop].name, obj2gco(name)); + fs->varmap[fs->nactvar+n] = (uint16_t)vtop; + ls->vtop = vtop+1; +} + +#define var_new_lit(ls, n, v) \ + var_new(ls, (n), lj_parse_keepstr(ls, "" v, sizeof(v)-1)) + +#define var_new_fixed(ls, n, vn) \ + var_new(ls, (n), (GCstr *)(uintptr_t)(vn)) + +/* Add local variables. */ +static void var_add(LexState *ls, BCReg nvars) +{ + FuncState *fs = ls->fs; + BCReg nactvar = fs->nactvar; + while (nvars--) { + VarInfo *v = &var_get(ls, fs, nactvar); + v->startpc = fs->pc; + v->slot = nactvar++; + v->info = 0; + } + fs->nactvar = nactvar; +} + +/* Remove local variables. */ +static void var_remove(LexState *ls, BCReg tolevel) +{ + FuncState *fs = ls->fs; + while (fs->nactvar > tolevel) + var_get(ls, fs, --fs->nactvar).endpc = fs->pc; +} + +/* Lookup local variable name. */ +static BCReg var_lookup_local(FuncState *fs, GCstr *n) +{ + int i; + for (i = fs->nactvar-1; i >= 0; i--) { + if (n == strref(var_get(fs->ls, fs, i).name)) + return (BCReg)i; + } + return (BCReg)-1; /* Not found. */ +} + +/* Lookup or add upvalue index. */ +static MSize var_lookup_uv(FuncState *fs, MSize vidx, ExpDesc *e) +{ + MSize i, n = fs->nuv; + for (i = 0; i < n; i++) + if (fs->uvmap[i] == vidx) + return i; /* Already exists. */ + /* Otherwise create a new one. */ + checklimit(fs, fs->nuv, LJ_MAX_UPVAL, "upvalues"); + lua_assert(e->k == VLOCAL || e->k == VUPVAL); + fs->uvmap[n] = (uint16_t)vidx; + fs->uvtmp[n] = (uint16_t)(e->k == VLOCAL ? vidx : LJ_MAX_VSTACK+e->u.s.info); + fs->nuv = n+1; + return n; +} + +/* Forward declaration. */ +static void fscope_uvmark(FuncState *fs, BCReg level); + +/* Recursively lookup variables in enclosing functions. */ +static MSize var_lookup_(FuncState *fs, GCstr *name, ExpDesc *e, int first) +{ + if (fs) { + BCReg reg = var_lookup_local(fs, name); + if ((int32_t)reg >= 0) { /* Local in this function? */ + expr_init(e, VLOCAL, reg); + if (!first) + fscope_uvmark(fs, reg); /* Scope now has an upvalue. */ + return (MSize)(e->u.s.aux = (uint32_t)fs->varmap[reg]); + } else { + MSize vidx = var_lookup_(fs->prev, name, e, 0); /* Var in outer func? */ + if ((int32_t)vidx >= 0) { /* Yes, make it an upvalue here. */ + e->u.s.info = (uint8_t)var_lookup_uv(fs, vidx, e); + e->k = VUPVAL; + return vidx; + } + } + } else { /* Not found in any function, must be a global. */ + expr_init(e, VGLOBAL, 0); + e->u.sval = name; + } + return (MSize)-1; /* Global. */ +} + +/* Lookup variable name. */ +#define var_lookup(ls, e) \ + var_lookup_((ls)->fs, lex_str(ls), (e), 1) + +/* -- Goto an label handling ---------------------------------------------- */ + +/* Add a new goto or label. */ +static MSize gola_new(LexState *ls, GCstr *name, uint8_t info, BCPos pc) +{ + FuncState *fs = ls->fs; + MSize vtop = ls->vtop; + if (LJ_UNLIKELY(vtop >= ls->sizevstack)) { + if (ls->sizevstack >= LJ_MAX_VSTACK) + lj_lex_error(ls, 0, LJ_ERR_XLIMC, LJ_MAX_VSTACK); + lj_mem_growvec(ls->L, ls->vstack, ls->sizevstack, LJ_MAX_VSTACK, VarInfo); + } + lua_assert(name == NAME_BREAK || lj_tab_getstr(fs->kt, name) != NULL); + /* NOBARRIER: name is anchored in fs->kt and ls->vstack is not a GCobj. */ + setgcref(ls->vstack[vtop].name, obj2gco(name)); + ls->vstack[vtop].startpc = pc; + ls->vstack[vtop].slot = (uint8_t)fs->nactvar; + ls->vstack[vtop].info = info; + ls->vtop = vtop+1; + return vtop; +} + +#define gola_isgoto(v) ((v)->info & VSTACK_GOTO) +#define gola_islabel(v) ((v)->info & VSTACK_LABEL) +#define gola_isgotolabel(v) ((v)->info & (VSTACK_GOTO|VSTACK_LABEL)) + +/* Patch goto to jump to label. */ +static void gola_patch(LexState *ls, VarInfo *vg, VarInfo *vl) +{ + FuncState *fs = ls->fs; + BCPos pc = vg->startpc; + setgcrefnull(vg->name); /* Invalidate pending goto. */ + setbc_a(&fs->bcbase[pc].ins, vl->slot); + jmp_patch(fs, pc, vl->startpc); +} + +/* Patch goto to close upvalues. */ +static void gola_close(LexState *ls, VarInfo *vg) +{ + FuncState *fs = ls->fs; + BCPos pc = vg->startpc; + BCIns *ip = &fs->bcbase[pc].ins; + lua_assert(gola_isgoto(vg)); + lua_assert(bc_op(*ip) == BC_JMP || bc_op(*ip) == BC_UCLO); + setbc_a(ip, vg->slot); + if (bc_op(*ip) == BC_JMP) { + BCPos next = jmp_next(fs, pc); + if (next != NO_JMP) jmp_patch(fs, next, pc); /* Jump to UCLO. */ + setbc_op(ip, BC_UCLO); /* Turn into UCLO. */ + setbc_j(ip, NO_JMP); + } +} + +/* Resolve pending forward gotos for label. */ +static void gola_resolve(LexState *ls, FuncScope *bl, MSize idx) +{ + VarInfo *vg = ls->vstack + bl->vstart; + VarInfo *vl = ls->vstack + idx; + for (; vg < vl; vg++) + if (gcrefeq(vg->name, vl->name) && gola_isgoto(vg)) { + if (vg->slot < vl->slot) { + GCstr *name = strref(var_get(ls, ls->fs, vg->slot).name); + lua_assert((uintptr_t)name >= VARNAME__MAX); + ls->linenumber = ls->fs->bcbase[vg->startpc].line; + lua_assert(strref(vg->name) != NAME_BREAK); + lj_lex_error(ls, 0, LJ_ERR_XGSCOPE, + strdata(strref(vg->name)), strdata(name)); + } + gola_patch(ls, vg, vl); + } +} + +/* Fixup remaining gotos and labels for scope. */ +static void gola_fixup(LexState *ls, FuncScope *bl) +{ + VarInfo *v = ls->vstack + bl->vstart; + VarInfo *ve = ls->vstack + ls->vtop; + for (; v < ve; v++) { + GCstr *name = strref(v->name); + if (name != NULL) { /* Only consider remaining valid gotos/labels. */ + if (gola_islabel(v)) { + VarInfo *vg; + setgcrefnull(v->name); /* Invalidate label that goes out of scope. */ + for (vg = v+1; vg < ve; vg++) /* Resolve pending backward gotos. */ + if (strref(vg->name) == name && gola_isgoto(vg)) { + if ((bl->flags&FSCOPE_UPVAL) && vg->slot > v->slot) + gola_close(ls, vg); + gola_patch(ls, vg, v); + } + } else if (gola_isgoto(v)) { + if (bl->prev) { /* Propagate goto or break to outer scope. */ + bl->prev->flags |= name == NAME_BREAK ? FSCOPE_BREAK : FSCOPE_GOLA; + v->slot = bl->nactvar; + if ((bl->flags & FSCOPE_UPVAL)) + gola_close(ls, v); + } else { /* No outer scope: undefined goto label or no loop. */ + ls->linenumber = ls->fs->bcbase[v->startpc].line; + if (name == NAME_BREAK) + lj_lex_error(ls, 0, LJ_ERR_XBREAK); + else + lj_lex_error(ls, 0, LJ_ERR_XLUNDEF, strdata(name)); + } + } + } + } +} + +/* Find existing label. */ +static VarInfo *gola_findlabel(LexState *ls, GCstr *name) +{ + VarInfo *v = ls->vstack + ls->fs->bl->vstart; + VarInfo *ve = ls->vstack + ls->vtop; + for (; v < ve; v++) + if (strref(v->name) == name && gola_islabel(v)) + return v; + return NULL; +} + +/* -- Scope handling ------------------------------------------------------ */ + +/* Begin a scope. */ +static void fscope_begin(FuncState *fs, FuncScope *bl, int flags) +{ + bl->nactvar = (uint8_t)fs->nactvar; + bl->flags = flags; + bl->vstart = fs->ls->vtop; + bl->prev = fs->bl; + fs->bl = bl; + lua_assert(fs->freereg == fs->nactvar); +} + +/* End a scope. */ +static void fscope_end(FuncState *fs) +{ + FuncScope *bl = fs->bl; + LexState *ls = fs->ls; + fs->bl = bl->prev; + var_remove(ls, bl->nactvar); + fs->freereg = fs->nactvar; + lua_assert(bl->nactvar == fs->nactvar); + if ((bl->flags & (FSCOPE_UPVAL|FSCOPE_NOCLOSE)) == FSCOPE_UPVAL) + bcemit_AJ(fs, BC_UCLO, bl->nactvar, 0); + if ((bl->flags & FSCOPE_BREAK)) { + if ((bl->flags & FSCOPE_LOOP)) { + MSize idx = gola_new(ls, NAME_BREAK, VSTACK_LABEL, fs->pc); + ls->vtop = idx; /* Drop break label immediately. */ + gola_resolve(ls, bl, idx); + } else { /* Need the fixup step to propagate the breaks. */ + gola_fixup(ls, bl); + return; + } + } + if ((bl->flags & FSCOPE_GOLA)) { + gola_fixup(ls, bl); + } +} + +/* Mark scope as having an upvalue. */ +static void fscope_uvmark(FuncState *fs, BCReg level) +{ + FuncScope *bl; + for (bl = fs->bl; bl && bl->nactvar > level; bl = bl->prev) + ; + if (bl) + bl->flags |= FSCOPE_UPVAL; +} + +/* -- Function state management ------------------------------------------- */ + +/* Fixup bytecode for prototype. */ +static void fs_fixup_bc(FuncState *fs, GCproto *pt, BCIns *bc, MSize n) +{ + BCInsLine *base = fs->bcbase; + MSize i; + pt->sizebc = n; + bc[0] = BCINS_AD((fs->flags & PROTO_VARARG) ? BC_FUNCV : BC_FUNCF, + fs->framesize, 0); + for (i = 1; i < n; i++) + bc[i] = base[i].ins; +} + +/* Fixup upvalues for child prototype, step #2. */ +static void fs_fixup_uv2(FuncState *fs, GCproto *pt) +{ + VarInfo *vstack = fs->ls->vstack; + uint16_t *uv = proto_uv(pt); + MSize i, n = pt->sizeuv; + for (i = 0; i < n; i++) { + VarIndex vidx = uv[i]; + if (vidx >= LJ_MAX_VSTACK) + uv[i] = vidx - LJ_MAX_VSTACK; + else if ((vstack[vidx].info & VSTACK_VAR_RW)) + uv[i] = vstack[vidx].slot | PROTO_UV_LOCAL; + else + uv[i] = vstack[vidx].slot | PROTO_UV_LOCAL | PROTO_UV_IMMUTABLE; + } +} + +/* Fixup constants for prototype. */ +static void fs_fixup_k(FuncState *fs, GCproto *pt, void *kptr) +{ + GCtab *kt; + TValue *array; + Node *node; + MSize i, hmask; + checklimitgt(fs, fs->nkn, BCMAX_D+1, "constants"); + checklimitgt(fs, fs->nkgc, BCMAX_D+1, "constants"); + setmref(pt->k, kptr); + pt->sizekn = fs->nkn; + pt->sizekgc = fs->nkgc; + kt = fs->kt; + array = tvref(kt->array); + for (i = 0; i < kt->asize; i++) + if (tvhaskslot(&array[i])) { + TValue *tv = &((TValue *)kptr)[tvkslot(&array[i])]; + if (LJ_DUALNUM) + setintV(tv, (int32_t)i); + else + setnumV(tv, (lua_Number)i); + } + node = noderef(kt->node); + hmask = kt->hmask; + for (i = 0; i <= hmask; i++) { + Node *n = &node[i]; + if (tvhaskslot(&n->val)) { + ptrdiff_t kidx = (ptrdiff_t)tvkslot(&n->val); + lua_assert(!tvisint(&n->key)); + if (tvisnum(&n->key)) { + TValue *tv = &((TValue *)kptr)[kidx]; + if (LJ_DUALNUM) { + lua_Number nn = numV(&n->key); + int32_t k = lj_num2int(nn); + lua_assert(!tvismzero(&n->key)); + if ((lua_Number)k == nn) + setintV(tv, k); + else + *tv = n->key; + } else { + *tv = n->key; + } + } else { + GCobj *o = gcV(&n->key); + setgcref(((GCRef *)kptr)[~kidx], o); + lj_gc_objbarrier(fs->L, pt, o); + if (tvisproto(&n->key)) + fs_fixup_uv2(fs, gco2pt(o)); + } + } + } +} + +/* Fixup upvalues for prototype, step #1. */ +static void fs_fixup_uv1(FuncState *fs, GCproto *pt, uint16_t *uv) +{ + setmref(pt->uv, uv); + pt->sizeuv = fs->nuv; + memcpy(uv, fs->uvtmp, fs->nuv*sizeof(VarIndex)); +} + +#ifndef LUAJIT_DISABLE_DEBUGINFO +/* Prepare lineinfo for prototype. */ +static size_t fs_prep_line(FuncState *fs, BCLine numline) +{ + return (fs->pc-1) << (numline < 256 ? 0 : numline < 65536 ? 1 : 2); +} + +/* Fixup lineinfo for prototype. */ +static void fs_fixup_line(FuncState *fs, GCproto *pt, + void *lineinfo, BCLine numline) +{ + BCInsLine *base = fs->bcbase + 1; + BCLine first = fs->linedefined; + MSize i = 0, n = fs->pc-1; + pt->firstline = fs->linedefined; + pt->numline = numline; + setmref(pt->lineinfo, lineinfo); + if (LJ_LIKELY(numline < 256)) { + uint8_t *li = (uint8_t *)lineinfo; + do { + BCLine delta = base[i].line - first; + lua_assert(delta >= 0 && delta < 256); + li[i] = (uint8_t)delta; + } while (++i < n); + } else if (LJ_LIKELY(numline < 65536)) { + uint16_t *li = (uint16_t *)lineinfo; + do { + BCLine delta = base[i].line - first; + lua_assert(delta >= 0 && delta < 65536); + li[i] = (uint16_t)delta; + } while (++i < n); + } else { + uint32_t *li = (uint32_t *)lineinfo; + do { + BCLine delta = base[i].line - first; + lua_assert(delta >= 0); + li[i] = (uint32_t)delta; + } while (++i < n); + } +} + +/* Prepare variable info for prototype. */ +static size_t fs_prep_var(LexState *ls, FuncState *fs, size_t *ofsvar) +{ + VarInfo *vs =ls->vstack, *ve; + MSize i, n; + BCPos lastpc; + lj_buf_reset(&ls->sb); /* Copy to temp. string buffer. */ + /* Store upvalue names. */ + for (i = 0, n = fs->nuv; i < n; i++) { + GCstr *s = strref(vs[fs->uvmap[i]].name); + MSize len = s->len+1; + char *p = lj_buf_more(&ls->sb, len); + p = lj_buf_wmem(p, strdata(s), len); + setsbufP(&ls->sb, p); + } + *ofsvar = sbuflen(&ls->sb); + lastpc = 0; + /* Store local variable names and compressed ranges. */ + for (ve = vs + ls->vtop, vs += fs->vbase; vs < ve; vs++) { + if (!gola_isgotolabel(vs)) { + GCstr *s = strref(vs->name); + BCPos startpc; + char *p; + if ((uintptr_t)s < VARNAME__MAX) { + p = lj_buf_more(&ls->sb, 1 + 2*5); + *p++ = (char)(uintptr_t)s; + } else { + MSize len = s->len+1; + p = lj_buf_more(&ls->sb, len + 2*5); + p = lj_buf_wmem(p, strdata(s), len); + } + startpc = vs->startpc; + p = lj_strfmt_wuleb128(p, startpc-lastpc); + p = lj_strfmt_wuleb128(p, vs->endpc-startpc); + setsbufP(&ls->sb, p); + lastpc = startpc; + } + } + lj_buf_putb(&ls->sb, '\0'); /* Terminator for varinfo. */ + return sbuflen(&ls->sb); +} + +/* Fixup variable info for prototype. */ +static void fs_fixup_var(LexState *ls, GCproto *pt, uint8_t *p, size_t ofsvar) +{ + setmref(pt->uvinfo, p); + setmref(pt->varinfo, (char *)p + ofsvar); + memcpy(p, sbufB(&ls->sb), sbuflen(&ls->sb)); /* Copy from temp. buffer. */ +} +#else + +/* Initialize with empty debug info, if disabled. */ +#define fs_prep_line(fs, numline) (UNUSED(numline), 0) +#define fs_fixup_line(fs, pt, li, numline) \ + pt->firstline = pt->numline = 0, setmref((pt)->lineinfo, NULL) +#define fs_prep_var(ls, fs, ofsvar) (UNUSED(ofsvar), 0) +#define fs_fixup_var(ls, pt, p, ofsvar) \ + setmref((pt)->uvinfo, NULL), setmref((pt)->varinfo, NULL) + +#endif + +/* Check if bytecode op returns. */ +static int bcopisret(BCOp op) +{ + switch (op) { + case BC_CALLMT: case BC_CALLT: + case BC_RETM: case BC_RET: case BC_RET0: case BC_RET1: + return 1; + default: + return 0; + } +} + +/* Fixup return instruction for prototype. */ +static void fs_fixup_ret(FuncState *fs) +{ + BCPos lastpc = fs->pc; + if (lastpc <= fs->lasttarget || !bcopisret(bc_op(fs->bcbase[lastpc-1].ins))) { + if ((fs->bl->flags & FSCOPE_UPVAL)) + bcemit_AJ(fs, BC_UCLO, 0, 0); + bcemit_AD(fs, BC_RET0, 0, 1); /* Need final return. */ + } + fs->bl->flags |= FSCOPE_NOCLOSE; /* Handled above. */ + fscope_end(fs); + lua_assert(fs->bl == NULL); + /* May need to fixup returns encoded before first function was created. */ + if (fs->flags & PROTO_FIXUP_RETURN) { + BCPos pc; + for (pc = 1; pc < lastpc; pc++) { + BCIns ins = fs->bcbase[pc].ins; + BCPos offset; + switch (bc_op(ins)) { + case BC_CALLMT: case BC_CALLT: + case BC_RETM: case BC_RET: case BC_RET0: case BC_RET1: + offset = bcemit_INS(fs, ins); /* Copy original instruction. */ + fs->bcbase[offset].line = fs->bcbase[pc].line; + offset = offset-(pc+1)+BCBIAS_J; + if (offset > BCMAX_D) + err_syntax(fs->ls, LJ_ERR_XFIXUP); + /* Replace with UCLO plus branch. */ + fs->bcbase[pc].ins = BCINS_AD(BC_UCLO, 0, offset); + break; + case BC_UCLO: + return; /* We're done. */ + default: + break; + } + } + } +} + +/* Finish a FuncState and return the new prototype. */ +static GCproto *fs_finish(LexState *ls, BCLine line) +{ + lua_State *L = ls->L; + FuncState *fs = ls->fs; + BCLine numline = line - fs->linedefined; + size_t sizept, ofsk, ofsuv, ofsli, ofsdbg, ofsvar; + GCproto *pt; + + /* Apply final fixups. */ + fs_fixup_ret(fs); + + /* Calculate total size of prototype including all colocated arrays. */ + sizept = sizeof(GCproto) + fs->pc*sizeof(BCIns) + fs->nkgc*sizeof(GCRef); + sizept = (sizept + sizeof(TValue)-1) & ~(sizeof(TValue)-1); + ofsk = sizept; sizept += fs->nkn*sizeof(TValue); + ofsuv = sizept; sizept += ((fs->nuv+1)&~1)*2; + ofsli = sizept; sizept += fs_prep_line(fs, numline); + ofsdbg = sizept; sizept += fs_prep_var(ls, fs, &ofsvar); + + /* Allocate prototype and initialize its fields. */ + pt = (GCproto *)lj_mem_newgco(L, (MSize)sizept); + pt->gct = ~LJ_TPROTO; + pt->sizept = (MSize)sizept; + pt->trace = 0; + pt->flags = (uint8_t)(fs->flags & ~(PROTO_HAS_RETURN|PROTO_FIXUP_RETURN)); + pt->numparams = fs->numparams; + pt->framesize = fs->framesize; + setgcref(pt->chunkname, obj2gco(ls->chunkname)); + + /* Close potentially uninitialized gap between bc and kgc. */ + *(uint32_t *)((char *)pt + ofsk - sizeof(GCRef)*(fs->nkgc+1)) = 0; + fs_fixup_bc(fs, pt, (BCIns *)((char *)pt + sizeof(GCproto)), fs->pc); + fs_fixup_k(fs, pt, (void *)((char *)pt + ofsk)); + fs_fixup_uv1(fs, pt, (uint16_t *)((char *)pt + ofsuv)); + fs_fixup_line(fs, pt, (void *)((char *)pt + ofsli), numline); + fs_fixup_var(ls, pt, (uint8_t *)((char *)pt + ofsdbg), ofsvar); + + lj_vmevent_send(L, BC, + setprotoV(L, L->top++, pt); + ); + + L->top--; /* Pop table of constants. */ + ls->vtop = fs->vbase; /* Reset variable stack. */ + ls->fs = fs->prev; + lua_assert(ls->fs != NULL || ls->tok == TK_eof); + return pt; +} + +/* Initialize a new FuncState. */ +static void fs_init(LexState *ls, FuncState *fs) +{ + lua_State *L = ls->L; + fs->prev = ls->fs; ls->fs = fs; /* Append to list. */ + fs->ls = ls; + fs->vbase = ls->vtop; + fs->L = L; + fs->pc = 0; + fs->lasttarget = 0; + fs->jpc = NO_JMP; + fs->freereg = 0; + fs->nkgc = 0; + fs->nkn = 0; + fs->nactvar = 0; + fs->nuv = 0; + fs->bl = NULL; + fs->flags = 0; + fs->framesize = 1; /* Minimum frame size. */ + fs->kt = lj_tab_new(L, 0, 0); + /* Anchor table of constants in stack to avoid being collected. */ + settabV(L, L->top, fs->kt); + incr_top(L); +} + +/* -- Expressions --------------------------------------------------------- */ + +/* Forward declaration. */ +static void expr(LexState *ls, ExpDesc *v); + +/* Return string expression. */ +static void expr_str(LexState *ls, ExpDesc *e) +{ + expr_init(e, VKSTR, 0); + e->u.sval = lex_str(ls); +} + +/* Return index expression. */ +static void expr_index(FuncState *fs, ExpDesc *t, ExpDesc *e) +{ + /* Already called: expr_toval(fs, e). */ + t->k = VINDEXED; + if (expr_isnumk(e)) { +#if LJ_DUALNUM + if (tvisint(expr_numtv(e))) { + int32_t k = intV(expr_numtv(e)); + if (checku8(k)) { + t->u.s.aux = BCMAX_C+1+(uint32_t)k; /* 256..511: const byte key */ + return; + } + } +#else + lua_Number n = expr_numberV(e); + int32_t k = lj_num2int(n); + if (checku8(k) && n == (lua_Number)k) { + t->u.s.aux = BCMAX_C+1+(uint32_t)k; /* 256..511: const byte key */ + return; + } +#endif + } else if (expr_isstrk(e)) { + BCReg idx = const_str(fs, e); + if (idx <= BCMAX_C) { + t->u.s.aux = ~idx; /* -256..-1: const string key */ + return; + } + } + t->u.s.aux = expr_toanyreg(fs, e); /* 0..255: register */ +} + +/* Parse index expression with named field. */ +static void expr_field(LexState *ls, ExpDesc *v) +{ + FuncState *fs = ls->fs; + ExpDesc key; + expr_toanyreg(fs, v); + lj_lex_next(ls); /* Skip dot or colon. */ + expr_str(ls, &key); + expr_index(fs, v, &key); +} + +/* Parse index expression with brackets. */ +static void expr_bracket(LexState *ls, ExpDesc *v) +{ + lj_lex_next(ls); /* Skip '['. */ + expr(ls, v); + expr_toval(ls->fs, v); + lex_check(ls, ']'); +} + +/* Get value of constant expression. */ +static void expr_kvalue(TValue *v, ExpDesc *e) +{ + if (e->k <= VKTRUE) { + setpriV(v, ~(uint32_t)e->k); + } else if (e->k == VKSTR) { + setgcVraw(v, obj2gco(e->u.sval), LJ_TSTR); + } else { + lua_assert(tvisnumber(expr_numtv(e))); + *v = *expr_numtv(e); + } +} + +/* Parse table constructor expression. */ +static void expr_table(LexState *ls, ExpDesc *e) +{ + FuncState *fs = ls->fs; + BCLine line = ls->linenumber; + GCtab *t = NULL; + int vcall = 0, needarr = 0, fixt = 0; + uint32_t narr = 1; /* First array index. */ + uint32_t nhash = 0; /* Number of hash entries. */ + BCReg freg = fs->freereg; + BCPos pc = bcemit_AD(fs, BC_TNEW, freg, 0); + expr_init(e, VNONRELOC, freg); + bcreg_reserve(fs, 1); + freg++; + lex_check(ls, '{'); + while (ls->tok != '}') { + ExpDesc key, val; + vcall = 0; + if (ls->tok == '[') { + expr_bracket(ls, &key); /* Already calls expr_toval. */ + if (!expr_isk(&key)) expr_index(fs, e, &key); + if (expr_isnumk(&key) && expr_numiszero(&key)) needarr = 1; else nhash++; + lex_check(ls, '='); + } else if ((ls->tok == TK_name || (!LJ_52 && ls->tok == TK_goto)) && + lj_lex_lookahead(ls) == '=') { + expr_str(ls, &key); + lex_check(ls, '='); + nhash++; + } else { + expr_init(&key, VKNUM, 0); + setintV(&key.u.nval, (int)narr); + narr++; + needarr = vcall = 1; + } + expr(ls, &val); + if (expr_isk(&key) && key.k != VKNIL && + (key.k == VKSTR || expr_isk_nojump(&val))) { + TValue k, *v; + if (!t) { /* Create template table on demand. */ + BCReg kidx; + t = lj_tab_new(fs->L, needarr ? narr : 0, hsize2hbits(nhash)); + kidx = const_gc(fs, obj2gco(t), LJ_TTAB); + fs->bcbase[pc].ins = BCINS_AD(BC_TDUP, freg-1, kidx); + } + vcall = 0; + expr_kvalue(&k, &key); + v = lj_tab_set(fs->L, t, &k); + lj_gc_anybarriert(fs->L, t); + if (expr_isk_nojump(&val)) { /* Add const key/value to template table. */ + expr_kvalue(v, &val); + } else { /* Otherwise create dummy string key (avoids lj_tab_newkey). */ + settabV(fs->L, v, t); /* Preserve key with table itself as value. */ + fixt = 1; /* Fix this later, after all resizes. */ + goto nonconst; + } + } else { + nonconst: + if (val.k != VCALL) { expr_toanyreg(fs, &val); vcall = 0; } + if (expr_isk(&key)) expr_index(fs, e, &key); + bcemit_store(fs, e, &val); + } + fs->freereg = freg; + if (!lex_opt(ls, ',') && !lex_opt(ls, ';')) break; + } + lex_match(ls, '}', '{', line); + if (vcall) { + BCInsLine *ilp = &fs->bcbase[fs->pc-1]; + ExpDesc en; + lua_assert(bc_a(ilp->ins) == freg && + bc_op(ilp->ins) == (narr > 256 ? BC_TSETV : BC_TSETB)); + expr_init(&en, VKNUM, 0); + en.u.nval.u32.lo = narr-1; + en.u.nval.u32.hi = 0x43300000; /* Biased integer to avoid denormals. */ + if (narr > 256) { fs->pc--; ilp--; } + ilp->ins = BCINS_AD(BC_TSETM, freg, const_num(fs, &en)); + setbc_b(&ilp[-1].ins, 0); + } + if (pc == fs->pc-1) { /* Make expr relocable if possible. */ + e->u.s.info = pc; + fs->freereg--; + e->k = VRELOCABLE; + } else { + e->k = VNONRELOC; /* May have been changed by expr_index. */ + } + if (!t) { /* Construct TNEW RD: hhhhhaaaaaaaaaaa. */ + BCIns *ip = &fs->bcbase[pc].ins; + if (!needarr) narr = 0; + else if (narr < 3) narr = 3; + else if (narr > 0x7ff) narr = 0x7ff; + setbc_d(ip, narr|(hsize2hbits(nhash)<<11)); + } else { + if (needarr && t->asize < narr) + lj_tab_reasize(fs->L, t, narr-1); + if (fixt) { /* Fix value for dummy keys in template table. */ + Node *node = noderef(t->node); + uint32_t i, hmask = t->hmask; + for (i = 0; i <= hmask; i++) { + Node *n = &node[i]; + if (tvistab(&n->val)) { + lua_assert(tabV(&n->val) == t); + setnilV(&n->val); /* Turn value into nil. */ + } + } + } + lj_gc_check(fs->L); + } +} + +/* Parse function parameters. */ +static BCReg parse_params(LexState *ls, int needself) +{ + FuncState *fs = ls->fs; + BCReg nparams = 0; + lex_check(ls, '('); + if (needself) + var_new_lit(ls, nparams++, "self"); + if (ls->tok != ')') { + do { + if (ls->tok == TK_name || (!LJ_52 && ls->tok == TK_goto)) { + var_new(ls, nparams++, lex_str(ls)); + } else if (ls->tok == TK_dots) { + lj_lex_next(ls); + fs->flags |= PROTO_VARARG; + break; + } else { + err_syntax(ls, LJ_ERR_XPARAM); + } + } while (lex_opt(ls, ',')); + } + var_add(ls, nparams); + lua_assert(fs->nactvar == nparams); + bcreg_reserve(fs, nparams); + lex_check(ls, ')'); + return nparams; +} + +/* Forward declaration. */ +static void parse_chunk(LexState *ls); + +/* Parse body of a function. */ +static void parse_body(LexState *ls, ExpDesc *e, int needself, BCLine line) +{ + FuncState fs, *pfs = ls->fs; + FuncScope bl; + GCproto *pt; + ptrdiff_t oldbase = pfs->bcbase - ls->bcstack; + fs_init(ls, &fs); + fscope_begin(&fs, &bl, 0); + fs.linedefined = line; + fs.numparams = (uint8_t)parse_params(ls, needself); + fs.bcbase = pfs->bcbase + pfs->pc; + fs.bclim = pfs->bclim - pfs->pc; + bcemit_AD(&fs, BC_FUNCF, 0, 0); /* Placeholder. */ + parse_chunk(ls); + if (ls->tok != TK_end) lex_match(ls, TK_end, TK_function, line); + pt = fs_finish(ls, (ls->lastline = ls->linenumber)); + pfs->bcbase = ls->bcstack + oldbase; /* May have been reallocated. */ + pfs->bclim = (BCPos)(ls->sizebcstack - oldbase); + /* Store new prototype in the constant array of the parent. */ + expr_init(e, VRELOCABLE, + bcemit_AD(pfs, BC_FNEW, 0, const_gc(pfs, obj2gco(pt), LJ_TPROTO))); +#if LJ_HASFFI + pfs->flags |= (fs.flags & PROTO_FFI); +#endif + if (!(pfs->flags & PROTO_CHILD)) { + if (pfs->flags & PROTO_HAS_RETURN) + pfs->flags |= PROTO_FIXUP_RETURN; + pfs->flags |= PROTO_CHILD; + } + lj_lex_next(ls); +} + +/* Parse expression list. Last expression is left open. */ +static BCReg expr_list(LexState *ls, ExpDesc *v) +{ + BCReg n = 1; + expr(ls, v); + while (lex_opt(ls, ',')) { + expr_tonextreg(ls->fs, v); + expr(ls, v); + n++; + } + return n; +} + +/* Parse function argument list. */ +static void parse_args(LexState *ls, ExpDesc *e) +{ + FuncState *fs = ls->fs; + ExpDesc args; + BCIns ins; + BCReg base; + BCLine line = ls->linenumber; + if (ls->tok == '(') { +#if !LJ_52 + if (line != ls->lastline) + err_syntax(ls, LJ_ERR_XAMBIG); +#endif + lj_lex_next(ls); + if (ls->tok == ')') { /* f(). */ + args.k = VVOID; + } else { + expr_list(ls, &args); + if (args.k == VCALL) /* f(a, b, g()) or f(a, b, ...). */ + setbc_b(bcptr(fs, &args), 0); /* Pass on multiple results. */ + } + lex_match(ls, ')', '(', line); + } else if (ls->tok == '{') { + expr_table(ls, &args); + } else if (ls->tok == TK_string) { + expr_init(&args, VKSTR, 0); + args.u.sval = strV(&ls->tokval); + lj_lex_next(ls); + } else { + err_syntax(ls, LJ_ERR_XFUNARG); + return; /* Silence compiler. */ + } + lua_assert(e->k == VNONRELOC); + base = e->u.s.info; /* Base register for call. */ + if (args.k == VCALL) { + ins = BCINS_ABC(BC_CALLM, base, 2, args.u.s.aux - base - 1 - LJ_FR2); + } else { + if (args.k != VVOID) + expr_tonextreg(fs, &args); + ins = BCINS_ABC(BC_CALL, base, 2, fs->freereg - base - LJ_FR2); + } + expr_init(e, VCALL, bcemit_INS(fs, ins)); + e->u.s.aux = base; + fs->bcbase[fs->pc - 1].line = line; + fs->freereg = base+1; /* Leave one result by default. */ +} + +/* Parse primary expression. */ +static void expr_primary(LexState *ls, ExpDesc *v) +{ + FuncState *fs = ls->fs; + /* Parse prefix expression. */ + if (ls->tok == '(') { + BCLine line = ls->linenumber; + lj_lex_next(ls); + expr(ls, v); + lex_match(ls, ')', '(', line); + expr_discharge(ls->fs, v); + } else if (ls->tok == TK_name || (!LJ_52 && ls->tok == TK_goto)) { + var_lookup(ls, v); + } else { + err_syntax(ls, LJ_ERR_XSYMBOL); + } + for (;;) { /* Parse multiple expression suffixes. */ + if (ls->tok == '.') { + expr_field(ls, v); + } else if (ls->tok == '[') { + ExpDesc key; + expr_toanyreg(fs, v); + expr_bracket(ls, &key); + expr_index(fs, v, &key); + } else if (ls->tok == ':') { + ExpDesc key; + lj_lex_next(ls); + expr_str(ls, &key); + bcemit_method(fs, v, &key); + parse_args(ls, v); + } else if (ls->tok == '(' || ls->tok == TK_string || ls->tok == '{') { + expr_tonextreg(fs, v); + if (LJ_FR2) bcreg_reserve(fs, 1); + parse_args(ls, v); + } else { + break; + } + } +} + +/* Parse simple expression. */ +static void expr_simple(LexState *ls, ExpDesc *v) +{ + switch (ls->tok) { + case TK_number: + expr_init(v, (LJ_HASFFI && tviscdata(&ls->tokval)) ? VKCDATA : VKNUM, 0); + copyTV(ls->L, &v->u.nval, &ls->tokval); + break; + case TK_string: + expr_init(v, VKSTR, 0); + v->u.sval = strV(&ls->tokval); + break; + case TK_nil: + expr_init(v, VKNIL, 0); + break; + case TK_true: + expr_init(v, VKTRUE, 0); + break; + case TK_false: + expr_init(v, VKFALSE, 0); + break; + case TK_dots: { /* Vararg. */ + FuncState *fs = ls->fs; + BCReg base; + checkcond(ls, fs->flags & PROTO_VARARG, LJ_ERR_XDOTS); + bcreg_reserve(fs, 1); + base = fs->freereg-1; + expr_init(v, VCALL, bcemit_ABC(fs, BC_VARG, base, 2, fs->numparams)); + v->u.s.aux = base; + break; + } + case '{': /* Table constructor. */ + expr_table(ls, v); + return; + case TK_function: + lj_lex_next(ls); + parse_body(ls, v, 0, ls->linenumber); + return; + default: + expr_primary(ls, v); + return; + } + lj_lex_next(ls); +} + +/* Manage syntactic levels to avoid blowing up the stack. */ +static void synlevel_begin(LexState *ls) +{ + if (++ls->level >= LJ_MAX_XLEVEL) + lj_lex_error(ls, 0, LJ_ERR_XLEVELS); +} + +#define synlevel_end(ls) ((ls)->level--) + +/* Convert token to binary operator. */ +static BinOpr token2binop(LexToken tok) +{ + switch (tok) { + case '+': return OPR_ADD; + case '-': return OPR_SUB; + case '*': return OPR_MUL; + case '/': return OPR_DIV; + case '%': return OPR_MOD; + case '^': return OPR_POW; + case TK_concat: return OPR_CONCAT; + case TK_ne: return OPR_NE; + case TK_eq: return OPR_EQ; + case '<': return OPR_LT; + case TK_le: return OPR_LE; + case '>': return OPR_GT; + case TK_ge: return OPR_GE; + case TK_and: return OPR_AND; + case TK_or: return OPR_OR; + default: return OPR_NOBINOPR; + } +} + +/* Priorities for each binary operator. ORDER OPR. */ +static const struct { + uint8_t left; /* Left priority. */ + uint8_t right; /* Right priority. */ +} priority[] = { + {6,6}, {6,6}, {7,7}, {7,7}, {7,7}, /* ADD SUB MUL DIV MOD */ + {10,9}, {5,4}, /* POW CONCAT (right associative) */ + {3,3}, {3,3}, /* EQ NE */ + {3,3}, {3,3}, {3,3}, {3,3}, /* LT GE GT LE */ + {2,2}, {1,1} /* AND OR */ +}; + +#define UNARY_PRIORITY 8 /* Priority for unary operators. */ + +/* Forward declaration. */ +static BinOpr expr_binop(LexState *ls, ExpDesc *v, uint32_t limit); + +/* Parse unary expression. */ +static void expr_unop(LexState *ls, ExpDesc *v) +{ + BCOp op; + if (ls->tok == TK_not) { + op = BC_NOT; + } else if (ls->tok == '-') { + op = BC_UNM; + } else if (ls->tok == '#') { + op = BC_LEN; + } else { + expr_simple(ls, v); + return; + } + lj_lex_next(ls); + expr_binop(ls, v, UNARY_PRIORITY); + bcemit_unop(ls->fs, op, v); +} + +/* Parse binary expressions with priority higher than the limit. */ +static BinOpr expr_binop(LexState *ls, ExpDesc *v, uint32_t limit) +{ + BinOpr op; + synlevel_begin(ls); + expr_unop(ls, v); + op = token2binop(ls->tok); + while (op != OPR_NOBINOPR && priority[op].left > limit) { + ExpDesc v2; + BinOpr nextop; + lj_lex_next(ls); + bcemit_binop_left(ls->fs, op, v); + /* Parse binary expression with higher priority. */ + nextop = expr_binop(ls, &v2, priority[op].right); + bcemit_binop(ls->fs, op, v, &v2); + op = nextop; + } + synlevel_end(ls); + return op; /* Return unconsumed binary operator (if any). */ +} + +/* Parse expression. */ +static void expr(LexState *ls, ExpDesc *v) +{ + expr_binop(ls, v, 0); /* Priority 0: parse whole expression. */ +} + +/* Assign expression to the next register. */ +static void expr_next(LexState *ls) +{ + ExpDesc e; + expr(ls, &e); + expr_tonextreg(ls->fs, &e); +} + +/* Parse conditional expression. */ +static BCPos expr_cond(LexState *ls) +{ + ExpDesc v; + expr(ls, &v); + if (v.k == VKNIL) v.k = VKFALSE; + bcemit_branch_t(ls->fs, &v); + return v.f; +} + +/* -- Assignments --------------------------------------------------------- */ + +/* List of LHS variables. */ +typedef struct LHSVarList { + ExpDesc v; /* LHS variable. */ + struct LHSVarList *prev; /* Link to previous LHS variable. */ +} LHSVarList; + +/* Eliminate write-after-read hazards for local variable assignment. */ +static void assign_hazard(LexState *ls, LHSVarList *lh, const ExpDesc *v) +{ + FuncState *fs = ls->fs; + BCReg reg = v->u.s.info; /* Check against this variable. */ + BCReg tmp = fs->freereg; /* Rename to this temp. register (if needed). */ + int hazard = 0; + for (; lh; lh = lh->prev) { + if (lh->v.k == VINDEXED) { + if (lh->v.u.s.info == reg) { /* t[i], t = 1, 2 */ + hazard = 1; + lh->v.u.s.info = tmp; + } + if (lh->v.u.s.aux == reg) { /* t[i], i = 1, 2 */ + hazard = 1; + lh->v.u.s.aux = tmp; + } + } + } + if (hazard) { + bcemit_AD(fs, BC_MOV, tmp, reg); /* Rename conflicting variable. */ + bcreg_reserve(fs, 1); + } +} + +/* Adjust LHS/RHS of an assignment. */ +static void assign_adjust(LexState *ls, BCReg nvars, BCReg nexps, ExpDesc *e) +{ + FuncState *fs = ls->fs; + int32_t extra = (int32_t)nvars - (int32_t)nexps; + if (e->k == VCALL) { + extra++; /* Compensate for the VCALL itself. */ + if (extra < 0) extra = 0; + setbc_b(bcptr(fs, e), extra+1); /* Fixup call results. */ + if (extra > 1) bcreg_reserve(fs, (BCReg)extra-1); + } else { + if (e->k != VVOID) + expr_tonextreg(fs, e); /* Close last expression. */ + if (extra > 0) { /* Leftover LHS are set to nil. */ + BCReg reg = fs->freereg; + bcreg_reserve(fs, (BCReg)extra); + bcemit_nil(fs, reg, (BCReg)extra); + } + } + if (nexps > nvars) + ls->fs->freereg -= nexps - nvars; /* Drop leftover regs. */ +} + +/* Recursively parse assignment statement. */ +static void parse_assignment(LexState *ls, LHSVarList *lh, BCReg nvars) +{ + ExpDesc e; + checkcond(ls, VLOCAL <= lh->v.k && lh->v.k <= VINDEXED, LJ_ERR_XSYNTAX); + if (lex_opt(ls, ',')) { /* Collect LHS list and recurse upwards. */ + LHSVarList vl; + vl.prev = lh; + expr_primary(ls, &vl.v); + if (vl.v.k == VLOCAL) + assign_hazard(ls, lh, &vl.v); + checklimit(ls->fs, ls->level + nvars, LJ_MAX_XLEVEL, "variable names"); + parse_assignment(ls, &vl, nvars+1); + } else { /* Parse RHS. */ + BCReg nexps; + lex_check(ls, '='); + nexps = expr_list(ls, &e); + if (nexps == nvars) { + if (e.k == VCALL) { + if (bc_op(*bcptr(ls->fs, &e)) == BC_VARG) { /* Vararg assignment. */ + ls->fs->freereg--; + e.k = VRELOCABLE; + } else { /* Multiple call results. */ + e.u.s.info = e.u.s.aux; /* Base of call is not relocatable. */ + e.k = VNONRELOC; + } + } + bcemit_store(ls->fs, &lh->v, &e); + return; + } + assign_adjust(ls, nvars, nexps, &e); + } + /* Assign RHS to LHS and recurse downwards. */ + expr_init(&e, VNONRELOC, ls->fs->freereg-1); + bcemit_store(ls->fs, &lh->v, &e); +} + +/* Parse call statement or assignment. */ +static void parse_call_assign(LexState *ls) +{ + FuncState *fs = ls->fs; + LHSVarList vl; + expr_primary(ls, &vl.v); + if (vl.v.k == VCALL) { /* Function call statement. */ + setbc_b(bcptr(fs, &vl.v), 1); /* No results. */ + } else { /* Start of an assignment. */ + vl.prev = NULL; + parse_assignment(ls, &vl, 1); + } +} + +/* Parse 'local' statement. */ +static void parse_local(LexState *ls) +{ + if (lex_opt(ls, TK_function)) { /* Local function declaration. */ + ExpDesc v, b; + FuncState *fs = ls->fs; + var_new(ls, 0, lex_str(ls)); + expr_init(&v, VLOCAL, fs->freereg); + v.u.s.aux = fs->varmap[fs->freereg]; + bcreg_reserve(fs, 1); + var_add(ls, 1); + parse_body(ls, &b, 0, ls->linenumber); + /* bcemit_store(fs, &v, &b) without setting VSTACK_VAR_RW. */ + expr_free(fs, &b); + expr_toreg(fs, &b, v.u.s.info); + /* The upvalue is in scope, but the local is only valid after the store. */ + var_get(ls, fs, fs->nactvar - 1).startpc = fs->pc; + } else { /* Local variable declaration. */ + ExpDesc e; + BCReg nexps, nvars = 0; + do { /* Collect LHS. */ + var_new(ls, nvars++, lex_str(ls)); + } while (lex_opt(ls, ',')); + if (lex_opt(ls, '=')) { /* Optional RHS. */ + nexps = expr_list(ls, &e); + } else { /* Or implicitly set to nil. */ + e.k = VVOID; + nexps = 0; + } + assign_adjust(ls, nvars, nexps, &e); + var_add(ls, nvars); + } +} + +/* Parse 'function' statement. */ +static void parse_func(LexState *ls, BCLine line) +{ + FuncState *fs; + ExpDesc v, b; + int needself = 0; + lj_lex_next(ls); /* Skip 'function'. */ + /* Parse function name. */ + var_lookup(ls, &v); + while (ls->tok == '.') /* Multiple dot-separated fields. */ + expr_field(ls, &v); + if (ls->tok == ':') { /* Optional colon to signify method call. */ + needself = 1; + expr_field(ls, &v); + } + parse_body(ls, &b, needself, line); + fs = ls->fs; + bcemit_store(fs, &v, &b); + fs->bcbase[fs->pc - 1].line = line; /* Set line for the store. */ +} + +/* -- Control transfer statements ----------------------------------------- */ + +/* Check for end of block. */ +static int parse_isend(LexToken tok) +{ + switch (tok) { + case TK_else: case TK_elseif: case TK_end: case TK_until: case TK_eof: + return 1; + default: + return 0; + } +} + +/* Parse 'return' statement. */ +static void parse_return(LexState *ls) +{ + BCIns ins; + FuncState *fs = ls->fs; + lj_lex_next(ls); /* Skip 'return'. */ + fs->flags |= PROTO_HAS_RETURN; + if (parse_isend(ls->tok) || ls->tok == ';') { /* Bare return. */ + ins = BCINS_AD(BC_RET0, 0, 1); + } else { /* Return with one or more values. */ + ExpDesc e; /* Receives the _last_ expression in the list. */ + BCReg nret = expr_list(ls, &e); + if (nret == 1) { /* Return one result. */ + if (e.k == VCALL) { /* Check for tail call. */ + BCIns *ip = bcptr(fs, &e); + /* It doesn't pay off to add BC_VARGT just for 'return ...'. */ + if (bc_op(*ip) == BC_VARG) goto notailcall; + fs->pc--; + ins = BCINS_AD(bc_op(*ip)-BC_CALL+BC_CALLT, bc_a(*ip), bc_c(*ip)); + } else { /* Can return the result from any register. */ + ins = BCINS_AD(BC_RET1, expr_toanyreg(fs, &e), 2); + } + } else { + if (e.k == VCALL) { /* Append all results from a call. */ + notailcall: + setbc_b(bcptr(fs, &e), 0); + ins = BCINS_AD(BC_RETM, fs->nactvar, e.u.s.aux - fs->nactvar); + } else { + expr_tonextreg(fs, &e); /* Force contiguous registers. */ + ins = BCINS_AD(BC_RET, fs->nactvar, nret+1); + } + } + } + if (fs->flags & PROTO_CHILD) + bcemit_AJ(fs, BC_UCLO, 0, 0); /* May need to close upvalues first. */ + bcemit_INS(fs, ins); +} + +/* Parse 'break' statement. */ +static void parse_break(LexState *ls) +{ + ls->fs->bl->flags |= FSCOPE_BREAK; + gola_new(ls, NAME_BREAK, VSTACK_GOTO, bcemit_jmp(ls->fs)); +} + +/* Parse 'goto' statement. */ +static void parse_goto(LexState *ls) +{ + FuncState *fs = ls->fs; + GCstr *name = lex_str(ls); + VarInfo *vl = gola_findlabel(ls, name); + if (vl) /* Treat backwards goto within same scope like a loop. */ + bcemit_AJ(fs, BC_LOOP, vl->slot, -1); /* No BC range check. */ + fs->bl->flags |= FSCOPE_GOLA; + gola_new(ls, name, VSTACK_GOTO, bcemit_jmp(fs)); +} + +/* Parse label. */ +static void parse_label(LexState *ls) +{ + FuncState *fs = ls->fs; + GCstr *name; + MSize idx; + fs->lasttarget = fs->pc; + fs->bl->flags |= FSCOPE_GOLA; + lj_lex_next(ls); /* Skip '::'. */ + name = lex_str(ls); + if (gola_findlabel(ls, name)) + lj_lex_error(ls, 0, LJ_ERR_XLDUP, strdata(name)); + idx = gola_new(ls, name, VSTACK_LABEL, fs->pc); + lex_check(ls, TK_label); + /* Recursively parse trailing statements: labels and ';' (Lua 5.2 only). */ + for (;;) { + if (ls->tok == TK_label) { + synlevel_begin(ls); + parse_label(ls); + synlevel_end(ls); + } else if (LJ_52 && ls->tok == ';') { + lj_lex_next(ls); + } else { + break; + } + } + /* Trailing label is considered to be outside of scope. */ + if (parse_isend(ls->tok) && ls->tok != TK_until) + ls->vstack[idx].slot = fs->bl->nactvar; + gola_resolve(ls, fs->bl, idx); +} + +/* -- Blocks, loops and conditional statements ---------------------------- */ + +/* Parse a block. */ +static void parse_block(LexState *ls) +{ + FuncState *fs = ls->fs; + FuncScope bl; + fscope_begin(fs, &bl, 0); + parse_chunk(ls); + fscope_end(fs); +} + +/* Parse 'while' statement. */ +static void parse_while(LexState *ls, BCLine line) +{ + FuncState *fs = ls->fs; + BCPos start, loop, condexit; + FuncScope bl; + lj_lex_next(ls); /* Skip 'while'. */ + start = fs->lasttarget = fs->pc; + condexit = expr_cond(ls); + fscope_begin(fs, &bl, FSCOPE_LOOP); + lex_check(ls, TK_do); + loop = bcemit_AD(fs, BC_LOOP, fs->nactvar, 0); + parse_block(ls); + jmp_patch(fs, bcemit_jmp(fs), start); + lex_match(ls, TK_end, TK_while, line); + fscope_end(fs); + jmp_tohere(fs, condexit); + jmp_patchins(fs, loop, fs->pc); +} + +/* Parse 'repeat' statement. */ +static void parse_repeat(LexState *ls, BCLine line) +{ + FuncState *fs = ls->fs; + BCPos loop = fs->lasttarget = fs->pc; + BCPos condexit; + FuncScope bl1, bl2; + fscope_begin(fs, &bl1, FSCOPE_LOOP); /* Breakable loop scope. */ + fscope_begin(fs, &bl2, 0); /* Inner scope. */ + lj_lex_next(ls); /* Skip 'repeat'. */ + bcemit_AD(fs, BC_LOOP, fs->nactvar, 0); + parse_chunk(ls); + lex_match(ls, TK_until, TK_repeat, line); + condexit = expr_cond(ls); /* Parse condition (still inside inner scope). */ + if (!(bl2.flags & FSCOPE_UPVAL)) { /* No upvalues? Just end inner scope. */ + fscope_end(fs); + } else { /* Otherwise generate: cond: UCLO+JMP out, !cond: UCLO+JMP loop. */ + parse_break(ls); /* Break from loop and close upvalues. */ + jmp_tohere(fs, condexit); + fscope_end(fs); /* End inner scope and close upvalues. */ + condexit = bcemit_jmp(fs); + } + jmp_patch(fs, condexit, loop); /* Jump backwards if !cond. */ + jmp_patchins(fs, loop, fs->pc); + fscope_end(fs); /* End loop scope. */ +} + +/* Parse numeric 'for'. */ +static void parse_for_num(LexState *ls, GCstr *varname, BCLine line) +{ + FuncState *fs = ls->fs; + BCReg base = fs->freereg; + FuncScope bl; + BCPos loop, loopend; + /* Hidden control variables. */ + var_new_fixed(ls, FORL_IDX, VARNAME_FOR_IDX); + var_new_fixed(ls, FORL_STOP, VARNAME_FOR_STOP); + var_new_fixed(ls, FORL_STEP, VARNAME_FOR_STEP); + /* Visible copy of index variable. */ + var_new(ls, FORL_EXT, varname); + lex_check(ls, '='); + expr_next(ls); + lex_check(ls, ','); + expr_next(ls); + if (lex_opt(ls, ',')) { + expr_next(ls); + } else { + bcemit_AD(fs, BC_KSHORT, fs->freereg, 1); /* Default step is 1. */ + bcreg_reserve(fs, 1); + } + var_add(ls, 3); /* Hidden control variables. */ + lex_check(ls, TK_do); + loop = bcemit_AJ(fs, BC_FORI, base, NO_JMP); + fscope_begin(fs, &bl, 0); /* Scope for visible variables. */ + var_add(ls, 1); + bcreg_reserve(fs, 1); + parse_block(ls); + fscope_end(fs); + /* Perform loop inversion. Loop control instructions are at the end. */ + loopend = bcemit_AJ(fs, BC_FORL, base, NO_JMP); + fs->bcbase[loopend].line = line; /* Fix line for control ins. */ + jmp_patchins(fs, loopend, loop+1); + jmp_patchins(fs, loop, fs->pc); +} + +/* Try to predict whether the iterator is next() and specialize the bytecode. +** Detecting next() and pairs() by name is simplistic, but quite effective. +** The interpreter backs off if the check for the closure fails at runtime. +*/ +static int predict_next(LexState *ls, FuncState *fs, BCPos pc) +{ + BCIns ins = fs->bcbase[pc].ins; + GCstr *name; + cTValue *o; + switch (bc_op(ins)) { + case BC_MOV: + name = gco2str(gcref(var_get(ls, fs, bc_d(ins)).name)); + break; + case BC_UGET: + name = gco2str(gcref(ls->vstack[fs->uvmap[bc_d(ins)]].name)); + break; + case BC_GGET: + /* There's no inverse index (yet), so lookup the strings. */ + o = lj_tab_getstr(fs->kt, lj_str_newlit(ls->L, "pairs")); + if (o && tvhaskslot(o) && tvkslot(o) == bc_d(ins)) + return 1; + o = lj_tab_getstr(fs->kt, lj_str_newlit(ls->L, "next")); + if (o && tvhaskslot(o) && tvkslot(o) == bc_d(ins)) + return 1; + return 0; + default: + return 0; + } + return (name->len == 5 && !strcmp(strdata(name), "pairs")) || + (name->len == 4 && !strcmp(strdata(name), "next")); +} + +/* Parse 'for' iterator. */ +static void parse_for_iter(LexState *ls, GCstr *indexname) +{ + FuncState *fs = ls->fs; + ExpDesc e; + BCReg nvars = 0; + BCLine line; + BCReg base = fs->freereg + 3; + BCPos loop, loopend, exprpc = fs->pc; + FuncScope bl; + int isnext; + /* Hidden control variables. */ + var_new_fixed(ls, nvars++, VARNAME_FOR_GEN); + var_new_fixed(ls, nvars++, VARNAME_FOR_STATE); + var_new_fixed(ls, nvars++, VARNAME_FOR_CTL); + /* Visible variables returned from iterator. */ + var_new(ls, nvars++, indexname); + while (lex_opt(ls, ',')) + var_new(ls, nvars++, lex_str(ls)); + lex_check(ls, TK_in); + line = ls->linenumber; + assign_adjust(ls, 3, expr_list(ls, &e), &e); + /* The iterator needs another 3 [4] slots (func [pc] | state ctl). */ + bcreg_bump(fs, 3+LJ_FR2); + isnext = (nvars <= 5 && predict_next(ls, fs, exprpc)); + var_add(ls, 3); /* Hidden control variables. */ + lex_check(ls, TK_do); + loop = bcemit_AJ(fs, isnext ? BC_ISNEXT : BC_JMP, base, NO_JMP); + fscope_begin(fs, &bl, 0); /* Scope for visible variables. */ + var_add(ls, nvars-3); + bcreg_reserve(fs, nvars-3); + parse_block(ls); + fscope_end(fs); + /* Perform loop inversion. Loop control instructions are at the end. */ + jmp_patchins(fs, loop, fs->pc); + bcemit_ABC(fs, isnext ? BC_ITERN : BC_ITERC, base, nvars-3+1, 2+1); + loopend = bcemit_AJ(fs, BC_ITERL, base, NO_JMP); + fs->bcbase[loopend-1].line = line; /* Fix line for control ins. */ + fs->bcbase[loopend].line = line; + jmp_patchins(fs, loopend, loop+1); +} + +/* Parse 'for' statement. */ +static void parse_for(LexState *ls, BCLine line) +{ + FuncState *fs = ls->fs; + GCstr *varname; + FuncScope bl; + fscope_begin(fs, &bl, FSCOPE_LOOP); + lj_lex_next(ls); /* Skip 'for'. */ + varname = lex_str(ls); /* Get first variable name. */ + if (ls->tok == '=') + parse_for_num(ls, varname, line); + else if (ls->tok == ',' || ls->tok == TK_in) + parse_for_iter(ls, varname); + else + err_syntax(ls, LJ_ERR_XFOR); + lex_match(ls, TK_end, TK_for, line); + fscope_end(fs); /* Resolve break list. */ +} + +/* Parse condition and 'then' block. */ +static BCPos parse_then(LexState *ls) +{ + BCPos condexit; + lj_lex_next(ls); /* Skip 'if' or 'elseif'. */ + condexit = expr_cond(ls); + lex_check(ls, TK_then); + parse_block(ls); + return condexit; +} + +/* Parse 'if' statement. */ +static void parse_if(LexState *ls, BCLine line) +{ + FuncState *fs = ls->fs; + BCPos flist; + BCPos escapelist = NO_JMP; + flist = parse_then(ls); + while (ls->tok == TK_elseif) { /* Parse multiple 'elseif' blocks. */ + jmp_append(fs, &escapelist, bcemit_jmp(fs)); + jmp_tohere(fs, flist); + flist = parse_then(ls); + } + if (ls->tok == TK_else) { /* Parse optional 'else' block. */ + jmp_append(fs, &escapelist, bcemit_jmp(fs)); + jmp_tohere(fs, flist); + lj_lex_next(ls); /* Skip 'else'. */ + parse_block(ls); + } else { + jmp_append(fs, &escapelist, flist); + } + jmp_tohere(fs, escapelist); + lex_match(ls, TK_end, TK_if, line); +} + +/* -- Parse statements ---------------------------------------------------- */ + +/* Parse a statement. Returns 1 if it must be the last one in a chunk. */ +static int parse_stmt(LexState *ls) +{ + BCLine line = ls->linenumber; + switch (ls->tok) { + case TK_if: + parse_if(ls, line); + break; + case TK_while: + parse_while(ls, line); + break; + case TK_do: + lj_lex_next(ls); + parse_block(ls); + lex_match(ls, TK_end, TK_do, line); + break; + case TK_for: + parse_for(ls, line); + break; + case TK_repeat: + parse_repeat(ls, line); + break; + case TK_function: + parse_func(ls, line); + break; + case TK_local: + lj_lex_next(ls); + parse_local(ls); + break; + case TK_return: + parse_return(ls); + return 1; /* Must be last. */ + case TK_break: + lj_lex_next(ls); + parse_break(ls); + return !LJ_52; /* Must be last in Lua 5.1. */ +#if LJ_52 + case ';': + lj_lex_next(ls); + break; +#endif + case TK_label: + parse_label(ls); + break; + case TK_goto: + if (LJ_52 || lj_lex_lookahead(ls) == TK_name) { + lj_lex_next(ls); + parse_goto(ls); + break; + } + /* fallthrough */ + default: + parse_call_assign(ls); + break; + } + return 0; +} + +/* A chunk is a list of statements optionally separated by semicolons. */ +static void parse_chunk(LexState *ls) +{ + int islast = 0; + synlevel_begin(ls); + while (!islast && !parse_isend(ls->tok)) { + islast = parse_stmt(ls); + lex_opt(ls, ';'); + lua_assert(ls->fs->framesize >= ls->fs->freereg && + ls->fs->freereg >= ls->fs->nactvar); + ls->fs->freereg = ls->fs->nactvar; /* Free registers after each stmt. */ + } + synlevel_end(ls); +} + +/* Entry point of bytecode parser. */ +GCproto *lj_parse(LexState *ls) +{ + FuncState fs; + FuncScope bl; + GCproto *pt; + lua_State *L = ls->L; +#ifdef LUAJIT_DISABLE_DEBUGINFO + ls->chunkname = lj_str_newlit(L, "="); +#else + ls->chunkname = lj_str_newz(L, ls->chunkarg); +#endif + setstrV(L, L->top, ls->chunkname); /* Anchor chunkname string. */ + incr_top(L); + ls->level = 0; + fs_init(ls, &fs); + fs.linedefined = 0; + fs.numparams = 0; + fs.bcbase = NULL; + fs.bclim = 0; + fs.flags |= PROTO_VARARG; /* Main chunk is always a vararg func. */ + fscope_begin(&fs, &bl, 0); + bcemit_AD(&fs, BC_FUNCV, 0, 0); /* Placeholder. */ + lj_lex_next(ls); /* Read-ahead first token. */ + parse_chunk(ls); + if (ls->tok != TK_eof) + err_token(ls, TK_eof); + pt = fs_finish(ls, ls->linenumber); + L->top--; /* Drop chunkname. */ + lua_assert(fs.prev == NULL); + lua_assert(ls->fs == NULL); + lua_assert(pt->sizeuv == 0); + return pt; +} + diff --git a/lib/LuaJIT/lj_parse.h b/lib/LuaJIT/lj_parse.h new file mode 100644 index 0000000..ceeab69 --- /dev/null +++ b/lib/LuaJIT/lj_parse.h @@ -0,0 +1,18 @@ +/* +** Lua parser (source code -> bytecode). +** Copyright (C) 2005-2017 Mike Pall. See Copyright Notice in luajit.h +*/ + +#ifndef _LJ_PARSE_H +#define _LJ_PARSE_H + +#include "lj_obj.h" +#include "lj_lex.h" + +LJ_FUNC GCproto *lj_parse(LexState *ls); +LJ_FUNC GCstr *lj_parse_keepstr(LexState *ls, const char *str, size_t l); +#if LJ_HASFFI +LJ_FUNC void lj_parse_keepcdata(LexState *ls, TValue *tv, GCcdata *cd); +#endif + +#endif diff --git a/lib/LuaJIT/lj_parse.o b/lib/LuaJIT/lj_parse.o new file mode 100644 index 0000000000000000000000000000000000000000..d8802a97c1686660da491e4c4002344d4846a22e GIT binary patch literal 36792 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⋙RT#;-4-5j{?Iusqh8fj)fd|>N%2xC+qElBs{sk zlO-_4<>YhcS@1d2N2b4!1_NA9KKGmjpGci^1o~%{k@4ct$ap3h6E0`D&zuGSIy^Ex z8-zyS(@(}d0)G4~_z*c+T-1(b_-D7{C8Ts*gi{r5{N^M)nSKGssb0x`azzrpG>Oln zNqmy|Kb}ON%;)z>cru?{3Nmmx^%+jWll%L3NjR~z>ow~p#)k1p?(cUc;VY8pTa)l) zIWM6g0GCsql}R`?sa@{nynhgVGM-9`$K~WRCx^lRgj=Z9UX(RA$!}5`Vf61(?#Jzw z8XLcy;~7MZ%f=^i+-JcFd=kfLZ-g$JejgtXvMo4)Pv-Jm=)kY%d~zK46`bDhz)LxO zt^@xGr=Q@!XL0&`2YxfhX^)OBTmJbR=WaNGFXA|Np9%a{jt3Z*VLr<_E<5llj+4#P zWtV$9$H@lhvT=Jn=4P9~>o`5dgml^T4IE!+!3q2>jxToLWn6Dd9r*p6ex(E7#qm`R z{9idfg-;E!^Ay#xOR$M13Azv6h41AmI+F$exTj<-7S|Kj)q4*Y*O z-sZsn!tqBO_!f>o=D@de{0Rr%&haN5co)Zia(vK%PvZC~2YwC5vFXAt z+YemFarWnHkYeN4bDSMUPr|2j+&-&l)0cAG=iq-M$Fm&xERJV8@S8b)p#z`K@f-)f zh~u9M5;)w{zS+b7}Km$8p*RO)j^Ny1 zk7NgaAE#Fw_yydq!VX;Ic)0^l;kZ3dvgJ8(r6u=52Yp|@g)esC$2h*!fxp1o~sNfoF339tS>-<4q2HJjY`W{CtkLI`AKH`~e3( ziQ{b!{8ElT;=nKG_+t)yBFCR_;16-VJ?X%&;q{ZBd;0DM24*Uy_=Q{AC9Jkl2Z2sX=&fmHv%H^+YZcEQLeK}89 z#qT!W#^0UsTRv}(%?|ni$9o;P-4FLWaNE9}<2w@!v_40>+wI|Z;I&*YMGibC#VRoD zz?X8|={M|iP|kiG@K}7D{mwoI<+NM-9F)xEvh``7gIego?Q>Ao4%|NP#Qq{bM6mhT z=bb)v(A(#o1|7J4-l>q++va1Rce3?iz%l0(LL+H?ek9i9k_koDQA?G&*pERcUtPe?ek6#IPk!^R{j?p_-2l0q@Ky& zKJP>y>CPVxP4Bk)q&gRrrvbm_W7mk zF=xtepI-_%aQl4M0}lL2?)aW@;P$zuHyyZrPU8y)ZlBW-Cf47%7JI0;N9wHmD*LIf zvU=IX2z`?{k!_|VD1kd36jqwB5wIb*Fm>5tmTsXtolrSkLfFZo(Wo07Q3S+~TmEw_E2 zj}-ekL4=1Uoh7P zPf3q0wFs7S|3PZJ5&xX|DcNZ|lyh)3{o=oH=i`p`_RsY{2MHtPZ_=$CQ9C2QJN5Vf E0^7p}8~^|S literal 0 HcmV?d00001 diff --git a/lib/LuaJIT/lj_parse_dyn.o b/lib/LuaJIT/lj_parse_dyn.o new file mode 100644 index 0000000000000000000000000000000000000000..d8802a97c1686660da491e4c4002344d4846a22e GIT binary patch literal 36792 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⋙RT#;-4-5j{?Iusqh8fj)fd|>N%2xC+qElBs{sk zlO-_4<>YhcS@1d2N2b4!1_NA9KKGmjpGci^1o~%{k@4ct$ap3h6E0`D&zuGSIy^Ex z8-zyS(@(}d0)G4~_z*c+T-1(b_-D7{C8Ts*gi{r5{N^M)nSKGssb0x`azzrpG>Oln zNqmy|Kb}ON%;)z>cru?{3Nmmx^%+jWll%L3NjR~z>ow~p#)k1p?(cUc;VY8pTa)l) zIWM6g0GCsql}R`?sa@{nynhgVGM-9`$K~WRCx^lRgj=Z9UX(RA$!}5`Vf61(?#Jzw z8XLcy;~7MZ%f=^i+-JcFd=kfLZ-g$JejgtXvMo4)Pv-Jm=)kY%d~zK46`bDhz)LxO zt^@xGr=Q@!XL0&`2YxfhX^)OBTmJbR=WaNGFXA|Np9%a{jt3Z*VLr<_E<5llj+4#P zWtV$9$H@lhvT=Jn=4P9~>o`5dgml^T4IE!+!3q2>jxToLWn6Dd9r*p6ex(E7#qm`R z{9idfg-;E!^Ay#xOR$M13Azv6h41AmI+F$exTj<-7S|Kj)q4*Y*O z-sZsn!tqBO_!f>o=D@de{0Rr%&haN5co)Zia(vK%PvZC~2YwC5vFXAt z+YemFarWnHkYeN4bDSMUPr|2j+&-&l)0cAG=iq-M$Fm&xERJV8@S8b)p#z`K@f-)f zh~u9M5;)w{zS+b7}Km$8p*RO)j^Ny1 zk7NgaAE#Fw_yydq!VX;Ic)0^l;kZ3dvgJ8(r6u=52Yp|@g)esC$2h*!fxp1o~sNfoF339tS>-<4q2HJjY`W{CtkLI`AKH`~e3( ziQ{b!{8ElT;=nKG_+t)yBFCR_;16-VJ?X%&;q{ZBd;0DM24*Uy_=Q{AC9Jkl2Z2sX=&fmHv%H^+YZcEQLeK}89 z#qT!W#^0UsTRv}(%?|ni$9o;P-4FLWaNE9}<2w@!v_40>+wI|Z;I&*YMGibC#VRoD zz?X8|={M|iP|kiG@K}7D{mwoI<+NM-9F)xEvh``7gIego?Q>Ao4%|NP#Qq{bM6mhT z=bb)v(A(#o1|7J4-l>q++va1Rce3?iz%l0(LL+H?ek9i9k_koDQA?G&*pERcUtPe?ek6#IPk!^R{j?p_-2l0q@Ky& zKJP>y>CPVxP4Bk)q&gRrrvbm_W7mk zF=xtepI-_%aQl4M0}lL2?)aW@;P$zuHyyZrPU8y)ZlBW-Cf47%7JI0;N9wHmD*LIf zvU=IX2z`?{k!_|VD1kd36jqwB5wIb*Fm>5tmTsXtolrSkLfFZo(Wo07Q3S+~TmEw_E2 zj}-ekL4=1Uoh7P zPf3q0wFs7S|3PZJ5&xX|DcNZ|lyh)3{o=oH=i`p`_RsY{2MHtPZ_=$CQ9C2QJN5Vf E0^7p}8~^|S literal 0 HcmV?d00001 diff --git a/lib/LuaJIT/lj_profile.c b/lib/LuaJIT/lj_profile.c new file mode 100644 index 0000000..3223697 --- /dev/null +++ b/lib/LuaJIT/lj_profile.c @@ -0,0 +1,368 @@ +/* +** Low-overhead profiling. +** Copyright (C) 2005-2017 Mike Pall. See Copyright Notice in luajit.h +*/ + +#define lj_profile_c +#define LUA_CORE + +#include "lj_obj.h" + +#if LJ_HASPROFILE + +#include "lj_buf.h" +#include "lj_frame.h" +#include "lj_debug.h" +#include "lj_dispatch.h" +#if LJ_HASJIT +#include "lj_jit.h" +#include "lj_trace.h" +#endif +#include "lj_profile.h" + +#include "luajit.h" + +#if LJ_PROFILE_SIGPROF + +#include +#include +#define profile_lock(ps) UNUSED(ps) +#define profile_unlock(ps) UNUSED(ps) + +#elif LJ_PROFILE_PTHREAD + +#include +#include +#if LJ_TARGET_PS3 +#include +#endif +#define profile_lock(ps) pthread_mutex_lock(&ps->lock) +#define profile_unlock(ps) pthread_mutex_unlock(&ps->lock) + +#elif LJ_PROFILE_WTHREAD + +#define WIN32_LEAN_AND_MEAN +#if LJ_TARGET_XBOX360 +#include +#include +#else +#include +#endif +typedef unsigned int (WINAPI *WMM_TPFUNC)(unsigned int); +#define profile_lock(ps) EnterCriticalSection(&ps->lock) +#define profile_unlock(ps) LeaveCriticalSection(&ps->lock) + +#endif + +/* Profiler state. */ +typedef struct ProfileState { + global_State *g; /* VM state that started the profiler. */ + luaJIT_profile_callback cb; /* Profiler callback. */ + void *data; /* Profiler callback data. */ + SBuf sb; /* String buffer for stack dumps. */ + int interval; /* Sample interval in milliseconds. */ + int samples; /* Number of samples for next callback. */ + int vmstate; /* VM state when profile timer triggered. */ +#if LJ_PROFILE_SIGPROF + struct sigaction oldsa; /* Previous SIGPROF state. */ +#elif LJ_PROFILE_PTHREAD + pthread_mutex_t lock; /* g->hookmask update lock. */ + pthread_t thread; /* Timer thread. */ + int abort; /* Abort timer thread. */ +#elif LJ_PROFILE_WTHREAD +#if LJ_TARGET_WINDOWS + HINSTANCE wmm; /* WinMM library handle. */ + WMM_TPFUNC wmm_tbp; /* WinMM timeBeginPeriod function. */ + WMM_TPFUNC wmm_tep; /* WinMM timeEndPeriod function. */ +#endif + CRITICAL_SECTION lock; /* g->hookmask update lock. */ + HANDLE thread; /* Timer thread. */ + int abort; /* Abort timer thread. */ +#endif +} ProfileState; + +/* Sadly, we have to use a static profiler state. +** +** The SIGPROF variant needs a static pointer to the global state, anyway. +** And it would be hard to extend for multiple threads. You can still use +** multiple VMs in multiple threads, but only profile one at a time. +*/ +static ProfileState profile_state; + +/* Default sample interval in milliseconds. */ +#define LJ_PROFILE_INTERVAL_DEFAULT 10 + +/* -- Profiler/hook interaction ------------------------------------------- */ + +#if !LJ_PROFILE_SIGPROF +void LJ_FASTCALL lj_profile_hook_enter(global_State *g) +{ + ProfileState *ps = &profile_state; + if (ps->g) { + profile_lock(ps); + hook_enter(g); + profile_unlock(ps); + } else { + hook_enter(g); + } +} + +void LJ_FASTCALL lj_profile_hook_leave(global_State *g) +{ + ProfileState *ps = &profile_state; + if (ps->g) { + profile_lock(ps); + hook_leave(g); + profile_unlock(ps); + } else { + hook_leave(g); + } +} +#endif + +/* -- Profile callbacks --------------------------------------------------- */ + +/* Callback from profile hook (HOOK_PROFILE already cleared). */ +void LJ_FASTCALL lj_profile_interpreter(lua_State *L) +{ + ProfileState *ps = &profile_state; + global_State *g = G(L); + uint8_t mask; + profile_lock(ps); + mask = (g->hookmask & ~HOOK_PROFILE); + if (!(mask & HOOK_VMEVENT)) { + int samples = ps->samples; + ps->samples = 0; + g->hookmask = HOOK_VMEVENT; + lj_dispatch_update(g); + profile_unlock(ps); + ps->cb(ps->data, L, samples, ps->vmstate); /* Invoke user callback. */ + profile_lock(ps); + mask |= (g->hookmask & HOOK_PROFILE); + } + g->hookmask = mask; + lj_dispatch_update(g); + profile_unlock(ps); +} + +/* Trigger profile hook. Asynchronous call from OS-specific profile timer. */ +static void profile_trigger(ProfileState *ps) +{ + global_State *g = ps->g; + uint8_t mask; + profile_lock(ps); + ps->samples++; /* Always increment number of samples. */ + mask = g->hookmask; + if (!(mask & (HOOK_PROFILE|HOOK_VMEVENT))) { /* Set profile hook. */ + int st = g->vmstate; + ps->vmstate = st >= 0 ? 'N' : + st == ~LJ_VMST_INTERP ? 'I' : + st == ~LJ_VMST_C ? 'C' : + st == ~LJ_VMST_GC ? 'G' : 'J'; + g->hookmask = (mask | HOOK_PROFILE); + lj_dispatch_update(g); + } + profile_unlock(ps); +} + +/* -- OS-specific profile timer handling ---------------------------------- */ + +#if LJ_PROFILE_SIGPROF + +/* SIGPROF handler. */ +static void profile_signal(int sig) +{ + UNUSED(sig); + profile_trigger(&profile_state); +} + +/* Start profiling timer. */ +static void profile_timer_start(ProfileState *ps) +{ + int interval = ps->interval; + struct itimerval tm; + struct sigaction sa; + tm.it_value.tv_sec = tm.it_interval.tv_sec = interval / 1000; + tm.it_value.tv_usec = tm.it_interval.tv_usec = (interval % 1000) * 1000; + setitimer(ITIMER_PROF, &tm, NULL); + sa.sa_flags = SA_RESTART; + sa.sa_handler = profile_signal; + sigemptyset(&sa.sa_mask); + sigaction(SIGPROF, &sa, &ps->oldsa); +} + +/* Stop profiling timer. */ +static void profile_timer_stop(ProfileState *ps) +{ + struct itimerval tm; + tm.it_value.tv_sec = tm.it_interval.tv_sec = 0; + tm.it_value.tv_usec = tm.it_interval.tv_usec = 0; + setitimer(ITIMER_PROF, &tm, NULL); + sigaction(SIGPROF, &ps->oldsa, NULL); +} + +#elif LJ_PROFILE_PTHREAD + +/* POSIX timer thread. */ +static void *profile_thread(ProfileState *ps) +{ + int interval = ps->interval; +#if !LJ_TARGET_PS3 + struct timespec ts; + ts.tv_sec = interval / 1000; + ts.tv_nsec = (interval % 1000) * 1000000; +#endif + while (1) { +#if LJ_TARGET_PS3 + sys_timer_usleep(interval * 1000); +#else + nanosleep(&ts, NULL); +#endif + if (ps->abort) break; + profile_trigger(ps); + } + return NULL; +} + +/* Start profiling timer thread. */ +static void profile_timer_start(ProfileState *ps) +{ + pthread_mutex_init(&ps->lock, 0); + ps->abort = 0; + pthread_create(&ps->thread, NULL, (void *(*)(void *))profile_thread, ps); +} + +/* Stop profiling timer thread. */ +static void profile_timer_stop(ProfileState *ps) +{ + ps->abort = 1; + pthread_join(ps->thread, NULL); + pthread_mutex_destroy(&ps->lock); +} + +#elif LJ_PROFILE_WTHREAD + +/* Windows timer thread. */ +static DWORD WINAPI profile_thread(void *psx) +{ + ProfileState *ps = (ProfileState *)psx; + int interval = ps->interval; +#if LJ_TARGET_WINDOWS && !LJ_TARGET_UWP + ps->wmm_tbp(interval); +#endif + while (1) { + Sleep(interval); + if (ps->abort) break; + profile_trigger(ps); + } +#if LJ_TARGET_WINDOWS && !LJ_TARGET_UWP + ps->wmm_tep(interval); +#endif + return 0; +} + +/* Start profiling timer thread. */ +static void profile_timer_start(ProfileState *ps) +{ +#if LJ_TARGET_WINDOWS && !LJ_TARGET_UWP + if (!ps->wmm) { /* Load WinMM library on-demand. */ + ps->wmm = LJ_WIN_LOADLIBA("winmm.dll"); + if (ps->wmm) { + ps->wmm_tbp = (WMM_TPFUNC)GetProcAddress(ps->wmm, "timeBeginPeriod"); + ps->wmm_tep = (WMM_TPFUNC)GetProcAddress(ps->wmm, "timeEndPeriod"); + if (!ps->wmm_tbp || !ps->wmm_tep) { + ps->wmm = NULL; + return; + } + } + } +#endif + InitializeCriticalSection(&ps->lock); + ps->abort = 0; + ps->thread = CreateThread(NULL, 0, profile_thread, ps, 0, NULL); +} + +/* Stop profiling timer thread. */ +static void profile_timer_stop(ProfileState *ps) +{ + ps->abort = 1; + WaitForSingleObject(ps->thread, INFINITE); + DeleteCriticalSection(&ps->lock); +} + +#endif + +/* -- Public profiling API ------------------------------------------------ */ + +/* Start profiling. */ +LUA_API void luaJIT_profile_start(lua_State *L, const char *mode, + luaJIT_profile_callback cb, void *data) +{ + ProfileState *ps = &profile_state; + int interval = LJ_PROFILE_INTERVAL_DEFAULT; + while (*mode) { + int m = *mode++; + switch (m) { + case 'i': + interval = 0; + while (*mode >= '0' && *mode <= '9') + interval = interval * 10 + (*mode++ - '0'); + if (interval <= 0) interval = 1; + break; +#if LJ_HASJIT + case 'l': case 'f': + L2J(L)->prof_mode = m; + lj_trace_flushall(L); + break; +#endif + default: /* Ignore unknown mode chars. */ + break; + } + } + if (ps->g) { + luaJIT_profile_stop(L); + if (ps->g) return; /* Profiler in use by another VM. */ + } + ps->g = G(L); + ps->interval = interval; + ps->cb = cb; + ps->data = data; + ps->samples = 0; + lj_buf_init(L, &ps->sb); + profile_timer_start(ps); +} + +/* Stop profiling. */ +LUA_API void luaJIT_profile_stop(lua_State *L) +{ + ProfileState *ps = &profile_state; + global_State *g = ps->g; + if (G(L) == g) { /* Only stop profiler if started by this VM. */ + profile_timer_stop(ps); + g->hookmask &= ~HOOK_PROFILE; + lj_dispatch_update(g); +#if LJ_HASJIT + G2J(g)->prof_mode = 0; + lj_trace_flushall(L); +#endif + lj_buf_free(g, &ps->sb); + setmref(ps->sb.b, NULL); + setmref(ps->sb.e, NULL); + ps->g = NULL; + } +} + +/* Return a compact stack dump. */ +LUA_API const char *luaJIT_profile_dumpstack(lua_State *L, const char *fmt, + int depth, size_t *len) +{ + ProfileState *ps = &profile_state; + SBuf *sb = &ps->sb; + setsbufL(sb, L); + lj_buf_reset(sb); + lj_debug_dumpstack(L, sb, fmt, depth); + *len = (size_t)sbuflen(sb); + return sbufB(sb); +} + +#endif diff --git a/lib/LuaJIT/lj_profile.h b/lib/LuaJIT/lj_profile.h new file mode 100644 index 0000000..0cccfd7 --- /dev/null +++ b/lib/LuaJIT/lj_profile.h @@ -0,0 +1,21 @@ +/* +** Low-overhead profiling. +** Copyright (C) 2005-2017 Mike Pall. See Copyright Notice in luajit.h +*/ + +#ifndef _LJ_PROFILE_H +#define _LJ_PROFILE_H + +#include "lj_obj.h" + +#if LJ_HASPROFILE + +LJ_FUNC void LJ_FASTCALL lj_profile_interpreter(lua_State *L); +#if !LJ_PROFILE_SIGPROF +LJ_FUNC void LJ_FASTCALL lj_profile_hook_enter(global_State *g); +LJ_FUNC void LJ_FASTCALL lj_profile_hook_leave(global_State *g); +#endif + +#endif + +#endif diff --git a/lib/LuaJIT/lj_profile.o b/lib/LuaJIT/lj_profile.o new file mode 100644 index 0000000000000000000000000000000000000000..af55586e731c7ccbc7917a9f26ff6c0345c9d032 GIT binary patch literal 4168 zcmbtXeQZ-z6u)f;lnq}iQL+RHO>A_FQnmz>K_>2Puk;SfMp!41IZGKGRJ*OVZ)T`- z!w%%7RF@bHiP2z!${(7LV8ASVRK{nFIzf#wF)T5`AwyvNAt+GKx$oZfaT&(Mv+Uh- zfA@Dj?mg$-z4iHn%j`BA5n>}tNns{XLg;-3y@shZWELqQH1j0R($ny1bL|iG?%CD7 zJkz*lr~!bjr+en4PxTuQ;_CK5?*$xP^EE<->Oo82$m^p+bxgah|5$k@edCtvouk>% zs=&ol?Tk0v-tKeFA3!@WtRZT%8ftE$>2YVK%jw$h?=^1RScA}v<^>{4f<=7*%Z6uZ(bKpsayl#AWD$l6p$JN+zhVG<><}JlXnYp{{tKKS_o%ZR?eE#Hs z_Y2q`2pDSK3Ql0vrp6zvSIn(V+-6&Y_X&P>z?HOMg z-%(?Nx1708bLY&nX41^3R-U7VBWUDWhFMm3JOXufOFU(PhURk5Vqc}Nf-gS6N88J* zh^@zMn>%ZUa}e^w*We+(7U5$RjKWSwtX6Sn?X^nj4u`6EwiT&LS$dkLRIDym$~48J zDy2ZERh-M9#Ap{s8;~46RK>l`4wSS*Q)-%ul~Mpy#py4xzb|wOv|Y#rxr%27?%uNd zmC`pIwTdTORI8Njn5HTf+oo$uReDB18MNC!FIK7mf>eM4shJr(U4nZVM@Jw};RC{@ z?Y)W<^?wlzTSTtrr=mEfynzXEz|U0T08g?g0N5>l#97Y++-BjASPUIxx@A^S9b`sf zV$mFA@-Cf39|O&ecb_CnVLsb#BMFYPI)mPfIAnB%<2gRYV03fcI`rKGH|~MIjrbJ# zd;~bg-HU|)VSNDO5d2OR_XOaK&r85+4cIy4PvM`$pARRJUG1@W)YnG17EZ-Bc1Gfb zsUFdz$lMT1B_ev;rf_#+1BZl3tW%FB6Uit%NxVCNb7g)~G_1zo81qcoTM06+~33er1XG~_|B8b2rHK#<30)L$2pYT!OWqiI* z!o7TckdGJ>|89x6M>#J12>;(o_$)sELBhqGbwk31f0g$S^6;G#cq7L}+(q0zWZ}Ck z{B;)Gnt#%QV_ypYJ(7QMr@kiPr9AGt5-#rN4<-CMuU88u4kGgZo8#>q$Nl1&iF`Wl z!T-R*x8{F9;(IX|$SDaIb@^VxMch#d_b@Mn{3hX|j(GKi_6Frcfc$O7w4&Q z9J$uIM=khlSQB~1B|oAcHe2}Ce%L4RMSYJ-xTxbP3HLy3ly4+l@XvA_^G8hN`Lo0q zaYrRw_*sZSK+uo1Z{K6+-MEM2c+R{O3|S=M2|gF2;9ueRZiz4Maj`D=dwJf(m)hK+ zN7lorC(Ut_(2~)3#HUAlbmD{CH$r^tQz_zW>+0x;b^_MfrAK}6fwRE8`h1KY-4t$5 zMmnNAjsHD(zyrRD@?YQ@z)&uWeVEh9{^V1F|36?2Y6QT?mHb1*gNq<|4`UqheIok# z5sph?$U<%h%XbLBHYPE=SJ9_zU&HN3CYh$-^!N1qiEF6{ALvWkc=#wD;QbSrPw zf6Tv;+gp1Q{jA~kPRY4AKM`v=C`hdl03Q?l0_)*(Jb(O}687T!(Y+iSP1f;Gtm`~J pzGL_++oNCk>;WQcFc`>$IZQsK?U4InWh(nIp8wiOOH+~U{{g5Ynk)bS literal 0 HcmV?d00001 diff --git a/lib/LuaJIT/lj_profile_dyn.o b/lib/LuaJIT/lj_profile_dyn.o new file mode 100644 index 0000000000000000000000000000000000000000..af55586e731c7ccbc7917a9f26ff6c0345c9d032 GIT binary patch literal 4168 zcmbtXeQZ-z6u)f;lnq}iQL+RHO>A_FQnmz>K_>2Puk;SfMp!41IZGKGRJ*OVZ)T`- z!w%%7RF@bHiP2z!${(7LV8ASVRK{nFIzf#wF)T5`AwyvNAt+GKx$oZfaT&(Mv+Uh- zfA@Dj?mg$-z4iHn%j`BA5n>}tNns{XLg;-3y@shZWELqQH1j0R($ny1bL|iG?%CD7 zJkz*lr~!bjr+en4PxTuQ;_CK5?*$xP^EE<->Oo82$m^p+bxgah|5$k@edCtvouk>% zs=&ol?Tk0v-tKeFA3!@WtRZT%8ftE$>2YVK%jw$h?=^1RScA}v<^>{4f<=7*%Z6uZ(bKpsayl#AWD$l6p$JN+zhVG<><}JlXnYp{{tKKS_o%ZR?eE#Hs z_Y2q`2pDSK3Ql0vrp6zvSIn(V+-6&Y_X&P>z?HOMg z-%(?Nx1708bLY&nX41^3R-U7VBWUDWhFMm3JOXufOFU(PhURk5Vqc}Nf-gS6N88J* zh^@zMn>%ZUa}e^w*We+(7U5$RjKWSwtX6Sn?X^nj4u`6EwiT&LS$dkLRIDym$~48J zDy2ZERh-M9#Ap{s8;~46RK>l`4wSS*Q)-%ul~Mpy#py4xzb|wOv|Y#rxr%27?%uNd zmC`pIwTdTORI8Njn5HTf+oo$uReDB18MNC!FIK7mf>eM4shJr(U4nZVM@Jw};RC{@ z?Y)W<^?wlzTSTtrr=mEfynzXEz|U0T08g?g0N5>l#97Y++-BjASPUIxx@A^S9b`sf zV$mFA@-Cf39|O&ecb_CnVLsb#BMFYPI)mPfIAnB%<2gRYV03fcI`rKGH|~MIjrbJ# zd;~bg-HU|)VSNDO5d2OR_XOaK&r85+4cIy4PvM`$pARRJUG1@W)YnG17EZ-Bc1Gfb zsUFdz$lMT1B_ev;rf_#+1BZl3tW%FB6Uit%NxVCNb7g)~G_1zo81qcoTM06+~33er1XG~_|B8b2rHK#<30)L$2pYT!OWqiI* z!o7TckdGJ>|89x6M>#J12>;(o_$)sELBhqGbwk31f0g$S^6;G#cq7L}+(q0zWZ}Ck z{B;)Gnt#%QV_ypYJ(7QMr@kiPr9AGt5-#rN4<-CMuU88u4kGgZo8#>q$Nl1&iF`Wl z!T-R*x8{F9;(IX|$SDaIb@^VxMch#d_b@Mn{3hX|j(GKi_6Frcfc$O7w4&Q z9J$uIM=khlSQB~1B|oAcHe2}Ce%L4RMSYJ-xTxbP3HLy3ly4+l@XvA_^G8hN`Lo0q zaYrRw_*sZSK+uo1Z{K6+-MEM2c+R{O3|S=M2|gF2;9ueRZiz4Maj`D=dwJf(m)hK+ zN7lorC(Ut_(2~)3#HUAlbmD{CH$r^tQz_zW>+0x;b^_MfrAK}6fwRE8`h1KY-4t$5 zMmnNAjsHD(zyrRD@?YQ@z)&uWeVEh9{^V1F|36?2Y6QT?mHb1*gNq<|4`UqheIok# z5sph?$U<%h%XbLBHYPE=SJ9_zU&HN3CYh$-^!N1qiEF6{ALvWkc=#wD;QbSrPw zf6Tv;+gp1Q{jA~kPRY4AKM`v=C`hdl03Q?l0_)*(Jb(O}687T!(Y+iSP1f;Gtm`~J pzGL_++oNCk>;WQcFc`>$IZQsK?U4InWh(nIp8wiOOH+~U{{g5Ynk)bS literal 0 HcmV?d00001 diff --git a/lib/LuaJIT/lj_record.c b/lib/LuaJIT/lj_record.c new file mode 100644 index 0000000..7f37d6c --- /dev/null +++ b/lib/LuaJIT/lj_record.c @@ -0,0 +1,2649 @@ +/* +** Trace recorder (bytecode -> SSA IR). +** Copyright (C) 2005-2017 Mike Pall. See Copyright Notice in luajit.h +*/ + +#define lj_record_c +#define LUA_CORE + +#include "lj_obj.h" + +#if LJ_HASJIT + +#include "lj_err.h" +#include "lj_str.h" +#include "lj_tab.h" +#include "lj_meta.h" +#include "lj_frame.h" +#if LJ_HASFFI +#include "lj_ctype.h" +#endif +#include "lj_bc.h" +#include "lj_ff.h" +#if LJ_HASPROFILE +#include "lj_debug.h" +#endif +#include "lj_ir.h" +#include "lj_jit.h" +#include "lj_ircall.h" +#include "lj_iropt.h" +#include "lj_trace.h" +#include "lj_record.h" +#include "lj_ffrecord.h" +#include "lj_snap.h" +#include "lj_dispatch.h" +#include "lj_vm.h" + +/* Some local macros to save typing. Undef'd at the end. */ +#define IR(ref) (&J->cur.ir[(ref)]) + +/* Pass IR on to next optimization in chain (FOLD). */ +#define emitir(ot, a, b) (lj_ir_set(J, (ot), (a), (b)), lj_opt_fold(J)) + +/* Emit raw IR without passing through optimizations. */ +#define emitir_raw(ot, a, b) (lj_ir_set(J, (ot), (a), (b)), lj_ir_emit(J)) + +/* -- Sanity checks ------------------------------------------------------- */ + +#ifdef LUA_USE_ASSERT +/* Sanity check the whole IR -- sloooow. */ +static void rec_check_ir(jit_State *J) +{ + IRRef i, nins = J->cur.nins, nk = J->cur.nk; + lua_assert(nk <= REF_BIAS && nins >= REF_BIAS && nins < 65536); + for (i = nk; i < nins; i++) { + IRIns *ir = IR(i); + uint32_t mode = lj_ir_mode[ir->o]; + IRRef op1 = ir->op1; + IRRef op2 = ir->op2; + switch (irm_op1(mode)) { + case IRMnone: lua_assert(op1 == 0); break; + case IRMref: lua_assert(op1 >= nk); + lua_assert(i >= REF_BIAS ? op1 < i : op1 > i); break; + case IRMlit: break; + case IRMcst: lua_assert(i < REF_BIAS); + if (irt_is64(ir->t) && ir->o != IR_KNULL) + i++; + continue; + } + switch (irm_op2(mode)) { + case IRMnone: lua_assert(op2 == 0); break; + case IRMref: lua_assert(op2 >= nk); + lua_assert(i >= REF_BIAS ? op2 < i : op2 > i); break; + case IRMlit: break; + case IRMcst: lua_assert(0); break; + } + if (ir->prev) { + lua_assert(ir->prev >= nk); + lua_assert(i >= REF_BIAS ? ir->prev < i : ir->prev > i); + lua_assert(ir->o == IR_NOP || IR(ir->prev)->o == ir->o); + } + } +} + +/* Compare stack slots and frames of the recorder and the VM. */ +static void rec_check_slots(jit_State *J) +{ + BCReg s, nslots = J->baseslot + J->maxslot; + int32_t depth = 0; + cTValue *base = J->L->base - J->baseslot; + lua_assert(J->baseslot >= 1+LJ_FR2); + lua_assert(J->baseslot == 1+LJ_FR2 || (J->slot[J->baseslot-1] & TREF_FRAME)); + lua_assert(nslots <= LJ_MAX_JSLOTS); + for (s = 0; s < nslots; s++) { + TRef tr = J->slot[s]; + if (tr) { + cTValue *tv = &base[s]; + IRRef ref = tref_ref(tr); + IRIns *ir = NULL; /* Silence compiler. */ + if (!LJ_FR2 || ref || !(tr & (TREF_FRAME | TREF_CONT))) { + lua_assert(ref >= J->cur.nk && ref < J->cur.nins); + ir = IR(ref); + lua_assert(irt_t(ir->t) == tref_t(tr)); + } + if (s == 0) { + lua_assert(tref_isfunc(tr)); +#if LJ_FR2 + } else if (s == 1) { + lua_assert((tr & ~TREF_FRAME) == 0); +#endif + } else if ((tr & TREF_FRAME)) { + GCfunc *fn = gco2func(frame_gc(tv)); + BCReg delta = (BCReg)(tv - frame_prev(tv)); +#if LJ_FR2 + if (ref) + lua_assert(ir_knum(ir)->u64 == tv->u64); + tr = J->slot[s-1]; + ir = IR(tref_ref(tr)); +#endif + lua_assert(tref_isfunc(tr)); + if (tref_isk(tr)) lua_assert(fn == ir_kfunc(ir)); + lua_assert(s > delta + LJ_FR2 ? (J->slot[s-delta] & TREF_FRAME) + : (s == delta + LJ_FR2)); + depth++; + } else if ((tr & TREF_CONT)) { +#if LJ_FR2 + if (ref) + lua_assert(ir_knum(ir)->u64 == tv->u64); +#else + lua_assert(ir_kptr(ir) == gcrefp(tv->gcr, void)); +#endif + lua_assert((J->slot[s+1+LJ_FR2] & TREF_FRAME)); + depth++; + } else { + if (tvisnumber(tv)) + lua_assert(tref_isnumber(tr)); /* Could be IRT_INT etc., too. */ + else + lua_assert(itype2irt(tv) == tref_type(tr)); + if (tref_isk(tr)) { /* Compare constants. */ + TValue tvk; + lj_ir_kvalue(J->L, &tvk, ir); + if (!(tvisnum(&tvk) && tvisnan(&tvk))) + lua_assert(lj_obj_equal(tv, &tvk)); + else + lua_assert(tvisnum(tv) && tvisnan(tv)); + } + } + } + } + lua_assert(J->framedepth == depth); +} +#endif + +/* -- Type handling and specialization ------------------------------------ */ + +/* Note: these functions return tagged references (TRef). */ + +/* Specialize a slot to a specific type. Note: slot can be negative! */ +static TRef sloadt(jit_State *J, int32_t slot, IRType t, int mode) +{ + /* Caller may set IRT_GUARD in t. */ + TRef ref = emitir_raw(IRT(IR_SLOAD, t), (int32_t)J->baseslot+slot, mode); + J->base[slot] = ref; + return ref; +} + +/* Specialize a slot to the runtime type. Note: slot can be negative! */ +static TRef sload(jit_State *J, int32_t slot) +{ + IRType t = itype2irt(&J->L->base[slot]); + TRef ref = emitir_raw(IRTG(IR_SLOAD, t), (int32_t)J->baseslot+slot, + IRSLOAD_TYPECHECK); + if (irtype_ispri(t)) ref = TREF_PRI(t); /* Canonicalize primitive refs. */ + J->base[slot] = ref; + return ref; +} + +/* Get TRef from slot. Load slot and specialize if not done already. */ +#define getslot(J, s) (J->base[(s)] ? J->base[(s)] : sload(J, (int32_t)(s))) + +/* Get TRef for current function. */ +static TRef getcurrf(jit_State *J) +{ + if (J->base[-1-LJ_FR2]) + return J->base[-1-LJ_FR2]; + lua_assert(J->baseslot == 1+LJ_FR2); + return sloadt(J, -1-LJ_FR2, IRT_FUNC, IRSLOAD_READONLY); +} + +/* Compare for raw object equality. +** Returns 0 if the objects are the same. +** Returns 1 if they are different, but the same type. +** Returns 2 for two different types. +** Comparisons between primitives always return 1 -- no caller cares about it. +*/ +int lj_record_objcmp(jit_State *J, TRef a, TRef b, cTValue *av, cTValue *bv) +{ + int diff = !lj_obj_equal(av, bv); + if (!tref_isk2(a, b)) { /* Shortcut, also handles primitives. */ + IRType ta = tref_isinteger(a) ? IRT_INT : tref_type(a); + IRType tb = tref_isinteger(b) ? IRT_INT : tref_type(b); + if (ta != tb) { + /* Widen mixed number/int comparisons to number/number comparison. */ + if (ta == IRT_INT && tb == IRT_NUM) { + a = emitir(IRTN(IR_CONV), a, IRCONV_NUM_INT); + ta = IRT_NUM; + } else if (ta == IRT_NUM && tb == IRT_INT) { + b = emitir(IRTN(IR_CONV), b, IRCONV_NUM_INT); + } else { + return 2; /* Two different types are never equal. */ + } + } + emitir(IRTG(diff ? IR_NE : IR_EQ, ta), a, b); + } + return diff; +} + +/* Constify a value. Returns 0 for non-representable object types. */ +TRef lj_record_constify(jit_State *J, cTValue *o) +{ + if (tvisgcv(o)) + return lj_ir_kgc(J, gcV(o), itype2irt(o)); + else if (tvisint(o)) + return lj_ir_kint(J, intV(o)); + else if (tvisnum(o)) + return lj_ir_knumint(J, numV(o)); + else if (tvisbool(o)) + return TREF_PRI(itype2irt(o)); + else + return 0; /* Can't represent lightuserdata (pointless). */ +} + +/* -- Record loop ops ----------------------------------------------------- */ + +/* Loop event. */ +typedef enum { + LOOPEV_LEAVE, /* Loop is left or not entered. */ + LOOPEV_ENTERLO, /* Loop is entered with a low iteration count left. */ + LOOPEV_ENTER /* Loop is entered. */ +} LoopEvent; + +/* Canonicalize slots: convert integers to numbers. */ +static void canonicalize_slots(jit_State *J) +{ + BCReg s; + if (LJ_DUALNUM) return; + for (s = J->baseslot+J->maxslot-1; s >= 1; s--) { + TRef tr = J->slot[s]; + if (tref_isinteger(tr)) { + IRIns *ir = IR(tref_ref(tr)); + if (!(ir->o == IR_SLOAD && (ir->op2 & IRSLOAD_READONLY))) + J->slot[s] = emitir(IRTN(IR_CONV), tr, IRCONV_NUM_INT); + } + } +} + +/* Stop recording. */ +void lj_record_stop(jit_State *J, TraceLink linktype, TraceNo lnk) +{ +#ifdef LUAJIT_ENABLE_TABLE_BUMP + if (J->retryrec) + lj_trace_err(J, LJ_TRERR_RETRY); +#endif + lj_trace_end(J); + J->cur.linktype = (uint8_t)linktype; + J->cur.link = (uint16_t)lnk; + /* Looping back at the same stack level? */ + if (lnk == J->cur.traceno && J->framedepth + J->retdepth == 0) { + if ((J->flags & JIT_F_OPT_LOOP)) /* Shall we try to create a loop? */ + goto nocanon; /* Do not canonicalize or we lose the narrowing. */ + if (J->cur.root) /* Otherwise ensure we always link to the root trace. */ + J->cur.link = J->cur.root; + } + canonicalize_slots(J); +nocanon: + /* Note: all loop ops must set J->pc to the following instruction! */ + lj_snap_add(J); /* Add loop snapshot. */ + J->needsnap = 0; + J->mergesnap = 1; /* In case recording continues. */ +} + +/* Search bytecode backwards for a int/num constant slot initializer. */ +static TRef find_kinit(jit_State *J, const BCIns *endpc, BCReg slot, IRType t) +{ + /* This algorithm is rather simplistic and assumes quite a bit about + ** how the bytecode is generated. It works fine for FORI initializers, + ** but it won't necessarily work in other cases (e.g. iterator arguments). + ** It doesn't do anything fancy, either (like backpropagating MOVs). + */ + const BCIns *pc, *startpc = proto_bc(J->pt); + for (pc = endpc-1; pc > startpc; pc--) { + BCIns ins = *pc; + BCOp op = bc_op(ins); + /* First try to find the last instruction that stores to this slot. */ + if (bcmode_a(op) == BCMbase && bc_a(ins) <= slot) { + return 0; /* Multiple results, e.g. from a CALL or KNIL. */ + } else if (bcmode_a(op) == BCMdst && bc_a(ins) == slot) { + if (op == BC_KSHORT || op == BC_KNUM) { /* Found const. initializer. */ + /* Now try to verify there's no forward jump across it. */ + const BCIns *kpc = pc; + for (; pc > startpc; pc--) + if (bc_op(*pc) == BC_JMP) { + const BCIns *target = pc+bc_j(*pc)+1; + if (target > kpc && target <= endpc) + return 0; /* Conditional assignment. */ + } + if (op == BC_KSHORT) { + int32_t k = (int32_t)(int16_t)bc_d(ins); + return t == IRT_INT ? lj_ir_kint(J, k) : lj_ir_knum(J, (lua_Number)k); + } else { + cTValue *tv = proto_knumtv(J->pt, bc_d(ins)); + if (t == IRT_INT) { + int32_t k = numberVint(tv); + if (tvisint(tv) || numV(tv) == (lua_Number)k) /* -0 is ok here. */ + return lj_ir_kint(J, k); + return 0; /* Type mismatch. */ + } else { + return lj_ir_knum(J, numberVnum(tv)); + } + } + } + return 0; /* Non-constant initializer. */ + } + } + return 0; /* No assignment to this slot found? */ +} + +/* Load and optionally convert a FORI argument from a slot. */ +static TRef fori_load(jit_State *J, BCReg slot, IRType t, int mode) +{ + int conv = (tvisint(&J->L->base[slot]) != (t==IRT_INT)) ? IRSLOAD_CONVERT : 0; + return sloadt(J, (int32_t)slot, + t + (((mode & IRSLOAD_TYPECHECK) || + (conv && t == IRT_INT && !(mode >> 16))) ? + IRT_GUARD : 0), + mode + conv); +} + +/* Peek before FORI to find a const initializer. Otherwise load from slot. */ +static TRef fori_arg(jit_State *J, const BCIns *fori, BCReg slot, + IRType t, int mode) +{ + TRef tr = J->base[slot]; + if (!tr) { + tr = find_kinit(J, fori, slot, t); + if (!tr) + tr = fori_load(J, slot, t, mode); + } + return tr; +} + +/* Return the direction of the FOR loop iterator. +** It's important to exactly reproduce the semantics of the interpreter. +*/ +static int rec_for_direction(cTValue *o) +{ + return (tvisint(o) ? intV(o) : (int32_t)o->u32.hi) >= 0; +} + +/* Simulate the runtime behavior of the FOR loop iterator. */ +static LoopEvent rec_for_iter(IROp *op, cTValue *o, int isforl) +{ + lua_Number stopv = numberVnum(&o[FORL_STOP]); + lua_Number idxv = numberVnum(&o[FORL_IDX]); + lua_Number stepv = numberVnum(&o[FORL_STEP]); + if (isforl) + idxv += stepv; + if (rec_for_direction(&o[FORL_STEP])) { + if (idxv <= stopv) { + *op = IR_LE; + return idxv + 2*stepv > stopv ? LOOPEV_ENTERLO : LOOPEV_ENTER; + } + *op = IR_GT; return LOOPEV_LEAVE; + } else { + if (stopv <= idxv) { + *op = IR_GE; + return idxv + 2*stepv < stopv ? LOOPEV_ENTERLO : LOOPEV_ENTER; + } + *op = IR_LT; return LOOPEV_LEAVE; + } +} + +/* Record checks for FOR loop overflow and step direction. */ +static void rec_for_check(jit_State *J, IRType t, int dir, + TRef stop, TRef step, int init) +{ + if (!tref_isk(step)) { + /* Non-constant step: need a guard for the direction. */ + TRef zero = (t == IRT_INT) ? lj_ir_kint(J, 0) : lj_ir_knum_zero(J); + emitir(IRTG(dir ? IR_GE : IR_LT, t), step, zero); + /* Add hoistable overflow checks for a narrowed FORL index. */ + if (init && t == IRT_INT) { + if (tref_isk(stop)) { + /* Constant stop: optimize check away or to a range check for step. */ + int32_t k = IR(tref_ref(stop))->i; + if (dir) { + if (k > 0) + emitir(IRTGI(IR_LE), step, lj_ir_kint(J, (int32_t)0x7fffffff-k)); + } else { + if (k < 0) + emitir(IRTGI(IR_GE), step, lj_ir_kint(J, (int32_t)0x80000000-k)); + } + } else { + /* Stop+step variable: need full overflow check. */ + TRef tr = emitir(IRTGI(IR_ADDOV), step, stop); + emitir(IRTI(IR_USE), tr, 0); /* ADDOV is weak. Avoid dead result. */ + } + } + } else if (init && t == IRT_INT && !tref_isk(stop)) { + /* Constant step: optimize overflow check to a range check for stop. */ + int32_t k = IR(tref_ref(step))->i; + k = (int32_t)(dir ? 0x7fffffff : 0x80000000) - k; + emitir(IRTGI(dir ? IR_LE : IR_GE), stop, lj_ir_kint(J, k)); + } +} + +/* Record a FORL instruction. */ +static void rec_for_loop(jit_State *J, const BCIns *fori, ScEvEntry *scev, + int init) +{ + BCReg ra = bc_a(*fori); + cTValue *tv = &J->L->base[ra]; + TRef idx = J->base[ra+FORL_IDX]; + IRType t = idx ? tref_type(idx) : + (init || LJ_DUALNUM) ? lj_opt_narrow_forl(J, tv) : IRT_NUM; + int mode = IRSLOAD_INHERIT + + ((!LJ_DUALNUM || tvisint(tv) == (t == IRT_INT)) ? IRSLOAD_READONLY : 0); + TRef stop = fori_arg(J, fori, ra+FORL_STOP, t, mode); + TRef step = fori_arg(J, fori, ra+FORL_STEP, t, mode); + int tc, dir = rec_for_direction(&tv[FORL_STEP]); + lua_assert(bc_op(*fori) == BC_FORI || bc_op(*fori) == BC_JFORI); + scev->t.irt = t; + scev->dir = dir; + scev->stop = tref_ref(stop); + scev->step = tref_ref(step); + rec_for_check(J, t, dir, stop, step, init); + scev->start = tref_ref(find_kinit(J, fori, ra+FORL_IDX, IRT_INT)); + tc = (LJ_DUALNUM && + !(scev->start && irref_isk(scev->stop) && irref_isk(scev->step) && + tvisint(&tv[FORL_IDX]) == (t == IRT_INT))) ? + IRSLOAD_TYPECHECK : 0; + if (tc) { + J->base[ra+FORL_STOP] = stop; + J->base[ra+FORL_STEP] = step; + } + if (!idx) + idx = fori_load(J, ra+FORL_IDX, t, + IRSLOAD_INHERIT + tc + (J->scev.start << 16)); + if (!init) + J->base[ra+FORL_IDX] = idx = emitir(IRT(IR_ADD, t), idx, step); + J->base[ra+FORL_EXT] = idx; + scev->idx = tref_ref(idx); + setmref(scev->pc, fori); + J->maxslot = ra+FORL_EXT+1; +} + +/* Record FORL/JFORL or FORI/JFORI. */ +static LoopEvent rec_for(jit_State *J, const BCIns *fori, int isforl) +{ + BCReg ra = bc_a(*fori); + TValue *tv = &J->L->base[ra]; + TRef *tr = &J->base[ra]; + IROp op; + LoopEvent ev; + TRef stop; + IRType t; + if (isforl) { /* Handle FORL/JFORL opcodes. */ + TRef idx = tr[FORL_IDX]; + if (mref(J->scev.pc, const BCIns) == fori && tref_ref(idx) == J->scev.idx) { + t = J->scev.t.irt; + stop = J->scev.stop; + idx = emitir(IRT(IR_ADD, t), idx, J->scev.step); + tr[FORL_EXT] = tr[FORL_IDX] = idx; + } else { + ScEvEntry scev; + rec_for_loop(J, fori, &scev, 0); + t = scev.t.irt; + stop = scev.stop; + } + } else { /* Handle FORI/JFORI opcodes. */ + BCReg i; + lj_meta_for(J->L, tv); + t = (LJ_DUALNUM || tref_isint(tr[FORL_IDX])) ? lj_opt_narrow_forl(J, tv) : + IRT_NUM; + for (i = FORL_IDX; i <= FORL_STEP; i++) { + if (!tr[i]) sload(J, ra+i); + lua_assert(tref_isnumber_str(tr[i])); + if (tref_isstr(tr[i])) + tr[i] = emitir(IRTG(IR_STRTO, IRT_NUM), tr[i], 0); + if (t == IRT_INT) { + if (!tref_isinteger(tr[i])) + tr[i] = emitir(IRTGI(IR_CONV), tr[i], IRCONV_INT_NUM|IRCONV_CHECK); + } else { + if (!tref_isnum(tr[i])) + tr[i] = emitir(IRTN(IR_CONV), tr[i], IRCONV_NUM_INT); + } + } + tr[FORL_EXT] = tr[FORL_IDX]; + stop = tr[FORL_STOP]; + rec_for_check(J, t, rec_for_direction(&tv[FORL_STEP]), + stop, tr[FORL_STEP], 1); + } + + ev = rec_for_iter(&op, tv, isforl); + if (ev == LOOPEV_LEAVE) { + J->maxslot = ra+FORL_EXT+1; + J->pc = fori+1; + } else { + J->maxslot = ra; + J->pc = fori+bc_j(*fori)+1; + } + lj_snap_add(J); + + emitir(IRTG(op, t), tr[FORL_IDX], stop); + + if (ev == LOOPEV_LEAVE) { + J->maxslot = ra; + J->pc = fori+bc_j(*fori)+1; + } else { + J->maxslot = ra+FORL_EXT+1; + J->pc = fori+1; + } + J->needsnap = 1; + return ev; +} + +/* Record ITERL/JITERL. */ +static LoopEvent rec_iterl(jit_State *J, const BCIns iterins) +{ + BCReg ra = bc_a(iterins); + if (!tref_isnil(getslot(J, ra))) { /* Looping back? */ + J->base[ra-1] = J->base[ra]; /* Copy result of ITERC to control var. */ + J->maxslot = ra-1+bc_b(J->pc[-1]); + J->pc += bc_j(iterins)+1; + return LOOPEV_ENTER; + } else { + J->maxslot = ra-3; + J->pc++; + return LOOPEV_LEAVE; + } +} + +/* Record LOOP/JLOOP. Now, that was easy. */ +static LoopEvent rec_loop(jit_State *J, BCReg ra) +{ + if (ra < J->maxslot) J->maxslot = ra; + J->pc++; + return LOOPEV_ENTER; +} + +/* Check if a loop repeatedly failed to trace because it didn't loop back. */ +static int innerloopleft(jit_State *J, const BCIns *pc) +{ + ptrdiff_t i; + for (i = 0; i < PENALTY_SLOTS; i++) + if (mref(J->penalty[i].pc, const BCIns) == pc) { + if ((J->penalty[i].reason == LJ_TRERR_LLEAVE || + J->penalty[i].reason == LJ_TRERR_LINNER) && + J->penalty[i].val >= 2*PENALTY_MIN) + return 1; + break; + } + return 0; +} + +/* Handle the case when an interpreted loop op is hit. */ +static void rec_loop_interp(jit_State *J, const BCIns *pc, LoopEvent ev) +{ + if (J->parent == 0 && J->exitno == 0) { + if (pc == J->startpc && J->framedepth + J->retdepth == 0) { + /* Same loop? */ + if (ev == LOOPEV_LEAVE) /* Must loop back to form a root trace. */ + lj_trace_err(J, LJ_TRERR_LLEAVE); + lj_record_stop(J, LJ_TRLINK_LOOP, J->cur.traceno); /* Looping trace. */ + } else if (ev != LOOPEV_LEAVE) { /* Entering inner loop? */ + /* It's usually better to abort here and wait until the inner loop + ** is traced. But if the inner loop repeatedly didn't loop back, + ** this indicates a low trip count. In this case try unrolling + ** an inner loop even in a root trace. But it's better to be a bit + ** more conservative here and only do it for very short loops. + */ + if (bc_j(*pc) != -1 && !innerloopleft(J, pc)) + lj_trace_err(J, LJ_TRERR_LINNER); /* Root trace hit an inner loop. */ + if ((ev != LOOPEV_ENTERLO && + J->loopref && J->cur.nins - J->loopref > 24) || --J->loopunroll < 0) + lj_trace_err(J, LJ_TRERR_LUNROLL); /* Limit loop unrolling. */ + J->loopref = J->cur.nins; + } + } else if (ev != LOOPEV_LEAVE) { /* Side trace enters an inner loop. */ + J->loopref = J->cur.nins; + if (--J->loopunroll < 0) + lj_trace_err(J, LJ_TRERR_LUNROLL); /* Limit loop unrolling. */ + } /* Side trace continues across a loop that's left or not entered. */ +} + +/* Handle the case when an already compiled loop op is hit. */ +static void rec_loop_jit(jit_State *J, TraceNo lnk, LoopEvent ev) +{ + if (J->parent == 0 && J->exitno == 0) { /* Root trace hit an inner loop. */ + /* Better let the inner loop spawn a side trace back here. */ + lj_trace_err(J, LJ_TRERR_LINNER); + } else if (ev != LOOPEV_LEAVE) { /* Side trace enters a compiled loop. */ + J->instunroll = 0; /* Cannot continue across a compiled loop op. */ + if (J->pc == J->startpc && J->framedepth + J->retdepth == 0) + lj_record_stop(J, LJ_TRLINK_LOOP, J->cur.traceno); /* Form extra loop. */ + else + lj_record_stop(J, LJ_TRLINK_ROOT, lnk); /* Link to the loop. */ + } /* Side trace continues across a loop that's left or not entered. */ +} + +/* -- Record profiler hook checks ----------------------------------------- */ + +#if LJ_HASPROFILE + +/* Need to insert profiler hook check? */ +static int rec_profile_need(jit_State *J, GCproto *pt, const BCIns *pc) +{ + GCproto *ppt; + lua_assert(J->prof_mode == 'f' || J->prof_mode == 'l'); + if (!pt) + return 0; + ppt = J->prev_pt; + J->prev_pt = pt; + if (pt != ppt && ppt) { + J->prev_line = -1; + return 1; + } + if (J->prof_mode == 'l') { + BCLine line = lj_debug_line(pt, proto_bcpos(pt, pc)); + BCLine pline = J->prev_line; + J->prev_line = line; + if (pline != line) + return 1; + } + return 0; +} + +static void rec_profile_ins(jit_State *J, const BCIns *pc) +{ + if (J->prof_mode && rec_profile_need(J, J->pt, pc)) { + emitir(IRTG(IR_PROF, IRT_NIL), 0, 0); + lj_snap_add(J); + } +} + +static void rec_profile_ret(jit_State *J) +{ + if (J->prof_mode == 'f') { + emitir(IRTG(IR_PROF, IRT_NIL), 0, 0); + J->prev_pt = NULL; + lj_snap_add(J); + } +} + +#endif + +/* -- Record calls and returns -------------------------------------------- */ + +/* Specialize to the runtime value of the called function or its prototype. */ +static TRef rec_call_specialize(jit_State *J, GCfunc *fn, TRef tr) +{ + TRef kfunc; + if (isluafunc(fn)) { + GCproto *pt = funcproto(fn); + /* Too many closures created? Probably not a monomorphic function. */ + if (pt->flags >= PROTO_CLC_POLY) { /* Specialize to prototype instead. */ + TRef trpt = emitir(IRT(IR_FLOAD, IRT_PGC), tr, IRFL_FUNC_PC); + emitir(IRTG(IR_EQ, IRT_PGC), trpt, lj_ir_kptr(J, proto_bc(pt))); + (void)lj_ir_kgc(J, obj2gco(pt), IRT_PROTO); /* Prevent GC of proto. */ + return tr; + } + } else { + /* Don't specialize to non-monomorphic builtins. */ + switch (fn->c.ffid) { + case FF_coroutine_wrap_aux: + case FF_string_gmatch_aux: + /* NYI: io_file_iter doesn't have an ffid, yet. */ + { /* Specialize to the ffid. */ + TRef trid = emitir(IRT(IR_FLOAD, IRT_U8), tr, IRFL_FUNC_FFID); + emitir(IRTG(IR_EQ, IRT_INT), trid, lj_ir_kint(J, fn->c.ffid)); + } + return tr; + default: + /* NYI: don't specialize to non-monomorphic C functions. */ + break; + } + } + /* Otherwise specialize to the function (closure) value itself. */ + kfunc = lj_ir_kfunc(J, fn); + emitir(IRTG(IR_EQ, IRT_FUNC), tr, kfunc); + return kfunc; +} + +/* Record call setup. */ +static void rec_call_setup(jit_State *J, BCReg func, ptrdiff_t nargs) +{ + RecordIndex ix; + TValue *functv = &J->L->base[func]; + TRef kfunc, *fbase = &J->base[func]; + ptrdiff_t i; + (void)getslot(J, func); /* Ensure func has a reference. */ + for (i = 1; i <= nargs; i++) + (void)getslot(J, func+LJ_FR2+i); /* Ensure all args have a reference. */ + if (!tref_isfunc(fbase[0])) { /* Resolve __call metamethod. */ + ix.tab = fbase[0]; + copyTV(J->L, &ix.tabv, functv); + if (!lj_record_mm_lookup(J, &ix, MM_call) || !tref_isfunc(ix.mobj)) + lj_trace_err(J, LJ_TRERR_NOMM); + for (i = ++nargs; i > LJ_FR2; i--) /* Shift arguments up. */ + fbase[i+LJ_FR2] = fbase[i+LJ_FR2-1]; +#if LJ_FR2 + fbase[2] = fbase[0]; +#endif + fbase[0] = ix.mobj; /* Replace function. */ + functv = &ix.mobjv; + } + kfunc = rec_call_specialize(J, funcV(functv), fbase[0]); +#if LJ_FR2 + fbase[0] = kfunc; + fbase[1] = TREF_FRAME; +#else + fbase[0] = kfunc | TREF_FRAME; +#endif + J->maxslot = (BCReg)nargs; +} + +/* Record call. */ +void lj_record_call(jit_State *J, BCReg func, ptrdiff_t nargs) +{ + rec_call_setup(J, func, nargs); + /* Bump frame. */ + J->framedepth++; + J->base += func+1+LJ_FR2; + J->baseslot += func+1+LJ_FR2; + if (J->baseslot + J->maxslot >= LJ_MAX_JSLOTS) + lj_trace_err(J, LJ_TRERR_STACKOV); +} + +/* Record tail call. */ +void lj_record_tailcall(jit_State *J, BCReg func, ptrdiff_t nargs) +{ + rec_call_setup(J, func, nargs); + if (frame_isvarg(J->L->base - 1)) { + BCReg cbase = (BCReg)frame_delta(J->L->base - 1); + if (--J->framedepth < 0) + lj_trace_err(J, LJ_TRERR_NYIRETL); + J->baseslot -= (BCReg)cbase; + J->base -= cbase; + func += cbase; + } + /* Move func + args down. */ + if (LJ_FR2 && J->baseslot == 2) + J->base[func+1] = TREF_FRAME; + memmove(&J->base[-1-LJ_FR2], &J->base[func], sizeof(TRef)*(J->maxslot+1+LJ_FR2)); + /* Note: the new TREF_FRAME is now at J->base[-1] (even for slot #0). */ + /* Tailcalls can form a loop, so count towards the loop unroll limit. */ + if (++J->tailcalled > J->loopunroll) + lj_trace_err(J, LJ_TRERR_LUNROLL); +} + +/* Check unroll limits for down-recursion. */ +static int check_downrec_unroll(jit_State *J, GCproto *pt) +{ + IRRef ptref; + for (ptref = J->chain[IR_KGC]; ptref; ptref = IR(ptref)->prev) + if (ir_kgc(IR(ptref)) == obj2gco(pt)) { + int count = 0; + IRRef ref; + for (ref = J->chain[IR_RETF]; ref; ref = IR(ref)->prev) + if (IR(ref)->op1 == ptref) + count++; + if (count) { + if (J->pc == J->startpc) { + if (count + J->tailcalled > J->param[JIT_P_recunroll]) + return 1; + } else { + lj_trace_err(J, LJ_TRERR_DOWNREC); + } + } + } + return 0; +} + +static TRef rec_cat(jit_State *J, BCReg baseslot, BCReg topslot); + +/* Record return. */ +void lj_record_ret(jit_State *J, BCReg rbase, ptrdiff_t gotresults) +{ + TValue *frame = J->L->base - 1; + ptrdiff_t i; + for (i = 0; i < gotresults; i++) + (void)getslot(J, rbase+i); /* Ensure all results have a reference. */ + while (frame_ispcall(frame)) { /* Immediately resolve pcall() returns. */ + BCReg cbase = (BCReg)frame_delta(frame); + if (--J->framedepth <= 0) + lj_trace_err(J, LJ_TRERR_NYIRETL); + lua_assert(J->baseslot > 1+LJ_FR2); + gotresults++; + rbase += cbase; + J->baseslot -= (BCReg)cbase; + J->base -= cbase; + J->base[--rbase] = TREF_TRUE; /* Prepend true to results. */ + frame = frame_prevd(frame); + } + /* Return to lower frame via interpreter for unhandled cases. */ + if (J->framedepth == 0 && J->pt && bc_isret(bc_op(*J->pc)) && + (!frame_islua(frame) || + (J->parent == 0 && J->exitno == 0 && + !bc_isret(bc_op(J->cur.startins))))) { + /* NYI: specialize to frame type and return directly, not via RET*. */ + for (i = 0; i < (ptrdiff_t)rbase; i++) + J->base[i] = 0; /* Purge dead slots. */ + J->maxslot = rbase + (BCReg)gotresults; + lj_record_stop(J, LJ_TRLINK_RETURN, 0); /* Return to interpreter. */ + return; + } + if (frame_isvarg(frame)) { + BCReg cbase = (BCReg)frame_delta(frame); + if (--J->framedepth < 0) /* NYI: return of vararg func to lower frame. */ + lj_trace_err(J, LJ_TRERR_NYIRETL); + lua_assert(J->baseslot > 1+LJ_FR2); + rbase += cbase; + J->baseslot -= (BCReg)cbase; + J->base -= cbase; + frame = frame_prevd(frame); + } + if (frame_islua(frame)) { /* Return to Lua frame. */ + BCIns callins = *(frame_pc(frame)-1); + ptrdiff_t nresults = bc_b(callins) ? (ptrdiff_t)bc_b(callins)-1 :gotresults; + BCReg cbase = bc_a(callins); + GCproto *pt = funcproto(frame_func(frame - (cbase+1+LJ_FR2))); + if ((pt->flags & PROTO_NOJIT)) + lj_trace_err(J, LJ_TRERR_CJITOFF); + if (J->framedepth == 0 && J->pt && frame == J->L->base - 1) { + if (check_downrec_unroll(J, pt)) { + J->maxslot = (BCReg)(rbase + gotresults); + lj_snap_purge(J); + lj_record_stop(J, LJ_TRLINK_DOWNREC, J->cur.traceno); /* Down-rec. */ + return; + } + lj_snap_add(J); + } + for (i = 0; i < nresults; i++) /* Adjust results. */ + J->base[i-1-LJ_FR2] = i < gotresults ? J->base[rbase+i] : TREF_NIL; + J->maxslot = cbase+(BCReg)nresults; + if (J->framedepth > 0) { /* Return to a frame that is part of the trace. */ + J->framedepth--; + lua_assert(J->baseslot > cbase+1+LJ_FR2); + J->baseslot -= cbase+1+LJ_FR2; + J->base -= cbase+1+LJ_FR2; + } else if (J->parent == 0 && J->exitno == 0 && + !bc_isret(bc_op(J->cur.startins))) { + /* Return to lower frame would leave the loop in a root trace. */ + lj_trace_err(J, LJ_TRERR_LLEAVE); + } else if (J->needsnap) { /* Tailcalled to ff with side-effects. */ + lj_trace_err(J, LJ_TRERR_NYIRETL); /* No way to insert snapshot here. */ + } else { /* Return to lower frame. Guard for the target we return to. */ + TRef trpt = lj_ir_kgc(J, obj2gco(pt), IRT_PROTO); + TRef trpc = lj_ir_kptr(J, (void *)frame_pc(frame)); + emitir(IRTG(IR_RETF, IRT_PGC), trpt, trpc); + J->retdepth++; + J->needsnap = 1; + lua_assert(J->baseslot == 1+LJ_FR2); + /* Shift result slots up and clear the slots of the new frame below. */ + memmove(J->base + cbase, J->base-1-LJ_FR2, sizeof(TRef)*nresults); + memset(J->base-1-LJ_FR2, 0, sizeof(TRef)*(cbase+1+LJ_FR2)); + } + } else if (frame_iscont(frame)) { /* Return to continuation frame. */ + ASMFunction cont = frame_contf(frame); + BCReg cbase = (BCReg)frame_delta(frame); + if ((J->framedepth -= 2) < 0) + lj_trace_err(J, LJ_TRERR_NYIRETL); + J->baseslot -= (BCReg)cbase; + J->base -= cbase; + J->maxslot = cbase-(2<base[dst] = gotresults ? J->base[cbase+rbase] : TREF_NIL; + if (dst >= J->maxslot) { + J->maxslot = dst+1; + } + } else if (cont == lj_cont_nop) { + /* Nothing to do here. */ + } else if (cont == lj_cont_cat) { + BCReg bslot = bc_b(*(frame_contpc(frame)-1)); + TRef tr = gotresults ? J->base[cbase+rbase] : TREF_NIL; + if (bslot != J->maxslot) { /* Concatenate the remainder. */ + TValue *b = J->L->base, save; /* Simulate lower frame and result. */ + J->base[J->maxslot] = tr; + copyTV(J->L, &save, b-(2<L, b-(2<L->base = b - cbase; + tr = rec_cat(J, bslot, cbase-(2<L->base + cbase; /* Undo. */ + J->L->base = b; + copyTV(J->L, b-(2<base[dst] = tr; + if (dst >= J->maxslot) { + J->maxslot = dst+1; + } + } /* Otherwise continue with another __concat call. */ + } else { + /* Result type already specialized. */ + lua_assert(cont == lj_cont_condf || cont == lj_cont_condt); + } + } else { + lj_trace_err(J, LJ_TRERR_NYIRETL); /* NYI: handle return to C frame. */ + } + lua_assert(J->baseslot >= 1+LJ_FR2); +} + +/* -- Metamethod handling ------------------------------------------------- */ + +/* Prepare to record call to metamethod. */ +static BCReg rec_mm_prep(jit_State *J, ASMFunction cont) +{ + BCReg s, top = cont == lj_cont_cat ? J->maxslot : curr_proto(J->L)->framesize; +#if LJ_FR2 + J->base[top] = lj_ir_k64(J, IR_KNUM, u64ptr(contptr(cont))); + J->base[top+1] = TREF_CONT; +#else + J->base[top] = lj_ir_kptr(J, contptr(cont)) | TREF_CONT; +#endif + J->framedepth++; + for (s = J->maxslot; s < top; s++) + J->base[s] = 0; /* Clear frame gap to avoid resurrecting previous refs. */ + return top+1+LJ_FR2; +} + +/* Record metamethod lookup. */ +int lj_record_mm_lookup(jit_State *J, RecordIndex *ix, MMS mm) +{ + RecordIndex mix; + GCtab *mt; + if (tref_istab(ix->tab)) { + mt = tabref(tabV(&ix->tabv)->metatable); + mix.tab = emitir(IRT(IR_FLOAD, IRT_TAB), ix->tab, IRFL_TAB_META); + } else if (tref_isudata(ix->tab)) { + int udtype = udataV(&ix->tabv)->udtype; + mt = tabref(udataV(&ix->tabv)->metatable); + /* The metatables of special userdata objects are treated as immutable. */ + if (udtype != UDTYPE_USERDATA) { + cTValue *mo; + if (LJ_HASFFI && udtype == UDTYPE_FFI_CLIB) { + /* Specialize to the C library namespace object. */ + emitir(IRTG(IR_EQ, IRT_PGC), ix->tab, lj_ir_kptr(J, udataV(&ix->tabv))); + } else { + /* Specialize to the type of userdata. */ + TRef tr = emitir(IRT(IR_FLOAD, IRT_U8), ix->tab, IRFL_UDATA_UDTYPE); + emitir(IRTGI(IR_EQ), tr, lj_ir_kint(J, udtype)); + } + immutable_mt: + mo = lj_tab_getstr(mt, mmname_str(J2G(J), mm)); + if (!mo || tvisnil(mo)) + return 0; /* No metamethod. */ + /* Treat metamethod or index table as immutable, too. */ + if (!(tvisfunc(mo) || tvistab(mo))) + lj_trace_err(J, LJ_TRERR_BADTYPE); + copyTV(J->L, &ix->mobjv, mo); + ix->mobj = lj_ir_kgc(J, gcV(mo), tvisfunc(mo) ? IRT_FUNC : IRT_TAB); + ix->mtv = mt; + ix->mt = TREF_NIL; /* Dummy value for comparison semantics. */ + return 1; /* Got metamethod or index table. */ + } + mix.tab = emitir(IRT(IR_FLOAD, IRT_TAB), ix->tab, IRFL_UDATA_META); + } else { + /* Specialize to base metatable. Must flush mcode in lua_setmetatable(). */ + mt = tabref(basemt_obj(J2G(J), &ix->tabv)); + if (mt == NULL) { + ix->mt = TREF_NIL; + return 0; /* No metamethod. */ + } + /* The cdata metatable is treated as immutable. */ + if (LJ_HASFFI && tref_iscdata(ix->tab)) goto immutable_mt; +#if LJ_GC64 + /* TODO: fix ARM32 asm_fload(), so we can use this for all archs. */ + ix->mt = mix.tab = lj_ir_ggfload(J, IRT_TAB, + GG_OFS(g.gcroot[GCROOT_BASEMT+itypemap(&ix->tabv)])); +#else + ix->mt = mix.tab = lj_ir_ktab(J, mt); +#endif + goto nocheck; + } + ix->mt = mt ? mix.tab : TREF_NIL; + emitir(IRTG(mt ? IR_NE : IR_EQ, IRT_TAB), mix.tab, lj_ir_knull(J, IRT_TAB)); +nocheck: + if (mt) { + GCstr *mmstr = mmname_str(J2G(J), mm); + cTValue *mo = lj_tab_getstr(mt, mmstr); + if (mo && !tvisnil(mo)) + copyTV(J->L, &ix->mobjv, mo); + ix->mtv = mt; + settabV(J->L, &mix.tabv, mt); + setstrV(J->L, &mix.keyv, mmstr); + mix.key = lj_ir_kstr(J, mmstr); + mix.val = 0; + mix.idxchain = 0; + ix->mobj = lj_record_idx(J, &mix); + return !tref_isnil(ix->mobj); /* 1 if metamethod found, 0 if not. */ + } + return 0; /* No metamethod. */ +} + +/* Record call to arithmetic metamethod. */ +static TRef rec_mm_arith(jit_State *J, RecordIndex *ix, MMS mm) +{ + /* Set up metamethod call first to save ix->tab and ix->tabv. */ + BCReg func = rec_mm_prep(J, mm == MM_concat ? lj_cont_cat : lj_cont_ra); + TRef *base = J->base + func; + TValue *basev = J->L->base + func; + base[1+LJ_FR2] = ix->tab; base[2+LJ_FR2] = ix->key; + copyTV(J->L, basev+1+LJ_FR2, &ix->tabv); + copyTV(J->L, basev+2+LJ_FR2, &ix->keyv); + if (!lj_record_mm_lookup(J, ix, mm)) { /* Lookup mm on 1st operand. */ + if (mm != MM_unm) { + ix->tab = ix->key; + copyTV(J->L, &ix->tabv, &ix->keyv); + if (lj_record_mm_lookup(J, ix, mm)) /* Lookup mm on 2nd operand. */ + goto ok; + } + lj_trace_err(J, LJ_TRERR_NOMM); + } +ok: + base[0] = ix->mobj; +#if LJ_FR2 + base[1] = 0; +#endif + copyTV(J->L, basev+0, &ix->mobjv); + lj_record_call(J, func, 2); + return 0; /* No result yet. */ +} + +/* Record call to __len metamethod. */ +static TRef rec_mm_len(jit_State *J, TRef tr, TValue *tv) +{ + RecordIndex ix; + ix.tab = tr; + copyTV(J->L, &ix.tabv, tv); + if (lj_record_mm_lookup(J, &ix, MM_len)) { + BCReg func = rec_mm_prep(J, lj_cont_ra); + TRef *base = J->base + func; + TValue *basev = J->L->base + func; + base[0] = ix.mobj; copyTV(J->L, basev+0, &ix.mobjv); + base += LJ_FR2; + basev += LJ_FR2; + base[1] = tr; copyTV(J->L, basev+1, tv); +#if LJ_52 + base[2] = tr; copyTV(J->L, basev+2, tv); +#else + base[2] = TREF_NIL; setnilV(basev+2); +#endif + lj_record_call(J, func, 2); + } else { + if (LJ_52 && tref_istab(tr)) + return lj_ir_call(J, IRCALL_lj_tab_len, tr); + lj_trace_err(J, LJ_TRERR_NOMM); + } + return 0; /* No result yet. */ +} + +/* Call a comparison metamethod. */ +static void rec_mm_callcomp(jit_State *J, RecordIndex *ix, int op) +{ + BCReg func = rec_mm_prep(J, (op&1) ? lj_cont_condf : lj_cont_condt); + TRef *base = J->base + func + LJ_FR2; + TValue *tv = J->L->base + func + LJ_FR2; + base[-LJ_FR2] = ix->mobj; base[1] = ix->val; base[2] = ix->key; + copyTV(J->L, tv-LJ_FR2, &ix->mobjv); + copyTV(J->L, tv+1, &ix->valv); + copyTV(J->L, tv+2, &ix->keyv); + lj_record_call(J, func, 2); +} + +/* Record call to equality comparison metamethod (for tab and udata only). */ +static void rec_mm_equal(jit_State *J, RecordIndex *ix, int op) +{ + ix->tab = ix->val; + copyTV(J->L, &ix->tabv, &ix->valv); + if (lj_record_mm_lookup(J, ix, MM_eq)) { /* Lookup mm on 1st operand. */ + cTValue *bv; + TRef mo1 = ix->mobj; + TValue mo1v; + copyTV(J->L, &mo1v, &ix->mobjv); + /* Avoid the 2nd lookup and the objcmp if the metatables are equal. */ + bv = &ix->keyv; + if (tvistab(bv) && tabref(tabV(bv)->metatable) == ix->mtv) { + TRef mt2 = emitir(IRT(IR_FLOAD, IRT_TAB), ix->key, IRFL_TAB_META); + emitir(IRTG(IR_EQ, IRT_TAB), mt2, ix->mt); + } else if (tvisudata(bv) && tabref(udataV(bv)->metatable) == ix->mtv) { + TRef mt2 = emitir(IRT(IR_FLOAD, IRT_TAB), ix->key, IRFL_UDATA_META); + emitir(IRTG(IR_EQ, IRT_TAB), mt2, ix->mt); + } else { /* Lookup metamethod on 2nd operand and compare both. */ + ix->tab = ix->key; + copyTV(J->L, &ix->tabv, bv); + if (!lj_record_mm_lookup(J, ix, MM_eq) || + lj_record_objcmp(J, mo1, ix->mobj, &mo1v, &ix->mobjv)) + return; + } + rec_mm_callcomp(J, ix, op); + } +} + +/* Record call to ordered comparison metamethods (for arbitrary objects). */ +static void rec_mm_comp(jit_State *J, RecordIndex *ix, int op) +{ + ix->tab = ix->val; + copyTV(J->L, &ix->tabv, &ix->valv); + while (1) { + MMS mm = (op & 2) ? MM_le : MM_lt; /* Try __le + __lt or only __lt. */ +#if LJ_52 + if (!lj_record_mm_lookup(J, ix, mm)) { /* Lookup mm on 1st operand. */ + ix->tab = ix->key; + copyTV(J->L, &ix->tabv, &ix->keyv); + if (!lj_record_mm_lookup(J, ix, mm)) /* Lookup mm on 2nd operand. */ + goto nomatch; + } + rec_mm_callcomp(J, ix, op); + return; +#else + if (lj_record_mm_lookup(J, ix, mm)) { /* Lookup mm on 1st operand. */ + cTValue *bv; + TRef mo1 = ix->mobj; + TValue mo1v; + copyTV(J->L, &mo1v, &ix->mobjv); + /* Avoid the 2nd lookup and the objcmp if the metatables are equal. */ + bv = &ix->keyv; + if (tvistab(bv) && tabref(tabV(bv)->metatable) == ix->mtv) { + TRef mt2 = emitir(IRT(IR_FLOAD, IRT_TAB), ix->key, IRFL_TAB_META); + emitir(IRTG(IR_EQ, IRT_TAB), mt2, ix->mt); + } else if (tvisudata(bv) && tabref(udataV(bv)->metatable) == ix->mtv) { + TRef mt2 = emitir(IRT(IR_FLOAD, IRT_TAB), ix->key, IRFL_UDATA_META); + emitir(IRTG(IR_EQ, IRT_TAB), mt2, ix->mt); + } else { /* Lookup metamethod on 2nd operand and compare both. */ + ix->tab = ix->key; + copyTV(J->L, &ix->tabv, bv); + if (!lj_record_mm_lookup(J, ix, mm) || + lj_record_objcmp(J, mo1, ix->mobj, &mo1v, &ix->mobjv)) + goto nomatch; + } + rec_mm_callcomp(J, ix, op); + return; + } +#endif + nomatch: + /* Lookup failed. Retry with __lt and swapped operands. */ + if (!(op & 2)) break; /* Already at __lt. Interpreter will throw. */ + ix->tab = ix->key; ix->key = ix->val; ix->val = ix->tab; + copyTV(J->L, &ix->tabv, &ix->keyv); + copyTV(J->L, &ix->keyv, &ix->valv); + copyTV(J->L, &ix->valv, &ix->tabv); + op ^= 3; + } +} + +#if LJ_HASFFI +/* Setup call to cdata comparison metamethod. */ +static void rec_mm_comp_cdata(jit_State *J, RecordIndex *ix, int op, MMS mm) +{ + lj_snap_add(J); + if (tref_iscdata(ix->val)) { + ix->tab = ix->val; + copyTV(J->L, &ix->tabv, &ix->valv); + } else { + lua_assert(tref_iscdata(ix->key)); + ix->tab = ix->key; + copyTV(J->L, &ix->tabv, &ix->keyv); + } + lj_record_mm_lookup(J, ix, mm); + rec_mm_callcomp(J, ix, op); +} +#endif + +/* -- Indexed access ------------------------------------------------------ */ + +#ifdef LUAJIT_ENABLE_TABLE_BUMP +/* Bump table allocations in bytecode when they grow during recording. */ +static void rec_idx_bump(jit_State *J, RecordIndex *ix) +{ + RBCHashEntry *rbc = &J->rbchash[(ix->tab & (RBCHASH_SLOTS-1))]; + if (tref_ref(ix->tab) == rbc->ref) { + const BCIns *pc = mref(rbc->pc, const BCIns); + GCtab *tb = tabV(&ix->tabv); + uint32_t nhbits; + IRIns *ir; + if (!tvisnil(&ix->keyv)) + (void)lj_tab_set(J->L, tb, &ix->keyv); /* Grow table right now. */ + nhbits = tb->hmask > 0 ? lj_fls(tb->hmask)+1 : 0; + ir = IR(tref_ref(ix->tab)); + if (ir->o == IR_TNEW) { + uint32_t ah = bc_d(*pc); + uint32_t asize = ah & 0x7ff, hbits = ah >> 11; + if (nhbits > hbits) hbits = nhbits; + if (tb->asize > asize) { + asize = tb->asize <= 0x7ff ? tb->asize : 0x7ff; + } + if ((asize | (hbits<<11)) != ah) { /* Has the size changed? */ + /* Patch bytecode, but continue recording (for more patching). */ + setbc_d(pc, (asize | (hbits<<11))); + /* Patching TNEW operands is only safe if the trace is aborted. */ + ir->op1 = asize; ir->op2 = hbits; + J->retryrec = 1; /* Abort the trace at the end of recording. */ + } + } else if (ir->o == IR_TDUP) { + GCtab *tpl = gco2tab(proto_kgc(&gcref(rbc->pt)->pt, ~(ptrdiff_t)bc_d(*pc))); + /* Grow template table, but preserve keys with nil values. */ + if ((tb->asize > tpl->asize && (1u << nhbits)-1 == tpl->hmask) || + (tb->asize == tpl->asize && (1u << nhbits)-1 > tpl->hmask)) { + Node *node = noderef(tpl->node); + uint32_t i, hmask = tpl->hmask, asize; + TValue *array; + for (i = 0; i <= hmask; i++) { + if (!tvisnil(&node[i].key) && tvisnil(&node[i].val)) + settabV(J->L, &node[i].val, tpl); + } + if (!tvisnil(&ix->keyv) && tref_isk(ix->key)) { + TValue *o = lj_tab_set(J->L, tpl, &ix->keyv); + if (tvisnil(o)) settabV(J->L, o, tpl); + } + lj_tab_resize(J->L, tpl, tb->asize, nhbits); + node = noderef(tpl->node); + hmask = tpl->hmask; + for (i = 0; i <= hmask; i++) { + /* This is safe, since template tables only hold immutable values. */ + if (tvistab(&node[i].val)) + setnilV(&node[i].val); + } + /* The shape of the table may have changed. Clean up array part, too. */ + asize = tpl->asize; + array = tvref(tpl->array); + for (i = 0; i < asize; i++) { + if (tvistab(&array[i])) + setnilV(&array[i]); + } + J->retryrec = 1; /* Abort the trace at the end of recording. */ + } + } + } +} +#endif + +/* Record bounds-check. */ +static void rec_idx_abc(jit_State *J, TRef asizeref, TRef ikey, uint32_t asize) +{ + /* Try to emit invariant bounds checks. */ + if ((J->flags & (JIT_F_OPT_LOOP|JIT_F_OPT_ABC)) == + (JIT_F_OPT_LOOP|JIT_F_OPT_ABC)) { + IRRef ref = tref_ref(ikey); + IRIns *ir = IR(ref); + int32_t ofs = 0; + IRRef ofsref = 0; + /* Handle constant offsets. */ + if (ir->o == IR_ADD && irref_isk(ir->op2)) { + ofsref = ir->op2; + ofs = IR(ofsref)->i; + ref = ir->op1; + ir = IR(ref); + } + /* Got scalar evolution analysis results for this reference? */ + if (ref == J->scev.idx) { + int32_t stop; + lua_assert(irt_isint(J->scev.t) && ir->o == IR_SLOAD); + stop = numberVint(&(J->L->base - J->baseslot)[ir->op1 + FORL_STOP]); + /* Runtime value for stop of loop is within bounds? */ + if ((uint64_t)stop + ofs < (uint64_t)asize) { + /* Emit invariant bounds check for stop. */ + emitir(IRTG(IR_ABC, IRT_P32), asizeref, ofs == 0 ? J->scev.stop : + emitir(IRTI(IR_ADD), J->scev.stop, ofsref)); + /* Emit invariant bounds check for start, if not const or negative. */ + if (!(J->scev.dir && J->scev.start && + (int64_t)IR(J->scev.start)->i + ofs >= 0)) + emitir(IRTG(IR_ABC, IRT_P32), asizeref, ikey); + return; + } + } + } + emitir(IRTGI(IR_ABC), asizeref, ikey); /* Emit regular bounds check. */ +} + +/* Record indexed key lookup. */ +static TRef rec_idx_key(jit_State *J, RecordIndex *ix, IRRef *rbref, + IRType1 *rbguard) +{ + TRef key; + GCtab *t = tabV(&ix->tabv); + ix->oldv = lj_tab_get(J->L, t, &ix->keyv); /* Lookup previous value. */ + *rbref = 0; + rbguard->irt = 0; + + /* Integer keys are looked up in the array part first. */ + key = ix->key; + if (tref_isnumber(key)) { + int32_t k = numberVint(&ix->keyv); + if (!tvisint(&ix->keyv) && numV(&ix->keyv) != (lua_Number)k) + k = LJ_MAX_ASIZE; + if ((MSize)k < LJ_MAX_ASIZE) { /* Potential array key? */ + TRef ikey = lj_opt_narrow_index(J, key); + TRef asizeref = emitir(IRTI(IR_FLOAD), ix->tab, IRFL_TAB_ASIZE); + if ((MSize)k < t->asize) { /* Currently an array key? */ + TRef arrayref; + rec_idx_abc(J, asizeref, ikey, t->asize); + arrayref = emitir(IRT(IR_FLOAD, IRT_PGC), ix->tab, IRFL_TAB_ARRAY); + return emitir(IRT(IR_AREF, IRT_PGC), arrayref, ikey); + } else { /* Currently not in array (may be an array extension)? */ + emitir(IRTGI(IR_ULE), asizeref, ikey); /* Inv. bounds check. */ + if (k == 0 && tref_isk(key)) + key = lj_ir_knum_zero(J); /* Canonicalize 0 or +-0.0 to +0.0. */ + /* And continue with the hash lookup. */ + } + } else if (!tref_isk(key)) { + /* We can rule out const numbers which failed the integerness test + ** above. But all other numbers are potential array keys. + */ + if (t->asize == 0) { /* True sparse tables have an empty array part. */ + /* Guard that the array part stays empty. */ + TRef tmp = emitir(IRTI(IR_FLOAD), ix->tab, IRFL_TAB_ASIZE); + emitir(IRTGI(IR_EQ), tmp, lj_ir_kint(J, 0)); + } else { + lj_trace_err(J, LJ_TRERR_NYITMIX); + } + } + } + + /* Otherwise the key is located in the hash part. */ + if (t->hmask == 0) { /* Shortcut for empty hash part. */ + /* Guard that the hash part stays empty. */ + TRef tmp = emitir(IRTI(IR_FLOAD), ix->tab, IRFL_TAB_HMASK); + emitir(IRTGI(IR_EQ), tmp, lj_ir_kint(J, 0)); + return lj_ir_kkptr(J, niltvg(J2G(J))); + } + if (tref_isinteger(key)) /* Hash keys are based on numbers, not ints. */ + key = emitir(IRTN(IR_CONV), key, IRCONV_NUM_INT); + if (tref_isk(key)) { + /* Optimize lookup of constant hash keys. */ + MSize hslot = (MSize)((char *)ix->oldv - (char *)&noderef(t->node)[0].val); + if (t->hmask > 0 && hslot <= t->hmask*(MSize)sizeof(Node) && + hslot <= 65535*(MSize)sizeof(Node)) { + TRef node, kslot, hm; + *rbref = J->cur.nins; /* Mark possible rollback point. */ + *rbguard = J->guardemit; + hm = emitir(IRTI(IR_FLOAD), ix->tab, IRFL_TAB_HMASK); + emitir(IRTGI(IR_EQ), hm, lj_ir_kint(J, (int32_t)t->hmask)); + node = emitir(IRT(IR_FLOAD, IRT_PGC), ix->tab, IRFL_TAB_NODE); + kslot = lj_ir_kslot(J, key, hslot / sizeof(Node)); + return emitir(IRTG(IR_HREFK, IRT_PGC), node, kslot); + } + } + /* Fall back to a regular hash lookup. */ + return emitir(IRT(IR_HREF, IRT_PGC), ix->tab, key); +} + +/* Determine whether a key is NOT one of the fast metamethod names. */ +static int nommstr(jit_State *J, TRef key) +{ + if (tref_isstr(key)) { + if (tref_isk(key)) { + GCstr *str = ir_kstr(IR(tref_ref(key))); + uint32_t mm; + for (mm = 0; mm <= MM_FAST; mm++) + if (mmname_str(J2G(J), mm) == str) + return 0; /* MUST be one the fast metamethod names. */ + } else { + return 0; /* Variable string key MAY be a metamethod name. */ + } + } + return 1; /* CANNOT be a metamethod name. */ +} + +/* Record indexed load/store. */ +TRef lj_record_idx(jit_State *J, RecordIndex *ix) +{ + TRef xref; + IROp xrefop, loadop; + IRRef rbref; + IRType1 rbguard; + cTValue *oldv; + + while (!tref_istab(ix->tab)) { /* Handle non-table lookup. */ + /* Never call raw lj_record_idx() on non-table. */ + lua_assert(ix->idxchain != 0); + if (!lj_record_mm_lookup(J, ix, ix->val ? MM_newindex : MM_index)) + lj_trace_err(J, LJ_TRERR_NOMM); + handlemm: + if (tref_isfunc(ix->mobj)) { /* Handle metamethod call. */ + BCReg func = rec_mm_prep(J, ix->val ? lj_cont_nop : lj_cont_ra); + TRef *base = J->base + func + LJ_FR2; + TValue *tv = J->L->base + func + LJ_FR2; + base[-LJ_FR2] = ix->mobj; base[1] = ix->tab; base[2] = ix->key; + setfuncV(J->L, tv-LJ_FR2, funcV(&ix->mobjv)); + copyTV(J->L, tv+1, &ix->tabv); + copyTV(J->L, tv+2, &ix->keyv); + if (ix->val) { + base[3] = ix->val; + copyTV(J->L, tv+3, &ix->valv); + lj_record_call(J, func, 3); /* mobj(tab, key, val) */ + return 0; + } else { + lj_record_call(J, func, 2); /* res = mobj(tab, key) */ + return 0; /* No result yet. */ + } + } + /* Otherwise retry lookup with metaobject. */ + ix->tab = ix->mobj; + copyTV(J->L, &ix->tabv, &ix->mobjv); + if (--ix->idxchain == 0) + lj_trace_err(J, LJ_TRERR_IDXLOOP); + } + + /* First catch nil and NaN keys for tables. */ + if (tvisnil(&ix->keyv) || (tvisnum(&ix->keyv) && tvisnan(&ix->keyv))) { + if (ix->val) /* Better fail early. */ + lj_trace_err(J, LJ_TRERR_STORENN); + if (tref_isk(ix->key)) { + if (ix->idxchain && lj_record_mm_lookup(J, ix, MM_index)) + goto handlemm; + return TREF_NIL; + } + } + + /* Record the key lookup. */ + xref = rec_idx_key(J, ix, &rbref, &rbguard); + xrefop = IR(tref_ref(xref))->o; + loadop = xrefop == IR_AREF ? IR_ALOAD : IR_HLOAD; + /* The lj_meta_tset() inconsistency is gone, but better play safe. */ + oldv = xrefop == IR_KKPTR ? (cTValue *)ir_kptr(IR(tref_ref(xref))) : ix->oldv; + + if (ix->val == 0) { /* Indexed load */ + IRType t = itype2irt(oldv); + TRef res; + if (oldv == niltvg(J2G(J))) { + emitir(IRTG(IR_EQ, IRT_PGC), xref, lj_ir_kkptr(J, niltvg(J2G(J)))); + res = TREF_NIL; + } else { + res = emitir(IRTG(loadop, t), xref, 0); + } + if (tref_ref(res) < rbref) { /* HREFK + load forwarded? */ + lj_ir_rollback(J, rbref); /* Rollback to eliminate hmask guard. */ + J->guardemit = rbguard; + } + if (t == IRT_NIL && ix->idxchain && lj_record_mm_lookup(J, ix, MM_index)) + goto handlemm; + if (irtype_ispri(t)) res = TREF_PRI(t); /* Canonicalize primitives. */ + return res; + } else { /* Indexed store. */ + GCtab *mt = tabref(tabV(&ix->tabv)->metatable); + int keybarrier = tref_isgcv(ix->key) && !tref_isnil(ix->val); + if (tref_ref(xref) < rbref) { /* HREFK forwarded? */ + lj_ir_rollback(J, rbref); /* Rollback to eliminate hmask guard. */ + J->guardemit = rbguard; + } + if (tvisnil(oldv)) { /* Previous value was nil? */ + /* Need to duplicate the hasmm check for the early guards. */ + int hasmm = 0; + if (ix->idxchain && mt) { + cTValue *mo = lj_tab_getstr(mt, mmname_str(J2G(J), MM_newindex)); + hasmm = mo && !tvisnil(mo); + } + if (hasmm) + emitir(IRTG(loadop, IRT_NIL), xref, 0); /* Guard for nil value. */ + else if (xrefop == IR_HREF) + emitir(IRTG(oldv == niltvg(J2G(J)) ? IR_EQ : IR_NE, IRT_PGC), + xref, lj_ir_kkptr(J, niltvg(J2G(J)))); + if (ix->idxchain && lj_record_mm_lookup(J, ix, MM_newindex)) { + lua_assert(hasmm); + goto handlemm; + } + lua_assert(!hasmm); + if (oldv == niltvg(J2G(J))) { /* Need to insert a new key. */ + TRef key = ix->key; + if (tref_isinteger(key)) /* NEWREF needs a TValue as a key. */ + key = emitir(IRTN(IR_CONV), key, IRCONV_NUM_INT); + xref = emitir(IRT(IR_NEWREF, IRT_PGC), ix->tab, key); + keybarrier = 0; /* NEWREF already takes care of the key barrier. */ +#ifdef LUAJIT_ENABLE_TABLE_BUMP + if ((J->flags & JIT_F_OPT_SINK)) /* Avoid a separate flag. */ + rec_idx_bump(J, ix); +#endif + } + } else if (!lj_opt_fwd_wasnonnil(J, loadop, tref_ref(xref))) { + /* Cannot derive that the previous value was non-nil, must do checks. */ + if (xrefop == IR_HREF) /* Guard against store to niltv. */ + emitir(IRTG(IR_NE, IRT_PGC), xref, lj_ir_kkptr(J, niltvg(J2G(J)))); + if (ix->idxchain) { /* Metamethod lookup required? */ + /* A check for NULL metatable is cheaper (hoistable) than a load. */ + if (!mt) { + TRef mtref = emitir(IRT(IR_FLOAD, IRT_TAB), ix->tab, IRFL_TAB_META); + emitir(IRTG(IR_EQ, IRT_TAB), mtref, lj_ir_knull(J, IRT_TAB)); + } else { + IRType t = itype2irt(oldv); + emitir(IRTG(loadop, t), xref, 0); /* Guard for non-nil value. */ + } + } + } else { + keybarrier = 0; /* Previous non-nil value kept the key alive. */ + } + /* Convert int to number before storing. */ + if (!LJ_DUALNUM && tref_isinteger(ix->val)) + ix->val = emitir(IRTN(IR_CONV), ix->val, IRCONV_NUM_INT); + emitir(IRT(loadop+IRDELTA_L2S, tref_type(ix->val)), xref, ix->val); + if (keybarrier || tref_isgcv(ix->val)) + emitir(IRT(IR_TBAR, IRT_NIL), ix->tab, 0); + /* Invalidate neg. metamethod cache for stores with certain string keys. */ + if (!nommstr(J, ix->key)) { + TRef fref = emitir(IRT(IR_FREF, IRT_PGC), ix->tab, IRFL_TAB_NOMM); + emitir(IRT(IR_FSTORE, IRT_U8), fref, lj_ir_kint(J, 0)); + } + J->needsnap = 1; + return 0; + } +} + +static void rec_tsetm(jit_State *J, BCReg ra, BCReg rn, int32_t i) +{ + RecordIndex ix; + cTValue *basev = J->L->base; + GCtab *t = tabV(&basev[ra-1]); + settabV(J->L, &ix.tabv, t); + ix.tab = getslot(J, ra-1); + ix.idxchain = 0; +#ifdef LUAJIT_ENABLE_TABLE_BUMP + if ((J->flags & JIT_F_OPT_SINK)) { + if (t->asize < i+rn-ra) + lj_tab_reasize(J->L, t, i+rn-ra); + setnilV(&ix.keyv); + rec_idx_bump(J, &ix); + } +#endif + for (; ra < rn; i++, ra++) { + setintV(&ix.keyv, i); + ix.key = lj_ir_kint(J, i); + copyTV(J->L, &ix.valv, &basev[ra]); + ix.val = getslot(J, ra); + lj_record_idx(J, &ix); + } +} + +/* -- Upvalue access ------------------------------------------------------ */ + +/* Check whether upvalue is immutable and ok to constify. */ +static int rec_upvalue_constify(jit_State *J, GCupval *uvp) +{ + if (uvp->immutable) { + cTValue *o = uvval(uvp); + /* Don't constify objects that may retain large amounts of memory. */ +#if LJ_HASFFI + if (tviscdata(o)) { + GCcdata *cd = cdataV(o); + if (!cdataisv(cd) && !(cd->marked & LJ_GC_CDATA_FIN)) { + CType *ct = ctype_raw(ctype_ctsG(J2G(J)), cd->ctypeid); + if (!ctype_hassize(ct->info) || ct->size <= 16) + return 1; + } + return 0; + } +#else + UNUSED(J); +#endif + if (!(tvistab(o) || tvisudata(o) || tvisthread(o))) + return 1; + } + return 0; +} + +/* Record upvalue load/store. */ +static TRef rec_upvalue(jit_State *J, uint32_t uv, TRef val) +{ + GCupval *uvp = &gcref(J->fn->l.uvptr[uv])->uv; + TRef fn = getcurrf(J); + IRRef uref; + int needbarrier = 0; + if (rec_upvalue_constify(J, uvp)) { /* Try to constify immutable upvalue. */ + TRef tr, kfunc; + lua_assert(val == 0); + if (!tref_isk(fn)) { /* Late specialization of current function. */ + if (J->pt->flags >= PROTO_CLC_POLY) + goto noconstify; + kfunc = lj_ir_kfunc(J, J->fn); + emitir(IRTG(IR_EQ, IRT_FUNC), fn, kfunc); +#if LJ_FR2 + J->base[-2] = kfunc; +#else + J->base[-1] = kfunc | TREF_FRAME; +#endif + fn = kfunc; + } + tr = lj_record_constify(J, uvval(uvp)); + if (tr) + return tr; + } +noconstify: + /* Note: this effectively limits LJ_MAX_UPVAL to 127. */ + uv = (uv << 8) | (hashrot(uvp->dhash, uvp->dhash + HASH_BIAS) & 0xff); + if (!uvp->closed) { + uref = tref_ref(emitir(IRTG(IR_UREFO, IRT_PGC), fn, uv)); + /* In current stack? */ + if (uvval(uvp) >= tvref(J->L->stack) && + uvval(uvp) < tvref(J->L->maxstack)) { + int32_t slot = (int32_t)(uvval(uvp) - (J->L->base - J->baseslot)); + if (slot >= 0) { /* Aliases an SSA slot? */ + emitir(IRTG(IR_EQ, IRT_PGC), + REF_BASE, + emitir(IRT(IR_ADD, IRT_PGC), uref, + lj_ir_kint(J, (slot - 1 - LJ_FR2) * -8))); + slot -= (int32_t)J->baseslot; /* Note: slot number may be negative! */ + if (val == 0) { + return getslot(J, slot); + } else { + J->base[slot] = val; + if (slot >= (int32_t)J->maxslot) J->maxslot = (BCReg)(slot+1); + return 0; + } + } + } + emitir(IRTG(IR_UGT, IRT_PGC), + emitir(IRT(IR_SUB, IRT_PGC), uref, REF_BASE), + lj_ir_kint(J, (J->baseslot + J->maxslot) * 8)); + } else { + needbarrier = 1; + uref = tref_ref(emitir(IRTG(IR_UREFC, IRT_PGC), fn, uv)); + } + if (val == 0) { /* Upvalue load */ + IRType t = itype2irt(uvval(uvp)); + TRef res = emitir(IRTG(IR_ULOAD, t), uref, 0); + if (irtype_ispri(t)) res = TREF_PRI(t); /* Canonicalize primitive refs. */ + return res; + } else { /* Upvalue store. */ + /* Convert int to number before storing. */ + if (!LJ_DUALNUM && tref_isinteger(val)) + val = emitir(IRTN(IR_CONV), val, IRCONV_NUM_INT); + emitir(IRT(IR_USTORE, tref_type(val)), uref, val); + if (needbarrier && tref_isgcv(val)) + emitir(IRT(IR_OBAR, IRT_NIL), uref, val); + J->needsnap = 1; + return 0; + } +} + +/* -- Record calls to Lua functions --------------------------------------- */ + +/* Check unroll limits for calls. */ +static void check_call_unroll(jit_State *J, TraceNo lnk) +{ + cTValue *frame = J->L->base - 1; + void *pc = mref(frame_func(frame)->l.pc, void); + int32_t depth = J->framedepth; + int32_t count = 0; + if ((J->pt->flags & PROTO_VARARG)) depth--; /* Vararg frame still missing. */ + for (; depth > 0; depth--) { /* Count frames with same prototype. */ + if (frame_iscont(frame)) depth--; + frame = frame_prev(frame); + if (mref(frame_func(frame)->l.pc, void) == pc) + count++; + } + if (J->pc == J->startpc) { + if (count + J->tailcalled > J->param[JIT_P_recunroll]) { + J->pc++; + if (J->framedepth + J->retdepth == 0) + lj_record_stop(J, LJ_TRLINK_TAILREC, J->cur.traceno); /* Tail-rec. */ + else + lj_record_stop(J, LJ_TRLINK_UPREC, J->cur.traceno); /* Up-recursion. */ + } + } else { + if (count > J->param[JIT_P_callunroll]) { + if (lnk) { /* Possible tail- or up-recursion. */ + lj_trace_flush(J, lnk); /* Flush trace that only returns. */ + /* Set a small, pseudo-random hotcount for a quick retry of JFUNC*. */ + hotcount_set(J2GG(J), J->pc+1, LJ_PRNG_BITS(J, 4)); + } + lj_trace_err(J, LJ_TRERR_CUNROLL); + } + } +} + +/* Record Lua function setup. */ +static void rec_func_setup(jit_State *J) +{ + GCproto *pt = J->pt; + BCReg s, numparams = pt->numparams; + if ((pt->flags & PROTO_NOJIT)) + lj_trace_err(J, LJ_TRERR_CJITOFF); + if (J->baseslot + pt->framesize >= LJ_MAX_JSLOTS) + lj_trace_err(J, LJ_TRERR_STACKOV); + /* Fill up missing parameters with nil. */ + for (s = J->maxslot; s < numparams; s++) + J->base[s] = TREF_NIL; + /* The remaining slots should never be read before they are written. */ + J->maxslot = numparams; +} + +/* Record Lua vararg function setup. */ +static void rec_func_vararg(jit_State *J) +{ + GCproto *pt = J->pt; + BCReg s, fixargs, vframe = J->maxslot+1+LJ_FR2; + lua_assert((pt->flags & PROTO_VARARG)); + if (J->baseslot + vframe + pt->framesize >= LJ_MAX_JSLOTS) + lj_trace_err(J, LJ_TRERR_STACKOV); + J->base[vframe-1-LJ_FR2] = J->base[-1-LJ_FR2]; /* Copy function up. */ +#if LJ_FR2 + J->base[vframe-1] = TREF_FRAME; +#endif + /* Copy fixarg slots up and set their original slots to nil. */ + fixargs = pt->numparams < J->maxslot ? pt->numparams : J->maxslot; + for (s = 0; s < fixargs; s++) { + J->base[vframe+s] = J->base[s]; + J->base[s] = TREF_NIL; + } + J->maxslot = fixargs; + J->framedepth++; + J->base += vframe; + J->baseslot += vframe; +} + +/* Record entry to a Lua function. */ +static void rec_func_lua(jit_State *J) +{ + rec_func_setup(J); + check_call_unroll(J, 0); +} + +/* Record entry to an already compiled function. */ +static void rec_func_jit(jit_State *J, TraceNo lnk) +{ + GCtrace *T; + rec_func_setup(J); + T = traceref(J, lnk); + if (T->linktype == LJ_TRLINK_RETURN) { /* Trace returns to interpreter? */ + check_call_unroll(J, lnk); + /* Temporarily unpatch JFUNC* to continue recording across function. */ + J->patchins = *J->pc; + J->patchpc = (BCIns *)J->pc; + *J->patchpc = T->startins; + return; + } + J->instunroll = 0; /* Cannot continue across a compiled function. */ + if (J->pc == J->startpc && J->framedepth + J->retdepth == 0) + lj_record_stop(J, LJ_TRLINK_TAILREC, J->cur.traceno); /* Extra tail-rec. */ + else + lj_record_stop(J, LJ_TRLINK_ROOT, lnk); /* Link to the function. */ +} + +/* -- Vararg handling ----------------------------------------------------- */ + +/* Detect y = select(x, ...) idiom. */ +static int select_detect(jit_State *J) +{ + BCIns ins = J->pc[1]; + if (bc_op(ins) == BC_CALLM && bc_b(ins) == 2 && bc_c(ins) == 1) { + cTValue *func = &J->L->base[bc_a(ins)]; + if (tvisfunc(func) && funcV(func)->c.ffid == FF_select) { + TRef kfunc = lj_ir_kfunc(J, funcV(func)); + emitir(IRTG(IR_EQ, IRT_FUNC), getslot(J, bc_a(ins)), kfunc); + return 1; + } + } + return 0; +} + +/* Record vararg instruction. */ +static void rec_varg(jit_State *J, BCReg dst, ptrdiff_t nresults) +{ + int32_t numparams = J->pt->numparams; + ptrdiff_t nvararg = frame_delta(J->L->base-1) - numparams - 1 - LJ_FR2; + lua_assert(frame_isvarg(J->L->base-1)); + if (LJ_FR2 && dst > J->maxslot) + J->base[dst-1] = 0; /* Prevent resurrection of unrelated slot. */ + if (J->framedepth > 0) { /* Simple case: varargs defined on-trace. */ + ptrdiff_t i; + if (nvararg < 0) nvararg = 0; + if (nresults == -1) { + nresults = nvararg; + J->maxslot = dst + (BCReg)nvararg; + } else if (dst + nresults > J->maxslot) { + J->maxslot = dst + (BCReg)nresults; + } + for (i = 0; i < nresults; i++) + J->base[dst+i] = i < nvararg ? getslot(J, i - nvararg - 1 - LJ_FR2) : TREF_NIL; + } else { /* Unknown number of varargs passed to trace. */ + TRef fr = emitir(IRTI(IR_SLOAD), LJ_FR2, IRSLOAD_READONLY|IRSLOAD_FRAME); + int32_t frofs = 8*(1+LJ_FR2+numparams)+FRAME_VARG; + if (nresults >= 0) { /* Known fixed number of results. */ + ptrdiff_t i; + if (nvararg > 0) { + ptrdiff_t nload = nvararg >= nresults ? nresults : nvararg; + TRef vbase; + if (nvararg >= nresults) + emitir(IRTGI(IR_GE), fr, lj_ir_kint(J, frofs+8*(int32_t)nresults)); + else + emitir(IRTGI(IR_EQ), fr, + lj_ir_kint(J, (int32_t)frame_ftsz(J->L->base-1))); + vbase = emitir(IRT(IR_SUB, IRT_IGC), REF_BASE, fr); + vbase = emitir(IRT(IR_ADD, IRT_PGC), vbase, lj_ir_kint(J, frofs-8)); + for (i = 0; i < nload; i++) { + IRType t = itype2irt(&J->L->base[i-1-LJ_FR2-nvararg]); + TRef aref = emitir(IRT(IR_AREF, IRT_PGC), + vbase, lj_ir_kint(J, (int32_t)i)); + TRef tr = emitir(IRTG(IR_VLOAD, t), aref, 0); + if (irtype_ispri(t)) tr = TREF_PRI(t); /* Canonicalize primitives. */ + J->base[dst+i] = tr; + } + } else { + emitir(IRTGI(IR_LE), fr, lj_ir_kint(J, frofs)); + nvararg = 0; + } + for (i = nvararg; i < nresults; i++) + J->base[dst+i] = TREF_NIL; + if (dst + (BCReg)nresults > J->maxslot) + J->maxslot = dst + (BCReg)nresults; + } else if (select_detect(J)) { /* y = select(x, ...) */ + TRef tridx = J->base[dst-1]; + TRef tr = TREF_NIL; + ptrdiff_t idx = lj_ffrecord_select_mode(J, tridx, &J->L->base[dst-1]); + if (idx < 0) goto nyivarg; + if (idx != 0 && !tref_isinteger(tridx)) + tridx = emitir(IRTGI(IR_CONV), tridx, IRCONV_INT_NUM|IRCONV_INDEX); + if (idx != 0 && tref_isk(tridx)) { + emitir(IRTGI(idx <= nvararg ? IR_GE : IR_LT), + fr, lj_ir_kint(J, frofs+8*(int32_t)idx)); + frofs -= 8; /* Bias for 1-based index. */ + } else if (idx <= nvararg) { /* Compute size. */ + TRef tmp = emitir(IRTI(IR_ADD), fr, lj_ir_kint(J, -frofs)); + if (numparams) + emitir(IRTGI(IR_GE), tmp, lj_ir_kint(J, 0)); + tr = emitir(IRTI(IR_BSHR), tmp, lj_ir_kint(J, 3)); + if (idx != 0) { + tridx = emitir(IRTI(IR_ADD), tridx, lj_ir_kint(J, -1)); + rec_idx_abc(J, tr, tridx, (uint32_t)nvararg); + } + } else { + TRef tmp = lj_ir_kint(J, frofs); + if (idx != 0) { + TRef tmp2 = emitir(IRTI(IR_BSHL), tridx, lj_ir_kint(J, 3)); + tmp = emitir(IRTI(IR_ADD), tmp2, tmp); + } else { + tr = lj_ir_kint(J, 0); + } + emitir(IRTGI(IR_LT), fr, tmp); + } + if (idx != 0 && idx <= nvararg) { + IRType t; + TRef aref, vbase = emitir(IRT(IR_SUB, IRT_IGC), REF_BASE, fr); + vbase = emitir(IRT(IR_ADD, IRT_PGC), vbase, + lj_ir_kint(J, frofs-(8<L->base[idx-2-LJ_FR2-nvararg]); + aref = emitir(IRT(IR_AREF, IRT_PGC), vbase, tridx); + tr = emitir(IRTG(IR_VLOAD, t), aref, 0); + if (irtype_ispri(t)) tr = TREF_PRI(t); /* Canonicalize primitives. */ + } + J->base[dst-2-LJ_FR2] = tr; + J->maxslot = dst-1-LJ_FR2; + J->bcskip = 2; /* Skip CALLM + select. */ + } else { + nyivarg: + setintV(&J->errinfo, BC_VARG); + lj_trace_err_info(J, LJ_TRERR_NYIBC); + } + } +} + +/* -- Record allocations -------------------------------------------------- */ + +static TRef rec_tnew(jit_State *J, uint32_t ah) +{ + uint32_t asize = ah & 0x7ff; + uint32_t hbits = ah >> 11; + TRef tr; + if (asize == 0x7ff) asize = 0x801; + tr = emitir(IRTG(IR_TNEW, IRT_TAB), asize, hbits); +#ifdef LUAJIT_ENABLE_TABLE_BUMP + J->rbchash[(tr & (RBCHASH_SLOTS-1))].ref = tref_ref(tr); + setmref(J->rbchash[(tr & (RBCHASH_SLOTS-1))].pc, J->pc); + setgcref(J->rbchash[(tr & (RBCHASH_SLOTS-1))].pt, obj2gco(J->pt)); +#endif + return tr; +} + +/* -- Concatenation ------------------------------------------------------- */ + +static TRef rec_cat(jit_State *J, BCReg baseslot, BCReg topslot) +{ + TRef *top = &J->base[topslot]; + TValue savetv[5]; + BCReg s; + RecordIndex ix; + lua_assert(baseslot < topslot); + for (s = baseslot; s <= topslot; s++) + (void)getslot(J, s); /* Ensure all arguments have a reference. */ + if (tref_isnumber_str(top[0]) && tref_isnumber_str(top[-1])) { + TRef tr, hdr, *trp, *xbase, *base = &J->base[baseslot]; + /* First convert numbers to strings. */ + for (trp = top; trp >= base; trp--) { + if (tref_isnumber(*trp)) + *trp = emitir(IRT(IR_TOSTR, IRT_STR), *trp, + tref_isnum(*trp) ? IRTOSTR_NUM : IRTOSTR_INT); + else if (!tref_isstr(*trp)) + break; + } + xbase = ++trp; + tr = hdr = emitir(IRT(IR_BUFHDR, IRT_PGC), + lj_ir_kptr(J, &J2G(J)->tmpbuf), IRBUFHDR_RESET); + do { + tr = emitir(IRT(IR_BUFPUT, IRT_PGC), tr, *trp++); + } while (trp <= top); + tr = emitir(IRT(IR_BUFSTR, IRT_STR), tr, hdr); + J->maxslot = (BCReg)(xbase - J->base); + if (xbase == base) return tr; /* Return simple concatenation result. */ + /* Pass partial result. */ + topslot = J->maxslot--; + *xbase = tr; + top = xbase; + setstrV(J->L, &ix.keyv, &J2G(J)->strempty); /* Simulate string result. */ + } else { + J->maxslot = topslot-1; + copyTV(J->L, &ix.keyv, &J->L->base[topslot]); + } + copyTV(J->L, &ix.tabv, &J->L->base[topslot-1]); + ix.tab = top[-1]; + ix.key = top[0]; + memcpy(savetv, &J->L->base[topslot-1], sizeof(savetv)); /* Save slots. */ + rec_mm_arith(J, &ix, MM_concat); /* Call __concat metamethod. */ + memcpy(&J->L->base[topslot-1], savetv, sizeof(savetv)); /* Restore slots. */ + return 0; /* No result yet. */ +} + +/* -- Record bytecode ops ------------------------------------------------- */ + +/* Prepare for comparison. */ +static void rec_comp_prep(jit_State *J) +{ + /* Prevent merging with snapshot #0 (GC exit) since we fixup the PC. */ + if (J->cur.nsnap == 1 && J->cur.snap[0].ref == J->cur.nins) + emitir_raw(IRT(IR_NOP, IRT_NIL), 0, 0); + lj_snap_add(J); +} + +/* Fixup comparison. */ +static void rec_comp_fixup(jit_State *J, const BCIns *pc, int cond) +{ + BCIns jmpins = pc[1]; + const BCIns *npc = pc + 2 + (cond ? bc_j(jmpins) : 0); + SnapShot *snap = &J->cur.snap[J->cur.nsnap-1]; + /* Set PC to opposite target to avoid re-recording the comp. in side trace. */ +#if LJ_FR2 + SnapEntry *flink = &J->cur.snapmap[snap->mapofs + snap->nent]; + uint64_t pcbase; + memcpy(&pcbase, flink, sizeof(uint64_t)); + pcbase = (pcbase & 0xff) | (u64ptr(npc) << 8); + memcpy(flink, &pcbase, sizeof(uint64_t)); +#else + J->cur.snapmap[snap->mapofs + snap->nent] = SNAP_MKPC(npc); +#endif + J->needsnap = 1; + if (bc_a(jmpins) < J->maxslot) J->maxslot = bc_a(jmpins); + lj_snap_shrink(J); /* Shrink last snapshot if possible. */ +} + +/* Record the next bytecode instruction (_before_ it's executed). */ +void lj_record_ins(jit_State *J) +{ + cTValue *lbase; + RecordIndex ix; + const BCIns *pc; + BCIns ins; + BCOp op; + TRef ra, rb, rc; + + /* Perform post-processing action before recording the next instruction. */ + if (LJ_UNLIKELY(J->postproc != LJ_POST_NONE)) { + switch (J->postproc) { + case LJ_POST_FIXCOMP: /* Fixup comparison. */ + pc = (const BCIns *)(uintptr_t)J2G(J)->tmptv.u64; + rec_comp_fixup(J, pc, (!tvistruecond(&J2G(J)->tmptv2) ^ (bc_op(*pc)&1))); + /* fallthrough */ + case LJ_POST_FIXGUARD: /* Fixup and emit pending guard. */ + case LJ_POST_FIXGUARDSNAP: /* Fixup and emit pending guard and snapshot. */ + if (!tvistruecond(&J2G(J)->tmptv2)) { + J->fold.ins.o ^= 1; /* Flip guard to opposite. */ + if (J->postproc == LJ_POST_FIXGUARDSNAP) { + SnapShot *snap = &J->cur.snap[J->cur.nsnap-1]; + J->cur.snapmap[snap->mapofs+snap->nent-1]--; /* False -> true. */ + } + } + lj_opt_fold(J); /* Emit pending guard. */ + /* fallthrough */ + case LJ_POST_FIXBOOL: + if (!tvistruecond(&J2G(J)->tmptv2)) { + BCReg s; + TValue *tv = J->L->base; + for (s = 0; s < J->maxslot; s++) /* Fixup stack slot (if any). */ + if (J->base[s] == TREF_TRUE && tvisfalse(&tv[s])) { + J->base[s] = TREF_FALSE; + break; + } + } + break; + case LJ_POST_FIXCONST: + { + BCReg s; + TValue *tv = J->L->base; + for (s = 0; s < J->maxslot; s++) /* Constify stack slots (if any). */ + if (J->base[s] == TREF_NIL && !tvisnil(&tv[s])) + J->base[s] = lj_record_constify(J, &tv[s]); + } + break; + case LJ_POST_FFRETRY: /* Suppress recording of retried fast function. */ + if (bc_op(*J->pc) >= BC__MAX) + return; + break; + default: lua_assert(0); break; + } + J->postproc = LJ_POST_NONE; + } + + /* Need snapshot before recording next bytecode (e.g. after a store). */ + if (J->needsnap) { + J->needsnap = 0; + lj_snap_purge(J); + lj_snap_add(J); + J->mergesnap = 1; + } + + /* Skip some bytecodes. */ + if (LJ_UNLIKELY(J->bcskip > 0)) { + J->bcskip--; + return; + } + + /* Record only closed loops for root traces. */ + pc = J->pc; + if (J->framedepth == 0 && + (MSize)((char *)pc - (char *)J->bc_min) >= J->bc_extent) + lj_trace_err(J, LJ_TRERR_LLEAVE); + +#ifdef LUA_USE_ASSERT + rec_check_slots(J); + rec_check_ir(J); +#endif + +#if LJ_HASPROFILE + rec_profile_ins(J, pc); +#endif + + /* Keep a copy of the runtime values of var/num/str operands. */ +#define rav (&ix.valv) +#define rbv (&ix.tabv) +#define rcv (&ix.keyv) + + lbase = J->L->base; + ins = *pc; + op = bc_op(ins); + ra = bc_a(ins); + ix.val = 0; + switch (bcmode_a(op)) { + case BCMvar: + copyTV(J->L, rav, &lbase[ra]); ix.val = ra = getslot(J, ra); break; + default: break; /* Handled later. */ + } + rb = bc_b(ins); + rc = bc_c(ins); + switch (bcmode_b(op)) { + case BCMnone: rb = 0; rc = bc_d(ins); break; /* Upgrade rc to 'rd'. */ + case BCMvar: + copyTV(J->L, rbv, &lbase[rb]); ix.tab = rb = getslot(J, rb); break; + default: break; /* Handled later. */ + } + switch (bcmode_c(op)) { + case BCMvar: + copyTV(J->L, rcv, &lbase[rc]); ix.key = rc = getslot(J, rc); break; + case BCMpri: setpriV(rcv, ~rc); ix.key = rc = TREF_PRI(IRT_NIL+rc); break; + case BCMnum: { cTValue *tv = proto_knumtv(J->pt, rc); + copyTV(J->L, rcv, tv); ix.key = rc = tvisint(tv) ? lj_ir_kint(J, intV(tv)) : + lj_ir_knumint(J, numV(tv)); } break; + case BCMstr: { GCstr *s = gco2str(proto_kgc(J->pt, ~(ptrdiff_t)rc)); + setstrV(J->L, rcv, s); ix.key = rc = lj_ir_kstr(J, s); } break; + default: break; /* Handled later. */ + } + + switch (op) { + + /* -- Comparison ops ---------------------------------------------------- */ + + case BC_ISLT: case BC_ISGE: case BC_ISLE: case BC_ISGT: +#if LJ_HASFFI + if (tref_iscdata(ra) || tref_iscdata(rc)) { + rec_mm_comp_cdata(J, &ix, op, ((int)op & 2) ? MM_le : MM_lt); + break; + } +#endif + /* Emit nothing for two numeric or string consts. */ + if (!(tref_isk2(ra,rc) && tref_isnumber_str(ra) && tref_isnumber_str(rc))) { + IRType ta = tref_isinteger(ra) ? IRT_INT : tref_type(ra); + IRType tc = tref_isinteger(rc) ? IRT_INT : tref_type(rc); + int irop; + if (ta != tc) { + /* Widen mixed number/int comparisons to number/number comparison. */ + if (ta == IRT_INT && tc == IRT_NUM) { + ra = emitir(IRTN(IR_CONV), ra, IRCONV_NUM_INT); + ta = IRT_NUM; + } else if (ta == IRT_NUM && tc == IRT_INT) { + rc = emitir(IRTN(IR_CONV), rc, IRCONV_NUM_INT); + } else if (LJ_52) { + ta = IRT_NIL; /* Force metamethod for different types. */ + } else if (!((ta == IRT_FALSE || ta == IRT_TRUE) && + (tc == IRT_FALSE || tc == IRT_TRUE))) { + break; /* Interpreter will throw for two different types. */ + } + } + rec_comp_prep(J); + irop = (int)op - (int)BC_ISLT + (int)IR_LT; + if (ta == IRT_NUM) { + if ((irop & 1)) irop ^= 4; /* ISGE/ISGT are unordered. */ + if (!lj_ir_numcmp(numberVnum(rav), numberVnum(rcv), (IROp)irop)) + irop ^= 5; + } else if (ta == IRT_INT) { + if (!lj_ir_numcmp(numberVnum(rav), numberVnum(rcv), (IROp)irop)) + irop ^= 1; + } else if (ta == IRT_STR) { + if (!lj_ir_strcmp(strV(rav), strV(rcv), (IROp)irop)) irop ^= 1; + ra = lj_ir_call(J, IRCALL_lj_str_cmp, ra, rc); + rc = lj_ir_kint(J, 0); + ta = IRT_INT; + } else { + rec_mm_comp(J, &ix, (int)op); + break; + } + emitir(IRTG(irop, ta), ra, rc); + rec_comp_fixup(J, J->pc, ((int)op ^ irop) & 1); + } + break; + + case BC_ISEQV: case BC_ISNEV: + case BC_ISEQS: case BC_ISNES: + case BC_ISEQN: case BC_ISNEN: + case BC_ISEQP: case BC_ISNEP: +#if LJ_HASFFI + if (tref_iscdata(ra) || tref_iscdata(rc)) { + rec_mm_comp_cdata(J, &ix, op, MM_eq); + break; + } +#endif + /* Emit nothing for two non-table, non-udata consts. */ + if (!(tref_isk2(ra, rc) && !(tref_istab(ra) || tref_isudata(ra)))) { + int diff; + rec_comp_prep(J); + diff = lj_record_objcmp(J, ra, rc, rav, rcv); + if (diff == 2 || !(tref_istab(ra) || tref_isudata(ra))) + rec_comp_fixup(J, J->pc, ((int)op & 1) == !diff); + else if (diff == 1) /* Only check __eq if different, but same type. */ + rec_mm_equal(J, &ix, (int)op); + } + break; + + /* -- Unary test and copy ops ------------------------------------------- */ + + case BC_ISTC: case BC_ISFC: + if ((op & 1) == tref_istruecond(rc)) + rc = 0; /* Don't store if condition is not true. */ + /* fallthrough */ + case BC_IST: case BC_ISF: /* Type specialization suffices. */ + if (bc_a(pc[1]) < J->maxslot) + J->maxslot = bc_a(pc[1]); /* Shrink used slots. */ + break; + + case BC_ISTYPE: case BC_ISNUM: + /* These coercions need to correspond with lj_meta_istype(). */ + if (LJ_DUALNUM && rc == ~LJ_TNUMX+1) + ra = lj_opt_narrow_toint(J, ra); + else if (rc == ~LJ_TNUMX+2) + ra = lj_ir_tonum(J, ra); + else if (rc == ~LJ_TSTR+1) + ra = lj_ir_tostr(J, ra); + /* else: type specialization suffices. */ + J->base[bc_a(ins)] = ra; + break; + + /* -- Unary ops --------------------------------------------------------- */ + + case BC_NOT: + /* Type specialization already forces const result. */ + rc = tref_istruecond(rc) ? TREF_FALSE : TREF_TRUE; + break; + + case BC_LEN: + if (tref_isstr(rc)) + rc = emitir(IRTI(IR_FLOAD), rc, IRFL_STR_LEN); + else if (!LJ_52 && tref_istab(rc)) + rc = lj_ir_call(J, IRCALL_lj_tab_len, rc); + else + rc = rec_mm_len(J, rc, rcv); + break; + + /* -- Arithmetic ops ---------------------------------------------------- */ + + case BC_UNM: + if (tref_isnumber_str(rc)) { + rc = lj_opt_narrow_unm(J, rc, rcv); + } else { + ix.tab = rc; + copyTV(J->L, &ix.tabv, rcv); + rc = rec_mm_arith(J, &ix, MM_unm); + } + break; + + case BC_ADDNV: case BC_SUBNV: case BC_MULNV: case BC_DIVNV: case BC_MODNV: + /* Swap rb/rc and rbv/rcv. rav is temp. */ + ix.tab = rc; ix.key = rc = rb; rb = ix.tab; + copyTV(J->L, rav, rbv); + copyTV(J->L, rbv, rcv); + copyTV(J->L, rcv, rav); + if (op == BC_MODNV) + goto recmod; + /* fallthrough */ + case BC_ADDVN: case BC_SUBVN: case BC_MULVN: case BC_DIVVN: + case BC_ADDVV: case BC_SUBVV: case BC_MULVV: case BC_DIVVV: { + MMS mm = bcmode_mm(op); + if (tref_isnumber_str(rb) && tref_isnumber_str(rc)) + rc = lj_opt_narrow_arith(J, rb, rc, rbv, rcv, + (int)mm - (int)MM_add + (int)IR_ADD); + else + rc = rec_mm_arith(J, &ix, mm); + break; + } + + case BC_MODVN: case BC_MODVV: + recmod: + if (tref_isnumber_str(rb) && tref_isnumber_str(rc)) + rc = lj_opt_narrow_mod(J, rb, rc, rbv, rcv); + else + rc = rec_mm_arith(J, &ix, MM_mod); + break; + + case BC_POW: + if (tref_isnumber_str(rb) && tref_isnumber_str(rc)) + rc = lj_opt_narrow_pow(J, rb, rc, rbv, rcv); + else + rc = rec_mm_arith(J, &ix, MM_pow); + break; + + /* -- Miscellaneous ops ------------------------------------------------- */ + + case BC_CAT: + rc = rec_cat(J, rb, rc); + break; + + /* -- Constant and move ops --------------------------------------------- */ + + case BC_MOV: + /* Clear gap of method call to avoid resurrecting previous refs. */ + if (ra > J->maxslot) { +#if LJ_FR2 + memset(J->base + J->maxslot, 0, (ra - J->maxslot) * sizeof(TRef)); +#else + J->base[ra-1] = 0; +#endif + } + break; + case BC_KSTR: case BC_KNUM: case BC_KPRI: + break; + case BC_KSHORT: + rc = lj_ir_kint(J, (int32_t)(int16_t)rc); + break; + case BC_KNIL: + if (LJ_FR2 && ra > J->maxslot) + J->base[ra-1] = 0; + while (ra <= rc) + J->base[ra++] = TREF_NIL; + if (rc >= J->maxslot) J->maxslot = rc+1; + break; +#if LJ_HASFFI + case BC_KCDATA: + rc = lj_ir_kgc(J, proto_kgc(J->pt, ~(ptrdiff_t)rc), IRT_CDATA); + break; +#endif + + /* -- Upvalue and function ops ------------------------------------------ */ + + case BC_UGET: + rc = rec_upvalue(J, rc, 0); + break; + case BC_USETV: case BC_USETS: case BC_USETN: case BC_USETP: + rec_upvalue(J, ra, rc); + break; + + /* -- Table ops --------------------------------------------------------- */ + + case BC_GGET: case BC_GSET: + settabV(J->L, &ix.tabv, tabref(J->fn->l.env)); + ix.tab = emitir(IRT(IR_FLOAD, IRT_TAB), getcurrf(J), IRFL_FUNC_ENV); + ix.idxchain = LJ_MAX_IDXCHAIN; + rc = lj_record_idx(J, &ix); + break; + + case BC_TGETB: case BC_TSETB: + setintV(&ix.keyv, (int32_t)rc); + ix.key = lj_ir_kint(J, (int32_t)rc); + /* fallthrough */ + case BC_TGETV: case BC_TGETS: case BC_TSETV: case BC_TSETS: + ix.idxchain = LJ_MAX_IDXCHAIN; + rc = lj_record_idx(J, &ix); + break; + case BC_TGETR: case BC_TSETR: + ix.idxchain = 0; + rc = lj_record_idx(J, &ix); + break; + + case BC_TSETM: + rec_tsetm(J, ra, (BCReg)(J->L->top - J->L->base), (int32_t)rcv->u32.lo); + break; + + case BC_TNEW: + rc = rec_tnew(J, rc); + break; + case BC_TDUP: + rc = emitir(IRTG(IR_TDUP, IRT_TAB), + lj_ir_ktab(J, gco2tab(proto_kgc(J->pt, ~(ptrdiff_t)rc))), 0); +#ifdef LUAJIT_ENABLE_TABLE_BUMP + J->rbchash[(rc & (RBCHASH_SLOTS-1))].ref = tref_ref(rc); + setmref(J->rbchash[(rc & (RBCHASH_SLOTS-1))].pc, pc); + setgcref(J->rbchash[(rc & (RBCHASH_SLOTS-1))].pt, obj2gco(J->pt)); +#endif + break; + + /* -- Calls and vararg handling ----------------------------------------- */ + + case BC_ITERC: + J->base[ra] = getslot(J, ra-3); + J->base[ra+1+LJ_FR2] = getslot(J, ra-2); + J->base[ra+2+LJ_FR2] = getslot(J, ra-1); + { /* Do the actual copy now because lj_record_call needs the values. */ + TValue *b = &J->L->base[ra]; + copyTV(J->L, b, b-3); + copyTV(J->L, b+1+LJ_FR2, b-2); + copyTV(J->L, b+2+LJ_FR2, b-1); + } + lj_record_call(J, ra, (ptrdiff_t)rc-1); + break; + + /* L->top is set to L->base+ra+rc+NARGS-1+1. See lj_dispatch_ins(). */ + case BC_CALLM: + rc = (BCReg)(J->L->top - J->L->base) - ra - LJ_FR2; + /* fallthrough */ + case BC_CALL: + lj_record_call(J, ra, (ptrdiff_t)rc-1); + break; + + case BC_CALLMT: + rc = (BCReg)(J->L->top - J->L->base) - ra - LJ_FR2; + /* fallthrough */ + case BC_CALLT: + lj_record_tailcall(J, ra, (ptrdiff_t)rc-1); + break; + + case BC_VARG: + rec_varg(J, ra, (ptrdiff_t)rb-1); + break; + + /* -- Returns ----------------------------------------------------------- */ + + case BC_RETM: + /* L->top is set to L->base+ra+rc+NRESULTS-1, see lj_dispatch_ins(). */ + rc = (BCReg)(J->L->top - J->L->base) - ra + 1; + /* fallthrough */ + case BC_RET: case BC_RET0: case BC_RET1: +#if LJ_HASPROFILE + rec_profile_ret(J); +#endif + lj_record_ret(J, ra, (ptrdiff_t)rc-1); + break; + + /* -- Loops and branches ------------------------------------------------ */ + + case BC_FORI: + if (rec_for(J, pc, 0) != LOOPEV_LEAVE) + J->loopref = J->cur.nins; + break; + case BC_JFORI: + lua_assert(bc_op(pc[(ptrdiff_t)rc-BCBIAS_J]) == BC_JFORL); + if (rec_for(J, pc, 0) != LOOPEV_LEAVE) /* Link to existing loop. */ + lj_record_stop(J, LJ_TRLINK_ROOT, bc_d(pc[(ptrdiff_t)rc-BCBIAS_J])); + /* Continue tracing if the loop is not entered. */ + break; + + case BC_FORL: + rec_loop_interp(J, pc, rec_for(J, pc+((ptrdiff_t)rc-BCBIAS_J), 1)); + break; + case BC_ITERL: + rec_loop_interp(J, pc, rec_iterl(J, *pc)); + break; + case BC_LOOP: + rec_loop_interp(J, pc, rec_loop(J, ra)); + break; + + case BC_JFORL: + rec_loop_jit(J, rc, rec_for(J, pc+bc_j(traceref(J, rc)->startins), 1)); + break; + case BC_JITERL: + rec_loop_jit(J, rc, rec_iterl(J, traceref(J, rc)->startins)); + break; + case BC_JLOOP: + rec_loop_jit(J, rc, rec_loop(J, ra)); + break; + + case BC_IFORL: + case BC_IITERL: + case BC_ILOOP: + case BC_IFUNCF: + case BC_IFUNCV: + lj_trace_err(J, LJ_TRERR_BLACKL); + break; + + case BC_JMP: + if (ra < J->maxslot) + J->maxslot = ra; /* Shrink used slots. */ + break; + + /* -- Function headers -------------------------------------------------- */ + + case BC_FUNCF: + rec_func_lua(J); + break; + case BC_JFUNCF: + rec_func_jit(J, rc); + break; + + case BC_FUNCV: + rec_func_vararg(J); + rec_func_lua(J); + break; + case BC_JFUNCV: + lua_assert(0); /* Cannot happen. No hotcall counting for varag funcs. */ + break; + + case BC_FUNCC: + case BC_FUNCCW: + lj_ffrecord_func(J); + break; + + default: + if (op >= BC__MAX) { + lj_ffrecord_func(J); + break; + } + /* fallthrough */ + case BC_ITERN: + case BC_ISNEXT: + case BC_UCLO: + case BC_FNEW: + setintV(&J->errinfo, (int32_t)op); + lj_trace_err_info(J, LJ_TRERR_NYIBC); + break; + } + + /* rc == 0 if we have no result yet, e.g. pending __index metamethod call. */ + if (bcmode_a(op) == BCMdst && rc) { + J->base[ra] = rc; + if (ra >= J->maxslot) { +#if LJ_FR2 + if (ra > J->maxslot) J->base[ra-1] = 0; +#endif + J->maxslot = ra+1; + } + } + +#undef rav +#undef rbv +#undef rcv + + /* Limit the number of recorded IR instructions and constants. */ + if (J->cur.nins > REF_FIRST+(IRRef)J->param[JIT_P_maxrecord] || + J->cur.nk < REF_BIAS-(IRRef)J->param[JIT_P_maxirconst]) + lj_trace_err(J, LJ_TRERR_TRACEOV); +} + +/* -- Recording setup ----------------------------------------------------- */ + +/* Setup recording for a root trace started by a hot loop. */ +static const BCIns *rec_setup_root(jit_State *J) +{ + /* Determine the next PC and the bytecode range for the loop. */ + const BCIns *pcj, *pc = J->pc; + BCIns ins = *pc; + BCReg ra = bc_a(ins); + switch (bc_op(ins)) { + case BC_FORL: + J->bc_extent = (MSize)(-bc_j(ins))*sizeof(BCIns); + pc += 1+bc_j(ins); + J->bc_min = pc; + break; + case BC_ITERL: + lua_assert(bc_op(pc[-1]) == BC_ITERC); + J->maxslot = ra + bc_b(pc[-1]) - 1; + J->bc_extent = (MSize)(-bc_j(ins))*sizeof(BCIns); + pc += 1+bc_j(ins); + lua_assert(bc_op(pc[-1]) == BC_JMP); + J->bc_min = pc; + break; + case BC_LOOP: + /* Only check BC range for real loops, but not for "repeat until true". */ + pcj = pc + bc_j(ins); + ins = *pcj; + if (bc_op(ins) == BC_JMP && bc_j(ins) < 0) { + J->bc_min = pcj+1 + bc_j(ins); + J->bc_extent = (MSize)(-bc_j(ins))*sizeof(BCIns); + } + J->maxslot = ra; + pc++; + break; + case BC_RET: + case BC_RET0: + case BC_RET1: + /* No bytecode range check for down-recursive root traces. */ + J->maxslot = ra + bc_d(ins) - 1; + break; + case BC_FUNCF: + /* No bytecode range check for root traces started by a hot call. */ + J->maxslot = J->pt->numparams; + pc++; + break; + case BC_CALLM: + case BC_CALL: + case BC_ITERC: + /* No bytecode range check for stitched traces. */ + pc++; + break; + default: + lua_assert(0); + break; + } + return pc; +} + +/* Setup for recording a new trace. */ +void lj_record_setup(jit_State *J) +{ + uint32_t i; + + /* Initialize state related to current trace. */ + memset(J->slot, 0, sizeof(J->slot)); + memset(J->chain, 0, sizeof(J->chain)); +#ifdef LUAJIT_ENABLE_TABLE_BUMP + memset(J->rbchash, 0, sizeof(J->rbchash)); +#endif + memset(J->bpropcache, 0, sizeof(J->bpropcache)); + J->scev.idx = REF_NIL; + setmref(J->scev.pc, NULL); + + J->baseslot = 1+LJ_FR2; /* Invoking function is at base[-1-LJ_FR2]. */ + J->base = J->slot + J->baseslot; + J->maxslot = 0; + J->framedepth = 0; + J->retdepth = 0; + + J->instunroll = J->param[JIT_P_instunroll]; + J->loopunroll = J->param[JIT_P_loopunroll]; + J->tailcalled = 0; + J->loopref = 0; + + J->bc_min = NULL; /* Means no limit. */ + J->bc_extent = ~(MSize)0; + + /* Emit instructions for fixed references. Also triggers initial IR alloc. */ + emitir_raw(IRT(IR_BASE, IRT_PGC), J->parent, J->exitno); + for (i = 0; i <= 2; i++) { + IRIns *ir = IR(REF_NIL-i); + ir->i = 0; + ir->t.irt = (uint8_t)(IRT_NIL+i); + ir->o = IR_KPRI; + ir->prev = 0; + } + J->cur.nk = REF_TRUE; + + J->startpc = J->pc; + setmref(J->cur.startpc, J->pc); + if (J->parent) { /* Side trace. */ + GCtrace *T = traceref(J, J->parent); + TraceNo root = T->root ? T->root : J->parent; + J->cur.root = (uint16_t)root; + J->cur.startins = BCINS_AD(BC_JMP, 0, 0); + /* Check whether we could at least potentially form an extra loop. */ + if (J->exitno == 0 && T->snap[0].nent == 0) { + /* We can narrow a FORL for some side traces, too. */ + if (J->pc > proto_bc(J->pt) && bc_op(J->pc[-1]) == BC_JFORI && + bc_d(J->pc[bc_j(J->pc[-1])-1]) == root) { + lj_snap_add(J); + rec_for_loop(J, J->pc-1, &J->scev, 1); + goto sidecheck; + } + } else { + J->startpc = NULL; /* Prevent forming an extra loop. */ + } + lj_snap_replay(J, T); + sidecheck: + if (traceref(J, J->cur.root)->nchild >= J->param[JIT_P_maxside] || + T->snap[J->exitno].count >= J->param[JIT_P_hotexit] + + J->param[JIT_P_tryside]) { + lj_record_stop(J, LJ_TRLINK_INTERP, 0); + } + } else { /* Root trace. */ + J->cur.root = 0; + J->cur.startins = *J->pc; + J->pc = rec_setup_root(J); + /* Note: the loop instruction itself is recorded at the end and not + ** at the start! So snapshot #0 needs to point to the *next* instruction. + */ + lj_snap_add(J); + if (bc_op(J->cur.startins) == BC_FORL) + rec_for_loop(J, J->pc-1, &J->scev, 1); + else if (bc_op(J->cur.startins) == BC_ITERC) + J->startpc = NULL; + if (1 + J->pt->framesize >= LJ_MAX_JSLOTS) + lj_trace_err(J, LJ_TRERR_STACKOV); + } +#if LJ_HASPROFILE + J->prev_pt = NULL; + J->prev_line = -1; +#endif +#ifdef LUAJIT_ENABLE_CHECKHOOK + /* Regularly check for instruction/line hooks from compiled code and + ** exit to the interpreter if the hooks are set. + ** + ** This is a compile-time option and disabled by default, since the + ** hook checks may be quite expensive in tight loops. + ** + ** Note this is only useful if hooks are *not* set most of the time. + ** Use this only if you want to *asynchronously* interrupt the execution. + ** + ** You can set the instruction hook via lua_sethook() with a count of 1 + ** from a signal handler or another native thread. Please have a look + ** at the first few functions in luajit.c for an example (Ctrl-C handler). + */ + { + TRef tr = emitir(IRT(IR_XLOAD, IRT_U8), + lj_ir_kptr(J, &J2G(J)->hookmask), IRXLOAD_VOLATILE); + tr = emitir(IRTI(IR_BAND), tr, lj_ir_kint(J, (LUA_MASKLINE|LUA_MASKCOUNT))); + emitir(IRTGI(IR_EQ), tr, lj_ir_kint(J, 0)); + } +#endif +} + +#undef IR +#undef emitir_raw +#undef emitir + +#endif diff --git a/lib/LuaJIT/lj_record.h b/lib/LuaJIT/lj_record.h new file mode 100644 index 0000000..93d374d --- /dev/null +++ b/lib/LuaJIT/lj_record.h @@ -0,0 +1,45 @@ +/* +** Trace recorder (bytecode -> SSA IR). +** Copyright (C) 2005-2017 Mike Pall. See Copyright Notice in luajit.h +*/ + +#ifndef _LJ_RECORD_H +#define _LJ_RECORD_H + +#include "lj_obj.h" +#include "lj_jit.h" + +#if LJ_HASJIT +/* Context for recording an indexed load/store. */ +typedef struct RecordIndex { + TValue tabv; /* Runtime value of table (or indexed object). */ + TValue keyv; /* Runtime value of key. */ + TValue valv; /* Runtime value of stored value. */ + TValue mobjv; /* Runtime value of metamethod object. */ + GCtab *mtv; /* Runtime value of metatable object. */ + cTValue *oldv; /* Runtime value of previously stored value. */ + TRef tab; /* Table (or indexed object) reference. */ + TRef key; /* Key reference. */ + TRef val; /* Value reference for a store or 0 for a load. */ + TRef mt; /* Metatable reference. */ + TRef mobj; /* Metamethod object reference. */ + int idxchain; /* Index indirections left or 0 for raw lookup. */ +} RecordIndex; + +LJ_FUNC int lj_record_objcmp(jit_State *J, TRef a, TRef b, + cTValue *av, cTValue *bv); +LJ_FUNC void lj_record_stop(jit_State *J, TraceLink linktype, TraceNo lnk); +LJ_FUNC TRef lj_record_constify(jit_State *J, cTValue *o); + +LJ_FUNC void lj_record_call(jit_State *J, BCReg func, ptrdiff_t nargs); +LJ_FUNC void lj_record_tailcall(jit_State *J, BCReg func, ptrdiff_t nargs); +LJ_FUNC void lj_record_ret(jit_State *J, BCReg rbase, ptrdiff_t gotresults); + +LJ_FUNC int lj_record_mm_lookup(jit_State *J, RecordIndex *ix, MMS mm); +LJ_FUNC TRef lj_record_idx(jit_State *J, RecordIndex *ix); + +LJ_FUNC void lj_record_ins(jit_State *J); +LJ_FUNC void lj_record_setup(jit_State *J); +#endif + +#endif diff --git a/lib/LuaJIT/lj_record.o b/lib/LuaJIT/lj_record.o new file mode 100644 index 0000000000000000000000000000000000000000..8b3e7665249bfd499c2377187023d3e42ffb4b2b GIT binary patch literal 46768 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 literal 0 HcmV?d00001 diff --git a/lib/LuaJIT/lj_record_dyn.o b/lib/LuaJIT/lj_record_dyn.o new file mode 100644 index 0000000000000000000000000000000000000000..8b3e7665249bfd499c2377187023d3e42ffb4b2b GIT binary patch literal 46768 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 literal 0 HcmV?d00001 diff --git a/lib/LuaJIT/lj_snap.c b/lib/LuaJIT/lj_snap.c new file mode 100644 index 0000000..ceaf2ca --- /dev/null +++ b/lib/LuaJIT/lj_snap.c @@ -0,0 +1,916 @@ +/* +** Snapshot handling. +** Copyright (C) 2005-2017 Mike Pall. See Copyright Notice in luajit.h +*/ + +#define lj_snap_c +#define LUA_CORE + +#include "lj_obj.h" + +#if LJ_HASJIT + +#include "lj_gc.h" +#include "lj_tab.h" +#include "lj_state.h" +#include "lj_frame.h" +#include "lj_bc.h" +#include "lj_ir.h" +#include "lj_jit.h" +#include "lj_iropt.h" +#include "lj_trace.h" +#include "lj_snap.h" +#include "lj_target.h" +#if LJ_HASFFI +#include "lj_ctype.h" +#include "lj_cdata.h" +#endif + +/* Pass IR on to next optimization in chain (FOLD). */ +#define emitir(ot, a, b) (lj_ir_set(J, (ot), (a), (b)), lj_opt_fold(J)) + +/* Emit raw IR without passing through optimizations. */ +#define emitir_raw(ot, a, b) (lj_ir_set(J, (ot), (a), (b)), lj_ir_emit(J)) + +/* -- Snapshot buffer allocation ------------------------------------------ */ + +/* Grow snapshot buffer. */ +void lj_snap_grow_buf_(jit_State *J, MSize need) +{ + MSize maxsnap = (MSize)J->param[JIT_P_maxsnap]; + if (need > maxsnap) + lj_trace_err(J, LJ_TRERR_SNAPOV); + lj_mem_growvec(J->L, J->snapbuf, J->sizesnap, maxsnap, SnapShot); + J->cur.snap = J->snapbuf; +} + +/* Grow snapshot map buffer. */ +void lj_snap_grow_map_(jit_State *J, MSize need) +{ + if (need < 2*J->sizesnapmap) + need = 2*J->sizesnapmap; + else if (need < 64) + need = 64; + J->snapmapbuf = (SnapEntry *)lj_mem_realloc(J->L, J->snapmapbuf, + J->sizesnapmap*sizeof(SnapEntry), need*sizeof(SnapEntry)); + J->cur.snapmap = J->snapmapbuf; + J->sizesnapmap = need; +} + +/* -- Snapshot generation ------------------------------------------------- */ + +/* Add all modified slots to the snapshot. */ +static MSize snapshot_slots(jit_State *J, SnapEntry *map, BCReg nslots) +{ + IRRef retf = J->chain[IR_RETF]; /* Limits SLOAD restore elimination. */ + BCReg s; + MSize n = 0; + for (s = 0; s < nslots; s++) { + TRef tr = J->slot[s]; + IRRef ref = tref_ref(tr); +#if LJ_FR2 + if (s == 1) { /* Ignore slot 1 in LJ_FR2 mode, except if tailcalled. */ + if ((tr & TREF_FRAME)) + map[n++] = SNAP(1, SNAP_FRAME | SNAP_NORESTORE, REF_NIL); + continue; + } + if ((tr & (TREF_FRAME | TREF_CONT)) && !ref) { + cTValue *base = J->L->base - J->baseslot; + tr = J->slot[s] = (tr & 0xff0000) | lj_ir_k64(J, IR_KNUM, base[s].u64); + ref = tref_ref(tr); + } +#endif + if (ref) { + SnapEntry sn = SNAP_TR(s, tr); + IRIns *ir = &J->cur.ir[ref]; + if ((LJ_FR2 || !(sn & (SNAP_CONT|SNAP_FRAME))) && + ir->o == IR_SLOAD && ir->op1 == s && ref > retf) { + /* No need to snapshot unmodified non-inherited slots. */ + if (!(ir->op2 & IRSLOAD_INHERIT)) + continue; + /* No need to restore readonly slots and unmodified non-parent slots. */ + if (!(LJ_DUALNUM && (ir->op2 & IRSLOAD_CONVERT)) && + (ir->op2 & (IRSLOAD_READONLY|IRSLOAD_PARENT)) != IRSLOAD_PARENT) + sn |= SNAP_NORESTORE; + } + if (LJ_SOFTFP32 && irt_isnum(ir->t)) + sn |= SNAP_SOFTFPNUM; + map[n++] = sn; + } + } + return n; +} + +/* Add frame links at the end of the snapshot. */ +static MSize snapshot_framelinks(jit_State *J, SnapEntry *map, uint8_t *topslot) +{ + cTValue *frame = J->L->base - 1; + cTValue *lim = J->L->base - J->baseslot + LJ_FR2; + GCfunc *fn = frame_func(frame); + cTValue *ftop = isluafunc(fn) ? (frame+funcproto(fn)->framesize) : J->L->top; +#if LJ_FR2 + uint64_t pcbase = (u64ptr(J->pc) << 8) | (J->baseslot - 2); + lua_assert(2 <= J->baseslot && J->baseslot <= 257); + memcpy(map, &pcbase, sizeof(uint64_t)); +#else + MSize f = 0; + map[f++] = SNAP_MKPC(J->pc); /* The current PC is always the first entry. */ +#endif + while (frame > lim) { /* Backwards traversal of all frames above base. */ + if (frame_islua(frame)) { +#if !LJ_FR2 + map[f++] = SNAP_MKPC(frame_pc(frame)); +#endif + frame = frame_prevl(frame); + } else if (frame_iscont(frame)) { +#if !LJ_FR2 + map[f++] = SNAP_MKFTSZ(frame_ftsz(frame)); + map[f++] = SNAP_MKPC(frame_contpc(frame)); +#endif + frame = frame_prevd(frame); + } else { + lua_assert(!frame_isc(frame)); +#if !LJ_FR2 + map[f++] = SNAP_MKFTSZ(frame_ftsz(frame)); +#endif + frame = frame_prevd(frame); + continue; + } + if (frame + funcproto(frame_func(frame))->framesize > ftop) + ftop = frame + funcproto(frame_func(frame))->framesize; + } + *topslot = (uint8_t)(ftop - lim); +#if LJ_FR2 + lua_assert(sizeof(SnapEntry) * 2 == sizeof(uint64_t)); + return 2; +#else + lua_assert(f == (MSize)(1 + J->framedepth)); + return f; +#endif +} + +/* Take a snapshot of the current stack. */ +static void snapshot_stack(jit_State *J, SnapShot *snap, MSize nsnapmap) +{ + BCReg nslots = J->baseslot + J->maxslot; + MSize nent; + SnapEntry *p; + /* Conservative estimate. */ + lj_snap_grow_map(J, nsnapmap + nslots + (MSize)(LJ_FR2?2:J->framedepth+1)); + p = &J->cur.snapmap[nsnapmap]; + nent = snapshot_slots(J, p, nslots); + snap->nent = (uint8_t)nent; + nent += snapshot_framelinks(J, p + nent, &snap->topslot); + snap->mapofs = (uint32_t)nsnapmap; + snap->ref = (IRRef1)J->cur.nins; + snap->nslots = (uint8_t)nslots; + snap->count = 0; + J->cur.nsnapmap = (uint32_t)(nsnapmap + nent); +} + +/* Add or merge a snapshot. */ +void lj_snap_add(jit_State *J) +{ + MSize nsnap = J->cur.nsnap; + MSize nsnapmap = J->cur.nsnapmap; + /* Merge if no ins. inbetween or if requested and no guard inbetween. */ + if ((nsnap > 0 && J->cur.snap[nsnap-1].ref == J->cur.nins) || + (J->mergesnap && !irt_isguard(J->guardemit))) { + if (nsnap == 1) { /* But preserve snap #0 PC. */ + emitir_raw(IRT(IR_NOP, IRT_NIL), 0, 0); + goto nomerge; + } + nsnapmap = J->cur.snap[--nsnap].mapofs; + } else { + nomerge: + lj_snap_grow_buf(J, nsnap+1); + J->cur.nsnap = (uint16_t)(nsnap+1); + } + J->mergesnap = 0; + J->guardemit.irt = 0; + snapshot_stack(J, &J->cur.snap[nsnap], nsnapmap); +} + +/* -- Snapshot modification ----------------------------------------------- */ + +#define SNAP_USEDEF_SLOTS (LJ_MAX_JSLOTS+LJ_STACK_EXTRA) + +/* Find unused slots with reaching-definitions bytecode data-flow analysis. */ +static BCReg snap_usedef(jit_State *J, uint8_t *udf, + const BCIns *pc, BCReg maxslot) +{ + BCReg s; + GCobj *o; + + if (maxslot == 0) return 0; +#ifdef LUAJIT_USE_VALGRIND + /* Avoid errors for harmless reads beyond maxslot. */ + memset(udf, 1, SNAP_USEDEF_SLOTS); +#else + memset(udf, 1, maxslot); +#endif + + /* Treat open upvalues as used. */ + o = gcref(J->L->openupval); + while (o) { + if (uvval(gco2uv(o)) < J->L->base) break; + udf[uvval(gco2uv(o)) - J->L->base] = 0; + o = gcref(o->gch.nextgc); + } + +#define USE_SLOT(s) udf[(s)] &= ~1 +#define DEF_SLOT(s) udf[(s)] *= 3 + + /* Scan through following bytecode and check for uses/defs. */ + lua_assert(pc >= proto_bc(J->pt) && pc < proto_bc(J->pt) + J->pt->sizebc); + for (;;) { + BCIns ins = *pc++; + BCOp op = bc_op(ins); + switch (bcmode_b(op)) { + case BCMvar: USE_SLOT(bc_b(ins)); break; + default: break; + } + switch (bcmode_c(op)) { + case BCMvar: USE_SLOT(bc_c(ins)); break; + case BCMrbase: + lua_assert(op == BC_CAT); + for (s = bc_b(ins); s <= bc_c(ins); s++) USE_SLOT(s); + for (; s < maxslot; s++) DEF_SLOT(s); + break; + case BCMjump: + handle_jump: { + BCReg minslot = bc_a(ins); + if (op >= BC_FORI && op <= BC_JFORL) minslot += FORL_EXT; + else if (op >= BC_ITERL && op <= BC_JITERL) minslot += bc_b(pc[-2])-1; + else if (op == BC_UCLO) { pc += bc_j(ins); break; } + for (s = minslot; s < maxslot; s++) DEF_SLOT(s); + return minslot < maxslot ? minslot : maxslot; + } + case BCMlit: + if (op == BC_JFORL || op == BC_JITERL || op == BC_JLOOP) { + goto handle_jump; + } else if (bc_isret(op)) { + BCReg top = op == BC_RETM ? maxslot : (bc_a(ins) + bc_d(ins)-1); + for (s = 0; s < bc_a(ins); s++) DEF_SLOT(s); + for (; s < top; s++) USE_SLOT(s); + for (; s < maxslot; s++) DEF_SLOT(s); + return 0; + } + break; + case BCMfunc: return maxslot; /* NYI: will abort, anyway. */ + default: break; + } + switch (bcmode_a(op)) { + case BCMvar: USE_SLOT(bc_a(ins)); break; + case BCMdst: + if (!(op == BC_ISTC || op == BC_ISFC)) DEF_SLOT(bc_a(ins)); + break; + case BCMbase: + if (op >= BC_CALLM && op <= BC_VARG) { + BCReg top = (op == BC_CALLM || op == BC_CALLMT || bc_c(ins) == 0) ? + maxslot : (bc_a(ins) + bc_c(ins)+LJ_FR2); + if (LJ_FR2) DEF_SLOT(bc_a(ins)+1); + s = bc_a(ins) - ((op == BC_ITERC || op == BC_ITERN) ? 3 : 0); + for (; s < top; s++) USE_SLOT(s); + for (; s < maxslot; s++) DEF_SLOT(s); + if (op == BC_CALLT || op == BC_CALLMT) { + for (s = 0; s < bc_a(ins); s++) DEF_SLOT(s); + return 0; + } + } else if (op == BC_KNIL) { + for (s = bc_a(ins); s <= bc_d(ins); s++) DEF_SLOT(s); + } else if (op == BC_TSETM) { + for (s = bc_a(ins)-1; s < maxslot; s++) USE_SLOT(s); + } + break; + default: break; + } + lua_assert(pc >= proto_bc(J->pt) && pc < proto_bc(J->pt) + J->pt->sizebc); + } + +#undef USE_SLOT +#undef DEF_SLOT + + return 0; /* unreachable */ +} + +/* Purge dead slots before the next snapshot. */ +void lj_snap_purge(jit_State *J) +{ + uint8_t udf[SNAP_USEDEF_SLOTS]; + BCReg maxslot = J->maxslot; + BCReg s = snap_usedef(J, udf, J->pc, maxslot); + for (; s < maxslot; s++) + if (udf[s] != 0) + J->base[s] = 0; /* Purge dead slots. */ +} + +/* Shrink last snapshot. */ +void lj_snap_shrink(jit_State *J) +{ + SnapShot *snap = &J->cur.snap[J->cur.nsnap-1]; + SnapEntry *map = &J->cur.snapmap[snap->mapofs]; + MSize n, m, nlim, nent = snap->nent; + uint8_t udf[SNAP_USEDEF_SLOTS]; + BCReg maxslot = J->maxslot; + BCReg baseslot = J->baseslot; + BCReg minslot = snap_usedef(J, udf, snap_pc(&map[nent]), maxslot); + maxslot += baseslot; + minslot += baseslot; + snap->nslots = (uint8_t)maxslot; + for (n = m = 0; n < nent; n++) { /* Remove unused slots from snapshot. */ + BCReg s = snap_slot(map[n]); + if (s < minslot || (s < maxslot && udf[s-baseslot] == 0)) + map[m++] = map[n]; /* Only copy used slots. */ + } + snap->nent = (uint8_t)m; + nlim = J->cur.nsnapmap - snap->mapofs - 1; + while (n <= nlim) map[m++] = map[n++]; /* Move PC + frame links down. */ + J->cur.nsnapmap = (uint32_t)(snap->mapofs + m); /* Free up space in map. */ +} + +/* -- Snapshot access ----------------------------------------------------- */ + +/* Initialize a Bloom Filter with all renamed refs. +** There are very few renames (often none), so the filter has +** very few bits set. This makes it suitable for negative filtering. +*/ +static BloomFilter snap_renamefilter(GCtrace *T, SnapNo lim) +{ + BloomFilter rfilt = 0; + IRIns *ir; + for (ir = &T->ir[T->nins-1]; ir->o == IR_RENAME; ir--) + if (ir->op2 <= lim) + bloomset(rfilt, ir->op1); + return rfilt; +} + +/* Process matching renames to find the original RegSP. */ +static RegSP snap_renameref(GCtrace *T, SnapNo lim, IRRef ref, RegSP rs) +{ + IRIns *ir; + for (ir = &T->ir[T->nins-1]; ir->o == IR_RENAME; ir--) + if (ir->op1 == ref && ir->op2 <= lim) + rs = ir->prev; + return rs; +} + +/* Copy RegSP from parent snapshot to the parent links of the IR. */ +IRIns *lj_snap_regspmap(GCtrace *T, SnapNo snapno, IRIns *ir) +{ + SnapShot *snap = &T->snap[snapno]; + SnapEntry *map = &T->snapmap[snap->mapofs]; + BloomFilter rfilt = snap_renamefilter(T, snapno); + MSize n = 0; + IRRef ref = 0; + for ( ; ; ir++) { + uint32_t rs; + if (ir->o == IR_SLOAD) { + if (!(ir->op2 & IRSLOAD_PARENT)) break; + for ( ; ; n++) { + lua_assert(n < snap->nent); + if (snap_slot(map[n]) == ir->op1) { + ref = snap_ref(map[n++]); + break; + } + } + } else if (LJ_SOFTFP32 && ir->o == IR_HIOP) { + ref++; + } else if (ir->o == IR_PVAL) { + ref = ir->op1 + REF_BIAS; + } else { + break; + } + rs = T->ir[ref].prev; + if (bloomtest(rfilt, ref)) + rs = snap_renameref(T, snapno, ref, rs); + ir->prev = (uint16_t)rs; + lua_assert(regsp_used(rs)); + } + return ir; +} + +/* -- Snapshot replay ----------------------------------------------------- */ + +/* Replay constant from parent trace. */ +static TRef snap_replay_const(jit_State *J, IRIns *ir) +{ + /* Only have to deal with constants that can occur in stack slots. */ + switch ((IROp)ir->o) { + case IR_KPRI: return TREF_PRI(irt_type(ir->t)); + case IR_KINT: return lj_ir_kint(J, ir->i); + case IR_KGC: return lj_ir_kgc(J, ir_kgc(ir), irt_t(ir->t)); + case IR_KNUM: case IR_KINT64: + return lj_ir_k64(J, (IROp)ir->o, ir_k64(ir)->u64); + case IR_KPTR: return lj_ir_kptr(J, ir_kptr(ir)); /* Continuation. */ + default: lua_assert(0); return TREF_NIL; break; + } +} + +/* De-duplicate parent reference. */ +static TRef snap_dedup(jit_State *J, SnapEntry *map, MSize nmax, IRRef ref) +{ + MSize j; + for (j = 0; j < nmax; j++) + if (snap_ref(map[j]) == ref) + return J->slot[snap_slot(map[j])] & ~(SNAP_CONT|SNAP_FRAME); + return 0; +} + +/* Emit parent reference with de-duplication. */ +static TRef snap_pref(jit_State *J, GCtrace *T, SnapEntry *map, MSize nmax, + BloomFilter seen, IRRef ref) +{ + IRIns *ir = &T->ir[ref]; + TRef tr; + if (irref_isk(ref)) + tr = snap_replay_const(J, ir); + else if (!regsp_used(ir->prev)) + tr = 0; + else if (!bloomtest(seen, ref) || (tr = snap_dedup(J, map, nmax, ref)) == 0) + tr = emitir(IRT(IR_PVAL, irt_type(ir->t)), ref - REF_BIAS, 0); + return tr; +} + +/* Check whether a sunk store corresponds to an allocation. Slow path. */ +static int snap_sunk_store2(GCtrace *T, IRIns *ira, IRIns *irs) +{ + if (irs->o == IR_ASTORE || irs->o == IR_HSTORE || + irs->o == IR_FSTORE || irs->o == IR_XSTORE) { + IRIns *irk = &T->ir[irs->op1]; + if (irk->o == IR_AREF || irk->o == IR_HREFK) + irk = &T->ir[irk->op1]; + return (&T->ir[irk->op1] == ira); + } + return 0; +} + +/* Check whether a sunk store corresponds to an allocation. Fast path. */ +static LJ_AINLINE int snap_sunk_store(GCtrace *T, IRIns *ira, IRIns *irs) +{ + if (irs->s != 255) + return (ira + irs->s == irs); /* Fast check. */ + return snap_sunk_store2(T, ira, irs); +} + +/* Replay snapshot state to setup side trace. */ +void lj_snap_replay(jit_State *J, GCtrace *T) +{ + SnapShot *snap = &T->snap[J->exitno]; + SnapEntry *map = &T->snapmap[snap->mapofs]; + MSize n, nent = snap->nent; + BloomFilter seen = 0; + int pass23 = 0; + J->framedepth = 0; + /* Emit IR for slots inherited from parent snapshot. */ + for (n = 0; n < nent; n++) { + SnapEntry sn = map[n]; + BCReg s = snap_slot(sn); + IRRef ref = snap_ref(sn); + IRIns *ir = &T->ir[ref]; + TRef tr; + /* The bloom filter avoids O(nent^2) overhead for de-duping slots. */ + if (bloomtest(seen, ref) && (tr = snap_dedup(J, map, n, ref)) != 0) + goto setslot; + bloomset(seen, ref); + if (irref_isk(ref)) { + /* See special treatment of LJ_FR2 slot 1 in snapshot_slots() above. */ + if (LJ_FR2 && (sn == SNAP(1, SNAP_FRAME | SNAP_NORESTORE, REF_NIL))) + tr = 0; + else + tr = snap_replay_const(J, ir); + } else if (!regsp_used(ir->prev)) { + pass23 = 1; + lua_assert(s != 0); + tr = s; + } else { + IRType t = irt_type(ir->t); + uint32_t mode = IRSLOAD_INHERIT|IRSLOAD_PARENT; + if (LJ_SOFTFP32 && (sn & SNAP_SOFTFPNUM)) t = IRT_NUM; + if (ir->o == IR_SLOAD) mode |= (ir->op2 & IRSLOAD_READONLY); + tr = emitir_raw(IRT(IR_SLOAD, t), s, mode); + } + setslot: + J->slot[s] = tr | (sn&(SNAP_CONT|SNAP_FRAME)); /* Same as TREF_* flags. */ + J->framedepth += ((sn & (SNAP_CONT|SNAP_FRAME)) && (s != LJ_FR2)); + if ((sn & SNAP_FRAME)) + J->baseslot = s+1; + } + if (pass23) { + IRIns *irlast = &T->ir[snap->ref]; + pass23 = 0; + /* Emit dependent PVALs. */ + for (n = 0; n < nent; n++) { + SnapEntry sn = map[n]; + IRRef refp = snap_ref(sn); + IRIns *ir = &T->ir[refp]; + if (regsp_reg(ir->r) == RID_SUNK) { + if (J->slot[snap_slot(sn)] != snap_slot(sn)) continue; + pass23 = 1; + lua_assert(ir->o == IR_TNEW || ir->o == IR_TDUP || + ir->o == IR_CNEW || ir->o == IR_CNEWI); + if (ir->op1 >= T->nk) snap_pref(J, T, map, nent, seen, ir->op1); + if (ir->op2 >= T->nk) snap_pref(J, T, map, nent, seen, ir->op2); + if (LJ_HASFFI && ir->o == IR_CNEWI) { + if (LJ_32 && refp+1 < T->nins && (ir+1)->o == IR_HIOP) + snap_pref(J, T, map, nent, seen, (ir+1)->op2); + } else { + IRIns *irs; + for (irs = ir+1; irs < irlast; irs++) + if (irs->r == RID_SINK && snap_sunk_store(T, ir, irs)) { + if (snap_pref(J, T, map, nent, seen, irs->op2) == 0) + snap_pref(J, T, map, nent, seen, T->ir[irs->op2].op1); + else if ((LJ_SOFTFP32 || (LJ_32 && LJ_HASFFI)) && + irs+1 < irlast && (irs+1)->o == IR_HIOP) + snap_pref(J, T, map, nent, seen, (irs+1)->op2); + } + } + } else if (!irref_isk(refp) && !regsp_used(ir->prev)) { + lua_assert(ir->o == IR_CONV && ir->op2 == IRCONV_NUM_INT); + J->slot[snap_slot(sn)] = snap_pref(J, T, map, nent, seen, ir->op1); + } + } + /* Replay sunk instructions. */ + for (n = 0; pass23 && n < nent; n++) { + SnapEntry sn = map[n]; + IRRef refp = snap_ref(sn); + IRIns *ir = &T->ir[refp]; + if (regsp_reg(ir->r) == RID_SUNK) { + TRef op1, op2; + if (J->slot[snap_slot(sn)] != snap_slot(sn)) { /* De-dup allocs. */ + J->slot[snap_slot(sn)] = J->slot[J->slot[snap_slot(sn)]]; + continue; + } + op1 = ir->op1; + if (op1 >= T->nk) op1 = snap_pref(J, T, map, nent, seen, op1); + op2 = ir->op2; + if (op2 >= T->nk) op2 = snap_pref(J, T, map, nent, seen, op2); + if (LJ_HASFFI && ir->o == IR_CNEWI) { + if (LJ_32 && refp+1 < T->nins && (ir+1)->o == IR_HIOP) { + lj_needsplit(J); /* Emit joining HIOP. */ + op2 = emitir_raw(IRT(IR_HIOP, IRT_I64), op2, + snap_pref(J, T, map, nent, seen, (ir+1)->op2)); + } + J->slot[snap_slot(sn)] = emitir(ir->ot & ~(IRT_MARK|IRT_ISPHI), op1, op2); + } else { + IRIns *irs; + TRef tr = emitir(ir->ot, op1, op2); + J->slot[snap_slot(sn)] = tr; + for (irs = ir+1; irs < irlast; irs++) + if (irs->r == RID_SINK && snap_sunk_store(T, ir, irs)) { + IRIns *irr = &T->ir[irs->op1]; + TRef val, key = irr->op2, tmp = tr; + if (irr->o != IR_FREF) { + IRIns *irk = &T->ir[key]; + if (irr->o == IR_HREFK) + key = lj_ir_kslot(J, snap_replay_const(J, &T->ir[irk->op1]), + irk->op2); + else + key = snap_replay_const(J, irk); + if (irr->o == IR_HREFK || irr->o == IR_AREF) { + IRIns *irf = &T->ir[irr->op1]; + tmp = emitir(irf->ot, tmp, irf->op2); + } + } + tmp = emitir(irr->ot, tmp, key); + val = snap_pref(J, T, map, nent, seen, irs->op2); + if (val == 0) { + IRIns *irc = &T->ir[irs->op2]; + lua_assert(irc->o == IR_CONV && irc->op2 == IRCONV_NUM_INT); + val = snap_pref(J, T, map, nent, seen, irc->op1); + val = emitir(IRTN(IR_CONV), val, IRCONV_NUM_INT); + } else if ((LJ_SOFTFP32 || (LJ_32 && LJ_HASFFI)) && + irs+1 < irlast && (irs+1)->o == IR_HIOP) { + IRType t = IRT_I64; + if (LJ_SOFTFP32 && irt_type((irs+1)->t) == IRT_SOFTFP) + t = IRT_NUM; + lj_needsplit(J); + if (irref_isk(irs->op2) && irref_isk((irs+1)->op2)) { + uint64_t k = (uint32_t)T->ir[irs->op2].i + + ((uint64_t)T->ir[(irs+1)->op2].i << 32); + val = lj_ir_k64(J, t == IRT_I64 ? IR_KINT64 : IR_KNUM, k); + } else { + val = emitir_raw(IRT(IR_HIOP, t), val, + snap_pref(J, T, map, nent, seen, (irs+1)->op2)); + } + tmp = emitir(IRT(irs->o, t), tmp, val); + continue; + } + tmp = emitir(irs->ot, tmp, val); + } else if (LJ_HASFFI && irs->o == IR_XBAR && ir->o == IR_CNEW) { + emitir(IRT(IR_XBAR, IRT_NIL), 0, 0); + } + } + } + } + } + J->base = J->slot + J->baseslot; + J->maxslot = snap->nslots - J->baseslot; + lj_snap_add(J); + if (pass23) /* Need explicit GC step _after_ initial snapshot. */ + emitir_raw(IRTG(IR_GCSTEP, IRT_NIL), 0, 0); +} + +/* -- Snapshot restore ---------------------------------------------------- */ + +static void snap_unsink(jit_State *J, GCtrace *T, ExitState *ex, + SnapNo snapno, BloomFilter rfilt, + IRIns *ir, TValue *o); + +/* Restore a value from the trace exit state. */ +static void snap_restoreval(jit_State *J, GCtrace *T, ExitState *ex, + SnapNo snapno, BloomFilter rfilt, + IRRef ref, TValue *o) +{ + IRIns *ir = &T->ir[ref]; + IRType1 t = ir->t; + RegSP rs = ir->prev; + if (irref_isk(ref)) { /* Restore constant slot. */ + lj_ir_kvalue(J->L, o, ir); + return; + } + if (LJ_UNLIKELY(bloomtest(rfilt, ref))) + rs = snap_renameref(T, snapno, ref, rs); + if (ra_hasspill(regsp_spill(rs))) { /* Restore from spill slot. */ + int32_t *sps = &ex->spill[regsp_spill(rs)]; + if (irt_isinteger(t)) { + setintV(o, *sps); +#if !LJ_SOFTFP32 + } else if (irt_isnum(t)) { + o->u64 = *(uint64_t *)sps; +#endif +#if LJ_64 && !LJ_GC64 + } else if (irt_islightud(t)) { + /* 64 bit lightuserdata which may escape already has the tag bits. */ + o->u64 = *(uint64_t *)sps; +#endif + } else { + lua_assert(!irt_ispri(t)); /* PRI refs never have a spill slot. */ + setgcV(J->L, o, (GCobj *)(uintptr_t)*(GCSize *)sps, irt_toitype(t)); + } + } else { /* Restore from register. */ + Reg r = regsp_reg(rs); + if (ra_noreg(r)) { + lua_assert(ir->o == IR_CONV && ir->op2 == IRCONV_NUM_INT); + snap_restoreval(J, T, ex, snapno, rfilt, ir->op1, o); + if (LJ_DUALNUM) setnumV(o, (lua_Number)intV(o)); + return; + } else if (irt_isinteger(t)) { + setintV(o, (int32_t)ex->gpr[r-RID_MIN_GPR]); +#if !LJ_SOFTFP + } else if (irt_isnum(t)) { + setnumV(o, ex->fpr[r-RID_MIN_FPR]); +#elif LJ_64 /* && LJ_SOFTFP */ + } else if (irt_isnum(t)) { + o->u64 = ex->gpr[r-RID_MIN_GPR]; +#endif +#if LJ_64 && !LJ_GC64 + } else if (irt_is64(t)) { + /* 64 bit values that already have the tag bits. */ + o->u64 = ex->gpr[r-RID_MIN_GPR]; +#endif + } else if (irt_ispri(t)) { + setpriV(o, irt_toitype(t)); + } else { + setgcV(J->L, o, (GCobj *)ex->gpr[r-RID_MIN_GPR], irt_toitype(t)); + } + } +} + +#if LJ_HASFFI +/* Restore raw data from the trace exit state. */ +static void snap_restoredata(GCtrace *T, ExitState *ex, + SnapNo snapno, BloomFilter rfilt, + IRRef ref, void *dst, CTSize sz) +{ + IRIns *ir = &T->ir[ref]; + RegSP rs = ir->prev; + int32_t *src; + uint64_t tmp; + if (irref_isk(ref)) { + if (ir->o == IR_KNUM || ir->o == IR_KINT64) { + src = (int32_t *)&ir[1]; + } else if (sz == 8) { + tmp = (uint64_t)(uint32_t)ir->i; + src = (int32_t *)&tmp; + } else { + src = &ir->i; + } + } else { + if (LJ_UNLIKELY(bloomtest(rfilt, ref))) + rs = snap_renameref(T, snapno, ref, rs); + if (ra_hasspill(regsp_spill(rs))) { + src = &ex->spill[regsp_spill(rs)]; + if (sz == 8 && !irt_is64(ir->t)) { + tmp = (uint64_t)(uint32_t)*src; + src = (int32_t *)&tmp; + } + } else { + Reg r = regsp_reg(rs); + if (ra_noreg(r)) { + /* Note: this assumes CNEWI is never used for SOFTFP split numbers. */ + lua_assert(sz == 8 && ir->o == IR_CONV && ir->op2 == IRCONV_NUM_INT); + snap_restoredata(T, ex, snapno, rfilt, ir->op1, dst, 4); + *(lua_Number *)dst = (lua_Number)*(int32_t *)dst; + return; + } + src = (int32_t *)&ex->gpr[r-RID_MIN_GPR]; +#if !LJ_SOFTFP + if (r >= RID_MAX_GPR) { + src = (int32_t *)&ex->fpr[r-RID_MIN_FPR]; +#if LJ_TARGET_PPC + if (sz == 4) { /* PPC FPRs are always doubles. */ + *(float *)dst = (float)*(double *)src; + return; + } +#else + if (LJ_BE && sz == 4) src++; +#endif + } else +#endif + if (LJ_64 && LJ_BE && sz == 4) src++; + } + } + lua_assert(sz == 1 || sz == 2 || sz == 4 || sz == 8); + if (sz == 4) *(int32_t *)dst = *src; + else if (sz == 8) *(int64_t *)dst = *(int64_t *)src; + else if (sz == 1) *(int8_t *)dst = (int8_t)*src; + else *(int16_t *)dst = (int16_t)*src; +} +#endif + +/* Unsink allocation from the trace exit state. Unsink sunk stores. */ +static void snap_unsink(jit_State *J, GCtrace *T, ExitState *ex, + SnapNo snapno, BloomFilter rfilt, + IRIns *ir, TValue *o) +{ + lua_assert(ir->o == IR_TNEW || ir->o == IR_TDUP || + ir->o == IR_CNEW || ir->o == IR_CNEWI); +#if LJ_HASFFI + if (ir->o == IR_CNEW || ir->o == IR_CNEWI) { + CTState *cts = ctype_cts(J->L); + CTypeID id = (CTypeID)T->ir[ir->op1].i; + CTSize sz; + CTInfo info = lj_ctype_info(cts, id, &sz); + GCcdata *cd = lj_cdata_newx(cts, id, sz, info); + setcdataV(J->L, o, cd); + if (ir->o == IR_CNEWI) { + uint8_t *p = (uint8_t *)cdataptr(cd); + lua_assert(sz == 4 || sz == 8); + if (LJ_32 && sz == 8 && ir+1 < T->ir + T->nins && (ir+1)->o == IR_HIOP) { + snap_restoredata(T, ex, snapno, rfilt, (ir+1)->op2, LJ_LE?p+4:p, 4); + if (LJ_BE) p += 4; + sz = 4; + } + snap_restoredata(T, ex, snapno, rfilt, ir->op2, p, sz); + } else { + IRIns *irs, *irlast = &T->ir[T->snap[snapno].ref]; + for (irs = ir+1; irs < irlast; irs++) + if (irs->r == RID_SINK && snap_sunk_store(T, ir, irs)) { + IRIns *iro = &T->ir[T->ir[irs->op1].op2]; + uint8_t *p = (uint8_t *)cd; + CTSize szs; + lua_assert(irs->o == IR_XSTORE && T->ir[irs->op1].o == IR_ADD); + lua_assert(iro->o == IR_KINT || iro->o == IR_KINT64); + if (irt_is64(irs->t)) szs = 8; + else if (irt_isi8(irs->t) || irt_isu8(irs->t)) szs = 1; + else if (irt_isi16(irs->t) || irt_isu16(irs->t)) szs = 2; + else szs = 4; + if (LJ_64 && iro->o == IR_KINT64) + p += (int64_t)ir_k64(iro)->u64; + else + p += iro->i; + lua_assert(p >= (uint8_t *)cdataptr(cd) && + p + szs <= (uint8_t *)cdataptr(cd) + sz); + if (LJ_32 && irs+1 < T->ir + T->nins && (irs+1)->o == IR_HIOP) { + lua_assert(szs == 4); + snap_restoredata(T, ex, snapno, rfilt, (irs+1)->op2, LJ_LE?p+4:p,4); + if (LJ_BE) p += 4; + } + snap_restoredata(T, ex, snapno, rfilt, irs->op2, p, szs); + } + } + } else +#endif + { + IRIns *irs, *irlast; + GCtab *t = ir->o == IR_TNEW ? lj_tab_new(J->L, ir->op1, ir->op2) : + lj_tab_dup(J->L, ir_ktab(&T->ir[ir->op1])); + settabV(J->L, o, t); + irlast = &T->ir[T->snap[snapno].ref]; + for (irs = ir+1; irs < irlast; irs++) + if (irs->r == RID_SINK && snap_sunk_store(T, ir, irs)) { + IRIns *irk = &T->ir[irs->op1]; + TValue tmp, *val; + lua_assert(irs->o == IR_ASTORE || irs->o == IR_HSTORE || + irs->o == IR_FSTORE); + if (irk->o == IR_FREF) { + lua_assert(irk->op2 == IRFL_TAB_META); + snap_restoreval(J, T, ex, snapno, rfilt, irs->op2, &tmp); + /* NOBARRIER: The table is new (marked white). */ + setgcref(t->metatable, obj2gco(tabV(&tmp))); + } else { + irk = &T->ir[irk->op2]; + if (irk->o == IR_KSLOT) irk = &T->ir[irk->op1]; + lj_ir_kvalue(J->L, &tmp, irk); + val = lj_tab_set(J->L, t, &tmp); + /* NOBARRIER: The table is new (marked white). */ + snap_restoreval(J, T, ex, snapno, rfilt, irs->op2, val); + if (LJ_SOFTFP32 && irs+1 < T->ir + T->nins && (irs+1)->o == IR_HIOP) { + snap_restoreval(J, T, ex, snapno, rfilt, (irs+1)->op2, &tmp); + val->u32.hi = tmp.u32.lo; + } + } + } + } +} + +/* Restore interpreter state from exit state with the help of a snapshot. */ +const BCIns *lj_snap_restore(jit_State *J, void *exptr) +{ + ExitState *ex = (ExitState *)exptr; + SnapNo snapno = J->exitno; /* For now, snapno == exitno. */ + GCtrace *T = traceref(J, J->parent); + SnapShot *snap = &T->snap[snapno]; + MSize n, nent = snap->nent; + SnapEntry *map = &T->snapmap[snap->mapofs]; +#if !LJ_FR2 || defined(LUA_USE_ASSERT) + SnapEntry *flinks = &T->snapmap[snap_nextofs(T, snap)-1-LJ_FR2]; +#endif +#if !LJ_FR2 + ptrdiff_t ftsz0; +#endif + TValue *frame; + BloomFilter rfilt = snap_renamefilter(T, snapno); + const BCIns *pc = snap_pc(&map[nent]); + lua_State *L = J->L; + + /* Set interpreter PC to the next PC to get correct error messages. */ + setcframe_pc(cframe_raw(L->cframe), pc+1); + + /* Make sure the stack is big enough for the slots from the snapshot. */ + if (LJ_UNLIKELY(L->base + snap->topslot >= tvref(L->maxstack))) { + L->top = curr_topL(L); + lj_state_growstack(L, snap->topslot - curr_proto(L)->framesize); + } + + /* Fill stack slots with data from the registers and spill slots. */ + frame = L->base-1-LJ_FR2; +#if !LJ_FR2 + ftsz0 = frame_ftsz(frame); /* Preserve link to previous frame in slot #0. */ +#endif + for (n = 0; n < nent; n++) { + SnapEntry sn = map[n]; + if (!(sn & SNAP_NORESTORE)) { + TValue *o = &frame[snap_slot(sn)]; + IRRef ref = snap_ref(sn); + IRIns *ir = &T->ir[ref]; + if (ir->r == RID_SUNK) { + MSize j; + for (j = 0; j < n; j++) + if (snap_ref(map[j]) == ref) { /* De-duplicate sunk allocations. */ + copyTV(L, o, &frame[snap_slot(map[j])]); + goto dupslot; + } + snap_unsink(J, T, ex, snapno, rfilt, ir, o); + dupslot: + continue; + } + snap_restoreval(J, T, ex, snapno, rfilt, ref, o); + if (LJ_SOFTFP32 && (sn & SNAP_SOFTFPNUM) && tvisint(o)) { + TValue tmp; + snap_restoreval(J, T, ex, snapno, rfilt, ref+1, &tmp); + o->u32.hi = tmp.u32.lo; +#if !LJ_FR2 + } else if ((sn & (SNAP_CONT|SNAP_FRAME))) { + /* Overwrite tag with frame link. */ + setframe_ftsz(o, snap_slot(sn) != 0 ? (int32_t)*flinks-- : ftsz0); + L->base = o+1; +#endif + } + } + } +#if LJ_FR2 + L->base += (map[nent+LJ_BE] & 0xff); +#endif + lua_assert(map + nent == flinks); + + /* Compute current stack top. */ + switch (bc_op(*pc)) { + default: + if (bc_op(*pc) < BC_FUNCF) { + L->top = curr_topL(L); + break; + } + /* fallthrough */ + case BC_CALLM: case BC_CALLMT: case BC_RETM: case BC_TSETM: + L->top = frame + snap->nslots; + break; + } + return pc; +} + +#undef emitir_raw +#undef emitir + +#endif diff --git a/lib/LuaJIT/lj_snap.h b/lib/LuaJIT/lj_snap.h new file mode 100644 index 0000000..2c9ae3d --- /dev/null +++ b/lib/LuaJIT/lj_snap.h @@ -0,0 +1,34 @@ +/* +** Snapshot handling. +** Copyright (C) 2005-2017 Mike Pall. See Copyright Notice in luajit.h +*/ + +#ifndef _LJ_SNAP_H +#define _LJ_SNAP_H + +#include "lj_obj.h" +#include "lj_jit.h" + +#if LJ_HASJIT +LJ_FUNC void lj_snap_add(jit_State *J); +LJ_FUNC void lj_snap_purge(jit_State *J); +LJ_FUNC void lj_snap_shrink(jit_State *J); +LJ_FUNC IRIns *lj_snap_regspmap(GCtrace *T, SnapNo snapno, IRIns *ir); +LJ_FUNC void lj_snap_replay(jit_State *J, GCtrace *T); +LJ_FUNC const BCIns *lj_snap_restore(jit_State *J, void *exptr); +LJ_FUNC void lj_snap_grow_buf_(jit_State *J, MSize need); +LJ_FUNC void lj_snap_grow_map_(jit_State *J, MSize need); + +static LJ_AINLINE void lj_snap_grow_buf(jit_State *J, MSize need) +{ + if (LJ_UNLIKELY(need > J->sizesnap)) lj_snap_grow_buf_(J, need); +} + +static LJ_AINLINE void lj_snap_grow_map(jit_State *J, MSize need) +{ + if (LJ_UNLIKELY(need > J->sizesnapmap)) lj_snap_grow_map_(J, need); +} + +#endif + +#endif diff --git a/lib/LuaJIT/lj_snap.o b/lib/LuaJIT/lj_snap.o new file mode 100644 index 0000000000000000000000000000000000000000..078ed427963f17393206ec5348ae20d78d5ef26e GIT binary patch literal 13408 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 literal 0 HcmV?d00001 diff --git a/lib/LuaJIT/lj_snap_dyn.o b/lib/LuaJIT/lj_snap_dyn.o new file mode 100644 index 0000000000000000000000000000000000000000..078ed427963f17393206ec5348ae20d78d5ef26e GIT binary patch literal 13408 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 literal 0 HcmV?d00001 diff --git a/lib/LuaJIT/lj_state.c b/lib/LuaJIT/lj_state.c new file mode 100644 index 0000000..632dd07 --- /dev/null +++ b/lib/LuaJIT/lj_state.c @@ -0,0 +1,300 @@ +/* +** State and stack handling. +** Copyright (C) 2005-2017 Mike Pall. See Copyright Notice in luajit.h +** +** Portions taken verbatim or adapted from the Lua interpreter. +** Copyright (C) 1994-2008 Lua.org, PUC-Rio. See Copyright Notice in lua.h +*/ + +#define lj_state_c +#define LUA_CORE + +#include "lj_obj.h" +#include "lj_gc.h" +#include "lj_err.h" +#include "lj_buf.h" +#include "lj_str.h" +#include "lj_tab.h" +#include "lj_func.h" +#include "lj_meta.h" +#include "lj_state.h" +#include "lj_frame.h" +#if LJ_HASFFI +#include "lj_ctype.h" +#endif +#include "lj_trace.h" +#include "lj_dispatch.h" +#include "lj_vm.h" +#include "lj_lex.h" +#include "lj_alloc.h" +#include "luajit.h" + +/* -- Stack handling ------------------------------------------------------ */ + +/* Stack sizes. */ +#define LJ_STACK_MIN LUA_MINSTACK /* Min. stack size. */ +#define LJ_STACK_MAX LUAI_MAXSTACK /* Max. stack size. */ +#define LJ_STACK_START (2*LJ_STACK_MIN) /* Starting stack size. */ +#define LJ_STACK_MAXEX (LJ_STACK_MAX + 1 + LJ_STACK_EXTRA) + +/* Explanation of LJ_STACK_EXTRA: +** +** Calls to metamethods store their arguments beyond the current top +** without checking for the stack limit. This avoids stack resizes which +** would invalidate passed TValue pointers. The stack check is performed +** later by the function header. This can safely resize the stack or raise +** an error. Thus we need some extra slots beyond the current stack limit. +** +** Most metamethods need 4 slots above top (cont, mobj, arg1, arg2) plus +** one extra slot if mobj is not a function. Only lj_meta_tset needs 5 +** slots above top, but then mobj is always a function. So we can get by +** with 5 extra slots. +** LJ_FR2: We need 2 more slots for the frame PC and the continuation PC. +*/ + +/* Resize stack slots and adjust pointers in state. */ +static void resizestack(lua_State *L, MSize n) +{ + TValue *st, *oldst = tvref(L->stack); + ptrdiff_t delta; + MSize oldsize = L->stacksize; + MSize realsize = n + 1 + LJ_STACK_EXTRA; + GCobj *up; + lua_assert((MSize)(tvref(L->maxstack)-oldst)==L->stacksize-LJ_STACK_EXTRA-1); + st = (TValue *)lj_mem_realloc(L, tvref(L->stack), + (MSize)(oldsize*sizeof(TValue)), + (MSize)(realsize*sizeof(TValue))); + setmref(L->stack, st); + delta = (char *)st - (char *)oldst; + setmref(L->maxstack, st + n); + while (oldsize < realsize) /* Clear new slots. */ + setnilV(st + oldsize++); + L->stacksize = realsize; + if ((size_t)(mref(G(L)->jit_base, char) - (char *)oldst) < oldsize) + setmref(G(L)->jit_base, mref(G(L)->jit_base, char) + delta); + L->base = (TValue *)((char *)L->base + delta); + L->top = (TValue *)((char *)L->top + delta); + for (up = gcref(L->openupval); up != NULL; up = gcnext(up)) + setmref(gco2uv(up)->v, (TValue *)((char *)uvval(gco2uv(up)) + delta)); +} + +/* Relimit stack after error, in case the limit was overdrawn. */ +void lj_state_relimitstack(lua_State *L) +{ + if (L->stacksize > LJ_STACK_MAXEX && L->top-tvref(L->stack) < LJ_STACK_MAX-1) + resizestack(L, LJ_STACK_MAX); +} + +/* Try to shrink the stack (called from GC). */ +void lj_state_shrinkstack(lua_State *L, MSize used) +{ + if (L->stacksize > LJ_STACK_MAXEX) + return; /* Avoid stack shrinking while handling stack overflow. */ + if (4*used < L->stacksize && + 2*(LJ_STACK_START+LJ_STACK_EXTRA) < L->stacksize && + /* Don't shrink stack of live trace. */ + (tvref(G(L)->jit_base) == NULL || obj2gco(L) != gcref(G(L)->cur_L))) + resizestack(L, L->stacksize >> 1); +} + +/* Try to grow stack. */ +void LJ_FASTCALL lj_state_growstack(lua_State *L, MSize need) +{ + MSize n; + if (L->stacksize > LJ_STACK_MAXEX) /* Overflow while handling overflow? */ + lj_err_throw(L, LUA_ERRERR); + n = L->stacksize + need; + if (n > LJ_STACK_MAX) { + n += 2*LUA_MINSTACK; + } else if (n < 2*L->stacksize) { + n = 2*L->stacksize; + if (n >= LJ_STACK_MAX) + n = LJ_STACK_MAX; + } + resizestack(L, n); + if (L->stacksize > LJ_STACK_MAXEX) + lj_err_msg(L, LJ_ERR_STKOV); +} + +void LJ_FASTCALL lj_state_growstack1(lua_State *L) +{ + lj_state_growstack(L, 1); +} + +/* Allocate basic stack for new state. */ +static void stack_init(lua_State *L1, lua_State *L) +{ + TValue *stend, *st = lj_mem_newvec(L, LJ_STACK_START+LJ_STACK_EXTRA, TValue); + setmref(L1->stack, st); + L1->stacksize = LJ_STACK_START + LJ_STACK_EXTRA; + stend = st + L1->stacksize; + setmref(L1->maxstack, stend - LJ_STACK_EXTRA - 1); + setthreadV(L1, st++, L1); /* Needed for curr_funcisL() on empty stack. */ + if (LJ_FR2) setnilV(st++); + L1->base = L1->top = st; + while (st < stend) /* Clear new slots. */ + setnilV(st++); +} + +/* -- State handling ------------------------------------------------------ */ + +/* Open parts that may cause memory-allocation errors. */ +static TValue *cpluaopen(lua_State *L, lua_CFunction dummy, void *ud) +{ + global_State *g = G(L); + UNUSED(dummy); + UNUSED(ud); + stack_init(L, L); + /* NOBARRIER: State initialization, all objects are white. */ + setgcref(L->env, obj2gco(lj_tab_new(L, 0, LJ_MIN_GLOBAL))); + settabV(L, registry(L), lj_tab_new(L, 0, LJ_MIN_REGISTRY)); + lj_str_resize(L, LJ_MIN_STRTAB-1); + lj_meta_init(L); + lj_lex_init(L); + fixstring(lj_err_str(L, LJ_ERR_ERRMEM)); /* Preallocate memory error msg. */ + g->gc.threshold = 4*g->gc.total; + lj_trace_initstate(g); + return NULL; +} + +static void close_state(lua_State *L) +{ + global_State *g = G(L); + lj_func_closeuv(L, tvref(L->stack)); + lj_gc_freeall(g); + lua_assert(gcref(g->gc.root) == obj2gco(L)); + lua_assert(g->strnum == 0); + lj_trace_freestate(g); +#if LJ_HASFFI + lj_ctype_freestate(g); +#endif + lj_mem_freevec(g, g->strhash, g->strmask+1, GCRef); + lj_buf_free(g, &g->tmpbuf); + lj_mem_freevec(g, tvref(L->stack), L->stacksize, TValue); + lua_assert(g->gc.total == sizeof(GG_State)); +#ifndef LUAJIT_USE_SYSMALLOC + if (g->allocf == lj_alloc_f) + lj_alloc_destroy(g->allocd); + else +#endif + g->allocf(g->allocd, G2GG(g), sizeof(GG_State), 0); +} + +#if LJ_64 && !LJ_GC64 && !(defined(LUAJIT_USE_VALGRIND) && defined(LUAJIT_USE_SYSMALLOC)) +lua_State *lj_state_newstate(lua_Alloc f, void *ud) +#else +LUA_API lua_State *lua_newstate(lua_Alloc f, void *ud) +#endif +{ + GG_State *GG = (GG_State *)f(ud, NULL, 0, sizeof(GG_State)); + lua_State *L = &GG->L; + global_State *g = &GG->g; + if (GG == NULL || !checkptrGC(GG)) return NULL; + memset(GG, 0, sizeof(GG_State)); + L->gct = ~LJ_TTHREAD; + L->marked = LJ_GC_WHITE0 | LJ_GC_FIXED | LJ_GC_SFIXED; /* Prevent free. */ + L->dummy_ffid = FF_C; + setmref(L->glref, g); + g->gc.currentwhite = LJ_GC_WHITE0 | LJ_GC_FIXED; + g->strempty.marked = LJ_GC_WHITE0; + g->strempty.gct = ~LJ_TSTR; + g->allocf = f; + g->allocd = ud; + setgcref(g->mainthref, obj2gco(L)); + setgcref(g->uvhead.prev, obj2gco(&g->uvhead)); + setgcref(g->uvhead.next, obj2gco(&g->uvhead)); + g->strmask = ~(MSize)0; + setnilV(registry(L)); + setnilV(&g->nilnode.val); + setnilV(&g->nilnode.key); +#if !LJ_GC64 + setmref(g->nilnode.freetop, &g->nilnode); +#endif + lj_buf_init(NULL, &g->tmpbuf); + g->gc.state = GCSpause; + setgcref(g->gc.root, obj2gco(L)); + setmref(g->gc.sweep, &g->gc.root); + g->gc.total = sizeof(GG_State); + g->gc.pause = LUAI_GCPAUSE; + g->gc.stepmul = LUAI_GCMUL; + lj_dispatch_init((GG_State *)L); + L->status = LUA_ERRERR+1; /* Avoid touching the stack upon memory error. */ + if (lj_vm_cpcall(L, NULL, NULL, cpluaopen) != 0) { + /* Memory allocation error: free partial state. */ + close_state(L); + return NULL; + } + L->status = LUA_OK; + return L; +} + +static TValue *cpfinalize(lua_State *L, lua_CFunction dummy, void *ud) +{ + UNUSED(dummy); + UNUSED(ud); + lj_gc_finalize_cdata(L); + lj_gc_finalize_udata(L); + /* Frame pop omitted. */ + return NULL; +} + +LUA_API void lua_close(lua_State *L) +{ + global_State *g = G(L); + int i; + L = mainthread(g); /* Only the main thread can be closed. */ +#if LJ_HASPROFILE + luaJIT_profile_stop(L); +#endif + setgcrefnull(g->cur_L); + lj_func_closeuv(L, tvref(L->stack)); + lj_gc_separateudata(g, 1); /* Separate udata which have GC metamethods. */ +#if LJ_HASJIT + G2J(g)->flags &= ~JIT_F_ON; + G2J(g)->state = LJ_TRACE_IDLE; + lj_dispatch_update(g); +#endif + for (i = 0;;) { + hook_enter(g); + L->status = LUA_OK; + L->base = L->top = tvref(L->stack) + 1 + LJ_FR2; + L->cframe = NULL; + if (lj_vm_cpcall(L, NULL, NULL, cpfinalize) == LUA_OK) { + if (++i >= 10) break; + lj_gc_separateudata(g, 1); /* Separate udata again. */ + if (gcref(g->gc.mmudata) == NULL) /* Until nothing is left to do. */ + break; + } + } + close_state(L); +} + +lua_State *lj_state_new(lua_State *L) +{ + lua_State *L1 = lj_mem_newobj(L, lua_State); + L1->gct = ~LJ_TTHREAD; + L1->dummy_ffid = FF_C; + L1->status = LUA_OK; + L1->stacksize = 0; + setmref(L1->stack, NULL); + L1->cframe = NULL; + /* NOBARRIER: The lua_State is new (marked white). */ + setgcrefnull(L1->openupval); + setmrefr(L1->glref, L->glref); + setgcrefr(L1->env, L->env); + stack_init(L1, L); /* init stack */ + lua_assert(iswhite(obj2gco(L1))); + return L1; +} + +void LJ_FASTCALL lj_state_free(global_State *g, lua_State *L) +{ + lua_assert(L != mainthread(g)); + if (obj2gco(L) == gcref(g->cur_L)) + setgcrefnull(g->cur_L); + lj_func_closeuv(L, tvref(L->stack)); + lua_assert(gcref(L->openupval) == NULL); + lj_mem_freevec(g, tvref(L->stack), L->stacksize, TValue); + lj_mem_freet(g, L); +} + diff --git a/lib/LuaJIT/lj_state.h b/lib/LuaJIT/lj_state.h new file mode 100644 index 0000000..02a0eaf --- /dev/null +++ b/lib/LuaJIT/lj_state.h @@ -0,0 +1,35 @@ +/* +** State and stack handling. +** Copyright (C) 2005-2017 Mike Pall. See Copyright Notice in luajit.h +*/ + +#ifndef _LJ_STATE_H +#define _LJ_STATE_H + +#include "lj_obj.h" + +#define incr_top(L) \ + (++L->top >= tvref(L->maxstack) && (lj_state_growstack1(L), 0)) + +#define savestack(L, p) ((char *)(p) - mref(L->stack, char)) +#define restorestack(L, n) ((TValue *)(mref(L->stack, char) + (n))) + +LJ_FUNC void lj_state_relimitstack(lua_State *L); +LJ_FUNC void lj_state_shrinkstack(lua_State *L, MSize used); +LJ_FUNCA void LJ_FASTCALL lj_state_growstack(lua_State *L, MSize need); +LJ_FUNC void LJ_FASTCALL lj_state_growstack1(lua_State *L); + +static LJ_AINLINE void lj_state_checkstack(lua_State *L, MSize need) +{ + if ((mref(L->maxstack, char) - (char *)L->top) <= + (ptrdiff_t)need*(ptrdiff_t)sizeof(TValue)) + lj_state_growstack(L, need); +} + +LJ_FUNC lua_State *lj_state_new(lua_State *L); +LJ_FUNC void LJ_FASTCALL lj_state_free(global_State *g, lua_State *L); +#if LJ_64 && !LJ_GC64 && !(defined(LUAJIT_USE_VALGRIND) && defined(LUAJIT_USE_SYSMALLOC)) +LJ_FUNC lua_State *lj_state_newstate(lua_Alloc f, void *ud); +#endif + +#endif diff --git a/lib/LuaJIT/lj_state.o b/lib/LuaJIT/lj_state.o new file mode 100644 index 0000000000000000000000000000000000000000..7b8f25d68abdeb5743cf7d5f277b7000bc24530f GIT binary patch literal 5864 zcmbtYZERat89x4K;x+A!*$Nm2#4rO1YeOvhBdJsrCw6kZMl|4LE%d|lT<3!(v1L0= zx>1_QN!znWeOB=Se+uE+-dCxt^ zxxI_|0k3rLIp;a=dEWQD=bZOk`(UV}&2FRZzPXthMKzzTx2bvOV$W2u#kmAgbEjNt)2ZRbkakIb+7;9mgIc^% zfBr5gRqf9mbyv`vaoJween*hrTC3W&xbb)<3)wZ*HO!{Ebv9WTes9OFz|O#~!0yvM z)ipuJA}9JVUHIz1pla{=Z*d=st&JOqI6u{hPB^OOkB}PtRr8%L%~Nc4en4Jn5f2_W z_{qUy%U$kk)A`}W9fCj5rE2%ORqaDHcg+>ldtLL-@PRj`D_}v`o3`_5-0G^ex?OK|v zt{LMhqIbux1!_)_zxT}=(>dMKQ!nQ;ez5T(Vz=)hz1^qk+dOLBDRunzxqFrD`%X-n zoqPkm3vC}REg9>tw>RcHe>Q&mFyN8OnM?Mj#gVGh%Y6Dam;MZGsmZh1D*cD=qoy|f zCHwdD^V;mf64u*fySk>Cb%PKtZYC#2g0|W5dt`8=)>dd*+_!c!HMk4aZHu&iuYzIx z0C#!h0Ze2nH(Pu4(#bwDBbN8%Dm3_%iT@-W&Z|Fy^TzrGy8IVTP{@ph)ZX#mQnfdi z{-jP8swcM5o;?-vzlA_oPi{g>&5ycp{=(XsX`2(x+~xPxA{bFEj?ICFSL;6T7sh9Z zb!Nfwj(Y5P44XBq4fr~-vwCPdIm7v&ubFBttpMaX-zyjy&Ohg)2vM7|2$fDMXM?^9 z8@>BJAJ%sIwuiKreFai127M}>8DncxEu5e5&2f$i-@7QuP>Kv?)#HCeo1Q>%!uJ+R z(x>`0Q4gd3BI+bmf7z&V`~57W?Ew=kK0z}JYZJaH^z+D)Yn2uL3kY6*Z5V5UdGZcq zjL-5;)}hT&>cAcvBUS|U>NV^qD55(5Ta3B-2HMDmudYUapsOSQ#jV(8-KEoYj0Vb8 z#lb+en4@r!aBF;Vv3$Idd#M3w;U1dBT$U1q)Z%$-Y|8<+9`cdPv!P13@ek0cxq?eA zh8jp!)k4TkA-9~yYk}OqY}s}9nb2ECTnsLsTFTVZ_!4fFWg3oExdm_lO=^Po3N`0@lw@+@_>{#)Ys_R7?9A zhHgfXj(JKtY#g`QY}g1Fx4v>#$?f$v^Gu>$=Lv=q-P!48IIsiBWO+VC&Pk44J;vHw zTQ_-Y+rQCW=WVL@*OPnV2r%1I-(t2=x9#CIm98nwlIHRm8U~rBSBv6)*50Cc@{Sh8 zd(;_HY7ed2qBP_x>@O;{0mX|hsI(|9#yn8pPGcb7p;th0=j`_F)JO5k5(asJUKn)L z9#lMs9PNtNKB;(GISctFxAw0{4;+U`FXsqCZ+x}lX;<8BiYr{DDjswO6c^b&N4gN6 zZ~2%AnPqM~bZkhzViX*cmw!ND#IcT68M2dJoa1||6<2E&aWsg&O6r5G#}gD-o=QF+ zxO9m8%sCxV8(akmBBNF@wh<~>p+Ac{>F4dxKk85w?;&SEsm-klDh>9ZD77F6$f#1> zeksQFig8EnZ8C1(R6Id$gZz12=naq__n*fN@jVD%R357i`10R`D4guyNZ%h7hYXsf z%fTEL#ArIeWR*1l>?R(*$AD4%2```|;rj>WBX|h+in;N<1B|{~xW2&oUIEr5@RZ0T zd_MpqaWMK8`KUOUT-E#g9E`4{`z45`TX36uB4zO1WpG-fm3a1-!6(b$Q)Td%%HY2) zgU^(~UoV4SE`z^a26tecR+_J;488&QgLWI660w$Nw^iT;fd^oZWV^uKVk`19416!} zmF(^-gXaWKO7O^?@w~vt1kTSq_7%sDuS9v4MePoSNC#lhL8Xh(Vr44-p%B z*_go!t%1a7sh&uuL7e7;)3JDhcW}oD>KPu2N4fRkebnxaM|;u03+Ue>LHBH%WZXgnz<>&zta1n(!u^PZBEV z?vy;k0;jlr+Jql4;SZbe-6EfnevJw5GvQtneu@+!R6K%5+Wnge_nGjGBHvK=mrZ!s zgq!2@xCyT{=_gHioeBS`2{+q4VZzNkizfV06VH&ybL6MlpS`BIeafWoMV*AYg$i%W zdq?60jxshXN{P$wnIBv5f>>|5cS)pvN*Gx*;1VwHNBa3mBJ~SGpYB&PF7L;U7F^zU zss)$#-KYhZ_tz^HT;4BopG$w_J;LfU`v;NkQD)Od*(YnrlCk=1Vl>O@dHSq>GLvET z@#NrOVhGq!61NV1#B4CqV?9?-^dW1<1{20C|Npla6f`C4mtr$8lHXf8tamn@E!soP zc+BzwB_GIfu`UmwPIl-w3JINIk}u&QTpT}wxZJhamADAMZ1UeF#QDFD_hT$^Hk25R zinLF;&n(iuEH|N3*3Ei~{E#w(E9h<&m1_QN!znWeOB=Se+uE+-dCxt^ zxxI_|0k3rLIp;a=dEWQD=bZOk`(UV}&2FRZzPXthMKzzTx2bvOV$W2u#kmAgbEjNt)2ZRbkakIb+7;9mgIc^% zfBr5gRqf9mbyv`vaoJween*hrTC3W&xbb)<3)wZ*HO!{Ebv9WTes9OFz|O#~!0yvM z)ipuJA}9JVUHIz1pla{=Z*d=st&JOqI6u{hPB^OOkB}PtRr8%L%~Nc4en4Jn5f2_W z_{qUy%U$kk)A`}W9fCj5rE2%ORqaDHcg+>ldtLL-@PRj`D_}v`o3`_5-0G^ex?OK|v zt{LMhqIbux1!_)_zxT}=(>dMKQ!nQ;ez5T(Vz=)hz1^qk+dOLBDRunzxqFrD`%X-n zoqPkm3vC}REg9>tw>RcHe>Q&mFyN8OnM?Mj#gVGh%Y6Dam;MZGsmZh1D*cD=qoy|f zCHwdD^V;mf64u*fySk>Cb%PKtZYC#2g0|W5dt`8=)>dd*+_!c!HMk4aZHu&iuYzIx z0C#!h0Ze2nH(Pu4(#bwDBbN8%Dm3_%iT@-W&Z|Fy^TzrGy8IVTP{@ph)ZX#mQnfdi z{-jP8swcM5o;?-vzlA_oPi{g>&5ycp{=(XsX`2(x+~xPxA{bFEj?ICFSL;6T7sh9Z zb!Nfwj(Y5P44XBq4fr~-vwCPdIm7v&ubFBttpMaX-zyjy&Ohg)2vM7|2$fDMXM?^9 z8@>BJAJ%sIwuiKreFai127M}>8DncxEu5e5&2f$i-@7QuP>Kv?)#HCeo1Q>%!uJ+R z(x>`0Q4gd3BI+bmf7z&V`~57W?Ew=kK0z}JYZJaH^z+D)Yn2uL3kY6*Z5V5UdGZcq zjL-5;)}hT&>cAcvBUS|U>NV^qD55(5Ta3B-2HMDmudYUapsOSQ#jV(8-KEoYj0Vb8 z#lb+en4@r!aBF;Vv3$Idd#M3w;U1dBT$U1q)Z%$-Y|8<+9`cdPv!P13@ek0cxq?eA zh8jp!)k4TkA-9~yYk}OqY}s}9nb2ECTnsLsTFTVZ_!4fFWg3oExdm_lO=^Po3N`0@lw@+@_>{#)Ys_R7?9A zhHgfXj(JKtY#g`QY}g1Fx4v>#$?f$v^Gu>$=Lv=q-P!48IIsiBWO+VC&Pk44J;vHw zTQ_-Y+rQCW=WVL@*OPnV2r%1I-(t2=x9#CIm98nwlIHRm8U~rBSBv6)*50Cc@{Sh8 zd(;_HY7ed2qBP_x>@O;{0mX|hsI(|9#yn8pPGcb7p;th0=j`_F)JO5k5(asJUKn)L z9#lMs9PNtNKB;(GISctFxAw0{4;+U`FXsqCZ+x}lX;<8BiYr{DDjswO6c^b&N4gN6 zZ~2%AnPqM~bZkhzViX*cmw!ND#IcT68M2dJoa1||6<2E&aWsg&O6r5G#}gD-o=QF+ zxO9m8%sCxV8(akmBBNF@wh<~>p+Ac{>F4dxKk85w?;&SEsm-klDh>9ZD77F6$f#1> zeksQFig8EnZ8C1(R6Id$gZz12=naq__n*fN@jVD%R357i`10R`D4guyNZ%h7hYXsf z%fTEL#ArIeWR*1l>?R(*$AD4%2```|;rj>WBX|h+in;N<1B|{~xW2&oUIEr5@RZ0T zd_MpqaWMK8`KUOUT-E#g9E`4{`z45`TX36uB4zO1WpG-fm3a1-!6(b$Q)Td%%HY2) zgU^(~UoV4SE`z^a26tecR+_J;488&QgLWI660w$Nw^iT;fd^oZWV^uKVk`19416!} zmF(^-gXaWKO7O^?@w~vt1kTSq_7%sDuS9v4MePoSNC#lhL8Xh(Vr44-p%B z*_go!t%1a7sh&uuL7e7;)3JDhcW}oD>KPu2N4fRkebnxaM|;u03+Ue>LHBH%WZXgnz<>&zta1n(!u^PZBEV z?vy;k0;jlr+Jql4;SZbe-6EfnevJw5GvQtneu@+!R6K%5+Wnge_nGjGBHvK=mrZ!s zgq!2@xCyT{=_gHioeBS`2{+q4VZzNkizfV06VH&ybL6MlpS`BIeafWoMV*AYg$i%W zdq?60jxshXN{P$wnIBv5f>>|5cS)pvN*Gx*;1VwHNBa3mBJ~SGpYB&PF7L;U7F^zU zss)$#-KYhZ_tz^HT;4BopG$w_J;LfU`v;NkQD)Od*(YnrlCk=1Vl>O@dHSq>GLvET z@#NrOVhGq!61NV1#B4CqV?9?-^dW1<1{20C|Npla6f`C4mtr$8lHXf8tamn@E!soP zc+BzwB_GIfu`UmwPIl-w3JINIk}u&QTpT}wxZJhamADAMZ1UeF#QDFD_hT$^Hk25R zinLF;&n(iuEH|N3*3Ei~{E#w(E9h<&mlen > b->len ? b->len : a->len; + for (i = 0; i < n; i += 4) { + /* Note: innocuous access up to end of string + 3. */ + uint32_t va = *(const uint32_t *)(strdata(a)+i); + uint32_t vb = *(const uint32_t *)(strdata(b)+i); + if (va != vb) { +#if LJ_LE + va = lj_bswap(va); vb = lj_bswap(vb); +#endif + i -= n; + if ((int32_t)i >= -3) { + va >>= 32+(i<<3); vb >>= 32+(i<<3); + if (va == vb) break; + } + return va < vb ? -1 : 1; + } + } + return (int32_t)(a->len - b->len); +} + +/* Fast string data comparison. Caveat: unaligned access to 1st string! */ +static LJ_AINLINE int str_fastcmp(const char *a, const char *b, MSize len) +{ + MSize i = 0; + lua_assert(len > 0); + lua_assert((((uintptr_t)a+len-1) & (LJ_PAGESIZE-1)) <= LJ_PAGESIZE-4); + do { /* Note: innocuous access up to end of string + 3. */ + uint32_t v = lj_getu32(a+i) ^ *(const uint32_t *)(b+i); + if (v) { + i -= len; +#if LJ_LE + return (int32_t)i >= -3 ? (v << (32+(i<<3))) : 1; +#else + return (int32_t)i >= -3 ? (v >> (32+(i<<3))) : 1; +#endif + } + i += 4; + } while (i < len); + return 0; +} + +/* Find fixed string p inside string s. */ +const char *lj_str_find(const char *s, const char *p, MSize slen, MSize plen) +{ + if (plen <= slen) { + if (plen == 0) { + return s; + } else { + int c = *(const uint8_t *)p++; + plen--; slen -= plen; + while (slen) { + const char *q = (const char *)memchr(s, c, slen); + if (!q) break; + if (memcmp(q+1, p, plen) == 0) return q; + q++; slen -= (MSize)(q-s); s = q; + } + } + } + return NULL; +} + +/* Check whether a string has a pattern matching character. */ +int lj_str_haspattern(GCstr *s) +{ + const char *p = strdata(s), *q = p + s->len; + while (p < q) { + int c = *(const uint8_t *)p++; + if (lj_char_ispunct(c) && strchr("^$*+?.([%-", c)) + return 1; /* Found a pattern matching char. */ + } + return 0; /* No pattern matching chars found. */ +} + +/* -- String interning ---------------------------------------------------- */ + +/* Resize the string hash table (grow and shrink). */ +void lj_str_resize(lua_State *L, MSize newmask) +{ + global_State *g = G(L); + GCRef *newhash; + MSize i; + if (g->gc.state == GCSsweepstring || newmask >= LJ_MAX_STRTAB-1) + return; /* No resizing during GC traversal or if already too big. */ + newhash = lj_mem_newvec(L, newmask+1, GCRef); + memset(newhash, 0, (newmask+1)*sizeof(GCRef)); + for (i = g->strmask; i != ~(MSize)0; i--) { /* Rehash old table. */ + GCobj *p = gcref(g->strhash[i]); + while (p) { /* Follow each hash chain and reinsert all strings. */ + MSize h = gco2str(p)->hash & newmask; + GCobj *next = gcnext(p); + /* NOBARRIER: The string table is a GC root. */ + setgcrefr(p->gch.nextgc, newhash[h]); + setgcref(newhash[h], p); + p = next; + } + } + lj_mem_freevec(g, g->strhash, g->strmask+1, GCRef); + g->strmask = newmask; + g->strhash = newhash; +} + +/* Intern a string and return string object. */ +GCstr *lj_str_new(lua_State *L, const char *str, size_t lenx) +{ + global_State *g; + GCstr *s; + GCobj *o; + MSize len = (MSize)lenx; + MSize a, b, h = len; + if (lenx >= LJ_MAX_STR) + lj_err_msg(L, LJ_ERR_STROV); + g = G(L); + /* Compute string hash. Constants taken from lookup3 hash by Bob Jenkins. */ + if (len >= 4) { /* Caveat: unaligned access! */ + a = lj_getu32(str); + h ^= lj_getu32(str+len-4); + b = lj_getu32(str+(len>>1)-2); + h ^= b; h -= lj_rol(b, 14); + b += lj_getu32(str+(len>>2)-1); + } else if (len > 0) { + a = *(const uint8_t *)str; + h ^= *(const uint8_t *)(str+len-1); + b = *(const uint8_t *)(str+(len>>1)); + h ^= b; h -= lj_rol(b, 14); + } else { + return &g->strempty; + } + a ^= h; a -= lj_rol(h, 11); + b ^= a; b -= lj_rol(a, 25); + h ^= b; h -= lj_rol(b, 16); + /* Check if the string has already been interned. */ + o = gcref(g->strhash[h & g->strmask]); + if (LJ_LIKELY((((uintptr_t)str+len-1) & (LJ_PAGESIZE-1)) <= LJ_PAGESIZE-4)) { + while (o != NULL) { + GCstr *sx = gco2str(o); + if (sx->len == len && str_fastcmp(str, strdata(sx), len) == 0) { + /* Resurrect if dead. Can only happen with fixstring() (keywords). */ + if (isdead(g, o)) flipwhite(o); + return sx; /* Return existing string. */ + } + o = gcnext(o); + } + } else { /* Slow path: end of string is too close to a page boundary. */ + while (o != NULL) { + GCstr *sx = gco2str(o); + if (sx->len == len && memcmp(str, strdata(sx), len) == 0) { + /* Resurrect if dead. Can only happen with fixstring() (keywords). */ + if (isdead(g, o)) flipwhite(o); + return sx; /* Return existing string. */ + } + o = gcnext(o); + } + } + /* Nope, create a new string. */ + s = lj_mem_newt(L, sizeof(GCstr)+len+1, GCstr); + newwhite(g, s); + s->gct = ~LJ_TSTR; + s->len = len; + s->hash = h; + s->reserved = 0; + memcpy(strdatawr(s), str, len); + strdatawr(s)[len] = '\0'; /* Zero-terminate string. */ + /* Add it to string hash table. */ + h &= g->strmask; + s->nextgc = g->strhash[h]; + /* NOBARRIER: The string table is a GC root. */ + setgcref(g->strhash[h], obj2gco(s)); + if (g->strnum++ > g->strmask) /* Allow a 100% load factor. */ + lj_str_resize(L, (g->strmask<<1)+1); /* Grow string table. */ + return s; /* Return newly interned string. */ +} + +void LJ_FASTCALL lj_str_free(global_State *g, GCstr *s) +{ + g->strnum--; + lj_mem_free(g, s, sizestring(s)); +} + diff --git a/lib/LuaJIT/lj_str.h b/lib/LuaJIT/lj_str.h new file mode 100644 index 0000000..85c1e40 --- /dev/null +++ b/lib/LuaJIT/lj_str.h @@ -0,0 +1,27 @@ +/* +** String handling. +** Copyright (C) 2005-2017 Mike Pall. See Copyright Notice in luajit.h +*/ + +#ifndef _LJ_STR_H +#define _LJ_STR_H + +#include + +#include "lj_obj.h" + +/* String helpers. */ +LJ_FUNC int32_t LJ_FASTCALL lj_str_cmp(GCstr *a, GCstr *b); +LJ_FUNC const char *lj_str_find(const char *s, const char *f, + MSize slen, MSize flen); +LJ_FUNC int lj_str_haspattern(GCstr *s); + +/* String interning. */ +LJ_FUNC void lj_str_resize(lua_State *L, MSize newmask); +LJ_FUNCA GCstr *lj_str_new(lua_State *L, const char *str, size_t len); +LJ_FUNC void LJ_FASTCALL lj_str_free(global_State *g, GCstr *s); + +#define lj_str_newz(L, s) (lj_str_new(L, s, strlen(s))) +#define lj_str_newlit(L, s) (lj_str_new(L, "" s, sizeof(s)-1)) + +#endif diff --git a/lib/LuaJIT/lj_str.o b/lib/LuaJIT/lj_str.o new file mode 100644 index 0000000000000000000000000000000000000000..bddad95ab905f40a7ffb6d7e8da7ebd9aa645ba0 GIT binary patch literal 3880 zcmbVPZ){W76~E7Rf=kSMrb9H0O1Q0=;+0x?bek}1GhgR9ex{FtAkbD&j7gn_HBKV? zrKMFwK`((`y}phQ>z97nx2c-65B(CMs;wMoXaXy9KxkS!DpakjL%NRc526vt+d1z~ z@@mX9^+@;Kdw=(w-?``9bFbxB(@K|*&!z5WnLPXDmBztNqMiRQF(qq$VO1w13_X-;%LbNjhnwWX99Ts&-2+^Hi7;E^6}& zBkPRNl?brX%eBj2)5MM8pcJc~6*eRZOx9JjDGnyb&@Fw6?xlNZcUt`Gp53zcRcm7A zrGm^Bvg|fE-p4L$vz#Q1|4cECa750xBa*kEh z2NhP6W3|{>HLkF~vri;;Q+8If>_d61GJHhNE{b5N|*Ub{Y?A3I2|M@Ga@j2kEhqXv{B+z72}(U&j|0P17%& zd<$lhF~EtJ#G5H2^m~{R%caDwi#O$o&wVocNM@53md0dl*F#aN&711uX;!t)RbmyY z2aZGPBG#OJ^3cwp9)vh#qx(&wZ)H=ggKZw09{z%AHI8bx%;5{v*u`b_k!Y(pt+0#e zh{n335o&fv!<6lh2D5Ba6eC*-3eG#rzO|3sK5zN>yt!z;DxBQvgDCV2*!(-o{=}|m z^AOqsgtFZj>L0r#jQ$zJWVgD6@%|1|7+*V66UGD0X>ox~P;qi%f~T>MB{nOG*C!@g zV&}!`#Q7DmDlS#RG&Y&l!TLJIu@ z*Vm7IDvbUXuq^v5@j__$YuPA=#md|&=RQ;diOCmZ>Mgy0>clsz|Jj)p34jQfVWJ~M4&$POsh5+;$l@(Li7vt?fi?YV<2=~e^!W{{7>q9Gor=VoQfZy+| z9d>#dde`nv#8Q0tSH2YA`i4KnM~pyW#I_;31?{^Nk9>6w^o-wNoW69(_X2!&%&aMdG&Vv zpYb1X?7cOQJWo6J2kXe){JXxMfkC75yJ7#3kyDN$PSMu2ch3*$>wTliX{Vr`b`~*@ nE(fl=_E;wMXNT)yr+(hYiAU+{&+u-^W^$D9B{ literal 0 HcmV?d00001 diff --git a/lib/LuaJIT/lj_str_dyn.o b/lib/LuaJIT/lj_str_dyn.o new file mode 100644 index 0000000000000000000000000000000000000000..bddad95ab905f40a7ffb6d7e8da7ebd9aa645ba0 GIT binary patch literal 3880 zcmbVPZ){W76~E7Rf=kSMrb9H0O1Q0=;+0x?bek}1GhgR9ex{FtAkbD&j7gn_HBKV? zrKMFwK`((`y}phQ>z97nx2c-65B(CMs;wMoXaXy9KxkS!DpakjL%NRc526vt+d1z~ z@@mX9^+@;Kdw=(w-?``9bFbxB(@K|*&!z5WnLPXDmBztNqMiRQF(qq$VO1w13_X-;%LbNjhnwWX99Ts&-2+^Hi7;E^6}& zBkPRNl?brX%eBj2)5MM8pcJc~6*eRZOx9JjDGnyb&@Fw6?xlNZcUt`Gp53zcRcm7A zrGm^Bvg|fE-p4L$vz#Q1|4cECa750xBa*kEh z2NhP6W3|{>HLkF~vri;;Q+8If>_d61GJHhNE{b5N|*Ub{Y?A3I2|M@Ga@j2kEhqXv{B+z72}(U&j|0P17%& zd<$lhF~EtJ#G5H2^m~{R%caDwi#O$o&wVocNM@53md0dl*F#aN&711uX;!t)RbmyY z2aZGPBG#OJ^3cwp9)vh#qx(&wZ)H=ggKZw09{z%AHI8bx%;5{v*u`b_k!Y(pt+0#e zh{n335o&fv!<6lh2D5Ba6eC*-3eG#rzO|3sK5zN>yt!z;DxBQvgDCV2*!(-o{=}|m z^AOqsgtFZj>L0r#jQ$zJWVgD6@%|1|7+*V66UGD0X>ox~P;qi%f~T>MB{nOG*C!@g zV&}!`#Q7DmDlS#RG&Y&l!TLJIu@ z*Vm7IDvbUXuq^v5@j__$YuPA=#md|&=RQ;diOCmZ>Mgy0>clsz|Jj)p34jQfVWJ~M4&$POsh5+;$l@(Li7vt?fi?YV<2=~e^!W{{7>q9Gor=VoQfZy+| z9d>#dde`nv#8Q0tSH2YA`i4KnM~pyW#I_;31?{^Nk9>6w^o-wNoW69(_X2!&%&aMdG&Vv zpYb1X?7cOQJWo6J2kXe){JXxMfkC75yJ7#3kyDN$PSMu2ch3*$>wTliX{Vr`b`~*@ nE(fl=_E;wMXNT)yr+(hYiAU+{&+u-^W^$D9B{ literal 0 HcmV?d00001 diff --git a/lib/LuaJIT/lj_strfmt.c b/lib/LuaJIT/lj_strfmt.c new file mode 100644 index 0000000..d7893ce --- /dev/null +++ b/lib/LuaJIT/lj_strfmt.c @@ -0,0 +1,472 @@ +/* +** String formatting. +** Copyright (C) 2005-2017 Mike Pall. See Copyright Notice in luajit.h +*/ + +#include + +#define lj_strfmt_c +#define LUA_CORE + +#include "lj_obj.h" +#include "lj_buf.h" +#include "lj_str.h" +#include "lj_state.h" +#include "lj_char.h" +#include "lj_strfmt.h" + +/* -- Format parser ------------------------------------------------------- */ + +static const uint8_t strfmt_map[('x'-'A')+1] = { + STRFMT_A,0,0,0,STRFMT_E,STRFMT_F,STRFMT_G,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,STRFMT_X,0,0, + 0,0,0,0,0,0, + STRFMT_A,0,STRFMT_C,STRFMT_D,STRFMT_E,STRFMT_F,STRFMT_G,0,STRFMT_I,0,0,0,0, + 0,STRFMT_O,STRFMT_P,STRFMT_Q,0,STRFMT_S,0,STRFMT_U,0,0,STRFMT_X +}; + +SFormat LJ_FASTCALL lj_strfmt_parse(FormatState *fs) +{ + const uint8_t *p = fs->p, *e = fs->e; + fs->str = (const char *)p; + for (; p < e; p++) { + if (*p == '%') { /* Escape char? */ + if (p[1] == '%') { /* '%%'? */ + fs->p = ++p+1; + goto retlit; + } else { + SFormat sf = 0; + uint32_t c; + if (p != (const uint8_t *)fs->str) + break; + for (p++; (uint32_t)*p - ' ' <= (uint32_t)('0' - ' '); p++) { + /* Parse flags. */ + if (*p == '-') sf |= STRFMT_F_LEFT; + else if (*p == '+') sf |= STRFMT_F_PLUS; + else if (*p == '0') sf |= STRFMT_F_ZERO; + else if (*p == ' ') sf |= STRFMT_F_SPACE; + else if (*p == '#') sf |= STRFMT_F_ALT; + else break; + } + if ((uint32_t)*p - '0' < 10) { /* Parse width. */ + uint32_t width = (uint32_t)*p++ - '0'; + if ((uint32_t)*p - '0' < 10) + width = (uint32_t)*p++ - '0' + width*10; + sf |= (width << STRFMT_SH_WIDTH); + } + if (*p == '.') { /* Parse precision. */ + uint32_t prec = 0; + p++; + if ((uint32_t)*p - '0' < 10) { + prec = (uint32_t)*p++ - '0'; + if ((uint32_t)*p - '0' < 10) + prec = (uint32_t)*p++ - '0' + prec*10; + } + sf |= ((prec+1) << STRFMT_SH_PREC); + } + /* Parse conversion. */ + c = (uint32_t)*p - 'A'; + if (LJ_LIKELY(c <= (uint32_t)('x' - 'A'))) { + uint32_t sx = strfmt_map[c]; + if (sx) { + fs->p = p+1; + return (sf | sx | ((c & 0x20) ? 0 : STRFMT_F_UPPER)); + } + } + /* Return error location. */ + if (*p >= 32) p++; + fs->len = (MSize)(p - (const uint8_t *)fs->str); + fs->p = fs->e; + return STRFMT_ERR; + } + } + } + fs->p = p; +retlit: + fs->len = (MSize)(p - (const uint8_t *)fs->str); + return fs->len ? STRFMT_LIT : STRFMT_EOF; +} + +/* -- Raw conversions ----------------------------------------------------- */ + +#define WINT_R(x, sh, sc) \ + { uint32_t d = (x*(((1<>sh; x -= d*sc; *p++ = (char)('0'+d); } + +/* Write integer to buffer. */ +char * LJ_FASTCALL lj_strfmt_wint(char *p, int32_t k) +{ + uint32_t u = (uint32_t)k; + if (k < 0) { u = (uint32_t)-k; *p++ = '-'; } + if (u < 10000) { + if (u < 10) goto dig1; + if (u < 100) goto dig2; + if (u < 1000) goto dig3; + } else { + uint32_t v = u / 10000; u -= v * 10000; + if (v < 10000) { + if (v < 10) goto dig5; + if (v < 100) goto dig6; + if (v < 1000) goto dig7; + } else { + uint32_t w = v / 10000; v -= w * 10000; + if (w >= 10) WINT_R(w, 10, 10) + *p++ = (char)('0'+w); + } + WINT_R(v, 23, 1000) + dig7: WINT_R(v, 12, 100) + dig6: WINT_R(v, 10, 10) + dig5: *p++ = (char)('0'+v); + } + WINT_R(u, 23, 1000) + dig3: WINT_R(u, 12, 100) + dig2: WINT_R(u, 10, 10) + dig1: *p++ = (char)('0'+u); + return p; +} +#undef WINT_R + +/* Write pointer to buffer. */ +char * LJ_FASTCALL lj_strfmt_wptr(char *p, const void *v) +{ + ptrdiff_t x = (ptrdiff_t)v; + MSize i, n = STRFMT_MAXBUF_PTR; + if (x == 0) { + *p++ = 'N'; *p++ = 'U'; *p++ = 'L'; *p++ = 'L'; + return p; + } +#if LJ_64 + /* Shorten output for 64 bit pointers. */ + n = 2+2*4+((x >> 32) ? 2+2*(lj_fls((uint32_t)(x >> 32))>>3) : 0); +#endif + p[0] = '0'; + p[1] = 'x'; + for (i = n-1; i >= 2; i--, x >>= 4) + p[i] = "0123456789abcdef"[(x & 15)]; + return p+n; +} + +/* Write ULEB128 to buffer. */ +char * LJ_FASTCALL lj_strfmt_wuleb128(char *p, uint32_t v) +{ + for (; v >= 0x80; v >>= 7) + *p++ = (char)((v & 0x7f) | 0x80); + *p++ = (char)v; + return p; +} + +/* Return string or write number to tmp buffer and return pointer to start. */ +const char *lj_strfmt_wstrnum(lua_State *L, cTValue *o, MSize *lenp) +{ + SBuf *sb; + if (tvisstr(o)) { + *lenp = strV(o)->len; + return strVdata(o); + } else if (tvisint(o)) { + sb = lj_strfmt_putint(lj_buf_tmp_(L), intV(o)); + } else if (tvisnum(o)) { + sb = lj_strfmt_putfnum(lj_buf_tmp_(L), STRFMT_G14, o->n); + } else { + return NULL; + } + *lenp = sbuflen(sb); + return sbufB(sb); +} + +/* -- Unformatted conversions to buffer ----------------------------------- */ + +/* Add integer to buffer. */ +SBuf * LJ_FASTCALL lj_strfmt_putint(SBuf *sb, int32_t k) +{ + setsbufP(sb, lj_strfmt_wint(lj_buf_more(sb, STRFMT_MAXBUF_INT), k)); + return sb; +} + +#if LJ_HASJIT +/* Add number to buffer. */ +SBuf * LJ_FASTCALL lj_strfmt_putnum(SBuf *sb, cTValue *o) +{ + return lj_strfmt_putfnum(sb, STRFMT_G14, o->n); +} +#endif + +SBuf * LJ_FASTCALL lj_strfmt_putptr(SBuf *sb, const void *v) +{ + setsbufP(sb, lj_strfmt_wptr(lj_buf_more(sb, STRFMT_MAXBUF_PTR), v)); + return sb; +} + +/* Add quoted string to buffer. */ +SBuf * LJ_FASTCALL lj_strfmt_putquoted(SBuf *sb, GCstr *str) +{ + const char *s = strdata(str); + MSize len = str->len; + lj_buf_putb(sb, '"'); + while (len--) { + uint32_t c = (uint32_t)(uint8_t)*s++; + char *p = lj_buf_more(sb, 4); + if (c == '"' || c == '\\' || c == '\n') { + *p++ = '\\'; + } else if (lj_char_iscntrl(c)) { /* This can only be 0-31 or 127. */ + uint32_t d; + *p++ = '\\'; + if (c >= 100 || lj_char_isdigit((uint8_t)*s)) { + *p++ = (char)('0'+(c >= 100)); if (c >= 100) c -= 100; + goto tens; + } else if (c >= 10) { + tens: + d = (c * 205) >> 11; c -= d * 10; *p++ = (char)('0'+d); + } + c += '0'; + } + *p++ = (char)c; + setsbufP(sb, p); + } + lj_buf_putb(sb, '"'); + return sb; +} + +/* -- Formatted conversions to buffer ------------------------------------- */ + +/* Add formatted char to buffer. */ +SBuf *lj_strfmt_putfchar(SBuf *sb, SFormat sf, int32_t c) +{ + MSize width = STRFMT_WIDTH(sf); + char *p = lj_buf_more(sb, width > 1 ? width : 1); + if ((sf & STRFMT_F_LEFT)) *p++ = (char)c; + while (width-- > 1) *p++ = ' '; + if (!(sf & STRFMT_F_LEFT)) *p++ = (char)c; + setsbufP(sb, p); + return sb; +} + +/* Add formatted string to buffer. */ +SBuf *lj_strfmt_putfstr(SBuf *sb, SFormat sf, GCstr *str) +{ + MSize len = str->len <= STRFMT_PREC(sf) ? str->len : STRFMT_PREC(sf); + MSize width = STRFMT_WIDTH(sf); + char *p = lj_buf_more(sb, width > len ? width : len); + if ((sf & STRFMT_F_LEFT)) p = lj_buf_wmem(p, strdata(str), len); + while (width-- > len) *p++ = ' '; + if (!(sf & STRFMT_F_LEFT)) p = lj_buf_wmem(p, strdata(str), len); + setsbufP(sb, p); + return sb; +} + +/* Add formatted signed/unsigned integer to buffer. */ +SBuf *lj_strfmt_putfxint(SBuf *sb, SFormat sf, uint64_t k) +{ + char buf[STRFMT_MAXBUF_XINT], *q = buf + sizeof(buf), *p; +#ifdef LUA_USE_ASSERT + char *ps; +#endif + MSize prefix = 0, len, prec, pprec, width, need; + + /* Figure out signed prefixes. */ + if (STRFMT_TYPE(sf) == STRFMT_INT) { + if ((int64_t)k < 0) { + k = (uint64_t)-(int64_t)k; + prefix = 256 + '-'; + } else if ((sf & STRFMT_F_PLUS)) { + prefix = 256 + '+'; + } else if ((sf & STRFMT_F_SPACE)) { + prefix = 256 + ' '; + } + } + + /* Convert number and store to fixed-size buffer in reverse order. */ + prec = STRFMT_PREC(sf); + if ((int32_t)prec >= 0) sf &= ~STRFMT_F_ZERO; + if (k == 0) { /* Special-case zero argument. */ + if (prec != 0 || + (sf & (STRFMT_T_OCT|STRFMT_F_ALT)) == (STRFMT_T_OCT|STRFMT_F_ALT)) + *--q = '0'; + } else if (!(sf & (STRFMT_T_HEX|STRFMT_T_OCT))) { /* Decimal. */ + uint32_t k2; + while ((k >> 32)) { *--q = (char)('0' + k % 10); k /= 10; } + k2 = (uint32_t)k; + do { *--q = (char)('0' + k2 % 10); k2 /= 10; } while (k2); + } else if ((sf & STRFMT_T_HEX)) { /* Hex. */ + const char *hexdig = (sf & STRFMT_F_UPPER) ? "0123456789ABCDEF" : + "0123456789abcdef"; + do { *--q = hexdig[(k & 15)]; k >>= 4; } while (k); + if ((sf & STRFMT_F_ALT)) prefix = 512 + ((sf & STRFMT_F_UPPER) ? 'X' : 'x'); + } else { /* Octal. */ + do { *--q = (char)('0' + (uint32_t)(k & 7)); k >>= 3; } while (k); + if ((sf & STRFMT_F_ALT)) *--q = '0'; + } + + /* Calculate sizes. */ + len = (MSize)(buf + sizeof(buf) - q); + if ((int32_t)len >= (int32_t)prec) prec = len; + width = STRFMT_WIDTH(sf); + pprec = prec + (prefix >> 8); + need = width > pprec ? width : pprec; + p = lj_buf_more(sb, need); +#ifdef LUA_USE_ASSERT + ps = p; +#endif + + /* Format number with leading/trailing whitespace and zeros. */ + if ((sf & (STRFMT_F_LEFT|STRFMT_F_ZERO)) == 0) + while (width-- > pprec) *p++ = ' '; + if (prefix) { + if ((char)prefix >= 'X') *p++ = '0'; + *p++ = (char)prefix; + } + if ((sf & (STRFMT_F_LEFT|STRFMT_F_ZERO)) == STRFMT_F_ZERO) + while (width-- > pprec) *p++ = '0'; + while (prec-- > len) *p++ = '0'; + while (q < buf + sizeof(buf)) *p++ = *q++; /* Add number itself. */ + if ((sf & STRFMT_F_LEFT)) + while (width-- > pprec) *p++ = ' '; + + lua_assert(need == (MSize)(p - ps)); + setsbufP(sb, p); + return sb; +} + +/* Add number formatted as signed integer to buffer. */ +SBuf *lj_strfmt_putfnum_int(SBuf *sb, SFormat sf, lua_Number n) +{ + int64_t k = (int64_t)n; + if (checki32(k) && sf == STRFMT_INT) + return lj_strfmt_putint(sb, (int32_t)k); /* Shortcut for plain %d. */ + else + return lj_strfmt_putfxint(sb, sf, (uint64_t)k); +} + +/* Add number formatted as unsigned integer to buffer. */ +SBuf *lj_strfmt_putfnum_uint(SBuf *sb, SFormat sf, lua_Number n) +{ + int64_t k; + if (n >= 9223372036854775808.0) + k = (int64_t)(n - 18446744073709551616.0); + else + k = (int64_t)n; + return lj_strfmt_putfxint(sb, sf, (uint64_t)k); +} + +/* -- Conversions to strings ---------------------------------------------- */ + +/* Convert integer to string. */ +GCstr * LJ_FASTCALL lj_strfmt_int(lua_State *L, int32_t k) +{ + char buf[STRFMT_MAXBUF_INT]; + MSize len = (MSize)(lj_strfmt_wint(buf, k) - buf); + return lj_str_new(L, buf, len); +} + +/* Convert integer or number to string. */ +GCstr * LJ_FASTCALL lj_strfmt_number(lua_State *L, cTValue *o) +{ + return tvisint(o) ? lj_strfmt_int(L, intV(o)) : lj_strfmt_num(L, o); +} + +#if LJ_HASJIT +/* Convert char value to string. */ +GCstr * LJ_FASTCALL lj_strfmt_char(lua_State *L, int c) +{ + char buf[1]; + buf[0] = c; + return lj_str_new(L, buf, 1); +} +#endif + +/* Raw conversion of object to string. */ +GCstr * LJ_FASTCALL lj_strfmt_obj(lua_State *L, cTValue *o) +{ + if (tvisstr(o)) { + return strV(o); + } else if (tvisnumber(o)) { + return lj_strfmt_number(L, o); + } else if (tvisnil(o)) { + return lj_str_newlit(L, "nil"); + } else if (tvisfalse(o)) { + return lj_str_newlit(L, "false"); + } else if (tvistrue(o)) { + return lj_str_newlit(L, "true"); + } else { + char buf[8+2+2+16], *p = buf; + p = lj_buf_wmem(p, lj_typename(o), (MSize)strlen(lj_typename(o))); + *p++ = ':'; *p++ = ' '; + if (tvisfunc(o) && isffunc(funcV(o))) { + p = lj_buf_wmem(p, "builtin#", 8); + p = lj_strfmt_wint(p, funcV(o)->c.ffid); + } else { + p = lj_strfmt_wptr(p, lj_obj_ptr(o)); + } + return lj_str_new(L, buf, (size_t)(p - buf)); + } +} + +/* -- Internal string formatting ------------------------------------------ */ + +/* +** These functions are only used for lua_pushfstring(), lua_pushvfstring() +** and for internal string formatting (e.g. error messages). Caveat: unlike +** string.format(), only a limited subset of formats and flags are supported! +** +** LuaJIT has support for a couple more formats than Lua 5.1/5.2: +** - %d %u %o %x with full formatting, 32 bit integers only. +** - %f and other FP formats are really %.14g. +** - %s %c %p without formatting. +*/ + +/* Push formatted message as a string object to Lua stack. va_list variant. */ +const char *lj_strfmt_pushvf(lua_State *L, const char *fmt, va_list argp) +{ + SBuf *sb = lj_buf_tmp_(L); + FormatState fs; + SFormat sf; + GCstr *str; + lj_strfmt_init(&fs, fmt, (MSize)strlen(fmt)); + while ((sf = lj_strfmt_parse(&fs)) != STRFMT_EOF) { + switch (STRFMT_TYPE(sf)) { + case STRFMT_LIT: + lj_buf_putmem(sb, fs.str, fs.len); + break; + case STRFMT_INT: + lj_strfmt_putfxint(sb, sf, va_arg(argp, int32_t)); + break; + case STRFMT_UINT: + lj_strfmt_putfxint(sb, sf, va_arg(argp, uint32_t)); + break; + case STRFMT_NUM: + lj_strfmt_putfnum(sb, STRFMT_G14, va_arg(argp, lua_Number)); + break; + case STRFMT_STR: { + const char *s = va_arg(argp, char *); + if (s == NULL) s = "(null)"; + lj_buf_putmem(sb, s, (MSize)strlen(s)); + break; + } + case STRFMT_CHAR: + lj_buf_putb(sb, va_arg(argp, int)); + break; + case STRFMT_PTR: + lj_strfmt_putptr(sb, va_arg(argp, void *)); + break; + case STRFMT_ERR: + default: + lj_buf_putb(sb, '?'); + lua_assert(0); + break; + } + } + str = lj_buf_str(L, sb); + setstrV(L, L->top, str); + incr_top(L); + return strdata(str); +} + +/* Push formatted message as a string object to Lua stack. Vararg variant. */ +const char *lj_strfmt_pushf(lua_State *L, const char *fmt, ...) +{ + const char *msg; + va_list argp; + va_start(argp, fmt); + msg = lj_strfmt_pushvf(L, fmt, argp); + va_end(argp); + return msg; +} + diff --git a/lib/LuaJIT/lj_strfmt.h b/lib/LuaJIT/lj_strfmt.h new file mode 100644 index 0000000..6e1d901 --- /dev/null +++ b/lib/LuaJIT/lj_strfmt.h @@ -0,0 +1,125 @@ +/* +** String formatting. +** Copyright (C) 2005-2017 Mike Pall. See Copyright Notice in luajit.h +*/ + +#ifndef _LJ_STRFMT_H +#define _LJ_STRFMT_H + +#include "lj_obj.h" + +typedef uint32_t SFormat; /* Format indicator. */ + +/* Format parser state. */ +typedef struct FormatState { + const uint8_t *p; /* Current format string pointer. */ + const uint8_t *e; /* End of format string. */ + const char *str; /* Returned literal string. */ + MSize len; /* Size of literal string. */ +} FormatState; + +/* Format types (max. 16). */ +typedef enum FormatType { + STRFMT_EOF, STRFMT_ERR, STRFMT_LIT, + STRFMT_INT, STRFMT_UINT, STRFMT_NUM, STRFMT_STR, STRFMT_CHAR, STRFMT_PTR +} FormatType; + +/* Format subtypes (bits are reused). */ +#define STRFMT_T_HEX 0x0010 /* STRFMT_UINT */ +#define STRFMT_T_OCT 0x0020 /* STRFMT_UINT */ +#define STRFMT_T_FP_A 0x0000 /* STRFMT_NUM */ +#define STRFMT_T_FP_E 0x0010 /* STRFMT_NUM */ +#define STRFMT_T_FP_F 0x0020 /* STRFMT_NUM */ +#define STRFMT_T_FP_G 0x0030 /* STRFMT_NUM */ +#define STRFMT_T_QUOTED 0x0010 /* STRFMT_STR */ + +/* Format flags. */ +#define STRFMT_F_LEFT 0x0100 +#define STRFMT_F_PLUS 0x0200 +#define STRFMT_F_ZERO 0x0400 +#define STRFMT_F_SPACE 0x0800 +#define STRFMT_F_ALT 0x1000 +#define STRFMT_F_UPPER 0x2000 + +/* Format indicator fields. */ +#define STRFMT_SH_WIDTH 16 +#define STRFMT_SH_PREC 24 + +#define STRFMT_TYPE(sf) ((FormatType)((sf) & 15)) +#define STRFMT_WIDTH(sf) (((sf) >> STRFMT_SH_WIDTH) & 255u) +#define STRFMT_PREC(sf) ((((sf) >> STRFMT_SH_PREC) & 255u) - 1u) +#define STRFMT_FP(sf) (((sf) >> 4) & 3) + +/* Formats for conversion characters. */ +#define STRFMT_A (STRFMT_NUM|STRFMT_T_FP_A) +#define STRFMT_C (STRFMT_CHAR) +#define STRFMT_D (STRFMT_INT) +#define STRFMT_E (STRFMT_NUM|STRFMT_T_FP_E) +#define STRFMT_F (STRFMT_NUM|STRFMT_T_FP_F) +#define STRFMT_G (STRFMT_NUM|STRFMT_T_FP_G) +#define STRFMT_I STRFMT_D +#define STRFMT_O (STRFMT_UINT|STRFMT_T_OCT) +#define STRFMT_P (STRFMT_PTR) +#define STRFMT_Q (STRFMT_STR|STRFMT_T_QUOTED) +#define STRFMT_S (STRFMT_STR) +#define STRFMT_U (STRFMT_UINT) +#define STRFMT_X (STRFMT_UINT|STRFMT_T_HEX) +#define STRFMT_G14 (STRFMT_G | ((14+1) << STRFMT_SH_PREC)) + +/* Maximum buffer sizes for conversions. */ +#define STRFMT_MAXBUF_XINT (1+22) /* '0' prefix + uint64_t in octal. */ +#define STRFMT_MAXBUF_INT (1+10) /* Sign + int32_t in decimal. */ +#define STRFMT_MAXBUF_NUM 32 /* Must correspond with STRFMT_G14. */ +#define STRFMT_MAXBUF_PTR (2+2*sizeof(ptrdiff_t)) /* "0x" + hex ptr. */ + +/* Format parser. */ +LJ_FUNC SFormat LJ_FASTCALL lj_strfmt_parse(FormatState *fs); + +static LJ_AINLINE void lj_strfmt_init(FormatState *fs, const char *p, MSize len) +{ + fs->p = (const uint8_t *)p; + fs->e = (const uint8_t *)p + len; + lua_assert(*fs->e == 0); /* Must be NUL-terminated (may have NULs inside). */ +} + +/* Raw conversions. */ +LJ_FUNC char * LJ_FASTCALL lj_strfmt_wint(char *p, int32_t k); +LJ_FUNC char * LJ_FASTCALL lj_strfmt_wptr(char *p, const void *v); +LJ_FUNC char * LJ_FASTCALL lj_strfmt_wuleb128(char *p, uint32_t v); +LJ_FUNC const char *lj_strfmt_wstrnum(lua_State *L, cTValue *o, MSize *lenp); + +/* Unformatted conversions to buffer. */ +LJ_FUNC SBuf * LJ_FASTCALL lj_strfmt_putint(SBuf *sb, int32_t k); +#if LJ_HASJIT +LJ_FUNC SBuf * LJ_FASTCALL lj_strfmt_putnum(SBuf *sb, cTValue *o); +#endif +LJ_FUNC SBuf * LJ_FASTCALL lj_strfmt_putptr(SBuf *sb, const void *v); +LJ_FUNC SBuf * LJ_FASTCALL lj_strfmt_putquoted(SBuf *sb, GCstr *str); + +/* Formatted conversions to buffer. */ +LJ_FUNC SBuf *lj_strfmt_putfxint(SBuf *sb, SFormat sf, uint64_t k); +LJ_FUNC SBuf *lj_strfmt_putfnum_int(SBuf *sb, SFormat sf, lua_Number n); +LJ_FUNC SBuf *lj_strfmt_putfnum_uint(SBuf *sb, SFormat sf, lua_Number n); +LJ_FUNC SBuf *lj_strfmt_putfnum(SBuf *sb, SFormat, lua_Number n); +LJ_FUNC SBuf *lj_strfmt_putfchar(SBuf *sb, SFormat, int32_t c); +LJ_FUNC SBuf *lj_strfmt_putfstr(SBuf *sb, SFormat, GCstr *str); + +/* Conversions to strings. */ +LJ_FUNC GCstr * LJ_FASTCALL lj_strfmt_int(lua_State *L, int32_t k); +LJ_FUNCA GCstr * LJ_FASTCALL lj_strfmt_num(lua_State *L, cTValue *o); +LJ_FUNCA GCstr * LJ_FASTCALL lj_strfmt_number(lua_State *L, cTValue *o); +#if LJ_HASJIT +LJ_FUNC GCstr * LJ_FASTCALL lj_strfmt_char(lua_State *L, int c); +#endif +LJ_FUNC GCstr * LJ_FASTCALL lj_strfmt_obj(lua_State *L, cTValue *o); + +/* Internal string formatting. */ +LJ_FUNC const char *lj_strfmt_pushvf(lua_State *L, const char *fmt, + va_list argp); +LJ_FUNC const char *lj_strfmt_pushf(lua_State *L, const char *fmt, ...) +#ifdef __GNUC__ + __attribute__ ((format (printf, 2, 3))) +#endif + ; + +#endif diff --git a/lib/LuaJIT/lj_strfmt.o b/lib/LuaJIT/lj_strfmt.o new file mode 100644 index 0000000000000000000000000000000000000000..a6afaf085bbebfc3c5572f2bd2a6319427d40ad5 GIT binary patch literal 11696 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 literal 0 HcmV?d00001 diff --git a/lib/LuaJIT/lj_strfmt_dyn.o b/lib/LuaJIT/lj_strfmt_dyn.o new file mode 100644 index 0000000000000000000000000000000000000000..a6afaf085bbebfc3c5572f2bd2a6319427d40ad5 GIT binary patch literal 11696 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 literal 0 HcmV?d00001 diff --git a/lib/LuaJIT/lj_strfmt_num.c b/lib/LuaJIT/lj_strfmt_num.c new file mode 100644 index 0000000..9271f68 --- /dev/null +++ b/lib/LuaJIT/lj_strfmt_num.c @@ -0,0 +1,592 @@ +/* +** String formatting for floating-point numbers. +** Copyright (C) 2005-2017 Mike Pall. See Copyright Notice in luajit.h +** Contributed by Peter Cawley. +*/ + +#include + +#define lj_strfmt_num_c +#define LUA_CORE + +#include "lj_obj.h" +#include "lj_buf.h" +#include "lj_str.h" +#include "lj_strfmt.h" + +/* -- Precomputed tables -------------------------------------------------- */ + +/* Rescale factors to push the exponent of a number towards zero. */ +#define RESCALE_EXPONENTS(P, N) \ + P(308), P(289), P(270), P(250), P(231), P(212), P(193), P(173), P(154), \ + P(135), P(115), P(96), P(77), P(58), P(38), P(0), P(0), P(0), N(39), N(58), \ + N(77), N(96), N(116), N(135), N(154), N(174), N(193), N(212), N(231), \ + N(251), N(270), N(289) + +#define ONE_E_P(X) 1e+0 ## X +#define ONE_E_N(X) 1e-0 ## X +static const int16_t rescale_e[] = { RESCALE_EXPONENTS(-, +) }; +static const double rescale_n[] = { RESCALE_EXPONENTS(ONE_E_P, ONE_E_N) }; +#undef ONE_E_N +#undef ONE_E_P + +/* +** For p in range -70 through 57, this table encodes pairs (m, e) such that +** 4*2^p <= (uint8_t)m*10^e, and is the smallest value for which this holds. +*/ +static const int8_t four_ulp_m_e[] = { + 34, -21, 68, -21, 14, -20, 28, -20, 55, -20, 2, -19, 3, -19, 5, -19, 9, -19, + -82, -18, 35, -18, 7, -17, -117, -17, 28, -17, 56, -17, 112, -16, -33, -16, + 45, -16, 89, -16, -78, -15, 36, -15, 72, -15, -113, -14, 29, -14, 57, -14, + 114, -13, -28, -13, 46, -13, 91, -12, -74, -12, 37, -12, 73, -12, 15, -11, 3, + -11, 59, -11, 2, -10, 3, -10, 5, -10, 1, -9, -69, -9, 38, -9, 75, -9, 15, -7, + 3, -7, 6, -7, 12, -6, -17, -7, 48, -7, 96, -7, -65, -6, 39, -6, 77, -6, -103, + -5, 31, -5, 62, -5, 123, -4, -11, -4, 49, -4, 98, -4, -60, -3, 4, -2, 79, -3, + 16, -2, 32, -2, 63, -2, 2, -1, 25, 0, 5, 1, 1, 2, 2, 2, 4, 2, 8, 2, 16, 2, + 32, 2, 64, 2, -128, 2, 26, 2, 52, 2, 103, 3, -51, 3, 41, 4, 82, 4, -92, 4, + 33, 4, 66, 4, -124, 5, 27, 5, 53, 5, 105, 6, 21, 6, 42, 6, 84, 6, 17, 7, 34, + 7, 68, 7, 2, 8, 3, 8, 6, 8, 108, 9, -41, 9, 43, 10, 86, 9, -84, 10, 35, 10, + 69, 10, -118, 11, 28, 11, 55, 12, 11, 13, 22, 13, 44, 13, 88, 13, -80, 13, + 36, 13, 71, 13, -115, 14, 29, 14, 57, 14, 113, 15, -30, 15, 46, 15, 91, 15, + 19, 16, 37, 16, 73, 16, 2, 17, 3, 17, 6, 17 +}; + +/* min(2^32-1, 10^e-1) for e in range 0 through 10 */ +static uint32_t ndigits_dec_threshold[] = { + 0, 9U, 99U, 999U, 9999U, 99999U, 999999U, + 9999999U, 99999999U, 999999999U, 0xffffffffU +}; + +/* -- Helper functions ---------------------------------------------------- */ + +/* Compute the number of digits in the decimal representation of x. */ +static MSize ndigits_dec(uint32_t x) +{ + MSize t = ((lj_fls(x | 1) * 77) >> 8) + 1; /* 2^8/77 is roughly log2(10) */ + return t + (x > ndigits_dec_threshold[t]); +} + +#define WINT_R(x, sh, sc) \ + { uint32_t d = (x*(((1<>sh; x -= d*sc; *p++ = (char)('0'+d); } + +/* Write 9-digit unsigned integer to buffer. */ +static char *lj_strfmt_wuint9(char *p, uint32_t u) +{ + uint32_t v = u / 10000, w; + u -= v * 10000; + w = v / 10000; + v -= w * 10000; + *p++ = (char)('0'+w); + WINT_R(v, 23, 1000) + WINT_R(v, 12, 100) + WINT_R(v, 10, 10) + *p++ = (char)('0'+v); + WINT_R(u, 23, 1000) + WINT_R(u, 12, 100) + WINT_R(u, 10, 10) + *p++ = (char)('0'+u); + return p; +} +#undef WINT_R + +/* -- Extended precision arithmetic --------------------------------------- */ + +/* +** The "nd" format is a fixed-precision decimal representation for numbers. It +** consists of up to 64 uint32_t values, with each uint32_t storing a value +** in the range [0, 1e9). A number in "nd" format consists of three variables: +** +** uint32_t nd[64]; +** uint32_t ndlo; +** uint32_t ndhi; +** +** The integral part of the number is stored in nd[0 ... ndhi], the value of +** which is sum{i in [0, ndhi] | nd[i] * 10^(9*i)}. If the fractional part of +** the number is zero, ndlo is zero. Otherwise, the fractional part is stored +** in nd[ndlo ... 63], the value of which is taken to be +** sum{i in [ndlo, 63] | nd[i] * 10^(9*(i-64))}. +** +** If the array part had 128 elements rather than 64, then every double would +** have an exact representation in "nd" format. With 64 elements, all integral +** doubles have an exact representation, and all non-integral doubles have +** enough digits to make both %.99e and %.99f do the right thing. +*/ + +#if LJ_64 +#define ND_MUL2K_MAX_SHIFT 29 +#define ND_MUL2K_DIV1E9(val) ((uint32_t)((val) / 1000000000)) +#else +#define ND_MUL2K_MAX_SHIFT 11 +#define ND_MUL2K_DIV1E9(val) ((uint32_t)((val) >> 9) / 1953125) +#endif + +/* Multiply nd by 2^k and add carry_in (ndlo is assumed to be zero). */ +static uint32_t nd_mul2k(uint32_t* nd, uint32_t ndhi, uint32_t k, + uint32_t carry_in, SFormat sf) +{ + uint32_t i, ndlo = 0, start = 1; + /* Performance hacks. */ + if (k > ND_MUL2K_MAX_SHIFT*2 && STRFMT_FP(sf) != STRFMT_FP(STRFMT_T_FP_F)) { + start = ndhi - (STRFMT_PREC(sf) + 17) / 8; + } + /* Real logic. */ + while (k >= ND_MUL2K_MAX_SHIFT) { + for (i = ndlo; i <= ndhi; i++) { + uint64_t val = ((uint64_t)nd[i] << ND_MUL2K_MAX_SHIFT) | carry_in; + carry_in = ND_MUL2K_DIV1E9(val); + nd[i] = (uint32_t)val - carry_in * 1000000000; + } + if (carry_in) { + nd[++ndhi] = carry_in; carry_in = 0; + if (start++ == ndlo) ++ndlo; + } + k -= ND_MUL2K_MAX_SHIFT; + } + if (k) { + for (i = ndlo; i <= ndhi; i++) { + uint64_t val = ((uint64_t)nd[i] << k) | carry_in; + carry_in = ND_MUL2K_DIV1E9(val); + nd[i] = (uint32_t)val - carry_in * 1000000000; + } + if (carry_in) nd[++ndhi] = carry_in; + } + return ndhi; +} + +/* Divide nd by 2^k (ndlo is assumed to be zero). */ +static uint32_t nd_div2k(uint32_t* nd, uint32_t ndhi, uint32_t k, SFormat sf) +{ + uint32_t ndlo = 0, stop1 = ~0, stop2 = ~0; + /* Performance hacks. */ + if (!ndhi) { + if (!nd[0]) { + return 0; + } else { + uint32_t s = lj_ffs(nd[0]); + if (s >= k) { nd[0] >>= k; return 0; } + nd[0] >>= s; k -= s; + } + } + if (k > 18) { + if (STRFMT_FP(sf) == STRFMT_FP(STRFMT_T_FP_F)) { + stop1 = 63 - (int32_t)STRFMT_PREC(sf) / 9; + } else { + int32_t floorlog2 = ndhi * 29 + lj_fls(nd[ndhi]) - k; + int32_t floorlog10 = (int32_t)(floorlog2 * 0.30102999566398114); + stop1 = 62 + (floorlog10 - (int32_t)STRFMT_PREC(sf)) / 9; + stop2 = 61 + ndhi - (int32_t)STRFMT_PREC(sf) / 8; + } + } + /* Real logic. */ + while (k >= 9) { + uint32_t i = ndhi, carry = 0; + for (;;) { + uint32_t val = nd[i]; + nd[i] = (val >> 9) + carry; + carry = (val & 0x1ff) * 1953125; + if (i == ndlo) break; + i = (i - 1) & 0x3f; + } + if (ndlo != stop1 && ndlo != stop2) { + if (carry) { ndlo = (ndlo - 1) & 0x3f; nd[ndlo] = carry; } + if (!nd[ndhi]) { ndhi = (ndhi - 1) & 0x3f; stop2--; } + } else if (!nd[ndhi]) { + if (ndhi != ndlo) { ndhi = (ndhi - 1) & 0x3f; stop2--; } + else return ndlo; + } + k -= 9; + } + if (k) { + uint32_t mask = (1U << k) - 1, mul = 1000000000 >> k, i = ndhi, carry = 0; + for (;;) { + uint32_t val = nd[i]; + nd[i] = (val >> k) + carry; + carry = (val & mask) * mul; + if (i == ndlo) break; + i = (i - 1) & 0x3f; + } + if (carry) { ndlo = (ndlo - 1) & 0x3f; nd[ndlo] = carry; } + } + return ndlo; +} + +/* Add m*10^e to nd (assumes ndlo <= e/9 <= ndhi and 0 <= m <= 9). */ +static uint32_t nd_add_m10e(uint32_t* nd, uint32_t ndhi, uint8_t m, int32_t e) +{ + uint32_t i, carry; + if (e >= 0) { + i = (uint32_t)e/9; + carry = m * (ndigits_dec_threshold[e - (int32_t)i*9] + 1); + } else { + int32_t f = (e-8)/9; + i = (uint32_t)(64 + f); + carry = m * (ndigits_dec_threshold[e - f*9] + 1); + } + for (;;) { + uint32_t val = nd[i] + carry; + if (LJ_UNLIKELY(val >= 1000000000)) { + val -= 1000000000; + nd[i] = val; + if (LJ_UNLIKELY(i == ndhi)) { + ndhi = (ndhi + 1) & 0x3f; + nd[ndhi] = 1; + break; + } + carry = 1; + i = (i + 1) & 0x3f; + } else { + nd[i] = val; + break; + } + } + return ndhi; +} + +/* Test whether two "nd" values are equal in their most significant digits. */ +static int nd_similar(uint32_t* nd, uint32_t ndhi, uint32_t* ref, MSize hilen, + MSize prec) +{ + char nd9[9], ref9[9]; + if (hilen <= prec) { + if (LJ_UNLIKELY(nd[ndhi] != *ref)) return 0; + prec -= hilen; ref--; ndhi = (ndhi - 1) & 0x3f; + if (prec >= 9) { + if (LJ_UNLIKELY(nd[ndhi] != *ref)) return 0; + prec -= 9; ref--; ndhi = (ndhi - 1) & 0x3f; + } + } else { + prec -= hilen - 9; + } + lua_assert(prec < 9); + lj_strfmt_wuint9(nd9, nd[ndhi]); + lj_strfmt_wuint9(ref9, *ref); + return !memcmp(nd9, ref9, prec) && (nd9[prec] < '5') == (ref9[prec] < '5'); +} + +/* -- Formatted conversions to buffer ------------------------------------- */ + +/* Write formatted floating-point number to either sb or p. */ +static char *lj_strfmt_wfnum(SBuf *sb, SFormat sf, lua_Number n, char *p) +{ + MSize width = STRFMT_WIDTH(sf), prec = STRFMT_PREC(sf), len; + TValue t; + t.n = n; + if (LJ_UNLIKELY((t.u32.hi << 1) >= 0xffe00000)) { + /* Handle non-finite values uniformly for %a, %e, %f, %g. */ + int prefix = 0, ch = (sf & STRFMT_F_UPPER) ? 0x202020 : 0; + if (((t.u32.hi & 0x000fffff) | t.u32.lo) != 0) { + ch ^= ('n' << 16) | ('a' << 8) | 'n'; + if ((sf & STRFMT_F_SPACE)) prefix = ' '; + } else { + ch ^= ('i' << 16) | ('n' << 8) | 'f'; + if ((t.u32.hi & 0x80000000)) prefix = '-'; + else if ((sf & STRFMT_F_PLUS)) prefix = '+'; + else if ((sf & STRFMT_F_SPACE)) prefix = ' '; + } + len = 3 + (prefix != 0); + if (!p) p = lj_buf_more(sb, width > len ? width : len); + if (!(sf & STRFMT_F_LEFT)) while (width-- > len) *p++ = ' '; + if (prefix) *p++ = prefix; + *p++ = (char)(ch >> 16); *p++ = (char)(ch >> 8); *p++ = (char)ch; + } else if (STRFMT_FP(sf) == STRFMT_FP(STRFMT_T_FP_A)) { + /* %a */ + const char *hexdig = (sf & STRFMT_F_UPPER) ? "0123456789ABCDEFPX" + : "0123456789abcdefpx"; + int32_t e = (t.u32.hi >> 20) & 0x7ff; + char prefix = 0, eprefix = '+'; + if (t.u32.hi & 0x80000000) prefix = '-'; + else if ((sf & STRFMT_F_PLUS)) prefix = '+'; + else if ((sf & STRFMT_F_SPACE)) prefix = ' '; + t.u32.hi &= 0xfffff; + if (e) { + t.u32.hi |= 0x100000; + e -= 1023; + } else if (t.u32.lo | t.u32.hi) { + /* Non-zero denormal - normalise it. */ + uint32_t shift = t.u32.hi ? 20-lj_fls(t.u32.hi) : 52-lj_fls(t.u32.lo); + e = -1022 - shift; + t.u64 <<= shift; + } + /* abs(n) == t.u64 * 2^(e - 52) */ + /* If n != 0, bit 52 of t.u64 is set, and is the highest set bit. */ + if ((int32_t)prec < 0) { + /* Default precision: use smallest precision giving exact result. */ + prec = t.u32.lo ? 13-lj_ffs(t.u32.lo)/4 : 5-lj_ffs(t.u32.hi|0x100000)/4; + } else if (prec < 13) { + /* Precision is sufficiently low as to maybe require rounding. */ + t.u64 += (((uint64_t)1) << (51 - prec*4)); + } + if (e < 0) { + eprefix = '-'; + e = -e; + } + len = 5 + ndigits_dec((uint32_t)e) + prec + (prefix != 0) + + ((prec | (sf & STRFMT_F_ALT)) != 0); + if (!p) p = lj_buf_more(sb, width > len ? width : len); + if (!(sf & (STRFMT_F_LEFT | STRFMT_F_ZERO))) { + while (width-- > len) *p++ = ' '; + } + if (prefix) *p++ = prefix; + *p++ = '0'; + *p++ = hexdig[17]; /* x or X */ + if ((sf & (STRFMT_F_LEFT | STRFMT_F_ZERO)) == STRFMT_F_ZERO) { + while (width-- > len) *p++ = '0'; + } + *p++ = '0' + (t.u32.hi >> 20); /* Usually '1', sometimes '0' or '2'. */ + if ((prec | (sf & STRFMT_F_ALT))) { + /* Emit fractional part. */ + char *q = p + 1 + prec; + *p = '.'; + if (prec < 13) t.u64 >>= (52 - prec*4); + else while (prec > 13) p[prec--] = '0'; + while (prec) { p[prec--] = hexdig[t.u64 & 15]; t.u64 >>= 4; } + p = q; + } + *p++ = hexdig[16]; /* p or P */ + *p++ = eprefix; /* + or - */ + p = lj_strfmt_wint(p, e); + } else { + /* %e or %f or %g - begin by converting n to "nd" format. */ + uint32_t nd[64]; + uint32_t ndhi = 0, ndlo, i; + int32_t e = (t.u32.hi >> 20) & 0x7ff, ndebias = 0; + char prefix = 0, *q; + if (t.u32.hi & 0x80000000) prefix = '-'; + else if ((sf & STRFMT_F_PLUS)) prefix = '+'; + else if ((sf & STRFMT_F_SPACE)) prefix = ' '; + prec += ((int32_t)prec >> 31) & 7; /* Default precision is 6. */ + if (STRFMT_FP(sf) == STRFMT_FP(STRFMT_T_FP_G)) { + /* %g - decrement precision if non-zero (to make it like %e). */ + prec--; + prec ^= (uint32_t)((int32_t)prec >> 31); + } + if ((sf & STRFMT_T_FP_E) && prec < 14 && n != 0) { + /* Precision is sufficiently low that rescaling will probably work. */ + if ((ndebias = rescale_e[e >> 6])) { + t.n = n * rescale_n[e >> 6]; + if (LJ_UNLIKELY(!e)) t.n *= 1e10, ndebias -= 10; + t.u64 -= 2; /* Convert 2ulp below (later we convert 2ulp above). */ + nd[0] = 0x100000 | (t.u32.hi & 0xfffff); + e = ((t.u32.hi >> 20) & 0x7ff) - 1075 - (ND_MUL2K_MAX_SHIFT < 29); + goto load_t_lo; rescale_failed: + t.n = n; + e = (t.u32.hi >> 20) & 0x7ff; + ndebias = ndhi = 0; + } + } + nd[0] = t.u32.hi & 0xfffff; + if (e == 0) e++; else nd[0] |= 0x100000; + e -= 1043; + if (t.u32.lo) { + e -= 32 + (ND_MUL2K_MAX_SHIFT < 29); load_t_lo: +#if ND_MUL2K_MAX_SHIFT >= 29 + nd[0] = (nd[0] << 3) | (t.u32.lo >> 29); + ndhi = nd_mul2k(nd, ndhi, 29, t.u32.lo & 0x1fffffff, sf); +#elif ND_MUL2K_MAX_SHIFT >= 11 + ndhi = nd_mul2k(nd, ndhi, 11, t.u32.lo >> 21, sf); + ndhi = nd_mul2k(nd, ndhi, 11, (t.u32.lo >> 10) & 0x7ff, sf); + ndhi = nd_mul2k(nd, ndhi, 11, (t.u32.lo << 1) & 0x7ff, sf); +#else +#error "ND_MUL2K_MAX_SHIFT too small" +#endif + } + if (e >= 0) { + ndhi = nd_mul2k(nd, ndhi, (uint32_t)e, 0, sf); + ndlo = 0; + } else { + ndlo = nd_div2k(nd, ndhi, (uint32_t)-e, sf); + if (ndhi && !nd[ndhi]) ndhi--; + } + /* abs(n) == nd * 10^ndebias (for slightly loose interpretation of ==) */ + if ((sf & STRFMT_T_FP_E)) { + /* %e or %g - assume %e and start by calculating nd's exponent (nde). */ + char eprefix = '+'; + int32_t nde = -1; + MSize hilen; + if (ndlo && !nd[ndhi]) { + ndhi = 64; do {} while (!nd[--ndhi]); + nde -= 64 * 9; + } + hilen = ndigits_dec(nd[ndhi]); + nde += ndhi * 9 + hilen; + if (ndebias) { + /* + ** Rescaling was performed, but this introduced some error, and might + ** have pushed us across a rounding boundary. We check whether this + ** error affected the result by introducing even more error (2ulp in + ** either direction), and seeing whether a roundary boundary was + ** crossed. Having already converted the -2ulp case, we save off its + ** most significant digits, convert the +2ulp case, and compare them. + */ + int32_t eidx = e + 70 + (ND_MUL2K_MAX_SHIFT < 29) + + (t.u32.lo >= 0xfffffffe && !(~t.u32.hi << 12)); + const int8_t *m_e = four_ulp_m_e + eidx * 2; + lua_assert(0 <= eidx && eidx < 128); + nd[33] = nd[ndhi]; + nd[32] = nd[(ndhi - 1) & 0x3f]; + nd[31] = nd[(ndhi - 2) & 0x3f]; + nd_add_m10e(nd, ndhi, (uint8_t)*m_e, m_e[1]); + if (LJ_UNLIKELY(!nd_similar(nd, ndhi, nd + 33, hilen, prec + 1))) { + goto rescale_failed; + } + } + if ((int32_t)(prec - nde) < (0x3f & -(int32_t)ndlo) * 9) { + /* Precision is sufficiently low as to maybe require rounding. */ + ndhi = nd_add_m10e(nd, ndhi, 5, nde - prec - 1); + nde += (hilen != ndigits_dec(nd[ndhi])); + } + nde += ndebias; + if ((sf & STRFMT_T_FP_F)) { + /* %g */ + if ((int32_t)prec >= nde && nde >= -4) { + if (nde < 0) ndhi = 0; + prec -= nde; + goto g_format_like_f; + } else if (!(sf & STRFMT_F_ALT) && prec && width > 5) { + /* Decrease precision in order to strip trailing zeroes. */ + char tail[9]; + uint32_t maxprec = hilen - 1 + ((ndhi - ndlo) & 0x3f) * 9; + if (prec >= maxprec) prec = maxprec; + else ndlo = (ndhi - (((int32_t)(prec - hilen) + 9) / 9)) & 0x3f; + i = prec - hilen - (((ndhi - ndlo) & 0x3f) * 9) + 10; + lj_strfmt_wuint9(tail, nd[ndlo]); + while (prec && tail[--i] == '0') { + prec--; + if (!i) { + if (ndlo == ndhi) { prec = 0; break; } + lj_strfmt_wuint9(tail, nd[++ndlo]); + i = 9; + } + } + } + } + if (nde < 0) { + /* Make nde non-negative. */ + eprefix = '-'; + nde = -nde; + } + len = 3 + prec + (prefix != 0) + ndigits_dec((uint32_t)nde) + (nde < 10) + + ((prec | (sf & STRFMT_F_ALT)) != 0); + if (!p) p = lj_buf_more(sb, (width > len ? width : len) + 5); + if (!(sf & (STRFMT_F_LEFT | STRFMT_F_ZERO))) { + while (width-- > len) *p++ = ' '; + } + if (prefix) *p++ = prefix; + if ((sf & (STRFMT_F_LEFT | STRFMT_F_ZERO)) == STRFMT_F_ZERO) { + while (width-- > len) *p++ = '0'; + } + q = lj_strfmt_wint(p + 1, nd[ndhi]); + p[0] = p[1]; /* Put leading digit in the correct place. */ + if ((prec | (sf & STRFMT_F_ALT))) { + /* Emit fractional part. */ + p[1] = '.'; p += 2; + prec -= (MSize)(q - p); p = q; /* Account for digits already emitted. */ + /* Then emit chunks of 9 digits (this may emit 8 digits too many). */ + for (i = ndhi; (int32_t)prec > 0 && i != ndlo; prec -= 9) { + i = (i - 1) & 0x3f; + p = lj_strfmt_wuint9(p, nd[i]); + } + if ((sf & STRFMT_T_FP_F) && !(sf & STRFMT_F_ALT)) { + /* %g (and not %#g) - strip trailing zeroes. */ + p += (int32_t)prec & ((int32_t)prec >> 31); + while (p[-1] == '0') p--; + if (p[-1] == '.') p--; + } else { + /* %e (or %#g) - emit trailing zeroes. */ + while ((int32_t)prec > 0) { *p++ = '0'; prec--; } + p += (int32_t)prec; + } + } else { + p++; + } + *p++ = (sf & STRFMT_F_UPPER) ? 'E' : 'e'; + *p++ = eprefix; /* + or - */ + if (nde < 10) *p++ = '0'; /* Always at least two digits of exponent. */ + p = lj_strfmt_wint(p, nde); + } else { + /* %f (or, shortly, %g in %f style) */ + if (prec < (MSize)(0x3f & -(int32_t)ndlo) * 9) { + /* Precision is sufficiently low as to maybe require rounding. */ + ndhi = nd_add_m10e(nd, ndhi, 5, 0 - prec - 1); + } + g_format_like_f: + if ((sf & STRFMT_T_FP_E) && !(sf & STRFMT_F_ALT) && prec && width) { + /* Decrease precision in order to strip trailing zeroes. */ + if (ndlo) { + /* nd has a fractional part; we need to look at its digits. */ + char tail[9]; + uint32_t maxprec = (64 - ndlo) * 9; + if (prec >= maxprec) prec = maxprec; + else ndlo = 64 - (prec + 8) / 9; + i = prec - ((63 - ndlo) * 9); + lj_strfmt_wuint9(tail, nd[ndlo]); + while (prec && tail[--i] == '0') { + prec--; + if (!i) { + if (ndlo == 63) { prec = 0; break; } + lj_strfmt_wuint9(tail, nd[++ndlo]); + i = 9; + } + } + } else { + /* nd has no fractional part, so precision goes straight to zero. */ + prec = 0; + } + } + len = ndhi * 9 + ndigits_dec(nd[ndhi]) + prec + (prefix != 0) + + ((prec | (sf & STRFMT_F_ALT)) != 0); + if (!p) p = lj_buf_more(sb, (width > len ? width : len) + 8); + if (!(sf & (STRFMT_F_LEFT | STRFMT_F_ZERO))) { + while (width-- > len) *p++ = ' '; + } + if (prefix) *p++ = prefix; + if ((sf & (STRFMT_F_LEFT | STRFMT_F_ZERO)) == STRFMT_F_ZERO) { + while (width-- > len) *p++ = '0'; + } + /* Emit integer part. */ + p = lj_strfmt_wint(p, nd[ndhi]); + i = ndhi; + while (i) p = lj_strfmt_wuint9(p, nd[--i]); + if ((prec | (sf & STRFMT_F_ALT))) { + /* Emit fractional part. */ + *p++ = '.'; + /* Emit chunks of 9 digits (this may emit 8 digits too many). */ + while ((int32_t)prec > 0 && i != ndlo) { + i = (i - 1) & 0x3f; + p = lj_strfmt_wuint9(p, nd[i]); + prec -= 9; + } + if ((sf & STRFMT_T_FP_E) && !(sf & STRFMT_F_ALT)) { + /* %g (and not %#g) - strip trailing zeroes. */ + p += (int32_t)prec & ((int32_t)prec >> 31); + while (p[-1] == '0') p--; + if (p[-1] == '.') p--; + } else { + /* %f (or %#g) - emit trailing zeroes. */ + while ((int32_t)prec > 0) { *p++ = '0'; prec--; } + p += (int32_t)prec; + } + } + } + } + if ((sf & STRFMT_F_LEFT)) while (width-- > len) *p++ = ' '; + return p; +} + +/* Add formatted floating-point number to buffer. */ +SBuf *lj_strfmt_putfnum(SBuf *sb, SFormat sf, lua_Number n) +{ + setsbufP(sb, lj_strfmt_wfnum(sb, sf, n, NULL)); + return sb; +} + +/* -- Conversions to strings ---------------------------------------------- */ + +/* Convert number to string. */ +GCstr * LJ_FASTCALL lj_strfmt_num(lua_State *L, cTValue *o) +{ + char buf[STRFMT_MAXBUF_NUM]; + MSize len = (MSize)(lj_strfmt_wfnum(NULL, STRFMT_G14, o->n, buf) - buf); + return lj_str_new(L, buf, len); +} + diff --git a/lib/LuaJIT/lj_strfmt_num.o b/lib/LuaJIT/lj_strfmt_num.o new file mode 100644 index 0000000000000000000000000000000000000000..7ee78da6170907bb6ab9082b59159ea2912d069b GIT binary patch literal 10856 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 + +#define lj_strscan_c +#define LUA_CORE + +#include "lj_obj.h" +#include "lj_char.h" +#include "lj_strscan.h" + +/* -- Scanning numbers ---------------------------------------------------- */ + +/* +** Rationale for the builtin string to number conversion library: +** +** It removes a dependency on libc's strtod(), which is a true portability +** nightmare. Mainly due to the plethora of supported OS and toolchain +** combinations. Sadly, the various implementations +** a) are often buggy, incomplete (no hex floats) and/or imprecise, +** b) sometimes crash or hang on certain inputs, +** c) return non-standard NaNs that need to be filtered out, and +** d) fail if the locale-specific decimal separator is not a dot, +** which can only be fixed with atrocious workarounds. +** +** Also, most of the strtod() implementations are hopelessly bloated, +** which is not just an I-cache hog, but a problem for static linkage +** on embedded systems, too. +** +** OTOH the builtin conversion function is very compact. Even though it +** does a lot more, like parsing long longs, octal or imaginary numbers +** and returning the result in different formats: +** a) It needs less than 3 KB (!) of machine code (on x64 with -Os), +** b) it doesn't perform any dynamic allocation and, +** c) it needs only around 600 bytes of stack space. +** +** The builtin function is faster than strtod() for typical inputs, e.g. +** "123", "1.5" or "1e6". Arguably, it's slower for very large exponents, +** which are not very common (this could be fixed, if needed). +** +** And most importantly, the builtin function is equally precise on all +** platforms. It correctly converts and rounds any input to a double. +** If this is not the case, please send a bug report -- but PLEASE verify +** that the implementation you're comparing to is not the culprit! +** +** The implementation quickly pre-scans the entire string first and +** handles simple integers on-the-fly. Otherwise, it dispatches to the +** base-specific parser. Hex and octal is straightforward. +** +** Decimal to binary conversion uses a fixed-length circular buffer in +** base 100. Some simple cases are handled directly. For other cases, the +** number in the buffer is up-scaled or down-scaled until the integer part +** is in the proper range. Then the integer part is rounded and converted +** to a double which is finally rescaled to the result. Denormals need +** special treatment to prevent incorrect 'double rounding'. +*/ + +/* Definitions for circular decimal digit buffer (base 100 = 2 digits/byte). */ +#define STRSCAN_DIG 1024 +#define STRSCAN_MAXDIG 800 /* 772 + extra are sufficient. */ +#define STRSCAN_DDIG (STRSCAN_DIG/2) +#define STRSCAN_DMASK (STRSCAN_DDIG-1) + +/* Helpers for circular buffer. */ +#define DNEXT(a) (((a)+1) & STRSCAN_DMASK) +#define DPREV(a) (((a)-1) & STRSCAN_DMASK) +#define DLEN(lo, hi) ((int32_t)(((lo)-(hi)) & STRSCAN_DMASK)) + +#define casecmp(c, k) (((c) | 0x20) == k) + +/* Final conversion to double. */ +static void strscan_double(uint64_t x, TValue *o, int32_t ex2, int32_t neg) +{ + double n; + + /* Avoid double rounding for denormals. */ + if (LJ_UNLIKELY(ex2 <= -1075 && x != 0)) { + /* NYI: all of this generates way too much code on 32 bit CPUs. */ +#if defined(__GNUC__) && LJ_64 + int32_t b = (int32_t)(__builtin_clzll(x)^63); +#else + int32_t b = (x>>32) ? 32+(int32_t)lj_fls((uint32_t)(x>>32)) : + (int32_t)lj_fls((uint32_t)x); +#endif + if ((int32_t)b + ex2 <= -1023 && (int32_t)b + ex2 >= -1075) { + uint64_t rb = (uint64_t)1 << (-1075-ex2); + if ((x & rb) && ((x & (rb+rb+rb-1)))) x += rb+rb; + x = (x & ~(rb+rb-1)); + } + } + + /* Convert to double using a signed int64_t conversion, then rescale. */ + lua_assert((int64_t)x >= 0); + n = (double)(int64_t)x; + if (neg) n = -n; + if (ex2) n = ldexp(n, ex2); + o->n = n; +} + +/* Parse hexadecimal number. */ +static StrScanFmt strscan_hex(const uint8_t *p, TValue *o, + StrScanFmt fmt, uint32_t opt, + int32_t ex2, int32_t neg, uint32_t dig) +{ + uint64_t x = 0; + uint32_t i; + + /* Scan hex digits. */ + for (i = dig > 16 ? 16 : dig ; i; i--, p++) { + uint32_t d = (*p != '.' ? *p : *++p); if (d > '9') d += 9; + x = (x << 4) + (d & 15); + } + + /* Summarize rounding-effect of excess digits. */ + for (i = 16; i < dig; i++, p++) + x |= ((*p != '.' ? *p : *++p) != '0'), ex2 += 4; + + /* Format-specific handling. */ + switch (fmt) { + case STRSCAN_INT: + if (!(opt & STRSCAN_OPT_TONUM) && x < 0x80000000u+neg) { + o->i = neg ? -(int32_t)x : (int32_t)x; + return STRSCAN_INT; /* Fast path for 32 bit integers. */ + } + if (!(opt & STRSCAN_OPT_C)) { fmt = STRSCAN_NUM; break; } + /* fallthrough */ + case STRSCAN_U32: + if (dig > 8) return STRSCAN_ERROR; + o->i = neg ? -(int32_t)x : (int32_t)x; + return STRSCAN_U32; + case STRSCAN_I64: + case STRSCAN_U64: + if (dig > 16) return STRSCAN_ERROR; + o->u64 = neg ? (uint64_t)-(int64_t)x : x; + return fmt; + default: + break; + } + + /* Reduce range, then convert to double. */ + if ((x & U64x(c0000000,0000000))) { x = (x >> 2) | (x & 3); ex2 += 2; } + strscan_double(x, o, ex2, neg); + return fmt; +} + +/* Parse octal number. */ +static StrScanFmt strscan_oct(const uint8_t *p, TValue *o, + StrScanFmt fmt, int32_t neg, uint32_t dig) +{ + uint64_t x = 0; + + /* Scan octal digits. */ + if (dig > 22 || (dig == 22 && *p > '1')) return STRSCAN_ERROR; + while (dig-- > 0) { + if (!(*p >= '0' && *p <= '7')) return STRSCAN_ERROR; + x = (x << 3) + (*p++ & 7); + } + + /* Format-specific handling. */ + switch (fmt) { + case STRSCAN_INT: + if (x >= 0x80000000u+neg) fmt = STRSCAN_U32; + /* fallthrough */ + case STRSCAN_U32: + if ((x >> 32)) return STRSCAN_ERROR; + o->i = neg ? -(int32_t)x : (int32_t)x; + break; + default: + case STRSCAN_I64: + case STRSCAN_U64: + o->u64 = neg ? (uint64_t)-(int64_t)x : x; + break; + } + return fmt; +} + +/* Parse decimal number. */ +static StrScanFmt strscan_dec(const uint8_t *p, TValue *o, + StrScanFmt fmt, uint32_t opt, + int32_t ex10, int32_t neg, uint32_t dig) +{ + uint8_t xi[STRSCAN_DDIG], *xip = xi; + + if (dig) { + uint32_t i = dig; + if (i > STRSCAN_MAXDIG) { + ex10 += (int32_t)(i - STRSCAN_MAXDIG); + i = STRSCAN_MAXDIG; + } + /* Scan unaligned leading digit. */ + if (((ex10^i) & 1)) + *xip++ = ((*p != '.' ? *p : *++p) & 15), i--, p++; + /* Scan aligned double-digits. */ + for ( ; i > 1; i -= 2) { + uint32_t d = 10 * ((*p != '.' ? *p : *++p) & 15); p++; + *xip++ = d + ((*p != '.' ? *p : *++p) & 15); p++; + } + /* Scan and realign trailing digit. */ + if (i) *xip++ = 10 * ((*p != '.' ? *p : *++p) & 15), ex10--, dig++, p++; + + /* Summarize rounding-effect of excess digits. */ + if (dig > STRSCAN_MAXDIG) { + do { + if ((*p != '.' ? *p : *++p) != '0') { xip[-1] |= 1; break; } + p++; + } while (--dig > STRSCAN_MAXDIG); + dig = STRSCAN_MAXDIG; + } else { /* Simplify exponent. */ + while (ex10 > 0 && dig <= 18) *xip++ = 0, ex10 -= 2, dig += 2; + } + } else { /* Only got zeros. */ + ex10 = 0; + xi[0] = 0; + } + + /* Fast path for numbers in integer format (but handles e.g. 1e6, too). */ + if (dig <= 20 && ex10 == 0) { + uint8_t *xis; + uint64_t x = xi[0]; + double n; + for (xis = xi+1; xis < xip; xis++) x = x * 100 + *xis; + if (!(dig == 20 && (xi[0] > 18 || (int64_t)x >= 0))) { /* No overflow? */ + /* Format-specific handling. */ + switch (fmt) { + case STRSCAN_INT: + if (!(opt & STRSCAN_OPT_TONUM) && x < 0x80000000u+neg) { + o->i = neg ? -(int32_t)x : (int32_t)x; + return STRSCAN_INT; /* Fast path for 32 bit integers. */ + } + if (!(opt & STRSCAN_OPT_C)) { fmt = STRSCAN_NUM; goto plainnumber; } + /* fallthrough */ + case STRSCAN_U32: + if ((x >> 32) != 0) return STRSCAN_ERROR; + o->i = neg ? -(int32_t)x : (int32_t)x; + return STRSCAN_U32; + case STRSCAN_I64: + case STRSCAN_U64: + o->u64 = neg ? (uint64_t)-(int64_t)x : x; + return fmt; + default: + plainnumber: /* Fast path for plain numbers < 2^63. */ + if ((int64_t)x < 0) break; + n = (double)(int64_t)x; + if (neg) n = -n; + o->n = n; + return fmt; + } + } + } + + /* Slow non-integer path. */ + if (fmt == STRSCAN_INT) { + if ((opt & STRSCAN_OPT_C)) return STRSCAN_ERROR; + fmt = STRSCAN_NUM; + } else if (fmt > STRSCAN_INT) { + return STRSCAN_ERROR; + } + { + uint32_t hi = 0, lo = (uint32_t)(xip-xi); + int32_t ex2 = 0, idig = (int32_t)lo + (ex10 >> 1); + + lua_assert(lo > 0 && (ex10 & 1) == 0); + + /* Handle simple overflow/underflow. */ + if (idig > 310/2) { if (neg) setminfV(o); else setpinfV(o); return fmt; } + else if (idig < -326/2) { o->n = neg ? -0.0 : 0.0; return fmt; } + + /* Scale up until we have at least 17 or 18 integer part digits. */ + while (idig < 9 && idig < DLEN(lo, hi)) { + uint32_t i, cy = 0; + ex2 -= 6; + for (i = DPREV(lo); ; i = DPREV(i)) { + uint32_t d = (xi[i] << 6) + cy; + cy = (((d >> 2) * 5243) >> 17); d = d - cy * 100; /* Div/mod 100. */ + xi[i] = (uint8_t)d; + if (i == hi) break; + if (d == 0 && i == DPREV(lo)) lo = i; + } + if (cy) { + hi = DPREV(hi); + if (xi[DPREV(lo)] == 0) lo = DPREV(lo); + else if (hi == lo) { lo = DPREV(lo); xi[DPREV(lo)] |= xi[lo]; } + xi[hi] = (uint8_t)cy; idig++; + } + } + + /* Scale down until no more than 17 or 18 integer part digits remain. */ + while (idig > 9) { + uint32_t i = hi, cy = 0; + ex2 += 6; + do { + cy += xi[i]; + xi[i] = (cy >> 6); + cy = 100 * (cy & 0x3f); + if (xi[i] == 0 && i == hi) hi = DNEXT(hi), idig--; + i = DNEXT(i); + } while (i != lo); + while (cy) { + if (hi == lo) { xi[DPREV(lo)] |= 1; break; } + xi[lo] = (cy >> 6); lo = DNEXT(lo); + cy = 100 * (cy & 0x3f); + } + } + + /* Collect integer part digits and convert to rescaled double. */ + { + uint64_t x = xi[hi]; + uint32_t i; + for (i = DNEXT(hi); --idig > 0 && i != lo; i = DNEXT(i)) + x = x * 100 + xi[i]; + if (i == lo) { + while (--idig >= 0) x = x * 100; + } else { /* Gather round bit from remaining digits. */ + x <<= 1; ex2--; + do { + if (xi[i]) { x |= 1; break; } + i = DNEXT(i); + } while (i != lo); + } + strscan_double(x, o, ex2, neg); + } + } + return fmt; +} + +/* Parse binary number. */ +static StrScanFmt strscan_bin(const uint8_t *p, TValue *o, + StrScanFmt fmt, uint32_t opt, + int32_t ex2, int32_t neg, uint32_t dig) +{ + uint64_t x = 0; + uint32_t i; + + if (ex2 || dig > 64) return STRSCAN_ERROR; + + /* Scan binary digits. */ + for (i = dig; i; i--, p++) { + if ((*p & ~1) != '0') return STRSCAN_ERROR; + x = (x << 1) | (*p & 1); + } + + /* Format-specific handling. */ + switch (fmt) { + case STRSCAN_INT: + if (!(opt & STRSCAN_OPT_TONUM) && x < 0x80000000u+neg) { + o->i = neg ? -(int32_t)x : (int32_t)x; + return STRSCAN_INT; /* Fast path for 32 bit integers. */ + } + if (!(opt & STRSCAN_OPT_C)) { fmt = STRSCAN_NUM; break; } + /* fallthrough */ + case STRSCAN_U32: + if (dig > 32) return STRSCAN_ERROR; + o->i = neg ? -(int32_t)x : (int32_t)x; + return STRSCAN_U32; + case STRSCAN_I64: + case STRSCAN_U64: + o->u64 = neg ? (uint64_t)-(int64_t)x : x; + return fmt; + default: + break; + } + + /* Reduce range, then convert to double. */ + if ((x & U64x(c0000000,0000000))) { x = (x >> 2) | (x & 3); ex2 += 2; } + strscan_double(x, o, ex2, neg); + return fmt; +} + +/* Scan string containing a number. Returns format. Returns value in o. */ +StrScanFmt lj_strscan_scan(const uint8_t *p, TValue *o, uint32_t opt) +{ + int32_t neg = 0; + + /* Remove leading space, parse sign and non-numbers. */ + if (LJ_UNLIKELY(!lj_char_isdigit(*p))) { + while (lj_char_isspace(*p)) p++; + if (*p == '+' || *p == '-') neg = (*p++ == '-'); + if (LJ_UNLIKELY(*p >= 'A')) { /* Parse "inf", "infinity" or "nan". */ + TValue tmp; + setnanV(&tmp); + if (casecmp(p[0],'i') && casecmp(p[1],'n') && casecmp(p[2],'f')) { + if (neg) setminfV(&tmp); else setpinfV(&tmp); + p += 3; + if (casecmp(p[0],'i') && casecmp(p[1],'n') && casecmp(p[2],'i') && + casecmp(p[3],'t') && casecmp(p[4],'y')) p += 5; + } else if (casecmp(p[0],'n') && casecmp(p[1],'a') && casecmp(p[2],'n')) { + p += 3; + } + while (lj_char_isspace(*p)) p++; + if (*p) return STRSCAN_ERROR; + o->u64 = tmp.u64; + return STRSCAN_NUM; + } + } + + /* Parse regular number. */ + { + StrScanFmt fmt = STRSCAN_INT; + int cmask = LJ_CHAR_DIGIT; + int base = (opt & STRSCAN_OPT_C) && *p == '0' ? 0 : 10; + const uint8_t *sp, *dp = NULL; + uint32_t dig = 0, hasdig = 0, x = 0; + int32_t ex = 0; + + /* Determine base and skip leading zeros. */ + if (LJ_UNLIKELY(*p <= '0')) { + if (*p == '0') { + if (casecmp(p[1], 'x')) + base = 16, cmask = LJ_CHAR_XDIGIT, p += 2; + else if (casecmp(p[1], 'b')) + base = 2, cmask = LJ_CHAR_DIGIT, p += 2; + } + for ( ; ; p++) { + if (*p == '0') { + hasdig = 1; + } else if (*p == '.') { + if (dp) return STRSCAN_ERROR; + dp = p; + } else { + break; + } + } + } + + /* Preliminary digit and decimal point scan. */ + for (sp = p; ; p++) { + if (LJ_LIKELY(lj_char_isa(*p, cmask))) { + x = x * 10 + (*p & 15); /* For fast path below. */ + dig++; + } else if (*p == '.') { + if (dp) return STRSCAN_ERROR; + dp = p; + } else { + break; + } + } + if (!(hasdig | dig)) return STRSCAN_ERROR; + + /* Handle decimal point. */ + if (dp) { + fmt = STRSCAN_NUM; + if (dig) { + ex = (int32_t)(dp-(p-1)); dp = p-1; + while (ex < 0 && *dp-- == '0') ex++, dig--; /* Skip trailing zeros. */ + if (base == 16) ex *= 4; + } + } + + /* Parse exponent. */ + if (base >= 10 && casecmp(*p, (uint32_t)(base == 16 ? 'p' : 'e'))) { + uint32_t xx; + int negx = 0; + fmt = STRSCAN_NUM; p++; + if (*p == '+' || *p == '-') negx = (*p++ == '-'); + if (!lj_char_isdigit(*p)) return STRSCAN_ERROR; + xx = (*p++ & 15); + while (lj_char_isdigit(*p)) { + if (xx < 65536) xx = xx * 10 + (*p & 15); + p++; + } + ex += negx ? -(int32_t)xx : (int32_t)xx; + } + + /* Parse suffix. */ + if (*p) { + /* I (IMAG), U (U32), LL (I64), ULL/LLU (U64), L (long), UL/LU (ulong). */ + /* NYI: f (float). Not needed until cp_number() handles non-integers. */ + if (casecmp(*p, 'i')) { + if (!(opt & STRSCAN_OPT_IMAG)) return STRSCAN_ERROR; + p++; fmt = STRSCAN_IMAG; + } else if (fmt == STRSCAN_INT) { + if (casecmp(*p, 'u')) p++, fmt = STRSCAN_U32; + if (casecmp(*p, 'l')) { + p++; + if (casecmp(*p, 'l')) p++, fmt += STRSCAN_I64 - STRSCAN_INT; + else if (!(opt & STRSCAN_OPT_C)) return STRSCAN_ERROR; + else if (sizeof(long) == 8) fmt += STRSCAN_I64 - STRSCAN_INT; + } + if (casecmp(*p, 'u') && (fmt == STRSCAN_INT || fmt == STRSCAN_I64)) + p++, fmt += STRSCAN_U32 - STRSCAN_INT; + if ((fmt == STRSCAN_U32 && !(opt & STRSCAN_OPT_C)) || + (fmt >= STRSCAN_I64 && !(opt & STRSCAN_OPT_LL))) + return STRSCAN_ERROR; + } + while (lj_char_isspace(*p)) p++; + if (*p) return STRSCAN_ERROR; + } + + /* Fast path for decimal 32 bit integers. */ + if (fmt == STRSCAN_INT && base == 10 && + (dig < 10 || (dig == 10 && *sp <= '2' && x < 0x80000000u+neg))) { + int32_t y = neg ? -(int32_t)x : (int32_t)x; + if ((opt & STRSCAN_OPT_TONUM)) { + o->n = (double)y; + return STRSCAN_NUM; + } else { + o->i = y; + return STRSCAN_INT; + } + } + + /* Dispatch to base-specific parser. */ + if (base == 0 && !(fmt == STRSCAN_NUM || fmt == STRSCAN_IMAG)) + return strscan_oct(sp, o, fmt, neg, dig); + if (base == 16) + fmt = strscan_hex(sp, o, fmt, opt, ex, neg, dig); + else if (base == 2) + fmt = strscan_bin(sp, o, fmt, opt, ex, neg, dig); + else + fmt = strscan_dec(sp, o, fmt, opt, ex, neg, dig); + + /* Try to convert number to integer, if requested. */ + if (fmt == STRSCAN_NUM && (opt & STRSCAN_OPT_TOINT)) { + double n = o->n; + int32_t i = lj_num2int(n); + if (n == (lua_Number)i) { o->i = i; return STRSCAN_INT; } + } + return fmt; + } +} + +int LJ_FASTCALL lj_strscan_num(GCstr *str, TValue *o) +{ + StrScanFmt fmt = lj_strscan_scan((const uint8_t *)strdata(str), o, + STRSCAN_OPT_TONUM); + lua_assert(fmt == STRSCAN_ERROR || fmt == STRSCAN_NUM); + return (fmt != STRSCAN_ERROR); +} + +#if LJ_DUALNUM +int LJ_FASTCALL lj_strscan_number(GCstr *str, TValue *o) +{ + StrScanFmt fmt = lj_strscan_scan((const uint8_t *)strdata(str), o, + STRSCAN_OPT_TOINT); + lua_assert(fmt == STRSCAN_ERROR || fmt == STRSCAN_NUM || fmt == STRSCAN_INT); + if (fmt == STRSCAN_INT) setitype(o, LJ_TISNUM); + return (fmt != STRSCAN_ERROR); +} +#endif + +#undef DNEXT +#undef DPREV +#undef DLEN + diff --git a/lib/LuaJIT/lj_strscan.h b/lib/LuaJIT/lj_strscan.h new file mode 100644 index 0000000..6fb0dda --- /dev/null +++ b/lib/LuaJIT/lj_strscan.h @@ -0,0 +1,39 @@ +/* +** String scanning. +** Copyright (C) 2005-2017 Mike Pall. See Copyright Notice in luajit.h +*/ + +#ifndef _LJ_STRSCAN_H +#define _LJ_STRSCAN_H + +#include "lj_obj.h" + +/* Options for accepted/returned formats. */ +#define STRSCAN_OPT_TOINT 0x01 /* Convert to int32_t, if possible. */ +#define STRSCAN_OPT_TONUM 0x02 /* Always convert to double. */ +#define STRSCAN_OPT_IMAG 0x04 +#define STRSCAN_OPT_LL 0x08 +#define STRSCAN_OPT_C 0x10 + +/* Returned format. */ +typedef enum { + STRSCAN_ERROR, + STRSCAN_NUM, STRSCAN_IMAG, + STRSCAN_INT, STRSCAN_U32, STRSCAN_I64, STRSCAN_U64, +} StrScanFmt; + +LJ_FUNC StrScanFmt lj_strscan_scan(const uint8_t *p, TValue *o, uint32_t opt); +LJ_FUNC int LJ_FASTCALL lj_strscan_num(GCstr *str, TValue *o); +#if LJ_DUALNUM +LJ_FUNC int LJ_FASTCALL lj_strscan_number(GCstr *str, TValue *o); +#else +#define lj_strscan_number(s, o) lj_strscan_num((s), (o)) +#endif + +/* Check for number or convert string to number/int in-place (!). */ +static LJ_AINLINE int lj_strscan_numberobj(TValue *o) +{ + return tvisnumber(o) || (tvisstr(o) && lj_strscan_number(strV(o), o)); +} + +#endif diff --git a/lib/LuaJIT/lj_strscan.o b/lib/LuaJIT/lj_strscan.o new file mode 100644 index 0000000000000000000000000000000000000000..0c755b67922b99699c47fedb9b761c36e0e45e0f GIT binary patch literal 6952 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 + return &n[hash & t->hmask]; +} + +/* String hashes are precomputed when they are interned. */ +#define hashstr(t, s) hashmask(t, (s)->hash) + +#define hashlohi(t, lo, hi) hashmask((t), hashrot((lo), (hi))) +#define hashnum(t, o) hashlohi((t), (o)->u32.lo, ((o)->u32.hi << 1)) +#if LJ_GC64 +#define hashgcref(t, r) \ + hashlohi((t), (uint32_t)gcrefu(r), (uint32_t)(gcrefu(r) >> 32)) +#else +#define hashgcref(t, r) hashlohi((t), gcrefu(r), gcrefu(r) + HASH_BIAS) +#endif + +/* Hash an arbitrary key and return its anchor position in the hash table. */ +static Node *hashkey(const GCtab *t, cTValue *key) +{ + lua_assert(!tvisint(key)); + if (tvisstr(key)) + return hashstr(t, strV(key)); + else if (tvisnum(key)) + return hashnum(t, key); + else if (tvisbool(key)) + return hashmask(t, boolV(key)); + else + return hashgcref(t, key->gcr); + /* Only hash 32 bits of lightuserdata on a 64 bit CPU. Good enough? */ +} + +/* -- Table creation and destruction -------------------------------------- */ + +/* Create new hash part for table. */ +static LJ_AINLINE void newhpart(lua_State *L, GCtab *t, uint32_t hbits) +{ + uint32_t hsize; + Node *node; + lua_assert(hbits != 0); + if (hbits > LJ_MAX_HBITS) + lj_err_msg(L, LJ_ERR_TABOV); + hsize = 1u << hbits; + node = lj_mem_newvec(L, hsize, Node); + setmref(t->node, node); + setfreetop(t, node, &node[hsize]); + t->hmask = hsize-1; +} + +/* +** Q: Why all of these copies of t->hmask, t->node etc. to local variables? +** A: Because alias analysis for C is _really_ tough. +** Even state-of-the-art C compilers won't produce good code without this. +*/ + +/* Clear hash part of table. */ +static LJ_AINLINE void clearhpart(GCtab *t) +{ + uint32_t i, hmask = t->hmask; + Node *node = noderef(t->node); + lua_assert(t->hmask != 0); + for (i = 0; i <= hmask; i++) { + Node *n = &node[i]; + setmref(n->next, NULL); + setnilV(&n->key); + setnilV(&n->val); + } +} + +/* Clear array part of table. */ +static LJ_AINLINE void clearapart(GCtab *t) +{ + uint32_t i, asize = t->asize; + TValue *array = tvref(t->array); + for (i = 0; i < asize; i++) + setnilV(&array[i]); +} + +/* Create a new table. Note: the slots are not initialized (yet). */ +static GCtab *newtab(lua_State *L, uint32_t asize, uint32_t hbits) +{ + GCtab *t; + /* First try to colocate the array part. */ + if (LJ_MAX_COLOSIZE != 0 && asize > 0 && asize <= LJ_MAX_COLOSIZE) { + Node *nilnode; + lua_assert((sizeof(GCtab) & 7) == 0); + t = (GCtab *)lj_mem_newgco(L, sizetabcolo(asize)); + t->gct = ~LJ_TTAB; + t->nomm = (uint8_t)~0; + t->colo = (int8_t)asize; + setmref(t->array, (TValue *)((char *)t + sizeof(GCtab))); + setgcrefnull(t->metatable); + t->asize = asize; + t->hmask = 0; + nilnode = &G(L)->nilnode; + setmref(t->node, nilnode); +#if LJ_GC64 + setmref(t->freetop, nilnode); +#endif + } else { /* Otherwise separately allocate the array part. */ + Node *nilnode; + t = lj_mem_newobj(L, GCtab); + t->gct = ~LJ_TTAB; + t->nomm = (uint8_t)~0; + t->colo = 0; + setmref(t->array, NULL); + setgcrefnull(t->metatable); + t->asize = 0; /* In case the array allocation fails. */ + t->hmask = 0; + nilnode = &G(L)->nilnode; + setmref(t->node, nilnode); +#if LJ_GC64 + setmref(t->freetop, nilnode); +#endif + if (asize > 0) { + if (asize > LJ_MAX_ASIZE) + lj_err_msg(L, LJ_ERR_TABOV); + setmref(t->array, lj_mem_newvec(L, asize, TValue)); + t->asize = asize; + } + } + if (hbits) + newhpart(L, t, hbits); + return t; +} + +/* Create a new table. +** +** IMPORTANT NOTE: The API differs from lua_createtable()! +** +** The array size is non-inclusive. E.g. asize=128 creates array slots +** for 0..127, but not for 128. If you need slots 1..128, pass asize=129 +** (slot 0 is wasted in this case). +** +** The hash size is given in hash bits. hbits=0 means no hash part. +** hbits=1 creates 2 hash slots, hbits=2 creates 4 hash slots and so on. +*/ +GCtab *lj_tab_new(lua_State *L, uint32_t asize, uint32_t hbits) +{ + GCtab *t = newtab(L, asize, hbits); + clearapart(t); + if (t->hmask > 0) clearhpart(t); + return t; +} + +/* The API of this function conforms to lua_createtable(). */ +GCtab *lj_tab_new_ah(lua_State *L, int32_t a, int32_t h) +{ + return lj_tab_new(L, (uint32_t)(a > 0 ? a+1 : 0), hsize2hbits(h)); +} + +#if LJ_HASJIT +GCtab * LJ_FASTCALL lj_tab_new1(lua_State *L, uint32_t ahsize) +{ + GCtab *t = newtab(L, ahsize & 0xffffff, ahsize >> 24); + clearapart(t); + if (t->hmask > 0) clearhpart(t); + return t; +} +#endif + +/* Duplicate a table. */ +GCtab * LJ_FASTCALL lj_tab_dup(lua_State *L, const GCtab *kt) +{ + GCtab *t; + uint32_t asize, hmask; + t = newtab(L, kt->asize, kt->hmask > 0 ? lj_fls(kt->hmask)+1 : 0); + lua_assert(kt->asize == t->asize && kt->hmask == t->hmask); + t->nomm = 0; /* Keys with metamethod names may be present. */ + asize = kt->asize; + if (asize > 0) { + TValue *array = tvref(t->array); + TValue *karray = tvref(kt->array); + if (asize < 64) { /* An inlined loop beats memcpy for < 512 bytes. */ + uint32_t i; + for (i = 0; i < asize; i++) + copyTV(L, &array[i], &karray[i]); + } else { + memcpy(array, karray, asize*sizeof(TValue)); + } + } + hmask = kt->hmask; + if (hmask > 0) { + uint32_t i; + Node *node = noderef(t->node); + Node *knode = noderef(kt->node); + ptrdiff_t d = (char *)node - (char *)knode; + setfreetop(t, node, (Node *)((char *)getfreetop(kt, knode) + d)); + for (i = 0; i <= hmask; i++) { + Node *kn = &knode[i]; + Node *n = &node[i]; + Node *next = nextnode(kn); + /* Don't use copyTV here, since it asserts on a copy of a dead key. */ + n->val = kn->val; n->key = kn->key; + setmref(n->next, next == NULL? next : (Node *)((char *)next + d)); + } + } + return t; +} + +/* Clear a table. */ +void LJ_FASTCALL lj_tab_clear(GCtab *t) +{ + clearapart(t); + if (t->hmask > 0) { + Node *node = noderef(t->node); + setfreetop(t, node, &node[t->hmask+1]); + clearhpart(t); + } +} + +/* Free a table. */ +void LJ_FASTCALL lj_tab_free(global_State *g, GCtab *t) +{ + if (t->hmask > 0) + lj_mem_freevec(g, noderef(t->node), t->hmask+1, Node); + if (t->asize > 0 && LJ_MAX_COLOSIZE != 0 && t->colo <= 0) + lj_mem_freevec(g, tvref(t->array), t->asize, TValue); + if (LJ_MAX_COLOSIZE != 0 && t->colo) + lj_mem_free(g, t, sizetabcolo((uint32_t)t->colo & 0x7f)); + else + lj_mem_freet(g, t); +} + +/* -- Table resizing ------------------------------------------------------ */ + +/* Resize a table to fit the new array/hash part sizes. */ +void lj_tab_resize(lua_State *L, GCtab *t, uint32_t asize, uint32_t hbits) +{ + Node *oldnode = noderef(t->node); + uint32_t oldasize = t->asize; + uint32_t oldhmask = t->hmask; + if (asize > oldasize) { /* Array part grows? */ + TValue *array; + uint32_t i; + if (asize > LJ_MAX_ASIZE) + lj_err_msg(L, LJ_ERR_TABOV); + if (LJ_MAX_COLOSIZE != 0 && t->colo > 0) { + /* A colocated array must be separated and copied. */ + TValue *oarray = tvref(t->array); + array = lj_mem_newvec(L, asize, TValue); + t->colo = (int8_t)(t->colo | 0x80); /* Mark as separated (colo < 0). */ + for (i = 0; i < oldasize; i++) + copyTV(L, &array[i], &oarray[i]); + } else { + array = (TValue *)lj_mem_realloc(L, tvref(t->array), + oldasize*sizeof(TValue), asize*sizeof(TValue)); + } + setmref(t->array, array); + t->asize = asize; + for (i = oldasize; i < asize; i++) /* Clear newly allocated slots. */ + setnilV(&array[i]); + } + /* Create new (empty) hash part. */ + if (hbits) { + newhpart(L, t, hbits); + clearhpart(t); + } else { + global_State *g = G(L); + setmref(t->node, &g->nilnode); +#if LJ_GC64 + setmref(t->freetop, &g->nilnode); +#endif + t->hmask = 0; + } + if (asize < oldasize) { /* Array part shrinks? */ + TValue *array = tvref(t->array); + uint32_t i; + t->asize = asize; /* Note: This 'shrinks' even colocated arrays. */ + for (i = asize; i < oldasize; i++) /* Reinsert old array values. */ + if (!tvisnil(&array[i])) + copyTV(L, lj_tab_setinth(L, t, (int32_t)i), &array[i]); + /* Physically shrink only separated arrays. */ + if (LJ_MAX_COLOSIZE != 0 && t->colo <= 0) + setmref(t->array, lj_mem_realloc(L, array, + oldasize*sizeof(TValue), asize*sizeof(TValue))); + } + if (oldhmask > 0) { /* Reinsert pairs from old hash part. */ + global_State *g; + uint32_t i; + for (i = 0; i <= oldhmask; i++) { + Node *n = &oldnode[i]; + if (!tvisnil(&n->val)) + copyTV(L, lj_tab_set(L, t, &n->key), &n->val); + } + g = G(L); + lj_mem_freevec(g, oldnode, oldhmask+1, Node); + } +} + +static uint32_t countint(cTValue *key, uint32_t *bins) +{ + lua_assert(!tvisint(key)); + if (tvisnum(key)) { + lua_Number nk = numV(key); + int32_t k = lj_num2int(nk); + if ((uint32_t)k < LJ_MAX_ASIZE && nk == (lua_Number)k) { + bins[(k > 2 ? lj_fls((uint32_t)(k-1)) : 0)]++; + return 1; + } + } + return 0; +} + +static uint32_t countarray(const GCtab *t, uint32_t *bins) +{ + uint32_t na, b, i; + if (t->asize == 0) return 0; + for (na = i = b = 0; b < LJ_MAX_ABITS; b++) { + uint32_t n, top = 2u << b; + TValue *array; + if (top >= t->asize) { + top = t->asize-1; + if (i > top) + break; + } + array = tvref(t->array); + for (n = 0; i <= top; i++) + if (!tvisnil(&array[i])) + n++; + bins[b] += n; + na += n; + } + return na; +} + +static uint32_t counthash(const GCtab *t, uint32_t *bins, uint32_t *narray) +{ + uint32_t total, na, i, hmask = t->hmask; + Node *node = noderef(t->node); + for (total = na = 0, i = 0; i <= hmask; i++) { + Node *n = &node[i]; + if (!tvisnil(&n->val)) { + na += countint(&n->key, bins); + total++; + } + } + *narray += na; + return total; +} + +static uint32_t bestasize(uint32_t bins[], uint32_t *narray) +{ + uint32_t b, sum, na = 0, sz = 0, nn = *narray; + for (b = 0, sum = 0; 2*nn > (1u< 0 && 2*(sum += bins[b]) > (1u<hmask > 0 ? lj_fls(t->hmask)+1 : 0); +} + +/* -- Table getters ------------------------------------------------------- */ + +cTValue * LJ_FASTCALL lj_tab_getinth(GCtab *t, int32_t key) +{ + TValue k; + Node *n; + k.n = (lua_Number)key; + n = hashnum(t, &k); + do { + if (tvisnum(&n->key) && n->key.n == k.n) + return &n->val; + } while ((n = nextnode(n))); + return NULL; +} + +cTValue *lj_tab_getstr(GCtab *t, GCstr *key) +{ + Node *n = hashstr(t, key); + do { + if (tvisstr(&n->key) && strV(&n->key) == key) + return &n->val; + } while ((n = nextnode(n))); + return NULL; +} + +cTValue *lj_tab_get(lua_State *L, GCtab *t, cTValue *key) +{ + if (tvisstr(key)) { + cTValue *tv = lj_tab_getstr(t, strV(key)); + if (tv) + return tv; + } else if (tvisint(key)) { + cTValue *tv = lj_tab_getint(t, intV(key)); + if (tv) + return tv; + } else if (tvisnum(key)) { + lua_Number nk = numV(key); + int32_t k = lj_num2int(nk); + if (nk == (lua_Number)k) { + cTValue *tv = lj_tab_getint(t, k); + if (tv) + return tv; + } else { + goto genlookup; /* Else use the generic lookup. */ + } + } else if (!tvisnil(key)) { + Node *n; + genlookup: + n = hashkey(t, key); + do { + if (lj_obj_equal(&n->key, key)) + return &n->val; + } while ((n = nextnode(n))); + } + return niltv(L); +} + +/* -- Table setters ------------------------------------------------------- */ + +/* Insert new key. Use Brent's variation to optimize the chain length. */ +TValue *lj_tab_newkey(lua_State *L, GCtab *t, cTValue *key) +{ + Node *n = hashkey(t, key); + if (!tvisnil(&n->val) || t->hmask == 0) { + Node *nodebase = noderef(t->node); + Node *collide, *freenode = getfreetop(t, nodebase); + lua_assert(freenode >= nodebase && freenode <= nodebase+t->hmask+1); + do { + if (freenode == nodebase) { /* No free node found? */ + rehashtab(L, t, key); /* Rehash table. */ + return lj_tab_set(L, t, key); /* Retry key insertion. */ + } + } while (!tvisnil(&(--freenode)->key)); + setfreetop(t, nodebase, freenode); + lua_assert(freenode != &G(L)->nilnode); + collide = hashkey(t, &n->key); + if (collide != n) { /* Colliding node not the main node? */ + while (noderef(collide->next) != n) /* Find predecessor. */ + collide = nextnode(collide); + setmref(collide->next, freenode); /* Relink chain. */ + /* Copy colliding node into free node and free main node. */ + freenode->val = n->val; + freenode->key = n->key; + freenode->next = n->next; + setmref(n->next, NULL); + setnilV(&n->val); + /* Rechain pseudo-resurrected string keys with colliding hashes. */ + while (nextnode(freenode)) { + Node *nn = nextnode(freenode); + if (tvisstr(&nn->key) && !tvisnil(&nn->val) && + hashstr(t, strV(&nn->key)) == n) { + freenode->next = nn->next; + nn->next = n->next; + setmref(n->next, nn); + /* + ** Rechaining a resurrected string key creates a new dilemma: + ** Another string key may have originally been resurrected via + ** _any_ of the previous nodes as a chain anchor. Including + ** a node that had to be moved, which makes them unreachable. + ** It's not feasible to check for all previous nodes, so rechain + ** any string key that's currently in a non-main positions. + */ + while ((nn = nextnode(freenode))) { + if (tvisstr(&nn->key) && !tvisnil(&nn->val)) { + Node *mn = hashstr(t, strV(&nn->key)); + if (mn != freenode) { + freenode->next = nn->next; + nn->next = mn->next; + setmref(mn->next, nn); + } else { + freenode = nn; + } + } else { + freenode = nn; + } + } + break; + } else { + freenode = nn; + } + } + } else { /* Otherwise use free node. */ + setmrefr(freenode->next, n->next); /* Insert into chain. */ + setmref(n->next, freenode); + n = freenode; + } + } + n->key.u64 = key->u64; + if (LJ_UNLIKELY(tvismzero(&n->key))) + n->key.u64 = 0; + lj_gc_anybarriert(L, t); + lua_assert(tvisnil(&n->val)); + return &n->val; +} + +TValue *lj_tab_setinth(lua_State *L, GCtab *t, int32_t key) +{ + TValue k; + Node *n; + k.n = (lua_Number)key; + n = hashnum(t, &k); + do { + if (tvisnum(&n->key) && n->key.n == k.n) + return &n->val; + } while ((n = nextnode(n))); + return lj_tab_newkey(L, t, &k); +} + +TValue *lj_tab_setstr(lua_State *L, GCtab *t, GCstr *key) +{ + TValue k; + Node *n = hashstr(t, key); + do { + if (tvisstr(&n->key) && strV(&n->key) == key) + return &n->val; + } while ((n = nextnode(n))); + setstrV(L, &k, key); + return lj_tab_newkey(L, t, &k); +} + +TValue *lj_tab_set(lua_State *L, GCtab *t, cTValue *key) +{ + Node *n; + t->nomm = 0; /* Invalidate negative metamethod cache. */ + if (tvisstr(key)) { + return lj_tab_setstr(L, t, strV(key)); + } else if (tvisint(key)) { + return lj_tab_setint(L, t, intV(key)); + } else if (tvisnum(key)) { + lua_Number nk = numV(key); + int32_t k = lj_num2int(nk); + if (nk == (lua_Number)k) + return lj_tab_setint(L, t, k); + if (tvisnan(key)) + lj_err_msg(L, LJ_ERR_NANIDX); + /* Else use the generic lookup. */ + } else if (tvisnil(key)) { + lj_err_msg(L, LJ_ERR_NILIDX); + } + n = hashkey(t, key); + do { + if (lj_obj_equal(&n->key, key)) + return &n->val; + } while ((n = nextnode(n))); + return lj_tab_newkey(L, t, key); +} + +/* -- Table traversal ----------------------------------------------------- */ + +/* Get the traversal index of a key. */ +static uint32_t keyindex(lua_State *L, GCtab *t, cTValue *key) +{ + TValue tmp; + if (tvisint(key)) { + int32_t k = intV(key); + if ((uint32_t)k < t->asize) + return (uint32_t)k; /* Array key indexes: [0..t->asize-1] */ + setnumV(&tmp, (lua_Number)k); + key = &tmp; + } else if (tvisnum(key)) { + lua_Number nk = numV(key); + int32_t k = lj_num2int(nk); + if ((uint32_t)k < t->asize && nk == (lua_Number)k) + return (uint32_t)k; /* Array key indexes: [0..t->asize-1] */ + } + if (!tvisnil(key)) { + Node *n = hashkey(t, key); + do { + if (lj_obj_equal(&n->key, key)) + return t->asize + (uint32_t)(n - noderef(t->node)); + /* Hash key indexes: [t->asize..t->asize+t->nmask] */ + } while ((n = nextnode(n))); + if (key->u32.hi == 0xfffe7fff) /* ITERN was despecialized while running. */ + return key->u32.lo - 1; + lj_err_msg(L, LJ_ERR_NEXTIDX); + return 0; /* unreachable */ + } + return ~0u; /* A nil key starts the traversal. */ +} + +/* Advance to the next step in a table traversal. */ +int lj_tab_next(lua_State *L, GCtab *t, TValue *key) +{ + uint32_t i = keyindex(L, t, key); /* Find predecessor key index. */ + for (i++; i < t->asize; i++) /* First traverse the array keys. */ + if (!tvisnil(arrayslot(t, i))) { + setintV(key, i); + copyTV(L, key+1, arrayslot(t, i)); + return 1; + } + for (i -= t->asize; i <= t->hmask; i++) { /* Then traverse the hash keys. */ + Node *n = &noderef(t->node)[i]; + if (!tvisnil(&n->val)) { + copyTV(L, key, &n->key); + copyTV(L, key+1, &n->val); + return 1; + } + } + return 0; /* End of traversal. */ +} + +/* -- Table length calculation -------------------------------------------- */ + +static MSize unbound_search(GCtab *t, MSize j) +{ + cTValue *tv; + MSize i = j; /* i is zero or a present index */ + j++; + /* find `i' and `j' such that i is present and j is not */ + while ((tv = lj_tab_getint(t, (int32_t)j)) && !tvisnil(tv)) { + i = j; + j *= 2; + if (j > (MSize)(INT_MAX-2)) { /* overflow? */ + /* table was built with bad purposes: resort to linear search */ + i = 1; + while ((tv = lj_tab_getint(t, (int32_t)i)) && !tvisnil(tv)) i++; + return i - 1; + } + } + /* now do a binary search between them */ + while (j - i > 1) { + MSize m = (i+j)/2; + cTValue *tvb = lj_tab_getint(t, (int32_t)m); + if (tvb && !tvisnil(tvb)) i = m; else j = m; + } + return i; +} + +/* +** Try to find a boundary in table `t'. A `boundary' is an integer index +** such that t[i] is non-nil and t[i+1] is nil (and 0 if t[1] is nil). +*/ +MSize LJ_FASTCALL lj_tab_len(GCtab *t) +{ + MSize j = (MSize)t->asize; + if (j > 1 && tvisnil(arrayslot(t, j-1))) { + MSize i = 1; + while (j - i > 1) { + MSize m = (i+j)/2; + if (tvisnil(arrayslot(t, m-1))) j = m; else i = m; + } + return i-1; + } + if (j) j--; + if (t->hmask <= 0) + return j; + return unbound_search(t, j); +} + diff --git a/lib/LuaJIT/lj_tab.h b/lib/LuaJIT/lj_tab.h new file mode 100644 index 0000000..71e3494 --- /dev/null +++ b/lib/LuaJIT/lj_tab.h @@ -0,0 +1,73 @@ +/* +** Table handling. +** Copyright (C) 2005-2017 Mike Pall. See Copyright Notice in luajit.h +*/ + +#ifndef _LJ_TAB_H +#define _LJ_TAB_H + +#include "lj_obj.h" + +/* Hash constants. Tuned using a brute force search. */ +#define HASH_BIAS (-0x04c11db7) +#define HASH_ROT1 14 +#define HASH_ROT2 5 +#define HASH_ROT3 13 + +/* Scramble the bits of numbers and pointers. */ +static LJ_AINLINE uint32_t hashrot(uint32_t lo, uint32_t hi) +{ +#if LJ_TARGET_X86ORX64 + /* Prefer variant that compiles well for a 2-operand CPU. */ + lo ^= hi; hi = lj_rol(hi, HASH_ROT1); + lo -= hi; hi = lj_rol(hi, HASH_ROT2); + hi ^= lo; hi -= lj_rol(lo, HASH_ROT3); +#else + lo ^= hi; + lo = lo - lj_rol(hi, HASH_ROT1); + hi = lo ^ lj_rol(hi, HASH_ROT1 + HASH_ROT2); + hi = hi - lj_rol(lo, HASH_ROT3); +#endif + return hi; +} + +#define hsize2hbits(s) ((s) ? ((s)==1 ? 1 : 1+lj_fls((uint32_t)((s)-1))) : 0) + +LJ_FUNCA GCtab *lj_tab_new(lua_State *L, uint32_t asize, uint32_t hbits); +LJ_FUNC GCtab *lj_tab_new_ah(lua_State *L, int32_t a, int32_t h); +#if LJ_HASJIT +LJ_FUNC GCtab * LJ_FASTCALL lj_tab_new1(lua_State *L, uint32_t ahsize); +#endif +LJ_FUNCA GCtab * LJ_FASTCALL lj_tab_dup(lua_State *L, const GCtab *kt); +LJ_FUNC void LJ_FASTCALL lj_tab_clear(GCtab *t); +LJ_FUNC void LJ_FASTCALL lj_tab_free(global_State *g, GCtab *t); +#if LJ_HASFFI +LJ_FUNC void lj_tab_rehash(lua_State *L, GCtab *t); +#endif +LJ_FUNC void lj_tab_resize(lua_State *L, GCtab *t, uint32_t asize, uint32_t hbits); +LJ_FUNCA void lj_tab_reasize(lua_State *L, GCtab *t, uint32_t nasize); + +/* Caveat: all getters except lj_tab_get() can return NULL! */ + +LJ_FUNCA cTValue * LJ_FASTCALL lj_tab_getinth(GCtab *t, int32_t key); +LJ_FUNC cTValue *lj_tab_getstr(GCtab *t, GCstr *key); +LJ_FUNCA cTValue *lj_tab_get(lua_State *L, GCtab *t, cTValue *key); + +/* Caveat: all setters require a write barrier for the stored value. */ + +LJ_FUNCA TValue *lj_tab_newkey(lua_State *L, GCtab *t, cTValue *key); +LJ_FUNCA TValue *lj_tab_setinth(lua_State *L, GCtab *t, int32_t key); +LJ_FUNC TValue *lj_tab_setstr(lua_State *L, GCtab *t, GCstr *key); +LJ_FUNC TValue *lj_tab_set(lua_State *L, GCtab *t, cTValue *key); + +#define inarray(t, key) ((MSize)(key) < (MSize)(t)->asize) +#define arrayslot(t, i) (&tvref((t)->array)[(i)]) +#define lj_tab_getint(t, key) \ + (inarray((t), (key)) ? arrayslot((t), (key)) : lj_tab_getinth((t), (key))) +#define lj_tab_setint(L, t, key) \ + (inarray((t), (key)) ? arrayslot((t), (key)) : lj_tab_setinth(L, (t), (key))) + +LJ_FUNCA int lj_tab_next(lua_State *L, GCtab *t, TValue *key); +LJ_FUNCA MSize LJ_FASTCALL lj_tab_len(GCtab *t); + +#endif diff --git a/lib/LuaJIT/lj_tab.o b/lib/LuaJIT/lj_tab.o new file mode 100644 index 0000000000000000000000000000000000000000..74860d119554d394f82e70b86e8090d8e836cb8d GIT binary patch literal 9120 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. */ +typedef uint32_t Reg; + +/* The hi-bit is NOT set for an allocated register. This means the value +** can be directly used without masking. The hi-bit is set for a register +** allocation hint or for RID_INIT, RID_SINK or RID_SUNK. +*/ +#define RID_NONE 0x80 +#define RID_MASK 0x7f +#define RID_INIT (RID_NONE|RID_MASK) +#define RID_SINK (RID_INIT-1) +#define RID_SUNK (RID_INIT-2) + +#define ra_noreg(r) ((r) & RID_NONE) +#define ra_hasreg(r) (!((r) & RID_NONE)) + +/* The ra_hashint() macro assumes a previous test for ra_noreg(). */ +#define ra_hashint(r) ((r) < RID_SUNK) +#define ra_gethint(r) ((Reg)((r) & RID_MASK)) +#define ra_sethint(rr, r) rr = (uint8_t)((r)|RID_NONE) +#define ra_samehint(r1, r2) (ra_gethint((r1)^(r2)) == 0) + +/* Spill slot 0 means no spill slot has been allocated. */ +#define SPS_NONE 0 + +#define ra_hasspill(s) ((s) != SPS_NONE) + +/* Combined register and spill slot (uint16_t in ir->prev). */ +typedef uint32_t RegSP; + +#define REGSP(r, s) ((r) + ((s) << 8)) +#define REGSP_HINT(r) ((r)|RID_NONE) +#define REGSP_INIT REGSP(RID_INIT, 0) + +#define regsp_reg(rs) ((rs) & 255) +#define regsp_spill(rs) ((rs) >> 8) +#define regsp_used(rs) \ + (((rs) & ~REGSP(RID_MASK, 0)) != REGSP(RID_NONE, 0)) + +/* -- Register sets ------------------------------------------------------- */ + +/* Bitset for registers. 32 registers suffice for most architectures. +** Note that one set holds bits for both GPRs and FPRs. +*/ +#if LJ_TARGET_PPC || LJ_TARGET_MIPS || LJ_TARGET_ARM64 +typedef uint64_t RegSet; +#else +typedef uint32_t RegSet; +#endif + +#define RID2RSET(r) (((RegSet)1) << (r)) +#define RSET_EMPTY ((RegSet)0) +#define RSET_RANGE(lo, hi) ((RID2RSET((hi)-(lo))-1) << (lo)) + +#define rset_test(rs, r) ((int)((rs) >> (r)) & 1) +#define rset_set(rs, r) (rs |= RID2RSET(r)) +#define rset_clear(rs, r) (rs &= ~RID2RSET(r)) +#define rset_exclude(rs, r) (rs & ~RID2RSET(r)) +#if LJ_TARGET_PPC || LJ_TARGET_MIPS || LJ_TARGET_ARM64 +#define rset_picktop(rs) ((Reg)(__builtin_clzll(rs)^63)) +#define rset_pickbot(rs) ((Reg)__builtin_ctzll(rs)) +#else +#define rset_picktop(rs) ((Reg)lj_fls(rs)) +#define rset_pickbot(rs) ((Reg)lj_ffs(rs)) +#endif + +/* -- Register allocation cost -------------------------------------------- */ + +/* The register allocation heuristic keeps track of the cost for allocating +** a specific register: +** +** A free register (obviously) has a cost of 0 and a 1-bit in the free mask. +** +** An already allocated register has the (non-zero) IR reference in the lowest +** bits and the result of a blended cost-model in the higher bits. +** +** The allocator first checks the free mask for a hit. Otherwise an (unrolled) +** linear search for the minimum cost is used. The search doesn't need to +** keep track of the position of the minimum, which makes it very fast. +** The lowest bits of the minimum cost show the desired IR reference whose +** register is the one to evict. +** +** Without the cost-model this degenerates to the standard heuristics for +** (reverse) linear-scan register allocation. Since code generation is done +** in reverse, a live interval extends from the last use to the first def. +** For an SSA IR the IR reference is the first (and only) def and thus +** trivially marks the end of the interval. The LSRA heuristics says to pick +** the register whose live interval has the furthest extent, i.e. the lowest +** IR reference in our case. +** +** A cost-model should take into account other factors, like spill-cost and +** restore- or rematerialization-cost, which depend on the kind of instruction. +** E.g. constants have zero spill costs, variant instructions have higher +** costs than invariants and PHIs should preferably never be spilled. +** +** Here's a first cut at simple, but effective blended cost-model for R-LSRA: +** - Due to careful design of the IR, constants already have lower IR +** references than invariants and invariants have lower IR references +** than variants. +** - The cost in the upper 16 bits is the sum of the IR reference and a +** weighted score. The score currently only takes into account whether +** the IRT_ISPHI bit is set in the instruction type. +** - The PHI weight is the minimum distance (in IR instructions) a PHI +** reference has to be further apart from a non-PHI reference to be spilled. +** - It should be a power of two (for speed) and must be between 2 and 32768. +** Good values for the PHI weight seem to be between 40 and 150. +** - Further study is required. +*/ +#define REGCOST_PHI_WEIGHT 64 + +/* Cost for allocating a specific register. */ +typedef uint32_t RegCost; + +/* Note: assumes 16 bit IRRef1. */ +#define REGCOST(cost, ref) ((RegCost)(ref) + ((RegCost)(cost) << 16)) +#define regcost_ref(rc) ((IRRef1)(rc)) + +#define REGCOST_T(t) \ + ((RegCost)((t)&IRT_ISPHI) * (((RegCost)(REGCOST_PHI_WEIGHT)<<16)/IRT_ISPHI)) +#define REGCOST_REF_T(ref, t) (REGCOST((ref), (ref)) + REGCOST_T((t))) + +/* -- Target-specific definitions ----------------------------------------- */ + +#if LJ_TARGET_X86ORX64 +#include "lj_target_x86.h" +#elif LJ_TARGET_ARM +#include "lj_target_arm.h" +#elif LJ_TARGET_ARM64 +#include "lj_target_arm64.h" +#elif LJ_TARGET_PPC +#include "lj_target_ppc.h" +#elif LJ_TARGET_MIPS +#include "lj_target_mips.h" +#else +#error "Missing include for target CPU" +#endif + +#ifdef EXITSTUBS_PER_GROUP +/* Return the address of an exit stub. */ +static LJ_AINLINE char *exitstub_addr_(char **group, uint32_t exitno) +{ + lua_assert(group[exitno / EXITSTUBS_PER_GROUP] != NULL); + return (char *)group[exitno / EXITSTUBS_PER_GROUP] + + EXITSTUB_SPACING*(exitno % EXITSTUBS_PER_GROUP); +} +/* Avoid dependence on lj_jit.h if only including lj_target.h. */ +#define exitstub_addr(J, exitno) \ + ((MCode *)exitstub_addr_((char **)((J)->exitstubgroup), (exitno))) +#endif + +#endif diff --git a/lib/LuaJIT/lj_target_arm.h b/lib/LuaJIT/lj_target_arm.h new file mode 100644 index 0000000..5551b1f --- /dev/null +++ b/lib/LuaJIT/lj_target_arm.h @@ -0,0 +1,270 @@ +/* +** Definitions for ARM CPUs. +** Copyright (C) 2005-2017 Mike Pall. See Copyright Notice in luajit.h +*/ + +#ifndef _LJ_TARGET_ARM_H +#define _LJ_TARGET_ARM_H + +/* -- Registers IDs ------------------------------------------------------- */ + +#define GPRDEF(_) \ + _(R0) _(R1) _(R2) _(R3) _(R4) _(R5) _(R6) _(R7) \ + _(R8) _(R9) _(R10) _(R11) _(R12) _(SP) _(LR) _(PC) +#if LJ_SOFTFP +#define FPRDEF(_) +#else +#define FPRDEF(_) \ + _(D0) _(D1) _(D2) _(D3) _(D4) _(D5) _(D6) _(D7) \ + _(D8) _(D9) _(D10) _(D11) _(D12) _(D13) _(D14) _(D15) +#endif +#define VRIDDEF(_) + +#define RIDENUM(name) RID_##name, + +enum { + GPRDEF(RIDENUM) /* General-purpose registers (GPRs). */ + FPRDEF(RIDENUM) /* Floating-point registers (FPRs). */ + RID_MAX, + RID_TMP = RID_LR, + + /* Calling conventions. */ + RID_RET = RID_R0, + RID_RETLO = RID_R0, + RID_RETHI = RID_R1, +#if LJ_SOFTFP + RID_FPRET = RID_R0, +#else + RID_FPRET = RID_D0, +#endif + + /* These definitions must match with the *.dasc file(s): */ + RID_BASE = RID_R9, /* Interpreter BASE. */ + RID_LPC = RID_R6, /* Interpreter PC. */ + RID_DISPATCH = RID_R7, /* Interpreter DISPATCH table. */ + RID_LREG = RID_R8, /* Interpreter L. */ + + /* Register ranges [min, max) and number of registers. */ + RID_MIN_GPR = RID_R0, + RID_MAX_GPR = RID_PC+1, + RID_MIN_FPR = RID_MAX_GPR, +#if LJ_SOFTFP + RID_MAX_FPR = RID_MIN_FPR, +#else + RID_MAX_FPR = RID_D15+1, +#endif + RID_NUM_GPR = RID_MAX_GPR - RID_MIN_GPR, + RID_NUM_FPR = RID_MAX_FPR - RID_MIN_FPR +}; + +#define RID_NUM_KREF RID_NUM_GPR +#define RID_MIN_KREF RID_R0 + +/* -- Register sets ------------------------------------------------------- */ + +/* Make use of all registers, except sp, lr and pc. */ +#define RSET_GPR (RSET_RANGE(RID_MIN_GPR, RID_R12+1)) +#define RSET_GPREVEN \ + (RID2RSET(RID_R0)|RID2RSET(RID_R2)|RID2RSET(RID_R4)|RID2RSET(RID_R6)| \ + RID2RSET(RID_R8)|RID2RSET(RID_R10)) +#define RSET_GPRODD \ + (RID2RSET(RID_R1)|RID2RSET(RID_R3)|RID2RSET(RID_R5)|RID2RSET(RID_R7)| \ + RID2RSET(RID_R9)|RID2RSET(RID_R11)) +#if LJ_SOFTFP +#define RSET_FPR 0 +#else +#define RSET_FPR (RSET_RANGE(RID_MIN_FPR, RID_MAX_FPR)) +#endif +#define RSET_ALL (RSET_GPR|RSET_FPR) +#define RSET_INIT RSET_ALL + +/* ABI-specific register sets. lr is an implicit scratch register. */ +#define RSET_SCRATCH_GPR_ (RSET_RANGE(RID_R0, RID_R3+1)|RID2RSET(RID_R12)) +#ifdef __APPLE__ +#define RSET_SCRATCH_GPR (RSET_SCRATCH_GPR_|RID2RSET(RID_R9)) +#else +#define RSET_SCRATCH_GPR RSET_SCRATCH_GPR_ +#endif +#if LJ_SOFTFP +#define RSET_SCRATCH_FPR 0 +#else +#define RSET_SCRATCH_FPR (RSET_RANGE(RID_D0, RID_D7+1)) +#endif +#define RSET_SCRATCH (RSET_SCRATCH_GPR|RSET_SCRATCH_FPR) +#define REGARG_FIRSTGPR RID_R0 +#define REGARG_LASTGPR RID_R3 +#define REGARG_NUMGPR 4 +#if LJ_ABI_SOFTFP +#define REGARG_FIRSTFPR 0 +#define REGARG_LASTFPR 0 +#define REGARG_NUMFPR 0 +#else +#define REGARG_FIRSTFPR RID_D0 +#define REGARG_LASTFPR RID_D7 +#define REGARG_NUMFPR 8 +#endif + +/* -- Spill slots --------------------------------------------------------- */ + +/* Spill slots are 32 bit wide. An even/odd pair is used for FPRs. +** +** SPS_FIXED: Available fixed spill slots in interpreter frame. +** This definition must match with the *.dasc file(s). +** +** SPS_FIRST: First spill slot for general use. Reserve min. two 32 bit slots. +*/ +#define SPS_FIXED 2 +#define SPS_FIRST 2 + +#define SPOFS_TMP 0 + +#define sps_scale(slot) (4 * (int32_t)(slot)) +#define sps_align(slot) (((slot) - SPS_FIXED + 1) & ~1) + +/* -- Exit state ---------------------------------------------------------- */ + +/* This definition must match with the *.dasc file(s). */ +typedef struct { +#if !LJ_SOFTFP + lua_Number fpr[RID_NUM_FPR]; /* Floating-point registers. */ +#endif + int32_t gpr[RID_NUM_GPR]; /* General-purpose registers. */ + int32_t spill[256]; /* Spill slots. */ +} ExitState; + +/* PC after instruction that caused an exit. Used to find the trace number. */ +#define EXITSTATE_PCREG RID_PC +/* Highest exit + 1 indicates stack check. */ +#define EXITSTATE_CHECKEXIT 1 + +#define EXITSTUB_SPACING 4 +#define EXITSTUBS_PER_GROUP 32 + +/* -- Instructions -------------------------------------------------------- */ + +/* Instruction fields. */ +#define ARMF_CC(ai, cc) (((ai) ^ ARMI_CCAL) | ((cc) << 28)) +#define ARMF_N(r) ((r) << 16) +#define ARMF_D(r) ((r) << 12) +#define ARMF_S(r) ((r) << 8) +#define ARMF_M(r) (r) +#define ARMF_SH(sh, n) (((sh) << 5) | ((n) << 7)) +#define ARMF_RSH(sh, r) (0x10 | ((sh) << 5) | ARMF_S(r)) + +typedef enum ARMIns { + ARMI_CCAL = 0xe0000000, + ARMI_S = 0x000100000, + ARMI_K12 = 0x02000000, + ARMI_KNEG = 0x00200000, + ARMI_LS_W = 0x00200000, + ARMI_LS_U = 0x00800000, + ARMI_LS_P = 0x01000000, + ARMI_LS_R = 0x02000000, + ARMI_LSX_I = 0x00400000, + + ARMI_AND = 0xe0000000, + ARMI_EOR = 0xe0200000, + ARMI_SUB = 0xe0400000, + ARMI_RSB = 0xe0600000, + ARMI_ADD = 0xe0800000, + ARMI_ADC = 0xe0a00000, + ARMI_SBC = 0xe0c00000, + ARMI_RSC = 0xe0e00000, + ARMI_TST = 0xe1100000, + ARMI_TEQ = 0xe1300000, + ARMI_CMP = 0xe1500000, + ARMI_CMN = 0xe1700000, + ARMI_ORR = 0xe1800000, + ARMI_MOV = 0xe1a00000, + ARMI_BIC = 0xe1c00000, + ARMI_MVN = 0xe1e00000, + + ARMI_NOP = 0xe1a00000, + + ARMI_MUL = 0xe0000090, + ARMI_SMULL = 0xe0c00090, + + ARMI_LDR = 0xe4100000, + ARMI_LDRB = 0xe4500000, + ARMI_LDRH = 0xe01000b0, + ARMI_LDRSB = 0xe01000d0, + ARMI_LDRSH = 0xe01000f0, + ARMI_LDRD = 0xe00000d0, + ARMI_STR = 0xe4000000, + ARMI_STRB = 0xe4400000, + ARMI_STRH = 0xe00000b0, + ARMI_STRD = 0xe00000f0, + ARMI_PUSH = 0xe92d0000, + + ARMI_B = 0xea000000, + ARMI_BL = 0xeb000000, + ARMI_BLX = 0xfa000000, + ARMI_BLXr = 0xe12fff30, + + /* ARMv6 */ + ARMI_REV = 0xe6bf0f30, + ARMI_SXTB = 0xe6af0070, + ARMI_SXTH = 0xe6bf0070, + ARMI_UXTB = 0xe6ef0070, + ARMI_UXTH = 0xe6ff0070, + + /* ARMv6T2 */ + ARMI_MOVW = 0xe3000000, + ARMI_MOVT = 0xe3400000, + + /* VFP */ + ARMI_VMOV_D = 0xeeb00b40, + ARMI_VMOV_S = 0xeeb00a40, + ARMI_VMOVI_D = 0xeeb00b00, + + ARMI_VMOV_R_S = 0xee100a10, + ARMI_VMOV_S_R = 0xee000a10, + ARMI_VMOV_RR_D = 0xec500b10, + ARMI_VMOV_D_RR = 0xec400b10, + + ARMI_VADD_D = 0xee300b00, + ARMI_VSUB_D = 0xee300b40, + ARMI_VMUL_D = 0xee200b00, + ARMI_VMLA_D = 0xee000b00, + ARMI_VMLS_D = 0xee000b40, + ARMI_VNMLS_D = 0xee100b00, + ARMI_VDIV_D = 0xee800b00, + + ARMI_VABS_D = 0xeeb00bc0, + ARMI_VNEG_D = 0xeeb10b40, + ARMI_VSQRT_D = 0xeeb10bc0, + + ARMI_VCMP_D = 0xeeb40b40, + ARMI_VCMPZ_D = 0xeeb50b40, + + ARMI_VMRS = 0xeef1fa10, + + ARMI_VCVT_S32_F32 = 0xeebd0ac0, + ARMI_VCVT_S32_F64 = 0xeebd0bc0, + ARMI_VCVT_U32_F32 = 0xeebc0ac0, + ARMI_VCVT_U32_F64 = 0xeebc0bc0, + ARMI_VCVT_F32_S32 = 0xeeb80ac0, + ARMI_VCVT_F64_S32 = 0xeeb80bc0, + ARMI_VCVT_F32_U32 = 0xeeb80a40, + ARMI_VCVT_F64_U32 = 0xeeb80b40, + ARMI_VCVT_F32_F64 = 0xeeb70bc0, + ARMI_VCVT_F64_F32 = 0xeeb70ac0, + + ARMI_VLDR_S = 0xed100a00, + ARMI_VLDR_D = 0xed100b00, + ARMI_VSTR_S = 0xed000a00, + ARMI_VSTR_D = 0xed000b00, +} ARMIns; + +typedef enum ARMShift { + ARMSH_LSL, ARMSH_LSR, ARMSH_ASR, ARMSH_ROR +} ARMShift; + +/* ARM condition codes. */ +typedef enum ARMCC { + CC_EQ, CC_NE, CC_CS, CC_CC, CC_MI, CC_PL, CC_VS, CC_VC, + CC_HI, CC_LS, CC_GE, CC_LT, CC_GT, CC_LE, CC_AL, + CC_HS = CC_CS, CC_LO = CC_CC +} ARMCC; + +#endif diff --git a/lib/LuaJIT/lj_target_arm64.h b/lib/LuaJIT/lj_target_arm64.h new file mode 100644 index 0000000..a207a2b --- /dev/null +++ b/lib/LuaJIT/lj_target_arm64.h @@ -0,0 +1,332 @@ +/* +** Definitions for ARM64 CPUs. +** Copyright (C) 2005-2017 Mike Pall. See Copyright Notice in luajit.h +*/ + +#ifndef _LJ_TARGET_ARM64_H +#define _LJ_TARGET_ARM64_H + +/* -- Registers IDs ------------------------------------------------------- */ + +#define GPRDEF(_) \ + _(X0) _(X1) _(X2) _(X3) _(X4) _(X5) _(X6) _(X7) \ + _(X8) _(X9) _(X10) _(X11) _(X12) _(X13) _(X14) _(X15) \ + _(X16) _(X17) _(X18) _(X19) _(X20) _(X21) _(X22) _(X23) \ + _(X24) _(X25) _(X26) _(X27) _(X28) _(FP) _(LR) _(SP) +#define FPRDEF(_) \ + _(D0) _(D1) _(D2) _(D3) _(D4) _(D5) _(D6) _(D7) \ + _(D8) _(D9) _(D10) _(D11) _(D12) _(D13) _(D14) _(D15) \ + _(D16) _(D17) _(D18) _(D19) _(D20) _(D21) _(D22) _(D23) \ + _(D24) _(D25) _(D26) _(D27) _(D28) _(D29) _(D30) _(D31) +#define VRIDDEF(_) + +#define RIDENUM(name) RID_##name, + +enum { + GPRDEF(RIDENUM) /* General-purpose registers (GPRs). */ + FPRDEF(RIDENUM) /* Floating-point registers (FPRs). */ + RID_MAX, + RID_TMP = RID_LR, + RID_ZERO = RID_SP, + + /* Calling conventions. */ + RID_RET = RID_X0, + RID_FPRET = RID_D0, + + /* These definitions must match with the *.dasc file(s): */ + RID_BASE = RID_X19, /* Interpreter BASE. */ + RID_LPC = RID_X21, /* Interpreter PC. */ + RID_GL = RID_X22, /* Interpreter GL. */ + RID_LREG = RID_X23, /* Interpreter L. */ + + /* Register ranges [min, max) and number of registers. */ + RID_MIN_GPR = RID_X0, + RID_MAX_GPR = RID_SP+1, + RID_MIN_FPR = RID_MAX_GPR, + RID_MAX_FPR = RID_D31+1, + RID_NUM_GPR = RID_MAX_GPR - RID_MIN_GPR, + RID_NUM_FPR = RID_MAX_FPR - RID_MIN_FPR +}; + +#define RID_NUM_KREF RID_NUM_GPR +#define RID_MIN_KREF RID_X0 + +/* -- Register sets ------------------------------------------------------- */ + +/* Make use of all registers, except for x18, fp, lr and sp. */ +#define RSET_FIXED \ + (RID2RSET(RID_X18)|RID2RSET(RID_FP)|RID2RSET(RID_LR)|RID2RSET(RID_SP)|\ + RID2RSET(RID_GL)) +#define RSET_GPR (RSET_RANGE(RID_MIN_GPR, RID_MAX_GPR) - RSET_FIXED) +#define RSET_FPR RSET_RANGE(RID_MIN_FPR, RID_MAX_FPR) +#define RSET_ALL (RSET_GPR|RSET_FPR) +#define RSET_INIT RSET_ALL + +/* lr is an implicit scratch register. */ +#define RSET_SCRATCH_GPR (RSET_RANGE(RID_X0, RID_X17+1)) +#define RSET_SCRATCH_FPR \ + (RSET_RANGE(RID_D0, RID_D7+1)|RSET_RANGE(RID_D16, RID_D31+1)) +#define RSET_SCRATCH (RSET_SCRATCH_GPR|RSET_SCRATCH_FPR) +#define REGARG_FIRSTGPR RID_X0 +#define REGARG_LASTGPR RID_X7 +#define REGARG_NUMGPR 8 +#define REGARG_FIRSTFPR RID_D0 +#define REGARG_LASTFPR RID_D7 +#define REGARG_NUMFPR 8 + +/* -- Spill slots --------------------------------------------------------- */ + +/* Spill slots are 32 bit wide. An even/odd pair is used for FPRs. +** +** SPS_FIXED: Available fixed spill slots in interpreter frame. +** This definition must match with the vm_arm64.dasc file. +** Pre-allocate some slots to avoid sp adjust in every root trace. +** +** SPS_FIRST: First spill slot for general use. Reserve min. two 32 bit slots. +*/ +#define SPS_FIXED 4 +#define SPS_FIRST 2 + +#define SPOFS_TMP 0 + +#define sps_scale(slot) (4 * (int32_t)(slot)) +#define sps_align(slot) (((slot) - SPS_FIXED + 3) & ~3) + +/* -- Exit state ---------------------------------------------------------- */ + +/* This definition must match with the *.dasc file(s). */ +typedef struct { + lua_Number fpr[RID_NUM_FPR]; /* Floating-point registers. */ + intptr_t gpr[RID_NUM_GPR]; /* General-purpose registers. */ + int32_t spill[256]; /* Spill slots. */ +} ExitState; + +/* Highest exit + 1 indicates stack check. */ +#define EXITSTATE_CHECKEXIT 1 + +/* Return the address of a per-trace exit stub. */ +static LJ_AINLINE uint32_t *exitstub_trace_addr_(uint32_t *p, uint32_t exitno) +{ + while (*p == (LJ_LE ? 0xd503201f : 0x1f2003d5)) p++; /* Skip A64I_NOP. */ + return p + 3 + exitno; +} +/* Avoid dependence on lj_jit.h if only including lj_target.h. */ +#define exitstub_trace_addr(T, exitno) \ + exitstub_trace_addr_((MCode *)((char *)(T)->mcode + (T)->szmcode), (exitno)) + +/* -- Instructions -------------------------------------------------------- */ + +/* ARM64 instructions are always little-endian. Swap for ARM64BE. */ +#if LJ_BE +#define A64I_LE(x) (lj_bswap(x)) +#else +#define A64I_LE(x) (x) +#endif + +/* Instruction fields. */ +#define A64F_D(r) (r) +#define A64F_N(r) ((r) << 5) +#define A64F_A(r) ((r) << 10) +#define A64F_M(r) ((r) << 16) +#define A64F_IMMS(x) ((x) << 10) +#define A64F_IMMR(x) ((x) << 16) +#define A64F_U16(x) ((x) << 5) +#define A64F_U12(x) ((x) << 10) +#define A64F_S26(x) (((uint32_t)(x) & 0x03ffffffu)) +#define A64F_S19(x) (((uint32_t)(x) & 0x7ffffu) << 5) +#define A64F_S14(x) (((uint32_t)(x) & 0x3fffu) << 5) +#define A64F_S9(x) ((x) << 12) +#define A64F_BIT(x) ((x) << 19) +#define A64F_SH(sh, x) (((sh) << 22) | ((x) << 10)) +#define A64F_EX(ex) (A64I_EX | ((ex) << 13)) +#define A64F_EXSH(ex,x) (A64I_EX | ((ex) << 13) | ((x) << 10)) +#define A64F_FP8(x) ((x) << 13) +#define A64F_CC(cc) ((cc) << 12) +#define A64F_LSL16(x) (((x) / 16) << 21) +#define A64F_BSH(sh) ((sh) << 10) + +/* Check for valid field range. */ +#define A64F_S_OK(x, b) ((((x) + (1 << (b-1))) >> (b)) == 0) + +typedef enum A64Ins { + A64I_S = 0x20000000, + A64I_X = 0x80000000, + A64I_EX = 0x00200000, + A64I_ON = 0x00200000, + A64I_K12 = 0x1a000000, + A64I_K13 = 0x18000000, + A64I_LS_U = 0x01000000, + A64I_LS_S = 0x00800000, + A64I_LS_R = 0x01200800, + A64I_LS_SH = 0x00001000, + A64I_LS_UXTWx = 0x00004000, + A64I_LS_SXTWx = 0x0000c000, + A64I_LS_SXTXx = 0x0000e000, + A64I_LS_LSLx = 0x00006000, + + A64I_ADDw = 0x0b000000, + A64I_ADDx = 0x8b000000, + A64I_ADDSw = 0x2b000000, + A64I_ADDSx = 0xab000000, + A64I_NEGw = 0x4b0003e0, + A64I_NEGx = 0xcb0003e0, + A64I_SUBw = 0x4b000000, + A64I_SUBx = 0xcb000000, + A64I_SUBSw = 0x6b000000, + A64I_SUBSx = 0xeb000000, + + A64I_MULw = 0x1b007c00, + A64I_MULx = 0x9b007c00, + A64I_SMULL = 0x9b207c00, + + A64I_ANDw = 0x0a000000, + A64I_ANDx = 0x8a000000, + A64I_ANDSw = 0x6a000000, + A64I_ANDSx = 0xea000000, + A64I_EORw = 0x4a000000, + A64I_EORx = 0xca000000, + A64I_ORRw = 0x2a000000, + A64I_ORRx = 0xaa000000, + A64I_TSTw = 0x6a00001f, + A64I_TSTx = 0xea00001f, + + A64I_CMPw = 0x6b00001f, + A64I_CMPx = 0xeb00001f, + A64I_CMNw = 0x2b00001f, + A64I_CMNx = 0xab00001f, + A64I_CCMPw = 0x7a400000, + A64I_CCMPx = 0xfa400000, + A64I_CSELw = 0x1a800000, + A64I_CSELx = 0x9a800000, + + A64I_ASRw = 0x13007c00, + A64I_ASRx = 0x9340fc00, + A64I_LSLx = 0xd3400000, + A64I_LSRx = 0xd340fc00, + A64I_SHRw = 0x1ac02000, + A64I_SHRx = 0x9ac02000, /* lsl/lsr/asr/ror x0, x0, x0 */ + A64I_REVw = 0x5ac00800, + A64I_REVx = 0xdac00c00, + + A64I_EXTRw = 0x13800000, + A64I_EXTRx = 0x93c00000, + A64I_SBFMw = 0x13000000, + A64I_SBFMx = 0x93400000, + A64I_SXTBw = 0x13001c00, + A64I_SXTHw = 0x13003c00, + A64I_SXTW = 0x93407c00, + A64I_UBFMw = 0x53000000, + A64I_UBFMx = 0xd3400000, + A64I_UXTBw = 0x53001c00, + A64I_UXTHw = 0x53003c00, + + A64I_MOVw = 0x2a0003e0, + A64I_MOVx = 0xaa0003e0, + A64I_MVNw = 0x2a2003e0, + A64I_MVNx = 0xaa2003e0, + A64I_MOVKw = 0x72800000, + A64I_MOVKx = 0xf2800000, + A64I_MOVZw = 0x52800000, + A64I_MOVZx = 0xd2800000, + A64I_MOVNw = 0x12800000, + A64I_MOVNx = 0x92800000, + + A64I_LDRB = 0x39400000, + A64I_LDRH = 0x79400000, + A64I_LDRw = 0xb9400000, + A64I_LDRx = 0xf9400000, + A64I_LDRLw = 0x18000000, + A64I_LDRLx = 0x58000000, + A64I_STRB = 0x39000000, + A64I_STRH = 0x79000000, + A64I_STRw = 0xb9000000, + A64I_STRx = 0xf9000000, + A64I_STPw = 0x29000000, + A64I_STPx = 0xa9000000, + A64I_LDPw = 0x29400000, + A64I_LDPx = 0xa9400000, + + A64I_B = 0x14000000, + A64I_BCC = 0x54000000, + A64I_BL = 0x94000000, + A64I_BR = 0xd61f0000, + A64I_BLR = 0xd63f0000, + A64I_TBZ = 0x36000000, + A64I_TBNZ = 0x37000000, + A64I_CBZ = 0x34000000, + A64I_CBNZ = 0x35000000, + + A64I_NOP = 0xd503201f, + + /* FP */ + A64I_FADDd = 0x1e602800, + A64I_FSUBd = 0x1e603800, + A64I_FMADDd = 0x1f400000, + A64I_FMSUBd = 0x1f408000, + A64I_FNMADDd = 0x1f600000, + A64I_FNMSUBd = 0x1f608000, + A64I_FMULd = 0x1e600800, + A64I_FDIVd = 0x1e601800, + A64I_FNEGd = 0x1e614000, + A64I_FABS = 0x1e60c000, + A64I_FSQRTd = 0x1e61c000, + A64I_LDRs = 0xbd400000, + A64I_LDRd = 0xfd400000, + A64I_STRs = 0xbd000000, + A64I_STRd = 0xfd000000, + A64I_LDPs = 0x2d400000, + A64I_LDPd = 0x6d400000, + A64I_STPs = 0x2d000000, + A64I_STPd = 0x6d000000, + A64I_FCMPd = 0x1e602000, + A64I_FCMPZd = 0x1e602008, + A64I_FCSELd = 0x1e600c00, + A64I_FRINTMd = 0x1e654000, + A64I_FRINTPd = 0x1e64c000, + A64I_FRINTZd = 0x1e65c000, + + A64I_FCVT_F32_F64 = 0x1e624000, + A64I_FCVT_F64_F32 = 0x1e22c000, + A64I_FCVT_F32_S32 = 0x1e220000, + A64I_FCVT_F64_S32 = 0x1e620000, + A64I_FCVT_F32_U32 = 0x1e230000, + A64I_FCVT_F64_U32 = 0x1e630000, + A64I_FCVT_F32_S64 = 0x9e220000, + A64I_FCVT_F64_S64 = 0x9e620000, + A64I_FCVT_F32_U64 = 0x9e230000, + A64I_FCVT_F64_U64 = 0x9e630000, + A64I_FCVT_S32_F64 = 0x1e780000, + A64I_FCVT_S32_F32 = 0x1e380000, + A64I_FCVT_U32_F64 = 0x1e790000, + A64I_FCVT_U32_F32 = 0x1e390000, + A64I_FCVT_S64_F64 = 0x9e780000, + A64I_FCVT_S64_F32 = 0x9e380000, + A64I_FCVT_U64_F64 = 0x9e790000, + A64I_FCVT_U64_F32 = 0x9e390000, + + A64I_FMOV_S = 0x1e204000, + A64I_FMOV_D = 0x1e604000, + A64I_FMOV_R_S = 0x1e260000, + A64I_FMOV_S_R = 0x1e270000, + A64I_FMOV_R_D = 0x9e660000, + A64I_FMOV_D_R = 0x9e670000, + A64I_FMOV_DI = 0x1e601000, +} A64Ins; + +typedef enum A64Shift { + A64SH_LSL, A64SH_LSR, A64SH_ASR, A64SH_ROR +} A64Shift; + +typedef enum A64Extend { + A64EX_UXTB, A64EX_UXTH, A64EX_UXTW, A64EX_UXTX, + A64EX_SXTB, A64EX_SXTH, A64EX_SXTW, A64EX_SXTX, +} A64Extend; + +/* ARM condition codes. */ +typedef enum A64CC { + CC_EQ, CC_NE, CC_CS, CC_CC, CC_MI, CC_PL, CC_VS, CC_VC, + CC_HI, CC_LS, CC_GE, CC_LT, CC_GT, CC_LE, CC_AL, + CC_HS = CC_CS, CC_LO = CC_CC +} A64CC; + +#endif diff --git a/lib/LuaJIT/lj_target_mips.h b/lib/LuaJIT/lj_target_mips.h new file mode 100644 index 0000000..740687b --- /dev/null +++ b/lib/LuaJIT/lj_target_mips.h @@ -0,0 +1,377 @@ +/* +** Definitions for MIPS CPUs. +** Copyright (C) 2005-2017 Mike Pall. See Copyright Notice in luajit.h +*/ + +#ifndef _LJ_TARGET_MIPS_H +#define _LJ_TARGET_MIPS_H + +/* -- Registers IDs ------------------------------------------------------- */ + +#define GPRDEF(_) \ + _(R0) _(R1) _(R2) _(R3) _(R4) _(R5) _(R6) _(R7) \ + _(R8) _(R9) _(R10) _(R11) _(R12) _(R13) _(R14) _(R15) \ + _(R16) _(R17) _(R18) _(R19) _(R20) _(R21) _(R22) _(R23) \ + _(R24) _(R25) _(SYS1) _(SYS2) _(R28) _(SP) _(R30) _(RA) +#if LJ_SOFTFP +#define FPRDEF(_) +#else +#define FPRDEF(_) \ + _(F0) _(F1) _(F2) _(F3) _(F4) _(F5) _(F6) _(F7) \ + _(F8) _(F9) _(F10) _(F11) _(F12) _(F13) _(F14) _(F15) \ + _(F16) _(F17) _(F18) _(F19) _(F20) _(F21) _(F22) _(F23) \ + _(F24) _(F25) _(F26) _(F27) _(F28) _(F29) _(F30) _(F31) +#endif +#define VRIDDEF(_) + +#define RIDENUM(name) RID_##name, + +enum { + GPRDEF(RIDENUM) /* General-purpose registers (GPRs). */ + FPRDEF(RIDENUM) /* Floating-point registers (FPRs). */ + RID_MAX, + RID_ZERO = RID_R0, + RID_TMP = RID_RA, + RID_GP = RID_R28, + + /* Calling conventions. */ + RID_RET = RID_R2, +#if LJ_LE + RID_RETHI = RID_R3, + RID_RETLO = RID_R2, +#else + RID_RETHI = RID_R2, + RID_RETLO = RID_R3, +#endif +#if LJ_SOFTFP + RID_FPRET = RID_R2, +#else + RID_FPRET = RID_F0, +#endif + RID_CFUNCADDR = RID_R25, + + /* These definitions must match with the *.dasc file(s): */ + RID_BASE = RID_R16, /* Interpreter BASE. */ + RID_LPC = RID_R18, /* Interpreter PC. */ + RID_DISPATCH = RID_R19, /* Interpreter DISPATCH table. */ + RID_LREG = RID_R20, /* Interpreter L. */ + RID_JGL = RID_R30, /* On-trace: global_State + 32768. */ + + /* Register ranges [min, max) and number of registers. */ + RID_MIN_GPR = RID_R0, + RID_MAX_GPR = RID_RA+1, + RID_MIN_FPR = RID_MAX_GPR, +#if LJ_SOFTFP + RID_MAX_FPR = RID_MIN_FPR, +#else + RID_MAX_FPR = RID_F31+1, +#endif + RID_NUM_GPR = RID_MAX_GPR - RID_MIN_GPR, + RID_NUM_FPR = RID_MAX_FPR - RID_MIN_FPR /* Only even regs are used. */ +}; + +#define RID_NUM_KREF RID_NUM_GPR +#define RID_MIN_KREF RID_R0 + +/* -- Register sets ------------------------------------------------------- */ + +/* Make use of all registers, except ZERO, TMP, SP, SYS1, SYS2, JGL and GP. */ +#define RSET_FIXED \ + (RID2RSET(RID_ZERO)|RID2RSET(RID_TMP)|RID2RSET(RID_SP)|\ + RID2RSET(RID_SYS1)|RID2RSET(RID_SYS2)|RID2RSET(RID_JGL)|RID2RSET(RID_GP)) +#define RSET_GPR (RSET_RANGE(RID_MIN_GPR, RID_MAX_GPR) - RSET_FIXED) +#if LJ_SOFTFP +#define RSET_FPR 0 +#else +#if LJ_32 +#define RSET_FPR \ + (RID2RSET(RID_F0)|RID2RSET(RID_F2)|RID2RSET(RID_F4)|RID2RSET(RID_F6)|\ + RID2RSET(RID_F8)|RID2RSET(RID_F10)|RID2RSET(RID_F12)|RID2RSET(RID_F14)|\ + RID2RSET(RID_F16)|RID2RSET(RID_F18)|RID2RSET(RID_F20)|RID2RSET(RID_F22)|\ + RID2RSET(RID_F24)|RID2RSET(RID_F26)|RID2RSET(RID_F28)|RID2RSET(RID_F30)) +#else +#define RSET_FPR RSET_RANGE(RID_MIN_FPR, RID_MAX_FPR) +#endif +#endif +#define RSET_ALL (RSET_GPR|RSET_FPR) +#define RSET_INIT RSET_ALL + +#define RSET_SCRATCH_GPR \ + (RSET_RANGE(RID_R1, RID_R15+1)|\ + RID2RSET(RID_R24)|RID2RSET(RID_R25)) +#if LJ_SOFTFP +#define RSET_SCRATCH_FPR 0 +#else +#if LJ_32 +#define RSET_SCRATCH_FPR \ + (RID2RSET(RID_F0)|RID2RSET(RID_F2)|RID2RSET(RID_F4)|RID2RSET(RID_F6)|\ + RID2RSET(RID_F8)|RID2RSET(RID_F10)|RID2RSET(RID_F12)|RID2RSET(RID_F14)|\ + RID2RSET(RID_F16)|RID2RSET(RID_F18)) +#else +#define RSET_SCRATCH_FPR RSET_RANGE(RID_F0, RID_F24) +#endif +#endif +#define RSET_SCRATCH (RSET_SCRATCH_GPR|RSET_SCRATCH_FPR) +#define REGARG_FIRSTGPR RID_R4 +#if LJ_32 +#define REGARG_LASTGPR RID_R7 +#define REGARG_NUMGPR 4 +#else +#define REGARG_LASTGPR RID_R11 +#define REGARG_NUMGPR 8 +#endif +#if LJ_ABI_SOFTFP +#define REGARG_FIRSTFPR 0 +#define REGARG_LASTFPR 0 +#define REGARG_NUMFPR 0 +#else +#define REGARG_FIRSTFPR RID_F12 +#if LJ_32 +#define REGARG_LASTFPR RID_F14 +#define REGARG_NUMFPR 2 +#else +#define REGARG_LASTFPR RID_F19 +#define REGARG_NUMFPR 8 +#endif +#endif + +/* -- Spill slots --------------------------------------------------------- */ + +/* Spill slots are 32 bit wide. An even/odd pair is used for FPRs. +** +** SPS_FIXED: Available fixed spill slots in interpreter frame. +** This definition must match with the *.dasc file(s). +** +** SPS_FIRST: First spill slot for general use. +*/ +#if LJ_32 +#define SPS_FIXED 5 +#else +#define SPS_FIXED 4 +#endif +#define SPS_FIRST 4 + +#define SPOFS_TMP 0 + +#define sps_scale(slot) (4 * (int32_t)(slot)) +#define sps_align(slot) (((slot) - SPS_FIXED + 1) & ~1) + +/* -- Exit state ---------------------------------------------------------- */ + +/* This definition must match with the *.dasc file(s). */ +typedef struct { +#if !LJ_SOFTFP + lua_Number fpr[RID_NUM_FPR]; /* Floating-point registers. */ +#endif + intptr_t gpr[RID_NUM_GPR]; /* General-purpose registers. */ + int32_t spill[256]; /* Spill slots. */ +} ExitState; + +/* Highest exit + 1 indicates stack check. */ +#define EXITSTATE_CHECKEXIT 1 + +/* Return the address of a per-trace exit stub. */ +static LJ_AINLINE uint32_t *exitstub_trace_addr_(uint32_t *p) +{ + while (*p == 0x00000000) p++; /* Skip MIPSI_NOP. */ + return p; +} +/* Avoid dependence on lj_jit.h if only including lj_target.h. */ +#define exitstub_trace_addr(T, exitno) \ + exitstub_trace_addr_((MCode *)((char *)(T)->mcode + (T)->szmcode)) + +/* -- Instructions -------------------------------------------------------- */ + +/* Instruction fields. */ +#define MIPSF_S(r) ((r) << 21) +#define MIPSF_T(r) ((r) << 16) +#define MIPSF_D(r) ((r) << 11) +#define MIPSF_R(r) ((r) << 21) +#define MIPSF_H(r) ((r) << 16) +#define MIPSF_G(r) ((r) << 11) +#define MIPSF_F(r) ((r) << 6) +#define MIPSF_A(n) ((n) << 6) +#define MIPSF_M(n) ((n) << 11) +#define MIPSF_L(n) ((n) << 6) + +typedef enum MIPSIns { + MIPSI_D = 0x38, + MIPSI_DV = 0x10, + MIPSI_D32 = 0x3c, + /* Integer instructions. */ + MIPSI_MOVE = 0x00000025, + MIPSI_NOP = 0x00000000, + + MIPSI_LI = 0x24000000, + MIPSI_LU = 0x34000000, + MIPSI_LUI = 0x3c000000, + + MIPSI_AND = 0x00000024, + MIPSI_ANDI = 0x30000000, + MIPSI_OR = 0x00000025, + MIPSI_ORI = 0x34000000, + MIPSI_XOR = 0x00000026, + MIPSI_XORI = 0x38000000, + MIPSI_NOR = 0x00000027, + + MIPSI_SLT = 0x0000002a, + MIPSI_SLTU = 0x0000002b, + MIPSI_SLTI = 0x28000000, + MIPSI_SLTIU = 0x2c000000, + + MIPSI_ADDU = 0x00000021, + MIPSI_ADDIU = 0x24000000, + MIPSI_SUB = 0x00000022, + MIPSI_SUBU = 0x00000023, + MIPSI_MUL = 0x70000002, + MIPSI_DIV = 0x0000001a, + MIPSI_DIVU = 0x0000001b, + + MIPSI_MOVZ = 0x0000000a, + MIPSI_MOVN = 0x0000000b, + MIPSI_MFHI = 0x00000010, + MIPSI_MFLO = 0x00000012, + MIPSI_MULT = 0x00000018, + + MIPSI_SLL = 0x00000000, + MIPSI_SRL = 0x00000002, + MIPSI_SRA = 0x00000003, + MIPSI_ROTR = 0x00200002, /* MIPSXXR2 */ + MIPSI_DROTR = 0x0020003a, + MIPSI_DROTR32 = 0x0020003e, + MIPSI_SLLV = 0x00000004, + MIPSI_SRLV = 0x00000006, + MIPSI_SRAV = 0x00000007, + MIPSI_ROTRV = 0x00000046, /* MIPSXXR2 */ + MIPSI_DROTRV = 0x00000056, + + MIPSI_SEB = 0x7c000420, /* MIPSXXR2 */ + MIPSI_SEH = 0x7c000620, /* MIPSXXR2 */ + MIPSI_WSBH = 0x7c0000a0, /* MIPSXXR2 */ + MIPSI_DSBH = 0x7c0000a4, + + MIPSI_B = 0x10000000, + MIPSI_J = 0x08000000, + MIPSI_JAL = 0x0c000000, + MIPSI_JALX = 0x74000000, + MIPSI_JR = 0x00000008, + MIPSI_JALR = 0x0000f809, + + MIPSI_BEQ = 0x10000000, + MIPSI_BNE = 0x14000000, + MIPSI_BLEZ = 0x18000000, + MIPSI_BGTZ = 0x1c000000, + MIPSI_BLTZ = 0x04000000, + MIPSI_BGEZ = 0x04010000, + + /* Load/store instructions. */ + MIPSI_LW = 0x8c000000, + MIPSI_LD = 0xdc000000, + MIPSI_SW = 0xac000000, + MIPSI_SD = 0xfc000000, + MIPSI_LB = 0x80000000, + MIPSI_SB = 0xa0000000, + MIPSI_LH = 0x84000000, + MIPSI_SH = 0xa4000000, + MIPSI_LBU = 0x90000000, + MIPSI_LHU = 0x94000000, + MIPSI_LWC1 = 0xc4000000, + MIPSI_SWC1 = 0xe4000000, + MIPSI_LDC1 = 0xd4000000, + MIPSI_SDC1 = 0xf4000000, + + /* MIPS64 instructions. */ + MIPSI_DADD = 0x0000002c, + MIPSI_DADDI = 0x60000000, + MIPSI_DADDU = 0x0000002d, + MIPSI_DADDIU = 0x64000000, + MIPSI_DSUB = 0x0000002e, + MIPSI_DSUBU = 0x0000002f, + MIPSI_DDIV = 0x0000001e, + MIPSI_DDIVU = 0x0000001f, + MIPSI_DMULT = 0x0000001c, + MIPSI_DMULTU = 0x0000001d, + + MIPSI_DSLL = 0x00000038, + MIPSI_DSRL = 0x0000003a, + MIPSI_DSLLV = 0x00000014, + MIPSI_DSRLV = 0x00000016, + MIPSI_DSRA = 0x0000003b, + MIPSI_DSRAV = 0x00000017, + MIPSI_DSRA32 = 0x0000003f, + MIPSI_DSLL32 = 0x0000003c, + MIPSI_DSRL32 = 0x0000003e, + MIPSI_DSHD = 0x7c000164, + + MIPSI_AADDU = LJ_32 ? MIPSI_ADDU : MIPSI_DADDU, + MIPSI_AADDIU = LJ_32 ? MIPSI_ADDIU : MIPSI_DADDIU, + MIPSI_ASUBU = LJ_32 ? MIPSI_SUBU : MIPSI_DSUBU, + MIPSI_AL = LJ_32 ? MIPSI_LW : MIPSI_LD, + MIPSI_AS = LJ_32 ? MIPSI_SW : MIPSI_SD, + + /* Extract/insert instructions. */ + MIPSI_DEXTM = 0x7c000001, + MIPSI_DEXTU = 0x7c000002, + MIPSI_DEXT = 0x7c000003, + MIPSI_DINSM = 0x7c000005, + MIPSI_DINSU = 0x7c000006, + MIPSI_DINS = 0x7c000007, + + MIPSI_RINT_D = 0x4620001a, + MIPSI_RINT_S = 0x4600001a, + MIPSI_RINT = 0x4400001a, + MIPSI_FLOOR_D = 0x4620000b, + MIPSI_CEIL_D = 0x4620000a, + MIPSI_ROUND_D = 0x46200008, + + /* FP instructions. */ + MIPSI_MOV_S = 0x46000006, + MIPSI_MOV_D = 0x46200006, + MIPSI_MOVT_D = 0x46210011, + MIPSI_MOVF_D = 0x46200011, + + MIPSI_ABS_D = 0x46200005, + MIPSI_NEG_D = 0x46200007, + + MIPSI_ADD_D = 0x46200000, + MIPSI_SUB_D = 0x46200001, + MIPSI_MUL_D = 0x46200002, + MIPSI_DIV_D = 0x46200003, + MIPSI_SQRT_D = 0x46200004, + + MIPSI_ADD_S = 0x46000000, + MIPSI_SUB_S = 0x46000001, + + MIPSI_CVT_D_S = 0x46000021, + MIPSI_CVT_W_S = 0x46000024, + MIPSI_CVT_S_D = 0x46200020, + MIPSI_CVT_W_D = 0x46200024, + MIPSI_CVT_S_W = 0x46800020, + MIPSI_CVT_D_W = 0x46800021, + MIPSI_CVT_S_L = 0x46a00020, + MIPSI_CVT_D_L = 0x46a00021, + + MIPSI_TRUNC_W_S = 0x4600000d, + MIPSI_TRUNC_W_D = 0x4620000d, + MIPSI_TRUNC_L_S = 0x46000009, + MIPSI_TRUNC_L_D = 0x46200009, + MIPSI_FLOOR_W_S = 0x4600000f, + MIPSI_FLOOR_W_D = 0x4620000f, + + MIPSI_MFC1 = 0x44000000, + MIPSI_MTC1 = 0x44800000, + MIPSI_DMTC1 = 0x44a00000, + MIPSI_DMFC1 = 0x44200000, + + MIPSI_BC1F = 0x45000000, + MIPSI_BC1T = 0x45010000, + + MIPSI_C_EQ_D = 0x46200032, + MIPSI_C_OLT_S = 0x46000034, + MIPSI_C_OLT_D = 0x46200034, + MIPSI_C_ULT_D = 0x46200035, + MIPSI_C_OLE_D = 0x46200036, + MIPSI_C_ULE_D = 0x46200037, +} MIPSIns; + +#endif diff --git a/lib/LuaJIT/lj_target_ppc.h b/lib/LuaJIT/lj_target_ppc.h new file mode 100644 index 0000000..c5c991a --- /dev/null +++ b/lib/LuaJIT/lj_target_ppc.h @@ -0,0 +1,280 @@ +/* +** Definitions for PPC CPUs. +** Copyright (C) 2005-2017 Mike Pall. See Copyright Notice in luajit.h +*/ + +#ifndef _LJ_TARGET_PPC_H +#define _LJ_TARGET_PPC_H + +/* -- Registers IDs ------------------------------------------------------- */ + +#define GPRDEF(_) \ + _(R0) _(SP) _(SYS1) _(R3) _(R4) _(R5) _(R6) _(R7) \ + _(R8) _(R9) _(R10) _(R11) _(R12) _(SYS2) _(R14) _(R15) \ + _(R16) _(R17) _(R18) _(R19) _(R20) _(R21) _(R22) _(R23) \ + _(R24) _(R25) _(R26) _(R27) _(R28) _(R29) _(R30) _(R31) +#define FPRDEF(_) \ + _(F0) _(F1) _(F2) _(F3) _(F4) _(F5) _(F6) _(F7) \ + _(F8) _(F9) _(F10) _(F11) _(F12) _(F13) _(F14) _(F15) \ + _(F16) _(F17) _(F18) _(F19) _(F20) _(F21) _(F22) _(F23) \ + _(F24) _(F25) _(F26) _(F27) _(F28) _(F29) _(F30) _(F31) +#define VRIDDEF(_) + +#define RIDENUM(name) RID_##name, + +enum { + GPRDEF(RIDENUM) /* General-purpose registers (GPRs). */ + FPRDEF(RIDENUM) /* Floating-point registers (FPRs). */ + RID_MAX, + RID_TMP = RID_R0, + + /* Calling conventions. */ + RID_RET = RID_R3, + RID_RETHI = RID_R3, + RID_RETLO = RID_R4, + RID_FPRET = RID_F1, + + /* These definitions must match with the *.dasc file(s): */ + RID_BASE = RID_R14, /* Interpreter BASE. */ + RID_LPC = RID_R16, /* Interpreter PC. */ + RID_DISPATCH = RID_R17, /* Interpreter DISPATCH table. */ + RID_LREG = RID_R18, /* Interpreter L. */ + RID_JGL = RID_R31, /* On-trace: global_State + 32768. */ + + /* Register ranges [min, max) and number of registers. */ + RID_MIN_GPR = RID_R0, + RID_MAX_GPR = RID_R31+1, + RID_MIN_FPR = RID_F0, + RID_MAX_FPR = RID_F31+1, + RID_NUM_GPR = RID_MAX_GPR - RID_MIN_GPR, + RID_NUM_FPR = RID_MAX_FPR - RID_MIN_FPR +}; + +#define RID_NUM_KREF RID_NUM_GPR +#define RID_MIN_KREF RID_R0 + +/* -- Register sets ------------------------------------------------------- */ + +/* Make use of all registers, except TMP, SP, SYS1, SYS2 and JGL. */ +#define RSET_FIXED \ + (RID2RSET(RID_TMP)|RID2RSET(RID_SP)|RID2RSET(RID_SYS1)|\ + RID2RSET(RID_SYS2)|RID2RSET(RID_JGL)) +#define RSET_GPR (RSET_RANGE(RID_MIN_GPR, RID_MAX_GPR) - RSET_FIXED) +#define RSET_FPR RSET_RANGE(RID_MIN_FPR, RID_MAX_FPR) +#define RSET_ALL (RSET_GPR|RSET_FPR) +#define RSET_INIT RSET_ALL + +#define RSET_SCRATCH_GPR (RSET_RANGE(RID_R3, RID_R12+1)) +#define RSET_SCRATCH_FPR (RSET_RANGE(RID_F0, RID_F13+1)) +#define RSET_SCRATCH (RSET_SCRATCH_GPR|RSET_SCRATCH_FPR) +#define REGARG_FIRSTGPR RID_R3 +#define REGARG_LASTGPR RID_R10 +#define REGARG_NUMGPR 8 +#define REGARG_FIRSTFPR RID_F1 +#define REGARG_LASTFPR RID_F8 +#define REGARG_NUMFPR 8 + +/* -- Spill slots --------------------------------------------------------- */ + +/* Spill slots are 32 bit wide. An even/odd pair is used for FPRs. +** +** SPS_FIXED: Available fixed spill slots in interpreter frame. +** This definition must match with the *.dasc file(s). +** +** SPS_FIRST: First spill slot for general use. +** [sp+12] tmplo word \ +** [sp+ 8] tmphi word / tmp dword, parameter area for callee +** [sp+ 4] tmpw, LR of callee +** [sp+ 0] stack chain +*/ +#define SPS_FIXED 7 +#define SPS_FIRST 4 + +/* Stack offsets for temporary slots. Used for FP<->int conversions etc. */ +#define SPOFS_TMPW 4 +#define SPOFS_TMP 8 +#define SPOFS_TMPHI 8 +#define SPOFS_TMPLO 12 + +#define sps_scale(slot) (4 * (int32_t)(slot)) +#define sps_align(slot) (((slot) - SPS_FIXED + 3) & ~3) + +/* -- Exit state ---------------------------------------------------------- */ + +/* This definition must match with the *.dasc file(s). */ +typedef struct { + lua_Number fpr[RID_NUM_FPR]; /* Floating-point registers. */ + intptr_t gpr[RID_NUM_GPR]; /* General-purpose registers. */ + int32_t spill[256]; /* Spill slots. */ +} ExitState; + +/* Highest exit + 1 indicates stack check. */ +#define EXITSTATE_CHECKEXIT 1 + +/* Return the address of a per-trace exit stub. */ +static LJ_AINLINE uint32_t *exitstub_trace_addr_(uint32_t *p, uint32_t exitno) +{ + while (*p == 0x60000000) p++; /* Skip PPCI_NOP. */ + return p + 3 + exitno; +} +/* Avoid dependence on lj_jit.h if only including lj_target.h. */ +#define exitstub_trace_addr(T, exitno) \ + exitstub_trace_addr_((MCode *)((char *)(T)->mcode + (T)->szmcode), (exitno)) + +/* -- Instructions -------------------------------------------------------- */ + +/* Instruction fields. */ +#define PPCF_CC(cc) ((((cc) & 3) << 16) | (((cc) & 4) << 22)) +#define PPCF_T(r) ((r) << 21) +#define PPCF_A(r) ((r) << 16) +#define PPCF_B(r) ((r) << 11) +#define PPCF_C(r) ((r) << 6) +#define PPCF_MB(n) ((n) << 6) +#define PPCF_ME(n) ((n) << 1) +#define PPCF_Y 0x00200000 +#define PPCF_DOT 0x00000001 + +typedef enum PPCIns { + /* Integer instructions. */ + PPCI_MR = 0x7c000378, + PPCI_NOP = 0x60000000, + + PPCI_LI = 0x38000000, + PPCI_LIS = 0x3c000000, + + PPCI_ADD = 0x7c000214, + PPCI_ADDC = 0x7c000014, + PPCI_ADDO = 0x7c000614, + PPCI_ADDE = 0x7c000114, + PPCI_ADDZE = 0x7c000194, + PPCI_ADDME = 0x7c0001d4, + PPCI_ADDI = 0x38000000, + PPCI_ADDIS = 0x3c000000, + PPCI_ADDIC = 0x30000000, + PPCI_ADDICDOT = 0x34000000, + + PPCI_SUBF = 0x7c000050, + PPCI_SUBFC = 0x7c000010, + PPCI_SUBFO = 0x7c000450, + PPCI_SUBFE = 0x7c000110, + PPCI_SUBFZE = 0x7c000190, + PPCI_SUBFME = 0x7c0001d0, + PPCI_SUBFIC = 0x20000000, + + PPCI_NEG = 0x7c0000d0, + + PPCI_AND = 0x7c000038, + PPCI_ANDC = 0x7c000078, + PPCI_NAND = 0x7c0003b8, + PPCI_ANDIDOT = 0x70000000, + PPCI_ANDISDOT = 0x74000000, + + PPCI_OR = 0x7c000378, + PPCI_NOR = 0x7c0000f8, + PPCI_ORI = 0x60000000, + PPCI_ORIS = 0x64000000, + + PPCI_XOR = 0x7c000278, + PPCI_EQV = 0x7c000238, + PPCI_XORI = 0x68000000, + PPCI_XORIS = 0x6c000000, + + PPCI_CMPW = 0x7c000000, + PPCI_CMPLW = 0x7c000040, + PPCI_CMPWI = 0x2c000000, + PPCI_CMPLWI = 0x28000000, + + PPCI_MULLW = 0x7c0001d6, + PPCI_MULLI = 0x1c000000, + PPCI_MULLWO = 0x7c0005d6, + + PPCI_EXTSB = 0x7c000774, + PPCI_EXTSH = 0x7c000734, + + PPCI_SLW = 0x7c000030, + PPCI_SRW = 0x7c000430, + PPCI_SRAW = 0x7c000630, + PPCI_SRAWI = 0x7c000670, + + PPCI_RLWNM = 0x5c000000, + PPCI_RLWINM = 0x54000000, + PPCI_RLWIMI = 0x50000000, + + PPCI_B = 0x48000000, + PPCI_BL = 0x48000001, + PPCI_BC = 0x40800000, + PPCI_BCL = 0x40800001, + PPCI_BCTR = 0x4e800420, + PPCI_BCTRL = 0x4e800421, + + PPCI_CRANDC = 0x4c000102, + PPCI_CRXOR = 0x4c000182, + PPCI_CRAND = 0x4c000202, + PPCI_CREQV = 0x4c000242, + PPCI_CRORC = 0x4c000342, + PPCI_CROR = 0x4c000382, + + PPCI_MFLR = 0x7c0802a6, + PPCI_MTCTR = 0x7c0903a6, + + PPCI_MCRXR = 0x7c000400, + + /* Load/store instructions. */ + PPCI_LWZ = 0x80000000, + PPCI_LBZ = 0x88000000, + PPCI_STW = 0x90000000, + PPCI_STB = 0x98000000, + PPCI_LHZ = 0xa0000000, + PPCI_LHA = 0xa8000000, + PPCI_STH = 0xb0000000, + + PPCI_STWU = 0x94000000, + + PPCI_LFS = 0xc0000000, + PPCI_LFD = 0xc8000000, + PPCI_STFS = 0xd0000000, + PPCI_STFD = 0xd8000000, + + PPCI_LWZX = 0x7c00002e, + PPCI_LBZX = 0x7c0000ae, + PPCI_STWX = 0x7c00012e, + PPCI_STBX = 0x7c0001ae, + PPCI_LHZX = 0x7c00022e, + PPCI_LHAX = 0x7c0002ae, + PPCI_STHX = 0x7c00032e, + + PPCI_LWBRX = 0x7c00042c, + PPCI_STWBRX = 0x7c00052c, + + PPCI_LFSX = 0x7c00042e, + PPCI_LFDX = 0x7c0004ae, + PPCI_STFSX = 0x7c00052e, + PPCI_STFDX = 0x7c0005ae, + + /* FP instructions. */ + PPCI_FMR = 0xfc000090, + PPCI_FNEG = 0xfc000050, + PPCI_FABS = 0xfc000210, + + PPCI_FRSP = 0xfc000018, + PPCI_FCTIWZ = 0xfc00001e, + + PPCI_FADD = 0xfc00002a, + PPCI_FSUB = 0xfc000028, + PPCI_FMUL = 0xfc000032, + PPCI_FDIV = 0xfc000024, + PPCI_FSQRT = 0xfc00002c, + + PPCI_FMADD = 0xfc00003a, + PPCI_FMSUB = 0xfc000038, + PPCI_FNMSUB = 0xfc00003c, + + PPCI_FCMPU = 0xfc000000, + PPCI_FSEL = 0xfc00002e, +} PPCIns; + +typedef enum PPCCC { + CC_GE, CC_LE, CC_NE, CC_NS, CC_LT, CC_GT, CC_EQ, CC_SO +} PPCCC; + +#endif diff --git a/lib/LuaJIT/lj_target_x86.h b/lib/LuaJIT/lj_target_x86.h new file mode 100644 index 0000000..356f792 --- /dev/null +++ b/lib/LuaJIT/lj_target_x86.h @@ -0,0 +1,362 @@ +/* +** Definitions for x86 and x64 CPUs. +** Copyright (C) 2005-2017 Mike Pall. See Copyright Notice in luajit.h +*/ + +#ifndef _LJ_TARGET_X86_H +#define _LJ_TARGET_X86_H + +/* -- Registers IDs ------------------------------------------------------- */ + +#if LJ_64 +#define GPRDEF(_) \ + _(EAX) _(ECX) _(EDX) _(EBX) _(ESP) _(EBP) _(ESI) _(EDI) \ + _(R8D) _(R9D) _(R10D) _(R11D) _(R12D) _(R13D) _(R14D) _(R15D) +#define FPRDEF(_) \ + _(XMM0) _(XMM1) _(XMM2) _(XMM3) _(XMM4) _(XMM5) _(XMM6) _(XMM7) \ + _(XMM8) _(XMM9) _(XMM10) _(XMM11) _(XMM12) _(XMM13) _(XMM14) _(XMM15) +#else +#define GPRDEF(_) \ + _(EAX) _(ECX) _(EDX) _(EBX) _(ESP) _(EBP) _(ESI) _(EDI) +#define FPRDEF(_) \ + _(XMM0) _(XMM1) _(XMM2) _(XMM3) _(XMM4) _(XMM5) _(XMM6) _(XMM7) +#endif +#define VRIDDEF(_) \ + _(MRM) _(RIP) + +#define RIDENUM(name) RID_##name, + +enum { + GPRDEF(RIDENUM) /* General-purpose registers (GPRs). */ + FPRDEF(RIDENUM) /* Floating-point registers (FPRs). */ + RID_MAX, + RID_MRM = RID_MAX, /* Pseudo-id for ModRM operand. */ + RID_RIP = RID_MAX+5, /* Pseudo-id for RIP (x64 only), rm bits = 5. */ + + /* Calling conventions. */ + RID_SP = RID_ESP, + RID_RET = RID_EAX, +#if LJ_64 + RID_FPRET = RID_XMM0, +#else + RID_RETLO = RID_EAX, + RID_RETHI = RID_EDX, +#endif + + /* These definitions must match with the *.dasc file(s): */ + RID_BASE = RID_EDX, /* Interpreter BASE. */ +#if LJ_64 && !LJ_ABI_WIN + RID_LPC = RID_EBX, /* Interpreter PC. */ + RID_DISPATCH = RID_R14D, /* Interpreter DISPATCH table. */ +#else + RID_LPC = RID_ESI, /* Interpreter PC. */ + RID_DISPATCH = RID_EBX, /* Interpreter DISPATCH table. */ +#endif + + /* Register ranges [min, max) and number of registers. */ + RID_MIN_GPR = RID_EAX, + RID_MIN_FPR = RID_XMM0, + RID_MAX_GPR = RID_MIN_FPR, + RID_MAX_FPR = RID_MAX, + RID_NUM_GPR = RID_MAX_GPR - RID_MIN_GPR, + RID_NUM_FPR = RID_MAX_FPR - RID_MIN_FPR, +}; + +/* -- Register sets ------------------------------------------------------- */ + +/* Make use of all registers, except the stack pointer (and maybe DISPATCH). */ +#define RSET_GPR (RSET_RANGE(RID_MIN_GPR, RID_MAX_GPR) \ + - RID2RSET(RID_ESP) \ + - LJ_GC64*RID2RSET(RID_DISPATCH)) +#define RSET_FPR (RSET_RANGE(RID_MIN_FPR, RID_MAX_FPR)) +#define RSET_ALL (RSET_GPR|RSET_FPR) +#define RSET_INIT RSET_ALL + +#if LJ_64 +/* Note: this requires the use of FORCE_REX! */ +#define RSET_GPR8 RSET_GPR +#else +#define RSET_GPR8 (RSET_RANGE(RID_EAX, RID_EBX+1)) +#endif + +/* ABI-specific register sets. */ +#define RSET_ACD (RID2RSET(RID_EAX)|RID2RSET(RID_ECX)|RID2RSET(RID_EDX)) +#if LJ_64 +#if LJ_ABI_WIN +/* Windows x64 ABI. */ +#define RSET_SCRATCH \ + (RSET_ACD|RSET_RANGE(RID_R8D, RID_R11D+1)|RSET_RANGE(RID_XMM0, RID_XMM5+1)) +#define REGARG_GPRS \ + (RID_ECX|((RID_EDX|((RID_R8D|(RID_R9D<<5))<<5))<<5)) +#define REGARG_NUMGPR 4 +#define REGARG_NUMFPR 4 +#define REGARG_FIRSTFPR RID_XMM0 +#define REGARG_LASTFPR RID_XMM3 +#define STACKARG_OFS (4*8) +#else +/* The rest of the civilized x64 world has a common ABI. */ +#define RSET_SCRATCH \ + (RSET_ACD|RSET_RANGE(RID_ESI, RID_R11D+1)|RSET_FPR) +#define REGARG_GPRS \ + (RID_EDI|((RID_ESI|((RID_EDX|((RID_ECX|((RID_R8D|(RID_R9D \ + <<5))<<5))<<5))<<5))<<5)) +#define REGARG_NUMGPR 6 +#define REGARG_NUMFPR 8 +#define REGARG_FIRSTFPR RID_XMM0 +#define REGARG_LASTFPR RID_XMM7 +#define STACKARG_OFS 0 +#endif +#else +/* Common x86 ABI. */ +#define RSET_SCRATCH (RSET_ACD|RSET_FPR) +#define REGARG_GPRS (RID_ECX|(RID_EDX<<5)) /* Fastcall only. */ +#define REGARG_NUMGPR 2 /* Fastcall only. */ +#define REGARG_NUMFPR 0 +#define STACKARG_OFS 0 +#endif + +#if LJ_64 +/* Prefer the low 8 regs of each type to reduce REX prefixes. */ +#undef rset_picktop +#define rset_picktop(rs) (lj_fls(lj_bswap(rs)) ^ 0x18) +#endif + +/* -- Spill slots --------------------------------------------------------- */ + +/* Spill slots are 32 bit wide. An even/odd pair is used for FPRs. +** +** SPS_FIXED: Available fixed spill slots in interpreter frame. +** This definition must match with the *.dasc file(s). +** +** SPS_FIRST: First spill slot for general use. Reserve min. two 32 bit slots. +*/ +#if LJ_64 +#if LJ_ABI_WIN +#define SPS_FIXED (4*2) +#define SPS_FIRST (4*2) /* Don't use callee register save area. */ +#else +#if LJ_GC64 +#define SPS_FIXED 2 +#else +#define SPS_FIXED 4 +#endif +#define SPS_FIRST 2 +#endif +#else +#define SPS_FIXED 6 +#define SPS_FIRST 2 +#endif + +#define SPOFS_TMP 0 + +#define sps_scale(slot) (4 * (int32_t)(slot)) +#define sps_align(slot) (((slot) - SPS_FIXED + 3) & ~3) + +/* -- Exit state ---------------------------------------------------------- */ + +/* This definition must match with the *.dasc file(s). */ +typedef struct { + lua_Number fpr[RID_NUM_FPR]; /* Floating-point registers. */ + intptr_t gpr[RID_NUM_GPR]; /* General-purpose registers. */ + int32_t spill[256]; /* Spill slots. */ +} ExitState; + +/* Limited by the range of a short fwd jump (127): (2+2)*(32-1)-2 = 122. */ +#define EXITSTUB_SPACING (2+2) +#define EXITSTUBS_PER_GROUP 32 + +/* -- x86 ModRM operand encoding ------------------------------------------ */ + +typedef enum { + XM_OFS0 = 0x00, XM_OFS8 = 0x40, XM_OFS32 = 0x80, XM_REG = 0xc0, + XM_SCALE1 = 0x00, XM_SCALE2 = 0x40, XM_SCALE4 = 0x80, XM_SCALE8 = 0xc0, + XM_MASK = 0xc0 +} x86Mode; + +/* Structure to hold variable ModRM operand. */ +typedef struct { + int32_t ofs; /* Offset. */ + uint8_t base; /* Base register or RID_NONE. */ + uint8_t idx; /* Index register or RID_NONE. */ + uint8_t scale; /* Index scale (XM_SCALE1 .. XM_SCALE8). */ +} x86ModRM; + +/* -- Opcodes ------------------------------------------------------------- */ + +/* Macros to construct variable-length x86 opcodes. -(len+1) is in LSB. */ +#define XO_(o) ((uint32_t)(0x0000fe + (0x##o<<24))) +#define XO_FPU(a,b) ((uint32_t)(0x00fd + (0x##a<<16)+(0x##b<<24))) +#define XO_0f(o) ((uint32_t)(0x0f00fd + (0x##o<<24))) +#define XO_66(o) ((uint32_t)(0x6600fd + (0x##o<<24))) +#define XO_660f(o) ((uint32_t)(0x0f66fc + (0x##o<<24))) +#define XO_f20f(o) ((uint32_t)(0x0ff2fc + (0x##o<<24))) +#define XO_f30f(o) ((uint32_t)(0x0ff3fc + (0x##o<<24))) + +#define XV_660f38(o) ((uint32_t)(0x79e2c4 + (0x##o<<24))) +#define XV_f20f38(o) ((uint32_t)(0x7be2c4 + (0x##o<<24))) +#define XV_f20f3a(o) ((uint32_t)(0x7be3c4 + (0x##o<<24))) +#define XV_f30f38(o) ((uint32_t)(0x7ae2c4 + (0x##o<<24))) + +/* This list of x86 opcodes is not intended to be complete. Opcodes are only +** included when needed. Take a look at DynASM or jit.dis_x86 to see the +** whole mess. +*/ +typedef enum { + /* Fixed length opcodes. XI_* prefix. */ + XI_O16 = 0x66, + XI_NOP = 0x90, + XI_XCHGa = 0x90, + XI_CALL = 0xe8, + XI_JMP = 0xe9, + XI_JMPs = 0xeb, + XI_PUSH = 0x50, /* Really 50+r. */ + XI_JCCs = 0x70, /* Really 7x. */ + XI_JCCn = 0x80, /* Really 0f8x. */ + XI_LEA = 0x8d, + XI_MOVrib = 0xb0, /* Really b0+r. */ + XI_MOVri = 0xb8, /* Really b8+r. */ + XI_ARITHib = 0x80, + XI_ARITHi = 0x81, + XI_ARITHi8 = 0x83, + XI_PUSHi8 = 0x6a, + XI_TESTb = 0x84, + XI_TEST = 0x85, + XI_INT3 = 0xcc, + XI_MOVmi = 0xc7, + XI_GROUP5 = 0xff, + + /* Note: little-endian byte-order! */ + XI_FLDZ = 0xeed9, + XI_FLD1 = 0xe8d9, + XI_FLDLG2 = 0xecd9, + XI_FLDLN2 = 0xedd9, + XI_FDUP = 0xc0d9, /* Really fld st0. */ + XI_FPOP = 0xd8dd, /* Really fstp st0. */ + XI_FPOP1 = 0xd9dd, /* Really fstp st1. */ + XI_FRNDINT = 0xfcd9, + XI_FSIN = 0xfed9, + XI_FCOS = 0xffd9, + XI_FPTAN = 0xf2d9, + XI_FPATAN = 0xf3d9, + XI_FSCALE = 0xfdd9, + XI_FYL2X = 0xf1d9, + + /* VEX-encoded instructions. XV_* prefix. */ + XV_RORX = XV_f20f3a(f0), + XV_SARX = XV_f30f38(f7), + XV_SHLX = XV_660f38(f7), + XV_SHRX = XV_f20f38(f7), + + /* Variable-length opcodes. XO_* prefix. */ + XO_OR = XO_(0b), + XO_MOV = XO_(8b), + XO_MOVto = XO_(89), + XO_MOVtow = XO_66(89), + XO_MOVtob = XO_(88), + XO_MOVmi = XO_(c7), + XO_MOVmib = XO_(c6), + XO_LEA = XO_(8d), + XO_ARITHib = XO_(80), + XO_ARITHi = XO_(81), + XO_ARITHi8 = XO_(83), + XO_ARITHiw8 = XO_66(83), + XO_SHIFTi = XO_(c1), + XO_SHIFT1 = XO_(d1), + XO_SHIFTcl = XO_(d3), + XO_IMUL = XO_0f(af), + XO_IMULi = XO_(69), + XO_IMULi8 = XO_(6b), + XO_CMP = XO_(3b), + XO_TESTb = XO_(84), + XO_TEST = XO_(85), + XO_GROUP3b = XO_(f6), + XO_GROUP3 = XO_(f7), + XO_GROUP5b = XO_(fe), + XO_GROUP5 = XO_(ff), + XO_MOVZXb = XO_0f(b6), + XO_MOVZXw = XO_0f(b7), + XO_MOVSXb = XO_0f(be), + XO_MOVSXw = XO_0f(bf), + XO_MOVSXd = XO_(63), + XO_BSWAP = XO_0f(c8), + XO_CMOV = XO_0f(40), + + XO_MOVSD = XO_f20f(10), + XO_MOVSDto = XO_f20f(11), + XO_MOVSS = XO_f30f(10), + XO_MOVSSto = XO_f30f(11), + XO_MOVLPD = XO_660f(12), + XO_MOVAPS = XO_0f(28), + XO_XORPS = XO_0f(57), + XO_ANDPS = XO_0f(54), + XO_ADDSD = XO_f20f(58), + XO_SUBSD = XO_f20f(5c), + XO_MULSD = XO_f20f(59), + XO_DIVSD = XO_f20f(5e), + XO_SQRTSD = XO_f20f(51), + XO_MINSD = XO_f20f(5d), + XO_MAXSD = XO_f20f(5f), + XO_ROUNDSD = 0x0b3a0ffc, /* Really 66 0f 3a 0b. See asm_fpmath. */ + XO_UCOMISD = XO_660f(2e), + XO_CVTSI2SD = XO_f20f(2a), + XO_CVTTSD2SI= XO_f20f(2c), + XO_CVTSI2SS = XO_f30f(2a), + XO_CVTTSS2SI= XO_f30f(2c), + XO_CVTSS2SD = XO_f30f(5a), + XO_CVTSD2SS = XO_f20f(5a), + XO_ADDSS = XO_f30f(58), + XO_MOVD = XO_660f(6e), + XO_MOVDto = XO_660f(7e), + + XO_FLDd = XO_(d9), XOg_FLDd = 0, + XO_FLDq = XO_(dd), XOg_FLDq = 0, + XO_FILDd = XO_(db), XOg_FILDd = 0, + XO_FILDq = XO_(df), XOg_FILDq = 5, + XO_FSTPd = XO_(d9), XOg_FSTPd = 3, + XO_FSTPq = XO_(dd), XOg_FSTPq = 3, + XO_FISTPq = XO_(df), XOg_FISTPq = 7, + XO_FISTTPq = XO_(dd), XOg_FISTTPq = 1, + XO_FADDq = XO_(dc), XOg_FADDq = 0, + XO_FLDCW = XO_(d9), XOg_FLDCW = 5, + XO_FNSTCW = XO_(d9), XOg_FNSTCW = 7 +} x86Op; + +/* x86 opcode groups. */ +typedef uint32_t x86Group; + +#define XG_(i8, i, g) ((x86Group)(((i8) << 16) + ((i) << 8) + (g))) +#define XG_ARITHi(g) XG_(XI_ARITHi8, XI_ARITHi, g) +#define XG_TOXOi(xg) ((x86Op)(0x000000fe + (((xg)<<16) & 0xff000000))) +#define XG_TOXOi8(xg) ((x86Op)(0x000000fe + (((xg)<<8) & 0xff000000))) + +#define XO_ARITH(a) ((x86Op)(0x030000fe + ((a)<<27))) +#define XO_ARITHw(a) ((x86Op)(0x036600fd + ((a)<<27))) + +typedef enum { + XOg_ADD, XOg_OR, XOg_ADC, XOg_SBB, XOg_AND, XOg_SUB, XOg_XOR, XOg_CMP, + XOg_X_IMUL +} x86Arith; + +typedef enum { + XOg_ROL, XOg_ROR, XOg_RCL, XOg_RCR, XOg_SHL, XOg_SHR, XOg_SAL, XOg_SAR +} x86Shift; + +typedef enum { + XOg_TEST, XOg_TEST_, XOg_NOT, XOg_NEG, XOg_MUL, XOg_IMUL, XOg_DIV, XOg_IDIV +} x86Group3; + +typedef enum { + XOg_INC, XOg_DEC, XOg_CALL, XOg_CALLfar, XOg_JMP, XOg_JMPfar, XOg_PUSH +} x86Group5; + +/* x86 condition codes. */ +typedef enum { + CC_O, CC_NO, CC_B, CC_NB, CC_E, CC_NE, CC_BE, CC_NBE, + CC_S, CC_NS, CC_P, CC_NP, CC_L, CC_NL, CC_LE, CC_NLE, + CC_C = CC_B, CC_NAE = CC_C, CC_NC = CC_NB, CC_AE = CC_NB, + CC_Z = CC_E, CC_NZ = CC_NE, CC_NA = CC_BE, CC_A = CC_NBE, + CC_PE = CC_P, CC_PO = CC_NP, CC_NGE = CC_L, CC_GE = CC_NL, + CC_NG = CC_LE, CC_G = CC_NLE +} x86CC; + +#endif diff --git a/lib/LuaJIT/lj_trace.c b/lib/LuaJIT/lj_trace.c new file mode 100644 index 0000000..d85b47f --- /dev/null +++ b/lib/LuaJIT/lj_trace.c @@ -0,0 +1,909 @@ +/* +** Trace management. +** Copyright (C) 2005-2017 Mike Pall. See Copyright Notice in luajit.h +*/ + +#define lj_trace_c +#define LUA_CORE + +#include "lj_obj.h" + +#if LJ_HASJIT + +#include "lj_gc.h" +#include "lj_err.h" +#include "lj_debug.h" +#include "lj_str.h" +#include "lj_frame.h" +#include "lj_state.h" +#include "lj_bc.h" +#include "lj_ir.h" +#include "lj_jit.h" +#include "lj_iropt.h" +#include "lj_mcode.h" +#include "lj_trace.h" +#include "lj_snap.h" +#include "lj_gdbjit.h" +#include "lj_record.h" +#include "lj_asm.h" +#include "lj_dispatch.h" +#include "lj_vm.h" +#include "lj_vmevent.h" +#include "lj_target.h" + +/* -- Error handling ------------------------------------------------------ */ + +/* Synchronous abort with error message. */ +void lj_trace_err(jit_State *J, TraceError e) +{ + setnilV(&J->errinfo); /* No error info. */ + setintV(J->L->top++, (int32_t)e); + lj_err_throw(J->L, LUA_ERRRUN); +} + +/* Synchronous abort with error message and error info. */ +void lj_trace_err_info(jit_State *J, TraceError e) +{ + setintV(J->L->top++, (int32_t)e); + lj_err_throw(J->L, LUA_ERRRUN); +} + +/* -- Trace management ---------------------------------------------------- */ + +/* The current trace is first assembled in J->cur. The variable length +** arrays point to shared, growable buffers (J->irbuf etc.). When trace +** recording ends successfully, the current trace and its data structures +** are copied to a new (compact) GCtrace object. +*/ + +/* Find a free trace number. */ +static TraceNo trace_findfree(jit_State *J) +{ + MSize osz, lim; + if (J->freetrace == 0) + J->freetrace = 1; + for (; J->freetrace < J->sizetrace; J->freetrace++) + if (traceref(J, J->freetrace) == NULL) + return J->freetrace++; + /* Need to grow trace array. */ + lim = (MSize)J->param[JIT_P_maxtrace] + 1; + if (lim < 2) lim = 2; else if (lim > 65535) lim = 65535; + osz = J->sizetrace; + if (osz >= lim) + return 0; /* Too many traces. */ + lj_mem_growvec(J->L, J->trace, J->sizetrace, lim, GCRef); + for (; osz < J->sizetrace; osz++) + setgcrefnull(J->trace[osz]); + return J->freetrace; +} + +#define TRACE_APPENDVEC(field, szfield, tp) \ + T->field = (tp *)p; \ + memcpy(p, J->cur.field, J->cur.szfield*sizeof(tp)); \ + p += J->cur.szfield*sizeof(tp); + +#ifdef LUAJIT_USE_PERFTOOLS +/* +** Create symbol table of JIT-compiled code. For use with Linux perf tools. +** Example usage: +** perf record -f -e cycles luajit test.lua +** perf report -s symbol +** rm perf.data /tmp/perf-*.map +*/ +#include +#include + +static void perftools_addtrace(GCtrace *T) +{ + static FILE *fp; + GCproto *pt = &gcref(T->startpt)->pt; + const BCIns *startpc = mref(T->startpc, const BCIns); + const char *name = proto_chunknamestr(pt); + BCLine lineno; + if (name[0] == '@' || name[0] == '=') + name++; + else + name = "(string)"; + lua_assert(startpc >= proto_bc(pt) && startpc < proto_bc(pt) + pt->sizebc); + lineno = lj_debug_line(pt, proto_bcpos(pt, startpc)); + if (!fp) { + char fname[40]; + sprintf(fname, "/tmp/perf-%d.map", getpid()); + if (!(fp = fopen(fname, "w"))) return; + setlinebuf(fp); + } + fprintf(fp, "%lx %x TRACE_%d::%s:%u\n", + (long)T->mcode, T->szmcode, T->traceno, name, lineno); +} +#endif + +/* Allocate space for copy of T. */ +GCtrace * LJ_FASTCALL lj_trace_alloc(lua_State *L, GCtrace *T) +{ + size_t sztr = ((sizeof(GCtrace)+7)&~7); + size_t szins = (T->nins-T->nk)*sizeof(IRIns); + size_t sz = sztr + szins + + T->nsnap*sizeof(SnapShot) + + T->nsnapmap*sizeof(SnapEntry); + GCtrace *T2 = lj_mem_newt(L, (MSize)sz, GCtrace); + char *p = (char *)T2 + sztr; + T2->gct = ~LJ_TTRACE; + T2->marked = 0; + T2->traceno = 0; + T2->ir = (IRIns *)p - T->nk; + T2->nins = T->nins; + T2->nk = T->nk; + T2->nsnap = T->nsnap; + T2->nsnapmap = T->nsnapmap; + memcpy(p, T->ir + T->nk, szins); + return T2; +} + +/* Save current trace by copying and compacting it. */ +static void trace_save(jit_State *J, GCtrace *T) +{ + size_t sztr = ((sizeof(GCtrace)+7)&~7); + size_t szins = (J->cur.nins-J->cur.nk)*sizeof(IRIns); + char *p = (char *)T + sztr; + memcpy(T, &J->cur, sizeof(GCtrace)); + setgcrefr(T->nextgc, J2G(J)->gc.root); + setgcrefp(J2G(J)->gc.root, T); + newwhite(J2G(J), T); + T->gct = ~LJ_TTRACE; + T->ir = (IRIns *)p - J->cur.nk; /* The IR has already been copied above. */ + p += szins; + TRACE_APPENDVEC(snap, nsnap, SnapShot) + TRACE_APPENDVEC(snapmap, nsnapmap, SnapEntry) + J->cur.traceno = 0; + J->curfinal = NULL; + setgcrefp(J->trace[T->traceno], T); + lj_gc_barriertrace(J2G(J), T->traceno); + lj_gdbjit_addtrace(J, T); +#ifdef LUAJIT_USE_PERFTOOLS + perftools_addtrace(T); +#endif +} + +void LJ_FASTCALL lj_trace_free(global_State *g, GCtrace *T) +{ + jit_State *J = G2J(g); + if (T->traceno) { + lj_gdbjit_deltrace(J, T); + if (T->traceno < J->freetrace) + J->freetrace = T->traceno; + setgcrefnull(J->trace[T->traceno]); + } + lj_mem_free(g, T, + ((sizeof(GCtrace)+7)&~7) + (T->nins-T->nk)*sizeof(IRIns) + + T->nsnap*sizeof(SnapShot) + T->nsnapmap*sizeof(SnapEntry)); +} + +/* Re-enable compiling a prototype by unpatching any modified bytecode. */ +void lj_trace_reenableproto(GCproto *pt) +{ + if ((pt->flags & PROTO_ILOOP)) { + BCIns *bc = proto_bc(pt); + BCPos i, sizebc = pt->sizebc;; + pt->flags &= ~PROTO_ILOOP; + if (bc_op(bc[0]) == BC_IFUNCF) + setbc_op(&bc[0], BC_FUNCF); + for (i = 1; i < sizebc; i++) { + BCOp op = bc_op(bc[i]); + if (op == BC_IFORL || op == BC_IITERL || op == BC_ILOOP) + setbc_op(&bc[i], (int)op+(int)BC_LOOP-(int)BC_ILOOP); + } + } +} + +/* Unpatch the bytecode modified by a root trace. */ +static void trace_unpatch(jit_State *J, GCtrace *T) +{ + BCOp op = bc_op(T->startins); + BCIns *pc = mref(T->startpc, BCIns); + UNUSED(J); + if (op == BC_JMP) + return; /* No need to unpatch branches in parent traces (yet). */ + switch (bc_op(*pc)) { + case BC_JFORL: + lua_assert(traceref(J, bc_d(*pc)) == T); + *pc = T->startins; + pc += bc_j(T->startins); + lua_assert(bc_op(*pc) == BC_JFORI); + setbc_op(pc, BC_FORI); + break; + case BC_JITERL: + case BC_JLOOP: + lua_assert(op == BC_ITERL || op == BC_LOOP || bc_isret(op)); + *pc = T->startins; + break; + case BC_JMP: + lua_assert(op == BC_ITERL); + pc += bc_j(*pc)+2; + if (bc_op(*pc) == BC_JITERL) { + lua_assert(traceref(J, bc_d(*pc)) == T); + *pc = T->startins; + } + break; + case BC_JFUNCF: + lua_assert(op == BC_FUNCF); + *pc = T->startins; + break; + default: /* Already unpatched. */ + break; + } +} + +/* Flush a root trace. */ +static void trace_flushroot(jit_State *J, GCtrace *T) +{ + GCproto *pt = &gcref(T->startpt)->pt; + lua_assert(T->root == 0 && pt != NULL); + /* First unpatch any modified bytecode. */ + trace_unpatch(J, T); + /* Unlink root trace from chain anchored in prototype. */ + if (pt->trace == T->traceno) { /* Trace is first in chain. Easy. */ + pt->trace = T->nextroot; + } else if (pt->trace) { /* Otherwise search in chain of root traces. */ + GCtrace *T2 = traceref(J, pt->trace); + if (T2) { + for (; T2->nextroot; T2 = traceref(J, T2->nextroot)) + if (T2->nextroot == T->traceno) { + T2->nextroot = T->nextroot; /* Unlink from chain. */ + break; + } + } + } +} + +/* Flush a trace. Only root traces are considered. */ +void lj_trace_flush(jit_State *J, TraceNo traceno) +{ + if (traceno > 0 && traceno < J->sizetrace) { + GCtrace *T = traceref(J, traceno); + if (T && T->root == 0) + trace_flushroot(J, T); + } +} + +/* Flush all traces associated with a prototype. */ +void lj_trace_flushproto(global_State *g, GCproto *pt) +{ + while (pt->trace != 0) + trace_flushroot(G2J(g), traceref(G2J(g), pt->trace)); +} + +/* Flush all traces. */ +int lj_trace_flushall(lua_State *L) +{ + jit_State *J = L2J(L); + ptrdiff_t i; + if ((J2G(J)->hookmask & HOOK_GC)) + return 1; + for (i = (ptrdiff_t)J->sizetrace-1; i > 0; i--) { + GCtrace *T = traceref(J, i); + if (T) { + if (T->root == 0) + trace_flushroot(J, T); + lj_gdbjit_deltrace(J, T); + T->traceno = T->link = 0; /* Blacklist the link for cont_stitch. */ + setgcrefnull(J->trace[i]); + } + } + J->cur.traceno = 0; + J->freetrace = 0; + /* Clear penalty cache. */ + memset(J->penalty, 0, sizeof(J->penalty)); + /* Free the whole machine code and invalidate all exit stub groups. */ + lj_mcode_free(J); + memset(J->exitstubgroup, 0, sizeof(J->exitstubgroup)); + lj_vmevent_send(L, TRACE, + setstrV(L, L->top++, lj_str_newlit(L, "flush")); + ); + return 0; +} + +/* Initialize JIT compiler state. */ +void lj_trace_initstate(global_State *g) +{ + jit_State *J = G2J(g); + TValue *tv; + + /* Initialize aligned SIMD constants. */ + tv = LJ_KSIMD(J, LJ_KSIMD_ABS); + tv[0].u64 = U64x(7fffffff,ffffffff); + tv[1].u64 = U64x(7fffffff,ffffffff); + tv = LJ_KSIMD(J, LJ_KSIMD_NEG); + tv[0].u64 = U64x(80000000,00000000); + tv[1].u64 = U64x(80000000,00000000); + + /* Initialize 32/64 bit constants. */ +#if LJ_TARGET_X86ORX64 + J->k64[LJ_K64_TOBIT].u64 = U64x(43380000,00000000); +#if LJ_32 + J->k64[LJ_K64_M2P64_31].u64 = U64x(c1e00000,00000000); +#endif + J->k64[LJ_K64_2P64].u64 = U64x(43f00000,00000000); + J->k32[LJ_K32_M2P64_31] = LJ_64 ? 0xdf800000 : 0xcf000000; +#endif +#if LJ_TARGET_X86ORX64 || LJ_TARGET_MIPS64 + J->k64[LJ_K64_M2P64].u64 = U64x(c3f00000,00000000); +#endif +#if LJ_TARGET_PPC + J->k32[LJ_K32_2P52_2P31] = 0x59800004; + J->k32[LJ_K32_2P52] = 0x59800000; +#endif +#if LJ_TARGET_PPC || LJ_TARGET_MIPS + J->k32[LJ_K32_2P31] = 0x4f000000; +#endif +#if LJ_TARGET_MIPS + J->k64[LJ_K64_2P31].u64 = U64x(41e00000,00000000); +#if LJ_64 + J->k64[LJ_K64_2P63].u64 = U64x(43e00000,00000000); + J->k32[LJ_K32_2P63] = 0x5f000000; + J->k32[LJ_K32_M2P64] = 0xdf800000; +#endif +#endif +} + +/* Free everything associated with the JIT compiler state. */ +void lj_trace_freestate(global_State *g) +{ + jit_State *J = G2J(g); +#ifdef LUA_USE_ASSERT + { /* This assumes all traces have already been freed. */ + ptrdiff_t i; + for (i = 1; i < (ptrdiff_t)J->sizetrace; i++) + lua_assert(i == (ptrdiff_t)J->cur.traceno || traceref(J, i) == NULL); + } +#endif + lj_mcode_free(J); + lj_mem_freevec(g, J->snapmapbuf, J->sizesnapmap, SnapEntry); + lj_mem_freevec(g, J->snapbuf, J->sizesnap, SnapShot); + lj_mem_freevec(g, J->irbuf + J->irbotlim, J->irtoplim - J->irbotlim, IRIns); + lj_mem_freevec(g, J->trace, J->sizetrace, GCRef); +} + +/* -- Penalties and blacklisting ------------------------------------------ */ + +/* Blacklist a bytecode instruction. */ +static void blacklist_pc(GCproto *pt, BCIns *pc) +{ + setbc_op(pc, (int)bc_op(*pc)+(int)BC_ILOOP-(int)BC_LOOP); + pt->flags |= PROTO_ILOOP; +} + +/* Penalize a bytecode instruction. */ +static void penalty_pc(jit_State *J, GCproto *pt, BCIns *pc, TraceError e) +{ + uint32_t i, val = PENALTY_MIN; + for (i = 0; i < PENALTY_SLOTS; i++) + if (mref(J->penalty[i].pc, const BCIns) == pc) { /* Cache slot found? */ + /* First try to bump its hotcount several times. */ + val = ((uint32_t)J->penalty[i].val << 1) + + LJ_PRNG_BITS(J, PENALTY_RNDBITS); + if (val > PENALTY_MAX) { + blacklist_pc(pt, pc); /* Blacklist it, if that didn't help. */ + return; + } + goto setpenalty; + } + /* Assign a new penalty cache slot. */ + i = J->penaltyslot; + J->penaltyslot = (J->penaltyslot + 1) & (PENALTY_SLOTS-1); + setmref(J->penalty[i].pc, pc); +setpenalty: + J->penalty[i].val = (uint16_t)val; + J->penalty[i].reason = e; + hotcount_set(J2GG(J), pc+1, val); +} + +/* -- Trace compiler state machine ---------------------------------------- */ + +/* Start tracing. */ +static void trace_start(jit_State *J) +{ + lua_State *L; + TraceNo traceno; + + if ((J->pt->flags & PROTO_NOJIT)) { /* JIT disabled for this proto? */ + if (J->parent == 0 && J->exitno == 0) { + /* Lazy bytecode patching to disable hotcount events. */ + lua_assert(bc_op(*J->pc) == BC_FORL || bc_op(*J->pc) == BC_ITERL || + bc_op(*J->pc) == BC_LOOP || bc_op(*J->pc) == BC_FUNCF); + setbc_op(J->pc, (int)bc_op(*J->pc)+(int)BC_ILOOP-(int)BC_LOOP); + J->pt->flags |= PROTO_ILOOP; + } + J->state = LJ_TRACE_IDLE; /* Silently ignored. */ + return; + } + + /* Get a new trace number. */ + traceno = trace_findfree(J); + if (LJ_UNLIKELY(traceno == 0)) { /* No free trace? */ + lua_assert((J2G(J)->hookmask & HOOK_GC) == 0); + lj_trace_flushall(J->L); + J->state = LJ_TRACE_IDLE; /* Silently ignored. */ + return; + } + setgcrefp(J->trace[traceno], &J->cur); + + /* Setup enough of the current trace to be able to send the vmevent. */ + memset(&J->cur, 0, sizeof(GCtrace)); + J->cur.traceno = traceno; + J->cur.nins = J->cur.nk = REF_BASE; + J->cur.ir = J->irbuf; + J->cur.snap = J->snapbuf; + J->cur.snapmap = J->snapmapbuf; + J->mergesnap = 0; + J->needsnap = 0; + J->bcskip = 0; + J->guardemit.irt = 0; + J->postproc = LJ_POST_NONE; + lj_resetsplit(J); + J->retryrec = 0; + J->ktrace = 0; + setgcref(J->cur.startpt, obj2gco(J->pt)); + + L = J->L; + lj_vmevent_send(L, TRACE, + setstrV(L, L->top++, lj_str_newlit(L, "start")); + setintV(L->top++, traceno); + setfuncV(L, L->top++, J->fn); + setintV(L->top++, proto_bcpos(J->pt, J->pc)); + if (J->parent) { + setintV(L->top++, J->parent); + setintV(L->top++, J->exitno); + } else { + BCOp op = bc_op(*J->pc); + if (op == BC_CALLM || op == BC_CALL || op == BC_ITERC) { + setintV(L->top++, J->exitno); /* Parent of stitched trace. */ + setintV(L->top++, -1); + } + } + ); + lj_record_setup(J); +} + +/* Stop tracing. */ +static void trace_stop(jit_State *J) +{ + BCIns *pc = mref(J->cur.startpc, BCIns); + BCOp op = bc_op(J->cur.startins); + GCproto *pt = &gcref(J->cur.startpt)->pt; + TraceNo traceno = J->cur.traceno; + GCtrace *T = J->curfinal; + lua_State *L; + + switch (op) { + case BC_FORL: + setbc_op(pc+bc_j(J->cur.startins), BC_JFORI); /* Patch FORI, too. */ + /* fallthrough */ + case BC_LOOP: + case BC_ITERL: + case BC_FUNCF: + /* Patch bytecode of starting instruction in root trace. */ + setbc_op(pc, (int)op+(int)BC_JLOOP-(int)BC_LOOP); + setbc_d(pc, traceno); + addroot: + /* Add to root trace chain in prototype. */ + J->cur.nextroot = pt->trace; + pt->trace = (TraceNo1)traceno; + break; + case BC_RET: + case BC_RET0: + case BC_RET1: + *pc = BCINS_AD(BC_JLOOP, J->cur.snap[0].nslots, traceno); + goto addroot; + case BC_JMP: + /* Patch exit branch in parent to side trace entry. */ + lua_assert(J->parent != 0 && J->cur.root != 0); + lj_asm_patchexit(J, traceref(J, J->parent), J->exitno, J->cur.mcode); + /* Avoid compiling a side trace twice (stack resizing uses parent exit). */ + traceref(J, J->parent)->snap[J->exitno].count = SNAPCOUNT_DONE; + /* Add to side trace chain in root trace. */ + { + GCtrace *root = traceref(J, J->cur.root); + root->nchild++; + J->cur.nextside = root->nextside; + root->nextside = (TraceNo1)traceno; + } + break; + case BC_CALLM: + case BC_CALL: + case BC_ITERC: + /* Trace stitching: patch link of previous trace. */ + traceref(J, J->exitno)->link = traceno; + break; + default: + lua_assert(0); + break; + } + + /* Commit new mcode only after all patching is done. */ + lj_mcode_commit(J, J->cur.mcode); + J->postproc = LJ_POST_NONE; + trace_save(J, T); + + L = J->L; + lj_vmevent_send(L, TRACE, + setstrV(L, L->top++, lj_str_newlit(L, "stop")); + setintV(L->top++, traceno); + setfuncV(L, L->top++, J->fn); + ); +} + +/* Start a new root trace for down-recursion. */ +static int trace_downrec(jit_State *J) +{ + /* Restart recording at the return instruction. */ + lua_assert(J->pt != NULL); + lua_assert(bc_isret(bc_op(*J->pc))); + if (bc_op(*J->pc) == BC_RETM) + return 0; /* NYI: down-recursion with RETM. */ + J->parent = 0; + J->exitno = 0; + J->state = LJ_TRACE_RECORD; + trace_start(J); + return 1; +} + +/* Abort tracing. */ +static int trace_abort(jit_State *J) +{ + lua_State *L = J->L; + TraceError e = LJ_TRERR_RECERR; + TraceNo traceno; + + J->postproc = LJ_POST_NONE; + lj_mcode_abort(J); + if (J->curfinal) { + lj_trace_free(J2G(J), J->curfinal); + J->curfinal = NULL; + } + if (tvisnumber(L->top-1)) + e = (TraceError)numberVint(L->top-1); + if (e == LJ_TRERR_MCODELM) { + L->top--; /* Remove error object */ + J->state = LJ_TRACE_ASM; + return 1; /* Retry ASM with new MCode area. */ + } + /* Penalize or blacklist starting bytecode instruction. */ + if (J->parent == 0 && !bc_isret(bc_op(J->cur.startins))) { + if (J->exitno == 0) { + BCIns *startpc = mref(J->cur.startpc, BCIns); + if (e == LJ_TRERR_RETRY) + hotcount_set(J2GG(J), startpc+1, 1); /* Immediate retry. */ + else + penalty_pc(J, &gcref(J->cur.startpt)->pt, startpc, e); + } else { + traceref(J, J->exitno)->link = J->exitno; /* Self-link is blacklisted. */ + } + } + + /* Is there anything to abort? */ + traceno = J->cur.traceno; + if (traceno) { + ptrdiff_t errobj = savestack(L, L->top-1); /* Stack may be resized. */ + J->cur.link = 0; + J->cur.linktype = LJ_TRLINK_NONE; + lj_vmevent_send(L, TRACE, + TValue *frame; + const BCIns *pc; + GCfunc *fn; + setstrV(L, L->top++, lj_str_newlit(L, "abort")); + setintV(L->top++, traceno); + /* Find original Lua function call to generate a better error message. */ + frame = J->L->base-1; + pc = J->pc; + while (!isluafunc(frame_func(frame))) { + pc = (frame_iscont(frame) ? frame_contpc(frame) : frame_pc(frame)) - 1; + frame = frame_prev(frame); + } + fn = frame_func(frame); + setfuncV(L, L->top++, fn); + setintV(L->top++, proto_bcpos(funcproto(fn), pc)); + copyTV(L, L->top++, restorestack(L, errobj)); + copyTV(L, L->top++, &J->errinfo); + ); + /* Drop aborted trace after the vmevent (which may still access it). */ + setgcrefnull(J->trace[traceno]); + if (traceno < J->freetrace) + J->freetrace = traceno; + J->cur.traceno = 0; + } + L->top--; /* Remove error object */ + if (e == LJ_TRERR_DOWNREC) + return trace_downrec(J); + else if (e == LJ_TRERR_MCODEAL) + lj_trace_flushall(L); + return 0; +} + +/* Perform pending re-patch of a bytecode instruction. */ +static LJ_AINLINE void trace_pendpatch(jit_State *J, int force) +{ + if (LJ_UNLIKELY(J->patchpc)) { + if (force || J->bcskip == 0) { + *J->patchpc = J->patchins; + J->patchpc = NULL; + } else { + J->bcskip = 0; + } + } +} + +/* State machine for the trace compiler. Protected callback. */ +static TValue *trace_state(lua_State *L, lua_CFunction dummy, void *ud) +{ + jit_State *J = (jit_State *)ud; + UNUSED(dummy); + do { + retry: + switch (J->state) { + case LJ_TRACE_START: + J->state = LJ_TRACE_RECORD; /* trace_start() may change state. */ + trace_start(J); + lj_dispatch_update(J2G(J)); + break; + + case LJ_TRACE_RECORD: + trace_pendpatch(J, 0); + setvmstate(J2G(J), RECORD); + lj_vmevent_send_(L, RECORD, + /* Save/restore tmptv state for trace recorder. */ + TValue savetv = J2G(J)->tmptv; + TValue savetv2 = J2G(J)->tmptv2; + setintV(L->top++, J->cur.traceno); + setfuncV(L, L->top++, J->fn); + setintV(L->top++, J->pt ? (int32_t)proto_bcpos(J->pt, J->pc) : -1); + setintV(L->top++, J->framedepth); + , + J2G(J)->tmptv = savetv; + J2G(J)->tmptv2 = savetv2; + ); + lj_record_ins(J); + break; + + case LJ_TRACE_END: + trace_pendpatch(J, 1); + J->loopref = 0; + if ((J->flags & JIT_F_OPT_LOOP) && + J->cur.link == J->cur.traceno && J->framedepth + J->retdepth == 0) { + setvmstate(J2G(J), OPT); + lj_opt_dce(J); + if (lj_opt_loop(J)) { /* Loop optimization failed? */ + J->cur.link = 0; + J->cur.linktype = LJ_TRLINK_NONE; + J->loopref = J->cur.nins; + J->state = LJ_TRACE_RECORD; /* Try to continue recording. */ + break; + } + J->loopref = J->chain[IR_LOOP]; /* Needed by assembler. */ + } + lj_opt_split(J); + lj_opt_sink(J); + if (!J->loopref) J->cur.snap[J->cur.nsnap-1].count = SNAPCOUNT_DONE; + J->state = LJ_TRACE_ASM; + break; + + case LJ_TRACE_ASM: + setvmstate(J2G(J), ASM); + lj_asm_trace(J, &J->cur); + trace_stop(J); + setvmstate(J2G(J), INTERP); + J->state = LJ_TRACE_IDLE; + lj_dispatch_update(J2G(J)); + return NULL; + + default: /* Trace aborted asynchronously. */ + setintV(L->top++, (int32_t)LJ_TRERR_RECERR); + /* fallthrough */ + case LJ_TRACE_ERR: + trace_pendpatch(J, 1); + if (trace_abort(J)) + goto retry; + setvmstate(J2G(J), INTERP); + J->state = LJ_TRACE_IDLE; + lj_dispatch_update(J2G(J)); + return NULL; + } + } while (J->state > LJ_TRACE_RECORD); + return NULL; +} + +/* -- Event handling ------------------------------------------------------ */ + +/* A bytecode instruction is about to be executed. Record it. */ +void lj_trace_ins(jit_State *J, const BCIns *pc) +{ + /* Note: J->L must already be set. pc is the true bytecode PC here. */ + J->pc = pc; + J->fn = curr_func(J->L); + J->pt = isluafunc(J->fn) ? funcproto(J->fn) : NULL; + while (lj_vm_cpcall(J->L, NULL, (void *)J, trace_state) != 0) + J->state = LJ_TRACE_ERR; +} + +/* A hotcount triggered. Start recording a root trace. */ +void LJ_FASTCALL lj_trace_hot(jit_State *J, const BCIns *pc) +{ + /* Note: pc is the interpreter bytecode PC here. It's offset by 1. */ + ERRNO_SAVE + /* Reset hotcount. */ + hotcount_set(J2GG(J), pc, J->param[JIT_P_hotloop]*HOTCOUNT_LOOP); + /* Only start a new trace if not recording or inside __gc call or vmevent. */ + if (J->state == LJ_TRACE_IDLE && + !(J2G(J)->hookmask & (HOOK_GC|HOOK_VMEVENT))) { + J->parent = 0; /* Root trace. */ + J->exitno = 0; + J->state = LJ_TRACE_START; + lj_trace_ins(J, pc-1); + } + ERRNO_RESTORE +} + +/* Check for a hot side exit. If yes, start recording a side trace. */ +static void trace_hotside(jit_State *J, const BCIns *pc) +{ + SnapShot *snap = &traceref(J, J->parent)->snap[J->exitno]; + if (!(J2G(J)->hookmask & (HOOK_GC|HOOK_VMEVENT)) && + isluafunc(curr_func(J->L)) && + snap->count != SNAPCOUNT_DONE && + ++snap->count >= J->param[JIT_P_hotexit]) { + lua_assert(J->state == LJ_TRACE_IDLE); + /* J->parent is non-zero for a side trace. */ + J->state = LJ_TRACE_START; + lj_trace_ins(J, pc); + } +} + +/* Stitch a new trace to the previous trace. */ +void LJ_FASTCALL lj_trace_stitch(jit_State *J, const BCIns *pc) +{ + /* Only start a new trace if not recording or inside __gc call or vmevent. */ + if (J->state == LJ_TRACE_IDLE && + !(J2G(J)->hookmask & (HOOK_GC|HOOK_VMEVENT))) { + J->parent = 0; /* Have to treat it like a root trace. */ + /* J->exitno is set to the invoking trace. */ + J->state = LJ_TRACE_START; + lj_trace_ins(J, pc); + } +} + + +/* Tiny struct to pass data to protected call. */ +typedef struct ExitDataCP { + jit_State *J; + void *exptr; /* Pointer to exit state. */ + const BCIns *pc; /* Restart interpreter at this PC. */ +} ExitDataCP; + +/* Need to protect lj_snap_restore because it may throw. */ +static TValue *trace_exit_cp(lua_State *L, lua_CFunction dummy, void *ud) +{ + ExitDataCP *exd = (ExitDataCP *)ud; + cframe_errfunc(L->cframe) = -1; /* Inherit error function. */ + exd->pc = lj_snap_restore(exd->J, exd->exptr); + UNUSED(dummy); + return NULL; +} + +#ifndef LUAJIT_DISABLE_VMEVENT +/* Push all registers from exit state. */ +static void trace_exit_regs(lua_State *L, ExitState *ex) +{ + int32_t i; + setintV(L->top++, RID_NUM_GPR); + setintV(L->top++, RID_NUM_FPR); + for (i = 0; i < RID_NUM_GPR; i++) { + if (sizeof(ex->gpr[i]) == sizeof(int32_t)) + setintV(L->top++, (int32_t)ex->gpr[i]); + else + setnumV(L->top++, (lua_Number)ex->gpr[i]); + } +#if !LJ_SOFTFP + for (i = 0; i < RID_NUM_FPR; i++) { + setnumV(L->top, ex->fpr[i]); + if (LJ_UNLIKELY(tvisnan(L->top))) + setnanV(L->top); + L->top++; + } +#endif +} +#endif + +#ifdef EXITSTATE_PCREG +/* Determine trace number from pc of exit instruction. */ +static TraceNo trace_exit_find(jit_State *J, MCode *pc) +{ + TraceNo traceno; + for (traceno = 1; traceno < J->sizetrace; traceno++) { + GCtrace *T = traceref(J, traceno); + if (T && pc >= T->mcode && pc < (MCode *)((char *)T->mcode + T->szmcode)) + return traceno; + } + lua_assert(0); + return 0; +} +#endif + +/* A trace exited. Restore interpreter state. */ +int LJ_FASTCALL lj_trace_exit(jit_State *J, void *exptr) +{ + ERRNO_SAVE + lua_State *L = J->L; + ExitState *ex = (ExitState *)exptr; + ExitDataCP exd; + int errcode; + const BCIns *pc; + void *cf; + GCtrace *T; +#ifdef EXITSTATE_PCREG + J->parent = trace_exit_find(J, (MCode *)(intptr_t)ex->gpr[EXITSTATE_PCREG]); +#endif + T = traceref(J, J->parent); UNUSED(T); +#ifdef EXITSTATE_CHECKEXIT + if (J->exitno == T->nsnap) { /* Treat stack check like a parent exit. */ + lua_assert(T->root != 0); + J->exitno = T->ir[REF_BASE].op2; + J->parent = T->ir[REF_BASE].op1; + T = traceref(J, J->parent); + } +#endif + lua_assert(T != NULL && J->exitno < T->nsnap); + exd.J = J; + exd.exptr = exptr; + errcode = lj_vm_cpcall(L, NULL, &exd, trace_exit_cp); + if (errcode) + return -errcode; /* Return negated error code. */ + + if (!(LJ_HASPROFILE && (G(L)->hookmask & HOOK_PROFILE))) + lj_vmevent_send(L, TEXIT, + lj_state_checkstack(L, 4+RID_NUM_GPR+RID_NUM_FPR+LUA_MINSTACK); + setintV(L->top++, J->parent); + setintV(L->top++, J->exitno); + trace_exit_regs(L, ex); + ); + + pc = exd.pc; + cf = cframe_raw(L->cframe); + setcframe_pc(cf, pc); + if (LJ_HASPROFILE && (G(L)->hookmask & HOOK_PROFILE)) { + /* Just exit to interpreter. */ + } else if (G(L)->gc.state == GCSatomic || G(L)->gc.state == GCSfinalize) { + if (!(G(L)->hookmask & HOOK_GC)) + lj_gc_step(L); /* Exited because of GC: drive GC forward. */ + } else { + trace_hotside(J, pc); + } + if (bc_op(*pc) == BC_JLOOP) { + BCIns *retpc = &traceref(J, bc_d(*pc))->startins; + if (bc_isret(bc_op(*retpc))) { + if (J->state == LJ_TRACE_RECORD) { + J->patchins = *pc; + J->patchpc = (BCIns *)pc; + *J->patchpc = *retpc; + J->bcskip = 1; + } else { + pc = retpc; + setcframe_pc(cf, pc); + } + } + } + /* Return MULTRES or 0. */ + ERRNO_RESTORE + switch (bc_op(*pc)) { + case BC_CALLM: case BC_CALLMT: + return (int)((BCReg)(L->top - L->base) - bc_a(*pc) - bc_c(*pc) - LJ_FR2); + case BC_RETM: + return (int)((BCReg)(L->top - L->base) + 1 - bc_a(*pc) - bc_d(*pc)); + case BC_TSETM: + return (int)((BCReg)(L->top - L->base) + 1 - bc_a(*pc)); + default: + if (bc_op(*pc) >= BC_FUNCF) + return (int)((BCReg)(L->top - L->base) + 1); + return 0; + } +} + +#endif diff --git a/lib/LuaJIT/lj_trace.h b/lib/LuaJIT/lj_trace.h new file mode 100644 index 0000000..22cae74 --- /dev/null +++ b/lib/LuaJIT/lj_trace.h @@ -0,0 +1,55 @@ +/* +** Trace management. +** Copyright (C) 2005-2017 Mike Pall. See Copyright Notice in luajit.h +*/ + +#ifndef _LJ_TRACE_H +#define _LJ_TRACE_H + +#include "lj_obj.h" + +#if LJ_HASJIT +#include "lj_jit.h" +#include "lj_dispatch.h" + +/* Trace errors. */ +typedef enum { +#define TREDEF(name, msg) LJ_TRERR_##name, +#include "lj_traceerr.h" + LJ_TRERR__MAX +} TraceError; + +LJ_FUNC_NORET void lj_trace_err(jit_State *J, TraceError e); +LJ_FUNC_NORET void lj_trace_err_info(jit_State *J, TraceError e); + +/* Trace management. */ +LJ_FUNC GCtrace * LJ_FASTCALL lj_trace_alloc(lua_State *L, GCtrace *T); +LJ_FUNC void LJ_FASTCALL lj_trace_free(global_State *g, GCtrace *T); +LJ_FUNC void lj_trace_reenableproto(GCproto *pt); +LJ_FUNC void lj_trace_flushproto(global_State *g, GCproto *pt); +LJ_FUNC void lj_trace_flush(jit_State *J, TraceNo traceno); +LJ_FUNC int lj_trace_flushall(lua_State *L); +LJ_FUNC void lj_trace_initstate(global_State *g); +LJ_FUNC void lj_trace_freestate(global_State *g); + +/* Event handling. */ +LJ_FUNC void lj_trace_ins(jit_State *J, const BCIns *pc); +LJ_FUNCA void LJ_FASTCALL lj_trace_hot(jit_State *J, const BCIns *pc); +LJ_FUNCA void LJ_FASTCALL lj_trace_stitch(jit_State *J, const BCIns *pc); +LJ_FUNCA int LJ_FASTCALL lj_trace_exit(jit_State *J, void *exptr); + +/* Signal asynchronous abort of trace or end of trace. */ +#define lj_trace_abort(g) (G2J(g)->state &= ~LJ_TRACE_ACTIVE) +#define lj_trace_end(J) (J->state = LJ_TRACE_END) + +#else + +#define lj_trace_flushall(L) (UNUSED(L), 0) +#define lj_trace_initstate(g) UNUSED(g) +#define lj_trace_freestate(g) UNUSED(g) +#define lj_trace_abort(g) UNUSED(g) +#define lj_trace_end(J) UNUSED(J) + +#endif + +#endif diff --git a/lib/LuaJIT/lj_trace.o b/lib/LuaJIT/lj_trace.o new file mode 100644 index 0000000000000000000000000000000000000000..8709e3503af72c5a116fc1cb18516f40618f89ef GIT binary patch literal 13264 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 = ~LJ_TUDATA; + ud->udtype = UDTYPE_USERDATA; + ud->len = sz; + /* NOBARRIER: The GCudata is new (marked white). */ + setgcrefnull(ud->metatable); + setgcref(ud->env, obj2gco(env)); + /* Chain to userdata list (after main thread). */ + setgcrefr(ud->nextgc, mainthread(g)->nextgc); + setgcref(mainthread(g)->nextgc, obj2gco(ud)); + return ud; +} + +void LJ_FASTCALL lj_udata_free(global_State *g, GCudata *ud) +{ + lj_mem_free(g, ud, sizeudata(ud)); +} + diff --git a/lib/LuaJIT/lj_udata.h b/lib/LuaJIT/lj_udata.h new file mode 100644 index 0000000..f271a42 --- /dev/null +++ b/lib/LuaJIT/lj_udata.h @@ -0,0 +1,14 @@ +/* +** Userdata handling. +** Copyright (C) 2005-2017 Mike Pall. See Copyright Notice in luajit.h +*/ + +#ifndef _LJ_UDATA_H +#define _LJ_UDATA_H + +#include "lj_obj.h" + +LJ_FUNC GCudata *lj_udata_new(lua_State *L, MSize sz, GCtab *env); +LJ_FUNC void LJ_FASTCALL lj_udata_free(global_State *g, GCudata *ud); + +#endif diff --git a/lib/LuaJIT/lj_udata.o b/lib/LuaJIT/lj_udata.o new file mode 100644 index 0000000000000000000000000000000000000000..e2c233c6b63cf19d3727ebd3baa75db9c9caad34 GIT binary patch literal 1544 zcmb_bzi-n}5I)B#HN|pG#b8t<%z{!0tY$%?19IAeQI>0K#_;OJ#cMM;Ruv&yj(GsqOZj-tL5m=_2_5u*B))h%gW@ttA%Jy-L-Whdf(;6 zXhV5k&A(SMY(&+u`1Mot;)zI!a<}i4)^{dPLhP9FO||%`5<5n3&zSjK?tL&K$1KN= zUWs=5-@sW|m@^B`%Ib`H-YQzN*aC4ZF(7SU1F5S^)9SeL3SXpv{0X+VOm<$=U*wiF z#5xEiw-nE?l?YrZeRL9FUqg7tyG8Rz1qXOZs=Nd;BKwuJ)*$({953-&1PHJ zYXqK$J@|K_H$05b?|uRkeFBXjj!_w~L!Qq8R_C!Fx|?XjAhBEA3%sUlg@_z=^3lVj%duj#F3AAx^wYwD*w~#9Cs~mFvfe%jjh3-(?N?dy)S1@8k7c|E#mA rx5)-on4Ms?qxIBxRC}r~u=CGRAb_1e} literal 0 HcmV?d00001 diff --git a/lib/LuaJIT/lj_udata_dyn.o b/lib/LuaJIT/lj_udata_dyn.o new file mode 100644 index 0000000000000000000000000000000000000000..e2c233c6b63cf19d3727ebd3baa75db9c9caad34 GIT binary patch literal 1544 zcmb_bzi-n}5I)B#HN|pG#b8t<%z{!0tY$%?19IAeQI>0K#_;OJ#cMM;Ruv&yj(GsqOZj-tL5m=_2_5u*B))h%gW@ttA%Jy-L-Whdf(;6 zXhV5k&A(SMY(&+u`1Mot;)zI!a<}i4)^{dPLhP9FO||%`5<5n3&zSjK?tL&K$1KN= zUWs=5-@sW|m@^B`%Ib`H-YQzN*aC4ZF(7SU1F5S^)9SeL3SXpv{0X+VOm<$=U*wiF z#5xEiw-nE?l?YrZeRL9FUqg7tyG8Rz1qXOZs=Nd;BKwuJ)*$({953-&1PHJ zYXqK$J@|K_H$05b?|uRkeFBXjj!_w~L!Qq8R_C!Fx|?XjAhBEA3%sUlg@_z=^3lVj%duj#F3AAx^wYwD*w~#9Cs~mFvfe%jjh3-(?N?dy)S1@8k7c|E#mA rx5)-on4Ms?qxIBxRC}r~u=CGRAb_1e} literal 0 HcmV?d00001 diff --git a/lib/LuaJIT/lj_vm.h b/lib/LuaJIT/lj_vm.h new file mode 100644 index 0000000..1cc7eed --- /dev/null +++ b/lib/LuaJIT/lj_vm.h @@ -0,0 +1,120 @@ +/* +** Assembler VM interface definitions. +** Copyright (C) 2005-2017 Mike Pall. See Copyright Notice in luajit.h +*/ + +#ifndef _LJ_VM_H +#define _LJ_VM_H + +#include "lj_obj.h" + +/* Entry points for ASM parts of VM. */ +LJ_ASMF void lj_vm_call(lua_State *L, TValue *base, int nres1); +LJ_ASMF int lj_vm_pcall(lua_State *L, TValue *base, int nres1, ptrdiff_t ef); +typedef TValue *(*lua_CPFunction)(lua_State *L, lua_CFunction func, void *ud); +LJ_ASMF int lj_vm_cpcall(lua_State *L, lua_CFunction func, void *ud, + lua_CPFunction cp); +LJ_ASMF int lj_vm_resume(lua_State *L, TValue *base, int nres1, ptrdiff_t ef); +LJ_ASMF_NORET void LJ_FASTCALL lj_vm_unwind_c(void *cframe, int errcode); +LJ_ASMF_NORET void LJ_FASTCALL lj_vm_unwind_ff(void *cframe); +#if LJ_ABI_WIN && LJ_TARGET_X86 +LJ_ASMF_NORET void LJ_FASTCALL lj_vm_rtlunwind(void *cframe, void *excptrec, + void *unwinder, int errcode); +#endif +LJ_ASMF void lj_vm_unwind_c_eh(void); +LJ_ASMF void lj_vm_unwind_ff_eh(void); +#if LJ_TARGET_X86ORX64 +LJ_ASMF void lj_vm_unwind_rethrow(void); +#endif + +/* Miscellaneous functions. */ +#if LJ_TARGET_X86ORX64 +LJ_ASMF int lj_vm_cpuid(uint32_t f, uint32_t res[4]); +#endif +#if LJ_TARGET_PPC +void lj_vm_cachesync(void *start, void *end); +#endif +LJ_ASMF double lj_vm_foldarith(double x, double y, int op); +#if LJ_HASJIT +LJ_ASMF double lj_vm_foldfpm(double x, int op); +#endif +#if !LJ_ARCH_HASFPU +/* Declared in lj_obj.h: LJ_ASMF int32_t lj_vm_tobit(double x); */ +#endif + +/* Dispatch targets for recording and hooks. */ +LJ_ASMF void lj_vm_record(void); +LJ_ASMF void lj_vm_inshook(void); +LJ_ASMF void lj_vm_rethook(void); +LJ_ASMF void lj_vm_callhook(void); +LJ_ASMF void lj_vm_profhook(void); + +/* Trace exit handling. */ +LJ_ASMF void lj_vm_exit_handler(void); +LJ_ASMF void lj_vm_exit_interp(void); + +/* Internal math helper functions. */ +#if LJ_TARGET_PPC || LJ_TARGET_ARM64 || (LJ_TARGET_MIPS && LJ_ABI_SOFTFP) +#define lj_vm_floor floor +#define lj_vm_ceil ceil +#else +LJ_ASMF double lj_vm_floor(double); +LJ_ASMF double lj_vm_ceil(double); +#if LJ_TARGET_ARM +LJ_ASMF double lj_vm_floor_sf(double); +LJ_ASMF double lj_vm_ceil_sf(double); +#endif +#endif +#ifdef LUAJIT_NO_LOG2 +LJ_ASMF double lj_vm_log2(double); +#else +#define lj_vm_log2 log2 +#endif +#if !(defined(_LJ_DISPATCH_H) && LJ_TARGET_MIPS) +LJ_ASMF int32_t LJ_FASTCALL lj_vm_modi(int32_t, int32_t); +#endif + +#if LJ_HASJIT +#if LJ_TARGET_X86ORX64 +LJ_ASMF void lj_vm_floor_sse(void); +LJ_ASMF void lj_vm_ceil_sse(void); +LJ_ASMF void lj_vm_trunc_sse(void); +LJ_ASMF void lj_vm_powi_sse(void); +#define lj_vm_powi NULL +#else +LJ_ASMF double lj_vm_powi(double, int32_t); +#endif +#if LJ_TARGET_PPC || LJ_TARGET_ARM64 +#define lj_vm_trunc trunc +#else +LJ_ASMF double lj_vm_trunc(double); +#if LJ_TARGET_ARM +LJ_ASMF double lj_vm_trunc_sf(double); +#endif +#endif +#ifdef LUAJIT_NO_EXP2 +LJ_ASMF double lj_vm_exp2(double); +#else +#define lj_vm_exp2 exp2 +#endif +#if LJ_HASFFI +LJ_ASMF int lj_vm_errno(void); +#endif +#endif + +/* Continuations for metamethods. */ +LJ_ASMF void lj_cont_cat(void); /* Continue with concatenation. */ +LJ_ASMF void lj_cont_ra(void); /* Store result in RA from instruction. */ +LJ_ASMF void lj_cont_nop(void); /* Do nothing, just continue execution. */ +LJ_ASMF void lj_cont_condt(void); /* Branch if result is true. */ +LJ_ASMF void lj_cont_condf(void); /* Branch if result is false. */ +LJ_ASMF void lj_cont_hook(void); /* Continue from hook yield. */ +LJ_ASMF void lj_cont_stitch(void); /* Trace stitching. */ + +/* Start of the ASM code. */ +LJ_ASMF char lj_vm_asm_begin[]; + +/* Bytecode offsets are relative to lj_vm_asm_begin. */ +#define makeasmfunc(ofs) ((ASMFunction)(lj_vm_asm_begin + (ofs))) + +#endif diff --git a/lib/LuaJIT/lj_vm.o b/lib/LuaJIT/lj_vm.o new file mode 100644 index 0000000000000000000000000000000000000000..878cf916c248dd1e11526483a77ae9c2e2211b10 GIT binary patch literal 29328 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 T52BTSg7se^3-Vp`l?D4>SncEZ literal 0 HcmV?d00001 diff --git a/lib/LuaJIT/lj_vm_dyn.o b/lib/LuaJIT/lj_vm_dyn.o new file mode 100644 index 0000000000000000000000000000000000000000..878cf916c248dd1e11526483a77ae9c2e2211b10 GIT binary patch literal 29328 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 T52BTSg7se^3-Vp`l?D4>SncEZ literal 0 HcmV?d00001 diff --git a/lib/LuaJIT/lj_vmevent.c b/lib/LuaJIT/lj_vmevent.c new file mode 100644 index 0000000..8664080 --- /dev/null +++ b/lib/LuaJIT/lj_vmevent.c @@ -0,0 +1,58 @@ +/* +** VM event handling. +** Copyright (C) 2005-2017 Mike Pall. See Copyright Notice in luajit.h +*/ + +#include + +#define lj_vmevent_c +#define LUA_CORE + +#include "lj_obj.h" +#include "lj_str.h" +#include "lj_tab.h" +#include "lj_state.h" +#include "lj_dispatch.h" +#include "lj_vm.h" +#include "lj_vmevent.h" + +ptrdiff_t lj_vmevent_prepare(lua_State *L, VMEvent ev) +{ + global_State *g = G(L); + GCstr *s = lj_str_newlit(L, LJ_VMEVENTS_REGKEY); + cTValue *tv = lj_tab_getstr(tabV(registry(L)), s); + if (tvistab(tv)) { + int hash = VMEVENT_HASH(ev); + tv = lj_tab_getint(tabV(tv), hash); + if (tv && tvisfunc(tv)) { + lj_state_checkstack(L, LUA_MINSTACK); + setfuncV(L, L->top++, funcV(tv)); + if (LJ_FR2) setnilV(L->top++); + return savestack(L, L->top); + } + } + g->vmevmask &= ~VMEVENT_MASK(ev); /* No handler: cache this fact. */ + return 0; +} + +void lj_vmevent_call(lua_State *L, ptrdiff_t argbase) +{ + global_State *g = G(L); + uint8_t oldmask = g->vmevmask; + uint8_t oldh = hook_save(g); + int status; + g->vmevmask = 0; /* Disable all events. */ + hook_vmevent(g); + status = lj_vm_pcall(L, restorestack(L, argbase), 0+1, 0); + if (LJ_UNLIKELY(status)) { + /* Really shouldn't use stderr here, but where else to complain? */ + L->top--; + fputs("VM handler failed: ", stderr); + fputs(tvisstr(L->top) ? strVdata(L->top) : "?", stderr); + fputc('\n', stderr); + } + hook_restore(g, oldh); + if (g->vmevmask != VMEVENT_NOCACHE) + g->vmevmask = oldmask; /* Restore event mask, but not if not modified. */ +} + diff --git a/lib/LuaJIT/lj_vmevent.h b/lib/LuaJIT/lj_vmevent.h new file mode 100644 index 0000000..050fb4d --- /dev/null +++ b/lib/LuaJIT/lj_vmevent.h @@ -0,0 +1,59 @@ +/* +** VM event handling. +** Copyright (C) 2005-2017 Mike Pall. See Copyright Notice in luajit.h +*/ + +#ifndef _LJ_VMEVENT_H +#define _LJ_VMEVENT_H + +#include "lj_obj.h" + +/* Registry key for VM event handler table. */ +#define LJ_VMEVENTS_REGKEY "_VMEVENTS" +#define LJ_VMEVENTS_HSIZE 4 + +#define VMEVENT_MASK(ev) ((uint8_t)1 << ((int)(ev) & 7)) +#define VMEVENT_HASH(ev) ((int)(ev) & ~7) +#define VMEVENT_HASHIDX(h) ((int)(h) << 3) +#define VMEVENT_NOCACHE 255 + +#define VMEVENT_DEF(name, hash) \ + LJ_VMEVENT_##name##_, \ + LJ_VMEVENT_##name = ((LJ_VMEVENT_##name##_) & 7)|((hash) << 3) + +/* VM event IDs. */ +typedef enum { + VMEVENT_DEF(BC, 0x00003883), + VMEVENT_DEF(TRACE, 0xb2d91467), + VMEVENT_DEF(RECORD, 0x9284bf4f), + VMEVENT_DEF(TEXIT, 0xb29df2b0), + LJ_VMEVENT__MAX +} VMEvent; + +#ifdef LUAJIT_DISABLE_VMEVENT +#define lj_vmevent_send(L, ev, args) UNUSED(L) +#define lj_vmevent_send_(L, ev, args, post) UNUSED(L) +#else +#define lj_vmevent_send(L, ev, args) \ + if (G(L)->vmevmask & VMEVENT_MASK(LJ_VMEVENT_##ev)) { \ + ptrdiff_t argbase = lj_vmevent_prepare(L, LJ_VMEVENT_##ev); \ + if (argbase) { \ + args \ + lj_vmevent_call(L, argbase); \ + } \ + } +#define lj_vmevent_send_(L, ev, args, post) \ + if (G(L)->vmevmask & VMEVENT_MASK(LJ_VMEVENT_##ev)) { \ + ptrdiff_t argbase = lj_vmevent_prepare(L, LJ_VMEVENT_##ev); \ + if (argbase) { \ + args \ + lj_vmevent_call(L, argbase); \ + post \ + } \ + } + +LJ_FUNC ptrdiff_t lj_vmevent_prepare(lua_State *L, VMEvent ev); +LJ_FUNC void lj_vmevent_call(lua_State *L, ptrdiff_t argbase); +#endif + +#endif diff --git a/lib/LuaJIT/lj_vmevent.o b/lib/LuaJIT/lj_vmevent.o new file mode 100644 index 0000000000000000000000000000000000000000..9d150328309cf063876b88e3c357f8c1ac358d62 GIT binary patch literal 2760 zcmbW2O>7%g5P-+A)3_nEL-?~4Q1_6cR4S{u0x2yjc>NP^kkcZZL?BXDuCq=;?If%> zKcxz;v?^q^1og~?95^6xLPDj6p;Pl(SSwH=KY9ThCbFyKz_*ZV_#Lm#wU(LE=EroXH)>J1Sb5fFe{Em8N74yCI zwBihbx{?g5b}DAK-%f^h_xARjd=I*8e-J%-1)aQk4BEH0KP|cA+69Fo`!JM*wu2<_=1UM*>-klTxrQhV($i( zi1c>og-EZ}nvL{bYLE0HP$DsONJhc~4|uUJv)KB6Y;Hd`O|j`nUrW{(oW|fLLR%Gr z-}f4I2OFMbfFS-FNf0wNk|4A;!k9&PDFlxogoP*1jJWFFJXBu5{2i`7XkxC}3RFtJ z1mTb$!4pWjT4*;sachGn)w8pOK|wv+gbz01RJTTXE+VIKH&K(+-4@_7>PG(W5NAH3 zdanupunGUT3IC)Chw|&%d_|wvr%kyCaxT?J%}HwFLZ3Ek=(7bwCsZ{JZCYO-(=1%j zCUg^t@H91TPI`U?Q`aVpnT4uZC|)GQM$(Ffa@l*m#MBr}-{$id*fz#RNd2Aui zqXMTpiNrsj_XYpQu%G9V$QgzF>Ca`18JutO4!L@%g5?Jpi|^@4ZW(&HAe;K43G#Ts zEP#BWS_Rpdp$Br0%W;s4GnEP+I*_MlOkKu%_9^d_$ZoVgsg;aEMdzd4x0W!Hmh8V7 z639qa*?xFW_r8PO*g>^Ua0jMW*_FlXPBo!a{D$-HDS#&#_us6WzHwPvHwU*#pTIxm zyx({__cYW={ENcJmqds*pBC2%#`yPd-<=&2dIv<|aa_~A$!*cEOFU#khu`-#7VmeD zF>pjC?sHBzZ&i-U4vT4OlLdCShbTj#}yGfXx zWK&Drz|fdsy5fr-U)qOVv9I6!Q>r~nBYDZ( zl8j#>X=^01MnE!GtW?ChzJRt3jG6Es`XTe@>ok+`L(_Mwo3}+i-Cm6^OU74`*UpH8 z;+Zd_2h#vnm-(CV)8EG`vFV(ZjYx&Ra&(PjO^=)pO3a@Nsf-dw*rY|nG!L4=a|y|Z9U zxxQ#~aiGWCh<~$0D;4|kF(vcgyd_7X*HW|T=(R$1B~=B{ylH&hxsaN*D(#~2<3=v9 zV&}QEJoO|w8mrOtzO}iF)ot|A!|8=W)phDJ?9SLE^bXVT3wQHtIZz9p)Z=z-R`W<%LBccfy~LiJiG`4z5J*$K2%gS zepneRszb+lfNUytobSk<>_5yO6XHTAh5~H^N#KoFATZMwIMC7*c?Z|ih3qkV1mF(o zB-d68X1UnqkjU}#;g`4$qan+6UTEez5Qtn19g;UA zIsS53(jc*m|dl*=7S!J^d1bMn8hbk?arBZa8As2u&(#ThN6b_52>Z zXAe&GtC#0Ia+-G)HHlqc)EDtz&;Kpr`1i{P2hO$1SY8nhzv@ugD|A%3JB-QWe~Ip>X3U}h=Wj=D3$OKfiOOys{&r0Pdil? z>}YjV9@dnS%4WM~&0{1zvj0s_AR~F--4Ca?``tN!15~>Nb8z()_X^_Oc-nh1*^TAg zI{;6(+<#Nv@y+X|-F4F*adX~lJe^~j>L{DQ)ogy=$Jw%&PT)A%bpLr}%XOeL?G+Zs zG~I*D_WHGY51D7c?|asb_q($@@jXv0nQZ?Veie;+^;>oi1bZ&3AH}kGRzF&ge{MwZ vu?rKfrtd3#d;IbA4*P3|2-av(D4#j%ZtH*k`*EaR{D!;!(Yuk3;gA0heUg#Y literal 0 HcmV?d00001 diff --git a/lib/LuaJIT/lj_vmmath.c b/lib/LuaJIT/lj_vmmath.c new file mode 100644 index 0000000..b231d3e --- /dev/null +++ b/lib/LuaJIT/lj_vmmath.c @@ -0,0 +1,152 @@ +/* +** Math helper functions for assembler VM. +** Copyright (C) 2005-2017 Mike Pall. See Copyright Notice in luajit.h +*/ + +#define lj_vmmath_c +#define LUA_CORE + +#include +#include + +#include "lj_obj.h" +#include "lj_ir.h" +#include "lj_vm.h" + +/* -- Wrapper functions --------------------------------------------------- */ + +#if LJ_TARGET_X86 && __ELF__ && __PIC__ +/* Wrapper functions to deal with the ELF/x86 PIC disaster. */ +LJ_FUNCA double lj_wrap_log(double x) { return log(x); } +LJ_FUNCA double lj_wrap_log10(double x) { return log10(x); } +LJ_FUNCA double lj_wrap_exp(double x) { return exp(x); } +LJ_FUNCA double lj_wrap_sin(double x) { return sin(x); } +LJ_FUNCA double lj_wrap_cos(double x) { return cos(x); } +LJ_FUNCA double lj_wrap_tan(double x) { return tan(x); } +LJ_FUNCA double lj_wrap_asin(double x) { return asin(x); } +LJ_FUNCA double lj_wrap_acos(double x) { return acos(x); } +LJ_FUNCA double lj_wrap_atan(double x) { return atan(x); } +LJ_FUNCA double lj_wrap_sinh(double x) { return sinh(x); } +LJ_FUNCA double lj_wrap_cosh(double x) { return cosh(x); } +LJ_FUNCA double lj_wrap_tanh(double x) { return tanh(x); } +LJ_FUNCA double lj_wrap_atan2(double x, double y) { return atan2(x, y); } +LJ_FUNCA double lj_wrap_pow(double x, double y) { return pow(x, y); } +LJ_FUNCA double lj_wrap_fmod(double x, double y) { return fmod(x, y); } +#endif + +/* -- Helper functions for generated machine code ------------------------- */ + +double lj_vm_foldarith(double x, double y, int op) +{ + switch (op) { + case IR_ADD - IR_ADD: return x+y; break; + case IR_SUB - IR_ADD: return x-y; break; + case IR_MUL - IR_ADD: return x*y; break; + case IR_DIV - IR_ADD: return x/y; break; + case IR_MOD - IR_ADD: return x-lj_vm_floor(x/y)*y; break; + case IR_POW - IR_ADD: return pow(x, y); break; + case IR_NEG - IR_ADD: return -x; break; + case IR_ABS - IR_ADD: return fabs(x); break; +#if LJ_HASJIT + case IR_ATAN2 - IR_ADD: return atan2(x, y); break; + case IR_LDEXP - IR_ADD: return ldexp(x, (int)y); break; + case IR_MIN - IR_ADD: return x > y ? y : x; break; + case IR_MAX - IR_ADD: return x < y ? y : x; break; +#endif + default: return x; + } +} + +#if (LJ_HASJIT && !(LJ_TARGET_ARM || LJ_TARGET_ARM64 || LJ_TARGET_PPC)) || LJ_TARGET_MIPS +int32_t LJ_FASTCALL lj_vm_modi(int32_t a, int32_t b) +{ + uint32_t y, ua, ub; + lua_assert(b != 0); /* This must be checked before using this function. */ + ua = a < 0 ? (uint32_t)-a : (uint32_t)a; + ub = b < 0 ? (uint32_t)-b : (uint32_t)b; + y = ua % ub; + if (y != 0 && (a^b) < 0) y = y - ub; + if (((int32_t)y^b) < 0) y = (uint32_t)-(int32_t)y; + return (int32_t)y; +} +#endif + +#if LJ_HASJIT + +#ifdef LUAJIT_NO_LOG2 +double lj_vm_log2(double a) +{ + return log(a) * 1.4426950408889634074; +} +#endif + +#ifdef LUAJIT_NO_EXP2 +double lj_vm_exp2(double a) +{ + return exp(a * 0.6931471805599453); +} +#endif + +#if !LJ_TARGET_X86ORX64 +/* Unsigned x^k. */ +static double lj_vm_powui(double x, uint32_t k) +{ + double y; + lua_assert(k != 0); + for (; (k & 1) == 0; k >>= 1) x *= x; + y = x; + if ((k >>= 1) != 0) { + for (;;) { + x *= x; + if (k == 1) break; + if (k & 1) y *= x; + k >>= 1; + } + y *= x; + } + return y; +} + +/* Signed x^k. */ +double lj_vm_powi(double x, int32_t k) +{ + if (k > 1) + return lj_vm_powui(x, (uint32_t)k); + else if (k == 1) + return x; + else if (k == 0) + return 1.0; + else + return 1.0 / lj_vm_powui(x, (uint32_t)-k); +} +#endif + +/* Computes fpm(x) for extended math functions. */ +double lj_vm_foldfpm(double x, int fpm) +{ + switch (fpm) { + case IRFPM_FLOOR: return lj_vm_floor(x); + case IRFPM_CEIL: return lj_vm_ceil(x); + case IRFPM_TRUNC: return lj_vm_trunc(x); + case IRFPM_SQRT: return sqrt(x); + case IRFPM_EXP: return exp(x); + case IRFPM_EXP2: return lj_vm_exp2(x); + case IRFPM_LOG: return log(x); + case IRFPM_LOG2: return lj_vm_log2(x); + case IRFPM_LOG10: return log10(x); + case IRFPM_SIN: return sin(x); + case IRFPM_COS: return cos(x); + case IRFPM_TAN: return tan(x); + default: lua_assert(0); + } + return 0; +} + +#if LJ_HASFFI +int lj_vm_errno(void) +{ + return errno; +} +#endif + +#endif diff --git a/lib/LuaJIT/lj_vmmath.o b/lib/LuaJIT/lj_vmmath.o new file mode 100644 index 0000000000000000000000000000000000000000..951557e01a79e89cc7a700499b54a348ee8772cc GIT binary patch literal 4632 zcmcJSZ)jUp6u@u#$7M_HOY5f9O%tTkR_db>ojPP>{Tnd4Iy-EoF3r+pjm@8$yxLa5 z6>8O>gMAt7@H;cJz8n-ofFIMy~ zZfgU}7=ob?_Aa_WT>mnH^=mQB(}DI`%l_E1F<=cHsh^nkO0oa7w+n>=an)~IYx!GZ z(C%xC&`*A?+l&D_WZ6%%-Dmc@_9)n~n9k$NZRT#~cRl%=o?PCy)tt{Q`F5L^d@DO! zzUwn9hSRr`Tbii%eVd!#`ElkiEik`ejk|eq{ddjdH|G=PQhOhBC^MhZ7^h4ZCEwev zk7<9bK%S7BzO=2qbYuKHW{s`Dl*9bqZhQaPAC^7X=;U<=b^Sfc-+~`v*UkmZ@&6Ra z|AzU#{JWDWHeq#9F0d`S+uM(Oo4Q{-)9ig#_vx)vaga*KTc?F%!tFR*v(I@6HWJn! zp}K0^SRJ+A#DQ98z%>HDFSv%_H|QELe*-2Lu&adVp_u2W?1OTM^jla$JA-Caht2P+ zz`I;_wI$x^DnIsGFOGx9zMuL=-wo2S9>(E*zSc=(Z-FUSiLJuk8X+pno;1iS%ee!s zvfeBU09$5@3Zan1`JD}`*8LMw%A5z>dS?t|+<{l`iB(tE3Fr@If0Mv*8KgI370*iQmg{;yuL8cN5r*O*=Uo zei3<@ysp^rVViblZTP1){Bs+A&4$m}@ULum9yrch59P*d^u3M0NW30;GnotVf;(S>hdHh@3i{(N zWIjmmA-CUxP9S~+AlQx<9`-*Y@rNaT8FUo%uY-sEzeWgxydECr%LqY`-vbZxerY^+ zNqmer`rj+@8HwK~@yimI=iw8HH%R_pNc?_@|0?kZB>tzw4@R7Eq;S3h`90-!4$^wGD*pIQf%2qP6vsJB{27JM5$Ex8``;1w zEB>3rI}~1pfgn78+)e}WpjBNQPYdxLh5L#3D!i9?pTe)ucm@>y8F9Y(F&v*Ch#}B5^9^oUt)K{(uZ(;*#(_V}xf2o^5^xRE|0=h41=WFtH!PqJjJ#A+CN2 zURC-fciu*93fxcF=AFhLgIFAw+8y^l2z9bxuCn;RD3|{fed_tCVay-jVKx7DVCCkY z6H>oa(N~reV3h0sdX449^NjuD*Rb0EX<*nU-f^^e}F(fu!`cZ8vhSFa8S$u literal 0 HcmV?d00001 diff --git a/lib/LuaJIT/lj_vmmath_dyn.o b/lib/LuaJIT/lj_vmmath_dyn.o new file mode 100644 index 0000000000000000000000000000000000000000..951557e01a79e89cc7a700499b54a348ee8772cc GIT binary patch literal 4632 zcmcJSZ)jUp6u@u#$7M_HOY5f9O%tTkR_db>ojPP>{Tnd4Iy-EoF3r+pjm@8$yxLa5 z6>8O>gMAt7@H;cJz8n-ofFIMy~ zZfgU}7=ob?_Aa_WT>mnH^=mQB(}DI`%l_E1F<=cHsh^nkO0oa7w+n>=an)~IYx!GZ z(C%xC&`*A?+l&D_WZ6%%-Dmc@_9)n~n9k$NZRT#~cRl%=o?PCy)tt{Q`F5L^d@DO! zzUwn9hSRr`Tbii%eVd!#`ElkiEik`ejk|eq{ddjdH|G=PQhOhBC^MhZ7^h4ZCEwev zk7<9bK%S7BzO=2qbYuKHW{s`Dl*9bqZhQaPAC^7X=;U<=b^Sfc-+~`v*UkmZ@&6Ra z|AzU#{JWDWHeq#9F0d`S+uM(Oo4Q{-)9ig#_vx)vaga*KTc?F%!tFR*v(I@6HWJn! zp}K0^SRJ+A#DQ98z%>HDFSv%_H|QELe*-2Lu&adVp_u2W?1OTM^jla$JA-Caht2P+ zz`I;_wI$x^DnIsGFOGx9zMuL=-wo2S9>(E*zSc=(Z-FUSiLJuk8X+pno;1iS%ee!s zvfeBU09$5@3Zan1`JD}`*8LMw%A5z>dS?t|+<{l`iB(tE3Fr@If0Mv*8KgI370*iQmg{;yuL8cN5r*O*=Uo zei3<@ysp^rVViblZTP1){Bs+A&4$m}@ULum9yrch59P*d^u3M0NW30;GnotVf;(S>hdHh@3i{(N zWIjmmA-CUxP9S~+AlQx<9`-*Y@rNaT8FUo%uY-sEzeWgxydECr%LqY`-vbZxerY^+ zNqmer`rj+@8HwK~@yimI=iw8HH%R_pNc?_@|0?kZB>tzw4@R7Eq;S3h`90-!4$^wGD*pIQf%2qP6vsJB{27JM5$Ex8``;1w zEB>3rI}~1pfgn78+)e}WpjBNQPYdxLh5L#3D!i9?pTe)ucm@>y8F9Y(F&v*Ch#}B5^9^oUt)K{(uZ(;*#(_V}xf2o^5^xRE|0=h41=WFtH!PqJjJ#A+CN2 zURC-fciu*93fxcF=AFhLgIFAw+8y^l2z9bxuCn;RD3|{fed_tCVay-jVKx7DVCCkY z6H>oa(N~reV3h0sdX449^NjuD*Rb0EX<*nU-f^^e}F(fu!`cZ8vhSFa8S$u literal 0 HcmV?d00001 diff --git a/lib/LuaJIT/ljamalg.c b/lib/LuaJIT/ljamalg.c new file mode 100644 index 0000000..f1f2862 --- /dev/null +++ b/lib/LuaJIT/ljamalg.c @@ -0,0 +1,97 @@ +/* +** LuaJIT core and libraries amalgamation. +** Copyright (C) 2005-2017 Mike Pall. See Copyright Notice in luajit.h +*/ + +/* ++--------------------------------------------------------------------------+ +| WARNING: Compiling the amalgamation needs a lot of virtual memory | +| (around 300 MB with GCC 4.x)! If you don't have enough physical memory | +| your machine will start swapping to disk and the compile will not finish | +| within a reasonable amount of time. | +| So either compile on a bigger machine or use the non-amalgamated build. | ++--------------------------------------------------------------------------+ +*/ + +#define ljamalg_c +#define LUA_CORE + +/* To get the mremap prototype. Must be defined before any system includes. */ +#if defined(__linux__) && !defined(_GNU_SOURCE) +#define _GNU_SOURCE +#endif + +#ifndef WINVER +#define WINVER 0x0501 +#endif + +#include "lua.h" +#include "lauxlib.h" + +#include "lj_gc.c" +#include "lj_err.c" +#include "lj_char.c" +#include "lj_bc.c" +#include "lj_obj.c" +#include "lj_buf.c" +#include "lj_str.c" +#include "lj_tab.c" +#include "lj_func.c" +#include "lj_udata.c" +#include "lj_meta.c" +#include "lj_debug.c" +#include "lj_state.c" +#include "lj_dispatch.c" +#include "lj_vmevent.c" +#include "lj_vmmath.c" +#include "lj_strscan.c" +#include "lj_strfmt.c" +#include "lj_strfmt_num.c" +#include "lj_api.c" +#include "lj_profile.c" +#include "lj_lex.c" +#include "lj_parse.c" +#include "lj_bcread.c" +#include "lj_bcwrite.c" +#include "lj_load.c" +#include "lj_ctype.c" +#include "lj_cdata.c" +#include "lj_cconv.c" +#include "lj_ccall.c" +#include "lj_ccallback.c" +#include "lj_carith.c" +#include "lj_clib.c" +#include "lj_cparse.c" +#include "lj_lib.c" +#include "lj_ir.c" +#include "lj_opt_mem.c" +#include "lj_opt_fold.c" +#include "lj_opt_narrow.c" +#include "lj_opt_dce.c" +#include "lj_opt_loop.c" +#include "lj_opt_split.c" +#include "lj_opt_sink.c" +#include "lj_mcode.c" +#include "lj_snap.c" +#include "lj_record.c" +#include "lj_crecord.c" +#include "lj_ffrecord.c" +#include "lj_asm.c" +#include "lj_trace.c" +#include "lj_gdbjit.c" +#include "lj_alloc.c" + +#include "lib_aux.c" +#include "lib_base.c" +#include "lib_math.c" +#include "lib_string.c" +#include "lib_table.c" +#include "lib_io.c" +#include "lib_os.c" +#include "lib_package.c" +#include "lib_debug.c" +#include "lib_bit.c" +#include "lib_jit.c" +#include "lib_ffi.c" +#include "lib_init.c" + diff --git a/lib/LuaJIT/lua.h b/lib/LuaJIT/lua.h new file mode 100644 index 0000000..850bd79 --- /dev/null +++ b/lib/LuaJIT/lua.h @@ -0,0 +1,402 @@ +/* +** $Id: lua.h,v 1.218.1.5 2008/08/06 13:30:12 roberto Exp $ +** Lua - An Extensible Extension Language +** Lua.org, PUC-Rio, Brazil (http://www.lua.org) +** See Copyright Notice at the end of this file +*/ + + +#ifndef lua_h +#define lua_h + +#include +#include + + +#include "luaconf.h" + + +#define LUA_VERSION "Lua 5.1" +#define LUA_RELEASE "Lua 5.1.4" +#define LUA_VERSION_NUM 501 +#define LUA_COPYRIGHT "Copyright (C) 1994-2008 Lua.org, PUC-Rio" +#define LUA_AUTHORS "R. Ierusalimschy, L. H. de Figueiredo & W. Celes" + + +/* mark for precompiled code (`Lua') */ +#define LUA_SIGNATURE "\033Lua" + +/* option for multiple returns in `lua_pcall' and `lua_call' */ +#define LUA_MULTRET (-1) + + +/* +** pseudo-indices +*/ +#define LUA_REGISTRYINDEX (-10000) +#define LUA_ENVIRONINDEX (-10001) +#define LUA_GLOBALSINDEX (-10002) +#define lua_upvalueindex(i) (LUA_GLOBALSINDEX-(i)) + + +/* thread status */ +#define LUA_OK 0 +#define LUA_YIELD 1 +#define LUA_ERRRUN 2 +#define LUA_ERRSYNTAX 3 +#define LUA_ERRMEM 4 +#define LUA_ERRERR 5 + + +typedef struct lua_State lua_State; + +typedef int (*lua_CFunction) (lua_State *L); + + +/* +** functions that read/write blocks when loading/dumping Lua chunks +*/ +typedef const char * (*lua_Reader) (lua_State *L, void *ud, size_t *sz); + +typedef int (*lua_Writer) (lua_State *L, const void* p, size_t sz, void* ud); + + +/* +** prototype for memory-allocation functions +*/ +typedef void * (*lua_Alloc) (void *ud, void *ptr, size_t osize, size_t nsize); + + +/* +** basic types +*/ +#define LUA_TNONE (-1) + +#define LUA_TNIL 0 +#define LUA_TBOOLEAN 1 +#define LUA_TLIGHTUSERDATA 2 +#define LUA_TNUMBER 3 +#define LUA_TSTRING 4 +#define LUA_TTABLE 5 +#define LUA_TFUNCTION 6 +#define LUA_TUSERDATA 7 +#define LUA_TTHREAD 8 + + + +/* minimum Lua stack available to a C function */ +#define LUA_MINSTACK 20 + + +/* +** generic extra include file +*/ +#if defined(LUA_USER_H) +#include LUA_USER_H +#endif + + +/* type of numbers in Lua */ +typedef LUA_NUMBER lua_Number; + + +/* type for integer functions */ +typedef LUA_INTEGER lua_Integer; + + + +/* +** state manipulation +*/ +LUA_API lua_State *(lua_newstate) (lua_Alloc f, void *ud); +LUA_API void (lua_close) (lua_State *L); +LUA_API lua_State *(lua_newthread) (lua_State *L); + +LUA_API lua_CFunction (lua_atpanic) (lua_State *L, lua_CFunction panicf); + + +/* +** basic stack manipulation +*/ +LUA_API int (lua_gettop) (lua_State *L); +LUA_API void (lua_settop) (lua_State *L, int idx); +LUA_API void (lua_pushvalue) (lua_State *L, int idx); +LUA_API void (lua_remove) (lua_State *L, int idx); +LUA_API void (lua_insert) (lua_State *L, int idx); +LUA_API void (lua_replace) (lua_State *L, int idx); +LUA_API int (lua_checkstack) (lua_State *L, int sz); + +LUA_API void (lua_xmove) (lua_State *from, lua_State *to, int n); + + +/* +** access functions (stack -> C) +*/ + +LUA_API int (lua_isnumber) (lua_State *L, int idx); +LUA_API int (lua_isstring) (lua_State *L, int idx); +LUA_API int (lua_iscfunction) (lua_State *L, int idx); +LUA_API int (lua_isuserdata) (lua_State *L, int idx); +LUA_API int (lua_type) (lua_State *L, int idx); +LUA_API const char *(lua_typename) (lua_State *L, int tp); + +LUA_API int (lua_equal) (lua_State *L, int idx1, int idx2); +LUA_API int (lua_rawequal) (lua_State *L, int idx1, int idx2); +LUA_API int (lua_lessthan) (lua_State *L, int idx1, int idx2); + +LUA_API lua_Number (lua_tonumber) (lua_State *L, int idx); +LUA_API lua_Integer (lua_tointeger) (lua_State *L, int idx); +LUA_API int (lua_toboolean) (lua_State *L, int idx); +LUA_API const char *(lua_tolstring) (lua_State *L, int idx, size_t *len); +LUA_API size_t (lua_objlen) (lua_State *L, int idx); +LUA_API lua_CFunction (lua_tocfunction) (lua_State *L, int idx); +LUA_API void *(lua_touserdata) (lua_State *L, int idx); +LUA_API lua_State *(lua_tothread) (lua_State *L, int idx); +LUA_API const void *(lua_topointer) (lua_State *L, int idx); + + +/* +** push functions (C -> stack) +*/ +LUA_API void (lua_pushnil) (lua_State *L); +LUA_API void (lua_pushnumber) (lua_State *L, lua_Number n); +LUA_API void (lua_pushinteger) (lua_State *L, lua_Integer n); +LUA_API void (lua_pushlstring) (lua_State *L, const char *s, size_t l); +LUA_API void (lua_pushstring) (lua_State *L, const char *s); +LUA_API const char *(lua_pushvfstring) (lua_State *L, const char *fmt, + va_list argp); +LUA_API const char *(lua_pushfstring) (lua_State *L, const char *fmt, ...); +LUA_API void (lua_pushcclosure) (lua_State *L, lua_CFunction fn, int n); +LUA_API void (lua_pushboolean) (lua_State *L, int b); +LUA_API void (lua_pushlightuserdata) (lua_State *L, void *p); +LUA_API int (lua_pushthread) (lua_State *L); + + +/* +** get functions (Lua -> stack) +*/ +LUA_API void (lua_gettable) (lua_State *L, int idx); +LUA_API void (lua_getfield) (lua_State *L, int idx, const char *k); +LUA_API void (lua_rawget) (lua_State *L, int idx); +LUA_API void (lua_rawgeti) (lua_State *L, int idx, int n); +LUA_API void (lua_createtable) (lua_State *L, int narr, int nrec); +LUA_API void *(lua_newuserdata) (lua_State *L, size_t sz); +LUA_API int (lua_getmetatable) (lua_State *L, int objindex); +LUA_API void (lua_getfenv) (lua_State *L, int idx); + + +/* +** set functions (stack -> Lua) +*/ +LUA_API void (lua_settable) (lua_State *L, int idx); +LUA_API void (lua_setfield) (lua_State *L, int idx, const char *k); +LUA_API void (lua_rawset) (lua_State *L, int idx); +LUA_API void (lua_rawseti) (lua_State *L, int idx, int n); +LUA_API int (lua_setmetatable) (lua_State *L, int objindex); +LUA_API int (lua_setfenv) (lua_State *L, int idx); + + +/* +** `load' and `call' functions (load and run Lua code) +*/ +LUA_API void (lua_call) (lua_State *L, int nargs, int nresults); +LUA_API int (lua_pcall) (lua_State *L, int nargs, int nresults, int errfunc); +LUA_API int (lua_cpcall) (lua_State *L, lua_CFunction func, void *ud); +LUA_API int (lua_load) (lua_State *L, lua_Reader reader, void *dt, + const char *chunkname); + +LUA_API int (lua_dump) (lua_State *L, lua_Writer writer, void *data); + + +/* +** coroutine functions +*/ +LUA_API int (lua_yield) (lua_State *L, int nresults); +LUA_API int (lua_resume) (lua_State *L, int narg); +LUA_API int (lua_status) (lua_State *L); + +/* +** garbage-collection function and options +*/ + +#define LUA_GCSTOP 0 +#define LUA_GCRESTART 1 +#define LUA_GCCOLLECT 2 +#define LUA_GCCOUNT 3 +#define LUA_GCCOUNTB 4 +#define LUA_GCSTEP 5 +#define LUA_GCSETPAUSE 6 +#define LUA_GCSETSTEPMUL 7 +#define LUA_GCISRUNNING 9 + +LUA_API int (lua_gc) (lua_State *L, int what, int data); + + +/* +** miscellaneous functions +*/ + +LUA_API int (lua_error) (lua_State *L); + +LUA_API int (lua_next) (lua_State *L, int idx); + +LUA_API void (lua_concat) (lua_State *L, int n); + +LUA_API lua_Alloc (lua_getallocf) (lua_State *L, void **ud); +LUA_API void lua_setallocf (lua_State *L, lua_Alloc f, void *ud); + + + +/* +** =============================================================== +** some useful macros +** =============================================================== +*/ + +#define lua_pop(L,n) lua_settop(L, -(n)-1) + +#define lua_newtable(L) lua_createtable(L, 0, 0) + +#define lua_register(L,n,f) (lua_pushcfunction(L, (f)), lua_setglobal(L, (n))) + +#define lua_pushcfunction(L,f) lua_pushcclosure(L, (f), 0) + +#define lua_strlen(L,i) lua_objlen(L, (i)) + +#define lua_isfunction(L,n) (lua_type(L, (n)) == LUA_TFUNCTION) +#define lua_istable(L,n) (lua_type(L, (n)) == LUA_TTABLE) +#define lua_islightuserdata(L,n) (lua_type(L, (n)) == LUA_TLIGHTUSERDATA) +#define lua_isnil(L,n) (lua_type(L, (n)) == LUA_TNIL) +#define lua_isboolean(L,n) (lua_type(L, (n)) == LUA_TBOOLEAN) +#define lua_isthread(L,n) (lua_type(L, (n)) == LUA_TTHREAD) +#define lua_isnone(L,n) (lua_type(L, (n)) == LUA_TNONE) +#define lua_isnoneornil(L, n) (lua_type(L, (n)) <= 0) + +#define lua_pushliteral(L, s) \ + lua_pushlstring(L, "" s, (sizeof(s)/sizeof(char))-1) + +#define lua_setglobal(L,s) lua_setfield(L, LUA_GLOBALSINDEX, (s)) +#define lua_getglobal(L,s) lua_getfield(L, LUA_GLOBALSINDEX, (s)) + +#define lua_tostring(L,i) lua_tolstring(L, (i), NULL) + + + +/* +** compatibility macros and functions +*/ + +#define lua_open() luaL_newstate() + +#define lua_getregistry(L) lua_pushvalue(L, LUA_REGISTRYINDEX) + +#define lua_getgccount(L) lua_gc(L, LUA_GCCOUNT, 0) + +#define lua_Chunkreader lua_Reader +#define lua_Chunkwriter lua_Writer + + +/* hack */ +LUA_API void lua_setlevel (lua_State *from, lua_State *to); + + +/* +** {====================================================================== +** Debug API +** ======================================================================= +*/ + + +/* +** Event codes +*/ +#define LUA_HOOKCALL 0 +#define LUA_HOOKRET 1 +#define LUA_HOOKLINE 2 +#define LUA_HOOKCOUNT 3 +#define LUA_HOOKTAILRET 4 + + +/* +** Event masks +*/ +#define LUA_MASKCALL (1 << LUA_HOOKCALL) +#define LUA_MASKRET (1 << LUA_HOOKRET) +#define LUA_MASKLINE (1 << LUA_HOOKLINE) +#define LUA_MASKCOUNT (1 << LUA_HOOKCOUNT) + +typedef struct lua_Debug lua_Debug; /* activation record */ + + +/* Functions to be called by the debuger in specific events */ +typedef void (*lua_Hook) (lua_State *L, lua_Debug *ar); + + +LUA_API int lua_getstack (lua_State *L, int level, lua_Debug *ar); +LUA_API int lua_getinfo (lua_State *L, const char *what, lua_Debug *ar); +LUA_API const char *lua_getlocal (lua_State *L, const lua_Debug *ar, int n); +LUA_API const char *lua_setlocal (lua_State *L, const lua_Debug *ar, int n); +LUA_API const char *lua_getupvalue (lua_State *L, int funcindex, int n); +LUA_API const char *lua_setupvalue (lua_State *L, int funcindex, int n); +LUA_API int lua_sethook (lua_State *L, lua_Hook func, int mask, int count); +LUA_API lua_Hook lua_gethook (lua_State *L); +LUA_API int lua_gethookmask (lua_State *L); +LUA_API int lua_gethookcount (lua_State *L); + +/* From Lua 5.2. */ +LUA_API void *lua_upvalueid (lua_State *L, int idx, int n); +LUA_API void lua_upvaluejoin (lua_State *L, int idx1, int n1, int idx2, int n2); +LUA_API int lua_loadx (lua_State *L, lua_Reader reader, void *dt, + const char *chunkname, const char *mode); +LUA_API const lua_Number *lua_version (lua_State *L); +LUA_API void lua_copy (lua_State *L, int fromidx, int toidx); +LUA_API lua_Number lua_tonumberx (lua_State *L, int idx, int *isnum); +LUA_API lua_Integer lua_tointegerx (lua_State *L, int idx, int *isnum); + +/* From Lua 5.3. */ +LUA_API int lua_isyieldable (lua_State *L); + + +struct lua_Debug { + int event; + const char *name; /* (n) */ + const char *namewhat; /* (n) `global', `local', `field', `method' */ + const char *what; /* (S) `Lua', `C', `main', `tail' */ + const char *source; /* (S) */ + int currentline; /* (l) */ + int nups; /* (u) number of upvalues */ + int linedefined; /* (S) */ + int lastlinedefined; /* (S) */ + char short_src[LUA_IDSIZE]; /* (S) */ + /* private part */ + int i_ci; /* active function */ +}; + +/* }====================================================================== */ + + +/****************************************************************************** +* Copyright (C) 1994-2008 Lua.org, PUC-Rio. All rights reserved. +* +* Permission is hereby granted, free of charge, to any person obtaining +* a copy of this software and associated documentation files (the +* "Software"), to deal in the Software without restriction, including +* without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to +* permit persons to whom the Software is furnished to do so, subject to +* the following conditions: +* +* The above copyright notice and this permission notice shall be +* included in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +******************************************************************************/ + + +#endif diff --git a/lib/LuaJIT/lua.hpp b/lib/LuaJIT/lua.hpp new file mode 100644 index 0000000..07e9002 --- /dev/null +++ b/lib/LuaJIT/lua.hpp @@ -0,0 +1,9 @@ +// C++ wrapper for LuaJIT header files. + +extern "C" { +#include "lua.h" +#include "lauxlib.h" +#include "lualib.h" +#include "luajit.h" +} + diff --git a/lib/LuaJIT/luaconf.h b/lib/LuaJIT/luaconf.h new file mode 100644 index 0000000..c2d29d9 --- /dev/null +++ b/lib/LuaJIT/luaconf.h @@ -0,0 +1,152 @@ +/* +** Configuration header. +** Copyright (C) 2005-2017 Mike Pall. See Copyright Notice in luajit.h +*/ + +#ifndef luaconf_h +#define luaconf_h + +#ifndef WINVER +#define WINVER 0x0501 +#endif +#include +#include + +/* Default path for loading Lua and C modules with require(). */ +#if defined(_WIN32) +/* +** In Windows, any exclamation mark ('!') in the path is replaced by the +** path of the directory of the executable file of the current process. +*/ +#define LUA_LDIR "!\\lua\\" +#define LUA_CDIR "!\\" +#define LUA_PATH_DEFAULT \ + ".\\?.lua;" LUA_LDIR"?.lua;" LUA_LDIR"?\\init.lua;" +#define LUA_CPATH_DEFAULT \ + ".\\?.dll;" LUA_CDIR"?.dll;" LUA_CDIR"loadall.dll" +#else +/* +** Note to distribution maintainers: do NOT patch the following lines! +** Please read ../doc/install.html#distro and pass PREFIX=/usr instead. +*/ +#ifndef LUA_MULTILIB +#define LUA_MULTILIB "lib" +#endif +#ifndef LUA_LMULTILIB +#define LUA_LMULTILIB "lib" +#endif +#define LUA_LROOT "/usr/local" +#define LUA_LUADIR "/lua/5.1/" +#define LUA_LJDIR "/luajit-2.1.0-beta3/" + +#ifdef LUA_ROOT +#define LUA_JROOT LUA_ROOT +#define LUA_RLDIR LUA_ROOT "/share" LUA_LUADIR +#define LUA_RCDIR LUA_ROOT "/" LUA_MULTILIB LUA_LUADIR +#define LUA_RLPATH ";" LUA_RLDIR "?.lua;" LUA_RLDIR "?/init.lua" +#define LUA_RCPATH ";" LUA_RCDIR "?.so" +#else +#define LUA_JROOT LUA_LROOT +#define LUA_RLPATH +#define LUA_RCPATH +#endif + +#define LUA_JPATH ";" LUA_JROOT "/share" LUA_LJDIR "?.lua" +#define LUA_LLDIR LUA_LROOT "/share" LUA_LUADIR +#define LUA_LCDIR LUA_LROOT "/" LUA_LMULTILIB LUA_LUADIR +#define LUA_LLPATH ";" LUA_LLDIR "?.lua;" LUA_LLDIR "?/init.lua" +#define LUA_LCPATH1 ";" LUA_LCDIR "?.so" +#define LUA_LCPATH2 ";" LUA_LCDIR "loadall.so" + +#define LUA_PATH_DEFAULT "./?.lua" LUA_JPATH LUA_LLPATH LUA_RLPATH +#define LUA_CPATH_DEFAULT "./?.so" LUA_LCPATH1 LUA_RCPATH LUA_LCPATH2 +#endif + +/* Environment variable names for path overrides and initialization code. */ +#define LUA_PATH "LUA_PATH" +#define LUA_CPATH "LUA_CPATH" +#define LUA_INIT "LUA_INIT" + +/* Special file system characters. */ +#if defined(_WIN32) +#define LUA_DIRSEP "\\" +#else +#define LUA_DIRSEP "/" +#endif +#define LUA_PATHSEP ";" +#define LUA_PATH_MARK "?" +#define LUA_EXECDIR "!" +#define LUA_IGMARK "-" +#define LUA_PATH_CONFIG \ + LUA_DIRSEP "\n" LUA_PATHSEP "\n" LUA_PATH_MARK "\n" \ + LUA_EXECDIR "\n" LUA_IGMARK "\n" + +/* Quoting in error messages. */ +#define LUA_QL(x) "'" x "'" +#define LUA_QS LUA_QL("%s") + +/* Various tunables. */ +#define LUAI_MAXSTACK 65500 /* Max. # of stack slots for a thread (<64K). */ +#define LUAI_MAXCSTACK 8000 /* Max. # of stack slots for a C func (<10K). */ +#define LUAI_GCPAUSE 200 /* Pause GC until memory is at 200%. */ +#define LUAI_GCMUL 200 /* Run GC at 200% of allocation speed. */ +#define LUA_MAXCAPTURES 32 /* Max. pattern captures. */ + +/* Configuration for the frontend (the luajit executable). */ +#if defined(luajit_c) +#define LUA_PROGNAME "luajit" /* Fallback frontend name. */ +#define LUA_PROMPT "> " /* Interactive prompt. */ +#define LUA_PROMPT2 ">> " /* Continuation prompt. */ +#define LUA_MAXINPUT 512 /* Max. input line length. */ +#endif + +/* Note: changing the following defines breaks the Lua 5.1 ABI. */ +#define LUA_INTEGER ptrdiff_t +#define LUA_IDSIZE 60 /* Size of lua_Debug.short_src. */ +/* +** Size of lauxlib and io.* on-stack buffers. Weird workaround to avoid using +** unreasonable amounts of stack space, but still retain ABI compatibility. +** Blame Lua for depending on BUFSIZ in the ABI, blame **** for wrecking it. +*/ +#define LUAL_BUFFERSIZE (BUFSIZ > 16384 ? 8192 : BUFSIZ) + +/* The following defines are here only for compatibility with luaconf.h +** from the standard Lua distribution. They must not be changed for LuaJIT. +*/ +#define LUA_NUMBER_DOUBLE +#define LUA_NUMBER double +#define LUAI_UACNUMBER double +#define LUA_NUMBER_SCAN "%lf" +#define LUA_NUMBER_FMT "%.14g" +#define lua_number2str(s, n) sprintf((s), LUA_NUMBER_FMT, (n)) +#define LUAI_MAXNUMBER2STR 32 +#define LUA_INTFRMLEN "l" +#define LUA_INTFRM_T long + +/* Linkage of public API functions. */ +#if defined(LUA_BUILD_AS_DLL) +#if defined(LUA_CORE) || defined(LUA_LIB) +#define LUA_API __declspec(dllexport) +#else +#define LUA_API __declspec(dllimport) +#endif +#else +#define LUA_API extern +#endif + +#define LUALIB_API LUA_API + +/* Support for internal assertions. */ +#if defined(LUA_USE_ASSERT) || defined(LUA_USE_APICHECK) +#include +#endif +#ifdef LUA_USE_ASSERT +#define lua_assert(x) assert(x) +#endif +#ifdef LUA_USE_APICHECK +#define luai_apicheck(L, o) { (void)L; assert(o); } +#else +#define luai_apicheck(L, o) { (void)L; } +#endif + +#endif diff --git a/lib/LuaJIT/luajit.c b/lib/LuaJIT/luajit.c new file mode 100644 index 0000000..86134ef --- /dev/null +++ b/lib/LuaJIT/luajit.c @@ -0,0 +1,587 @@ +/* +** LuaJIT frontend. Runs commands, scripts, read-eval-print (REPL) etc. +** Copyright (C) 2005-2017 Mike Pall. See Copyright Notice in luajit.h +** +** Major portions taken verbatim or adapted from the Lua interpreter. +** Copyright (C) 1994-2008 Lua.org, PUC-Rio. See Copyright Notice in lua.h +*/ + +#include +#include +#include + +#define luajit_c + +#include "lua.h" +#include "lauxlib.h" +#include "lualib.h" +#include "luajit.h" + +#include "lj_arch.h" + +#if LJ_TARGET_POSIX +#include +#define lua_stdin_is_tty() isatty(0) +#elif LJ_TARGET_WINDOWS +#include +#ifdef __BORLANDC__ +#define lua_stdin_is_tty() isatty(_fileno(stdin)) +#else +#define lua_stdin_is_tty() _isatty(_fileno(stdin)) +#endif +#else +#define lua_stdin_is_tty() 1 +#endif + +#if !LJ_TARGET_CONSOLE +#include +#endif + +static lua_State *globalL = NULL; +static const char *progname = LUA_PROGNAME; + +#if !LJ_TARGET_CONSOLE +static void lstop(lua_State *L, lua_Debug *ar) +{ + (void)ar; /* unused arg. */ + lua_sethook(L, NULL, 0, 0); + /* Avoid luaL_error -- a C hook doesn't add an extra frame. */ + luaL_where(L, 0); + lua_pushfstring(L, "%sinterrupted!", lua_tostring(L, -1)); + lua_error(L); +} + +static void laction(int i) +{ + signal(i, SIG_DFL); /* if another SIGINT happens before lstop, + terminate process (default action) */ + lua_sethook(globalL, lstop, LUA_MASKCALL | LUA_MASKRET | LUA_MASKCOUNT, 1); +} +#endif + +static void print_usage(void) +{ + fputs("usage: ", stderr); + fputs(progname, stderr); + fputs(" [options]... [script [args]...].\n" + "Available options are:\n" + " -e chunk Execute string " LUA_QL("chunk") ".\n" + " -l name Require library " LUA_QL("name") ".\n" + " -b ... Save or list bytecode.\n" + " -j cmd Perform LuaJIT control command.\n" + " -O[opt] Control LuaJIT optimizations.\n" + " -i Enter interactive mode after executing " LUA_QL("script") ".\n" + " -v Show version information.\n" + " -E Ignore environment variables.\n" + " -- Stop handling options.\n" + " - Execute stdin and stop handling options.\n", stderr); + fflush(stderr); +} + +static void l_message(const char *pname, const char *msg) +{ + if (pname) { fputs(pname, stderr); fputc(':', stderr); fputc(' ', stderr); } + fputs(msg, stderr); fputc('\n', stderr); + fflush(stderr); +} + +static int report(lua_State *L, int status) +{ + if (status && !lua_isnil(L, -1)) { + const char *msg = lua_tostring(L, -1); + if (msg == NULL) msg = "(error object is not a string)"; + l_message(progname, msg); + lua_pop(L, 1); + } + return status; +} + +static int traceback(lua_State *L) +{ + if (!lua_isstring(L, 1)) { /* Non-string error object? Try metamethod. */ + if (lua_isnoneornil(L, 1) || + !luaL_callmeta(L, 1, "__tostring") || + !lua_isstring(L, -1)) + return 1; /* Return non-string error object. */ + lua_remove(L, 1); /* Replace object by result of __tostring metamethod. */ + } + luaL_traceback(L, L, lua_tostring(L, 1), 1); + return 1; +} + +static int docall(lua_State *L, int narg, int clear) +{ + int status; + int base = lua_gettop(L) - narg; /* function index */ + lua_pushcfunction(L, traceback); /* push traceback function */ + lua_insert(L, base); /* put it under chunk and args */ +#if !LJ_TARGET_CONSOLE + signal(SIGINT, laction); +#endif + status = lua_pcall(L, narg, (clear ? 0 : LUA_MULTRET), base); +#if !LJ_TARGET_CONSOLE + signal(SIGINT, SIG_DFL); +#endif + lua_remove(L, base); /* remove traceback function */ + /* force a complete garbage collection in case of errors */ + if (status != LUA_OK) lua_gc(L, LUA_GCCOLLECT, 0); + return status; +} + +static void print_version(void) +{ + fputs(LUAJIT_VERSION " -- " LUAJIT_COPYRIGHT ". " LUAJIT_URL "\n", stdout); +} + +static void print_jit_status(lua_State *L) +{ + int n; + const char *s; + lua_getfield(L, LUA_REGISTRYINDEX, "_LOADED"); + lua_getfield(L, -1, "jit"); /* Get jit.* module table. */ + lua_remove(L, -2); + lua_getfield(L, -1, "status"); + lua_remove(L, -2); + n = lua_gettop(L); + lua_call(L, 0, LUA_MULTRET); + fputs(lua_toboolean(L, n) ? "JIT: ON" : "JIT: OFF", stdout); + for (n++; (s = lua_tostring(L, n)); n++) { + putc(' ', stdout); + fputs(s, stdout); + } + putc('\n', stdout); + lua_settop(L, 0); /* clear stack */ +} + +static void createargtable(lua_State *L, char **argv, int argc, int argf) +{ + int i; + lua_createtable(L, argc - argf, argf); + for (i = 0; i < argc; i++) { + lua_pushstring(L, argv[i]); + lua_rawseti(L, -2, i - argf); + } + lua_setglobal(L, "arg"); +} + +static int dofile(lua_State *L, const char *name) +{ + int status = luaL_loadfile(L, name) || docall(L, 0, 1); + return report(L, status); +} + +static int dostring(lua_State *L, const char *s, const char *name) +{ + int status = luaL_loadbuffer(L, s, strlen(s), name) || docall(L, 0, 1); + return report(L, status); +} + +static int dolibrary(lua_State *L, const char *name) +{ + lua_getglobal(L, "require"); + lua_pushstring(L, name); + return report(L, docall(L, 1, 1)); +} + +static void write_prompt(lua_State *L, int firstline) +{ + const char *p; + lua_getfield(L, LUA_GLOBALSINDEX, firstline ? "_PROMPT" : "_PROMPT2"); + p = lua_tostring(L, -1); + if (p == NULL) p = firstline ? LUA_PROMPT : LUA_PROMPT2; + fputs(p, stdout); + fflush(stdout); + lua_pop(L, 1); /* remove global */ +} + +static int incomplete(lua_State *L, int status) +{ + if (status == LUA_ERRSYNTAX) { + size_t lmsg; + const char *msg = lua_tolstring(L, -1, &lmsg); + const char *tp = msg + lmsg - (sizeof(LUA_QL("")) - 1); + if (strstr(msg, LUA_QL("")) == tp) { + lua_pop(L, 1); + return 1; + } + } + return 0; /* else... */ +} + +static int pushline(lua_State *L, int firstline) +{ + char buf[LUA_MAXINPUT]; + write_prompt(L, firstline); + if (fgets(buf, LUA_MAXINPUT, stdin)) { + size_t len = strlen(buf); + if (len > 0 && buf[len-1] == '\n') + buf[len-1] = '\0'; + if (firstline && buf[0] == '=') + lua_pushfstring(L, "return %s", buf+1); + else + lua_pushstring(L, buf); + return 1; + } + return 0; +} + +static int loadline(lua_State *L) +{ + int status; + lua_settop(L, 0); + if (!pushline(L, 1)) + return -1; /* no input */ + for (;;) { /* repeat until gets a complete line */ + status = luaL_loadbuffer(L, lua_tostring(L, 1), lua_strlen(L, 1), "=stdin"); + if (!incomplete(L, status)) break; /* cannot try to add lines? */ + if (!pushline(L, 0)) /* no more input? */ + return -1; + lua_pushliteral(L, "\n"); /* add a new line... */ + lua_insert(L, -2); /* ...between the two lines */ + lua_concat(L, 3); /* join them */ + } + lua_remove(L, 1); /* remove line */ + return status; +} + +static void dotty(lua_State *L) +{ + int status; + const char *oldprogname = progname; + progname = NULL; + while ((status = loadline(L)) != -1) { + if (status == LUA_OK) status = docall(L, 0, 0); + report(L, status); + if (status == LUA_OK && lua_gettop(L) > 0) { /* any result to print? */ + lua_getglobal(L, "print"); + lua_insert(L, 1); + if (lua_pcall(L, lua_gettop(L)-1, 0, 0) != 0) + l_message(progname, + lua_pushfstring(L, "error calling " LUA_QL("print") " (%s)", + lua_tostring(L, -1))); + } + } + lua_settop(L, 0); /* clear stack */ + fputs("\n", stdout); + fflush(stdout); + progname = oldprogname; +} + +static int handle_script(lua_State *L, char **argx) +{ + int status; + const char *fname = argx[0]; + if (strcmp(fname, "-") == 0 && strcmp(argx[-1], "--") != 0) + fname = NULL; /* stdin */ + status = luaL_loadfile(L, fname); + if (status == LUA_OK) { + /* Fetch args from arg table. LUA_INIT or -e might have changed them. */ + int narg = 0; + lua_getglobal(L, "arg"); + if (lua_istable(L, -1)) { + do { + narg++; + lua_rawgeti(L, -narg, narg); + } while (!lua_isnil(L, -1)); + lua_pop(L, 1); + lua_remove(L, -narg); + narg--; + } else { + lua_pop(L, 1); + } + status = docall(L, narg, 0); + } + return report(L, status); +} + +/* Load add-on module. */ +static int loadjitmodule(lua_State *L) +{ + lua_getglobal(L, "require"); + lua_pushliteral(L, "jit."); + lua_pushvalue(L, -3); + lua_concat(L, 2); + if (lua_pcall(L, 1, 1, 0)) { + const char *msg = lua_tostring(L, -1); + if (msg && !strncmp(msg, "module ", 7)) + goto nomodule; + return report(L, 1); + } + lua_getfield(L, -1, "start"); + if (lua_isnil(L, -1)) { + nomodule: + l_message(progname, + "unknown luaJIT command or jit.* modules not installed"); + return 1; + } + lua_remove(L, -2); /* Drop module table. */ + return 0; +} + +/* Run command with options. */ +static int runcmdopt(lua_State *L, const char *opt) +{ + int narg = 0; + if (opt && *opt) { + for (;;) { /* Split arguments. */ + const char *p = strchr(opt, ','); + narg++; + if (!p) break; + if (p == opt) + lua_pushnil(L); + else + lua_pushlstring(L, opt, (size_t)(p - opt)); + opt = p + 1; + } + if (*opt) + lua_pushstring(L, opt); + else + lua_pushnil(L); + } + return report(L, lua_pcall(L, narg, 0, 0)); +} + +/* JIT engine control command: try jit library first or load add-on module. */ +static int dojitcmd(lua_State *L, const char *cmd) +{ + const char *opt = strchr(cmd, '='); + lua_pushlstring(L, cmd, opt ? (size_t)(opt - cmd) : strlen(cmd)); + lua_getfield(L, LUA_REGISTRYINDEX, "_LOADED"); + lua_getfield(L, -1, "jit"); /* Get jit.* module table. */ + lua_remove(L, -2); + lua_pushvalue(L, -2); + lua_gettable(L, -2); /* Lookup library function. */ + if (!lua_isfunction(L, -1)) { + lua_pop(L, 2); /* Drop non-function and jit.* table, keep module name. */ + if (loadjitmodule(L)) + return 1; + } else { + lua_remove(L, -2); /* Drop jit.* table. */ + } + lua_remove(L, -2); /* Drop module name. */ + return runcmdopt(L, opt ? opt+1 : opt); +} + +/* Optimization flags. */ +static int dojitopt(lua_State *L, const char *opt) +{ + lua_getfield(L, LUA_REGISTRYINDEX, "_LOADED"); + lua_getfield(L, -1, "jit.opt"); /* Get jit.opt.* module table. */ + lua_remove(L, -2); + lua_getfield(L, -1, "start"); + lua_remove(L, -2); + return runcmdopt(L, opt); +} + +/* Save or list bytecode. */ +static int dobytecode(lua_State *L, char **argv) +{ + int narg = 0; + lua_pushliteral(L, "bcsave"); + if (loadjitmodule(L)) + return 1; + if (argv[0][2]) { + narg++; + argv[0][1] = '-'; + lua_pushstring(L, argv[0]+1); + } + for (argv++; *argv != NULL; narg++, argv++) + lua_pushstring(L, *argv); + report(L, lua_pcall(L, narg, 0, 0)); + return -1; +} + +/* check that argument has no extra characters at the end */ +#define notail(x) {if ((x)[2] != '\0') return -1;} + +#define FLAGS_INTERACTIVE 1 +#define FLAGS_VERSION 2 +#define FLAGS_EXEC 4 +#define FLAGS_OPTION 8 +#define FLAGS_NOENV 16 + +static int collectargs(char **argv, int *flags) +{ + int i; + for (i = 1; argv[i] != NULL; i++) { + if (argv[i][0] != '-') /* Not an option? */ + return i; + switch (argv[i][1]) { /* Check option. */ + case '-': + notail(argv[i]); + return i+1; + case '\0': + return i; + case 'i': + notail(argv[i]); + *flags |= FLAGS_INTERACTIVE; + /* fallthrough */ + case 'v': + notail(argv[i]); + *flags |= FLAGS_VERSION; + break; + case 'e': + *flags |= FLAGS_EXEC; + /* fallthrough */ + case 'j': /* LuaJIT extension */ + case 'l': + *flags |= FLAGS_OPTION; + if (argv[i][2] == '\0') { + i++; + if (argv[i] == NULL) return -1; + } + break; + case 'O': break; /* LuaJIT extension */ + case 'b': /* LuaJIT extension */ + if (*flags) return -1; + *flags |= FLAGS_EXEC; + return i+1; + case 'E': + *flags |= FLAGS_NOENV; + break; + default: return -1; /* invalid option */ + } + } + return i; +} + +static int runargs(lua_State *L, char **argv, int argn) +{ + int i; + for (i = 1; i < argn; i++) { + if (argv[i] == NULL) continue; + lua_assert(argv[i][0] == '-'); + switch (argv[i][1]) { + case 'e': { + const char *chunk = argv[i] + 2; + if (*chunk == '\0') chunk = argv[++i]; + lua_assert(chunk != NULL); + if (dostring(L, chunk, "=(command line)") != 0) + return 1; + break; + } + case 'l': { + const char *filename = argv[i] + 2; + if (*filename == '\0') filename = argv[++i]; + lua_assert(filename != NULL); + if (dolibrary(L, filename)) + return 1; + break; + } + case 'j': { /* LuaJIT extension. */ + const char *cmd = argv[i] + 2; + if (*cmd == '\0') cmd = argv[++i]; + lua_assert(cmd != NULL); + if (dojitcmd(L, cmd)) + return 1; + break; + } + case 'O': /* LuaJIT extension. */ + if (dojitopt(L, argv[i] + 2)) + return 1; + break; + case 'b': /* LuaJIT extension. */ + return dobytecode(L, argv+i); + default: break; + } + } + return LUA_OK; +} + +static int handle_luainit(lua_State *L) +{ +#if LJ_TARGET_CONSOLE + const char *init = NULL; +#else + const char *init = getenv(LUA_INIT); +#endif + if (init == NULL) + return LUA_OK; + else if (init[0] == '@') + return dofile(L, init+1); + else + return dostring(L, init, "=" LUA_INIT); +} + +static struct Smain { + char **argv; + int argc; + int status; +} smain; + +static int pmain(lua_State *L) +{ + struct Smain *s = &smain; + char **argv = s->argv; + int argn; + int flags = 0; + globalL = L; + if (argv[0] && argv[0][0]) progname = argv[0]; + + LUAJIT_VERSION_SYM(); /* Linker-enforced version check. */ + + argn = collectargs(argv, &flags); + if (argn < 0) { /* Invalid args? */ + print_usage(); + s->status = 1; + return 0; + } + + if ((flags & FLAGS_NOENV)) { + lua_pushboolean(L, 1); + lua_setfield(L, LUA_REGISTRYINDEX, "LUA_NOENV"); + } + + /* Stop collector during library initialization. */ + lua_gc(L, LUA_GCSTOP, 0); + luaL_openlibs(L); + lua_gc(L, LUA_GCRESTART, -1); + + createargtable(L, argv, s->argc, argn); + + if (!(flags & FLAGS_NOENV)) { + s->status = handle_luainit(L); + if (s->status != LUA_OK) return 0; + } + + if ((flags & FLAGS_VERSION)) print_version(); + + s->status = runargs(L, argv, argn); + if (s->status != LUA_OK) return 0; + + if (s->argc > argn) { + s->status = handle_script(L, argv + argn); + if (s->status != LUA_OK) return 0; + } + + if ((flags & FLAGS_INTERACTIVE)) { + print_jit_status(L); + dotty(L); + } else if (s->argc == argn && !(flags & (FLAGS_EXEC|FLAGS_VERSION))) { + if (lua_stdin_is_tty()) { + print_version(); + print_jit_status(L); + dotty(L); + } else { + dofile(L, NULL); /* Executes stdin as a file. */ + } + } + return 0; +} + +int main(int argc, char **argv) +{ + int status; + lua_State *L = lua_open(); + if (L == NULL) { + l_message(argv[0], "cannot create state: not enough memory"); + return EXIT_FAILURE; + } + smain.argc = argc; + smain.argv = argv; + status = lua_cpcall(L, pmain, NULL); + report(L, status); + lua_close(L); + return (status || smain.status > 0) ? EXIT_FAILURE : EXIT_SUCCESS; +} + diff --git a/lib/LuaJIT/luajit.h b/lib/LuaJIT/luajit.h new file mode 100644 index 0000000..708a5a1 --- /dev/null +++ b/lib/LuaJIT/luajit.h @@ -0,0 +1,79 @@ +/* +** LuaJIT -- a Just-In-Time Compiler for Lua. http://luajit.org/ +** +** Copyright (C) 2005-2017 Mike Pall. All rights reserved. +** +** Permission is hereby granted, free of charge, to any person obtaining +** a copy of this software and associated documentation files (the +** "Software"), to deal in the Software without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Software, and to +** permit persons to whom the Software is furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be +** included in all copies or substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +** SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +** +** [ MIT license: http://www.opensource.org/licenses/mit-license.php ] +*/ + +#ifndef _LUAJIT_H +#define _LUAJIT_H + +#include "lua.h" + +#define LUAJIT_VERSION "LuaJIT 2.1.0-beta3" +#define LUAJIT_VERSION_NUM 20100 /* Version 2.1.0 = 02.01.00. */ +#define LUAJIT_VERSION_SYM luaJIT_version_2_1_0_beta3 +#define LUAJIT_COPYRIGHT "Copyright (C) 2005-2017 Mike Pall" +#define LUAJIT_URL "http://luajit.org/" + +/* Modes for luaJIT_setmode. */ +#define LUAJIT_MODE_MASK 0x00ff + +enum { + LUAJIT_MODE_ENGINE, /* Set mode for whole JIT engine. */ + LUAJIT_MODE_DEBUG, /* Set debug mode (idx = level). */ + + LUAJIT_MODE_FUNC, /* Change mode for a function. */ + LUAJIT_MODE_ALLFUNC, /* Recurse into subroutine protos. */ + LUAJIT_MODE_ALLSUBFUNC, /* Change only the subroutines. */ + + LUAJIT_MODE_TRACE, /* Flush a compiled trace. */ + + LUAJIT_MODE_WRAPCFUNC = 0x10, /* Set wrapper mode for C function calls. */ + + LUAJIT_MODE_MAX +}; + +/* Flags or'ed in to the mode. */ +#define LUAJIT_MODE_OFF 0x0000 /* Turn feature off. */ +#define LUAJIT_MODE_ON 0x0100 /* Turn feature on. */ +#define LUAJIT_MODE_FLUSH 0x0200 /* Flush JIT-compiled code. */ + +/* LuaJIT public C API. */ + +/* Control the JIT engine. */ +LUA_API int luaJIT_setmode(lua_State *L, int idx, int mode); + +/* Low-overhead profiling API. */ +typedef void (*luaJIT_profile_callback)(void *data, lua_State *L, + int samples, int vmstate); +LUA_API void luaJIT_profile_start(lua_State *L, const char *mode, + luaJIT_profile_callback cb, void *data); +LUA_API void luaJIT_profile_stop(lua_State *L); +LUA_API const char *luaJIT_profile_dumpstack(lua_State *L, const char *fmt, + int depth, size_t *len); + +/* Enforce (dynamic) linker error for version mismatches. Call from main. */ +LUA_API void LUAJIT_VERSION_SYM(void); + +#endif diff --git a/lib/LuaJIT/luajit.o b/lib/LuaJIT/luajit.o new file mode 100644 index 0000000000000000000000000000000000000000..fa856a263a6b7141fcbb8c49dd3459b1e733e424 GIT binary patch literal 20448 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 Fe*wKyyqEw0 literal 0 HcmV?d00001 diff --git a/lib/LuaJIT/lualib.h b/lib/LuaJIT/lualib.h new file mode 100644 index 0000000..bfc130a --- /dev/null +++ b/lib/LuaJIT/lualib.h @@ -0,0 +1,43 @@ +/* +** Standard library header. +** Copyright (C) 2005-2017 Mike Pall. See Copyright Notice in luajit.h +*/ + +#ifndef _LUALIB_H +#define _LUALIB_H + +#include "lua.h" + +#define LUA_FILEHANDLE "FILE*" + +#define LUA_COLIBNAME "coroutine" +#define LUA_MATHLIBNAME "math" +#define LUA_STRLIBNAME "string" +#define LUA_TABLIBNAME "table" +#define LUA_IOLIBNAME "io" +#define LUA_OSLIBNAME "os" +#define LUA_LOADLIBNAME "package" +#define LUA_DBLIBNAME "debug" +#define LUA_BITLIBNAME "bit" +#define LUA_JITLIBNAME "jit" +#define LUA_FFILIBNAME "ffi" + +LUALIB_API int luaopen_base(lua_State *L); +LUALIB_API int luaopen_math(lua_State *L); +LUALIB_API int luaopen_string(lua_State *L); +LUALIB_API int luaopen_table(lua_State *L); +LUALIB_API int luaopen_io(lua_State *L); +LUALIB_API int luaopen_os(lua_State *L); +LUALIB_API int luaopen_package(lua_State *L); +LUALIB_API int luaopen_debug(lua_State *L); +LUALIB_API int luaopen_bit(lua_State *L); +LUALIB_API int luaopen_jit(lua_State *L); +LUALIB_API int luaopen_ffi(lua_State *L); + +LUALIB_API void luaL_openlibs(lua_State *L); + +#ifndef lua_assert +#define lua_assert(x) ((void)0) +#endif + +#endif diff --git a/lib/LuaJIT/msvcbuild.bat b/lib/LuaJIT/msvcbuild.bat new file mode 100644 index 0000000..71bde75 --- /dev/null +++ b/lib/LuaJIT/msvcbuild.bat @@ -0,0 +1,122 @@ +@rem Script to build LuaJIT with MSVC. +@rem Copyright (C) 2005-2017 Mike Pall. See Copyright Notice in luajit.h +@rem +@rem Either open a "Visual Studio .NET Command Prompt" +@rem (Note that the Express Edition does not contain an x64 compiler) +@rem -or- +@rem Open a "Windows SDK Command Shell" and set the compiler environment: +@rem setenv /release /x86 +@rem -or- +@rem setenv /release /x64 +@rem +@rem Then cd to this directory and run this script. + +@if not defined INCLUDE goto :FAIL + +@setlocal +@set LJCOMPILE=cl /nologo /c /O2 /W3 /D_CRT_SECURE_NO_DEPRECATE /D_CRT_STDIO_INLINE=__declspec(dllexport)__inline +@set LJLINK=link /nologo +@set LJMT=mt /nologo +@set LJLIB=lib /nologo /nodefaultlib +@set DASMDIR=..\dynasm +@set DASM=%DASMDIR%\dynasm.lua +@set DASC=vm_x86.dasc +@set LJDLLNAME=lua51.dll +@set LJLIBNAME=lua51.lib +@set ALL_LIB=lib_base.c lib_math.c lib_bit.c lib_string.c lib_table.c lib_io.c lib_os.c lib_package.c lib_debug.c lib_jit.c lib_ffi.c + +%LJCOMPILE% host\minilua.c +@if errorlevel 1 goto :BAD +%LJLINK% /out:minilua.exe minilua.obj +@if errorlevel 1 goto :BAD +if exist minilua.exe.manifest^ + %LJMT% -manifest minilua.exe.manifest -outputresource:minilua.exe + +@set DASMFLAGS=-D WIN -D JIT -D FFI -D P64 +@set LJARCH=x64 +@minilua +@if errorlevel 8 goto :X64 +@set DASMFLAGS=-D WIN -D JIT -D FFI +@set LJARCH=x86 +@set LJCOMPILE=%LJCOMPILE% /arch:SSE2 +:X64 +@if "%1" neq "gc64" goto :NOGC64 +@shift +@set DASC=vm_x64.dasc +@set LJCOMPILE=%LJCOMPILE% /DLUAJIT_ENABLE_GC64 +:NOGC64 +minilua %DASM% -LN %DASMFLAGS% -o host\buildvm_arch.h %DASC% +@if errorlevel 1 goto :BAD + +%LJCOMPILE% /I "." /I %DASMDIR% host\buildvm*.c +@if errorlevel 1 goto :BAD +%LJLINK% /out:buildvm.exe buildvm*.obj +@if errorlevel 1 goto :BAD +if exist buildvm.exe.manifest^ + %LJMT% -manifest buildvm.exe.manifest -outputresource:buildvm.exe + +buildvm -m peobj -o lj_vm.obj +@if errorlevel 1 goto :BAD +buildvm -m bcdef -o lj_bcdef.h %ALL_LIB% +@if errorlevel 1 goto :BAD +buildvm -m ffdef -o lj_ffdef.h %ALL_LIB% +@if errorlevel 1 goto :BAD +buildvm -m libdef -o lj_libdef.h %ALL_LIB% +@if errorlevel 1 goto :BAD +buildvm -m recdef -o lj_recdef.h %ALL_LIB% +@if errorlevel 1 goto :BAD +buildvm -m vmdef -o jit\vmdef.lua %ALL_LIB% +@if errorlevel 1 goto :BAD +buildvm -m folddef -o lj_folddef.h lj_opt_fold.c +@if errorlevel 1 goto :BAD + +@if "%1" neq "debug" goto :NODEBUG +@shift +@set LJCOMPILE=%LJCOMPILE% /Zi +@set LJLINK=%LJLINK% /debug /opt:ref /opt:icf /incremental:no +:NODEBUG +@if "%1"=="amalg" goto :AMALGDLL +@if "%1"=="static" goto :STATIC +%LJCOMPILE% /MD /DLUA_BUILD_AS_DLL lj_*.c lib_*.c +@if errorlevel 1 goto :BAD +%LJLINK% /DLL /out:%LJDLLNAME% lj_*.obj lib_*.obj +@if errorlevel 1 goto :BAD +@goto :MTDLL +:STATIC +%LJCOMPILE% lj_*.c lib_*.c +@if errorlevel 1 goto :BAD +%LJLIB% /OUT:%LJLIBNAME% lj_*.obj lib_*.obj +@if errorlevel 1 goto :BAD +@goto :MTDLL +:AMALGDLL +%LJCOMPILE% /MD /DLUA_BUILD_AS_DLL ljamalg.c +@if errorlevel 1 goto :BAD +%LJLINK% /DLL /out:%LJDLLNAME% ljamalg.obj lj_vm.obj +@if errorlevel 1 goto :BAD +:MTDLL +if exist %LJDLLNAME%.manifest^ + %LJMT% -manifest %LJDLLNAME%.manifest -outputresource:%LJDLLNAME%;2 + +%LJCOMPILE% luajit.c +@if errorlevel 1 goto :BAD +%LJLINK% /out:luajit.exe luajit.obj %LJLIBNAME% +@if errorlevel 1 goto :BAD +if exist luajit.exe.manifest^ + %LJMT% -manifest luajit.exe.manifest -outputresource:luajit.exe + +@del *.obj *.manifest minilua.exe buildvm.exe +@del host\buildvm_arch.h +@del lj_bcdef.h lj_ffdef.h lj_libdef.h lj_recdef.h lj_folddef.h +@echo. +@echo === Successfully built LuaJIT for Windows/%LJARCH% === + +@goto :END +:BAD +@echo. +@echo ******************************************************* +@echo *** Build FAILED -- Please check the error messages *** +@echo ******************************************************* +@goto :END +:FAIL +@echo You must open a "Visual Studio .NET Command Prompt" to run this script +:END diff --git a/lib/LuaJIT/ps4build.bat b/lib/LuaJIT/ps4build.bat new file mode 100644 index 0000000..e4a7def --- /dev/null +++ b/lib/LuaJIT/ps4build.bat @@ -0,0 +1,123 @@ +@rem Script to build LuaJIT with the PS4 SDK. +@rem Donated to the public domain. +@rem +@rem Open a "Visual Studio .NET Command Prompt" (64 bit host compiler) +@rem or "VS2015 x64 Native Tools Command Prompt". +@rem +@rem Then cd to this directory and run this script. +@rem +@rem Recommended invocation: +@rem +@rem ps4build release build, amalgamated, 64-bit GC +@rem ps4build debug debug build, amalgamated, 64-bit GC +@rem +@rem Additional command-line options (not generally recommended): +@rem +@rem gc32 (before debug) 32-bit GC +@rem noamalg (after debug) non-amalgamated build + +@if not defined INCLUDE goto :FAIL +@if not defined SCE_ORBIS_SDK_DIR goto :FAIL + +@setlocal +@rem ---- Host compiler ---- +@set LJCOMPILE=cl /nologo /c /MD /O2 /W3 /D_CRT_SECURE_NO_DEPRECATE +@set LJLINK=link /nologo +@set LJMT=mt /nologo +@set DASMDIR=..\dynasm +@set DASM=%DASMDIR%\dynasm.lua +@set ALL_LIB=lib_base.c lib_math.c lib_bit.c lib_string.c lib_table.c lib_io.c lib_os.c lib_package.c lib_debug.c lib_jit.c lib_ffi.c +@set GC64=-DLUAJIT_ENABLE_GC64 +@set DASC=vm_x64.dasc + +@if "%1" neq "gc32" goto :NOGC32 +@shift +@set GC64= +@set DASC=vm_x86.dasc +:NOGC32 + +%LJCOMPILE% host\minilua.c +@if errorlevel 1 goto :BAD +%LJLINK% /out:minilua.exe minilua.obj +@if errorlevel 1 goto :BAD +if exist minilua.exe.manifest^ + %LJMT% -manifest minilua.exe.manifest -outputresource:minilua.exe + +@rem Check for 64 bit host compiler. +@minilua +@if not errorlevel 8 goto :FAIL + +@set DASMFLAGS=-D P64 -D NO_UNWIND +minilua %DASM% -LN %DASMFLAGS% -o host\buildvm_arch.h %DASC% +@if errorlevel 1 goto :BAD + +%LJCOMPILE% /I "." /I %DASMDIR% %GC64% -DLUAJIT_TARGET=LUAJIT_ARCH_X64 -DLUAJIT_OS=LUAJIT_OS_OTHER -DLUAJIT_DISABLE_JIT -DLUAJIT_DISABLE_FFI -DLUAJIT_NO_UNWIND host\buildvm*.c +@if errorlevel 1 goto :BAD +%LJLINK% /out:buildvm.exe buildvm*.obj +@if errorlevel 1 goto :BAD +if exist buildvm.exe.manifest^ + %LJMT% -manifest buildvm.exe.manifest -outputresource:buildvm.exe + +buildvm -m elfasm -o lj_vm.s +@if errorlevel 1 goto :BAD +buildvm -m bcdef -o lj_bcdef.h %ALL_LIB% +@if errorlevel 1 goto :BAD +buildvm -m ffdef -o lj_ffdef.h %ALL_LIB% +@if errorlevel 1 goto :BAD +buildvm -m libdef -o lj_libdef.h %ALL_LIB% +@if errorlevel 1 goto :BAD +buildvm -m recdef -o lj_recdef.h %ALL_LIB% +@if errorlevel 1 goto :BAD +buildvm -m vmdef -o jit\vmdef.lua %ALL_LIB% +@if errorlevel 1 goto :BAD +buildvm -m folddef -o lj_folddef.h lj_opt_fold.c +@if errorlevel 1 goto :BAD + +@rem ---- Cross compiler ---- +@set LJCOMPILE="%SCE_ORBIS_SDK_DIR%\host_tools\bin\orbis-clang" -c -Wall -DLUAJIT_DISABLE_FFI %GC64% +@set LJLIB="%SCE_ORBIS_SDK_DIR%\host_tools\bin\orbis-ar" rcus +@set INCLUDE="" + +orbis-as -o lj_vm.o lj_vm.s + +@if "%1" neq "debug" goto :NODEBUG +@shift +@set LJCOMPILE=%LJCOMPILE% -g -O0 +@set TARGETLIB=libluajitD_ps4.a +goto :BUILD +:NODEBUG +@set LJCOMPILE=%LJCOMPILE% -O2 +@set TARGETLIB=libluajit_ps4.a +:BUILD +del %TARGETLIB% +@if "%1" neq "noamalg" goto :AMALG +for %%f in (lj_*.c lib_*.c) do ( + %LJCOMPILE% %%f + @if errorlevel 1 goto :BAD +) + +%LJLIB% %TARGETLIB% lj_*.o lib_*.o +@if errorlevel 1 goto :BAD +@goto :NOAMALG +:AMALG +%LJCOMPILE% ljamalg.c +@if errorlevel 1 goto :BAD +%LJLIB% %TARGETLIB% ljamalg.o lj_vm.o +@if errorlevel 1 goto :BAD +:NOAMALG + +@del *.o *.obj *.manifest minilua.exe buildvm.exe +@echo. +@echo === Successfully built LuaJIT for PS4 === + +@goto :END +:BAD +@echo. +@echo ******************************************************* +@echo *** Build FAILED -- Please check the error messages *** +@echo ******************************************************* +@goto :END +:FAIL +@echo To run this script you must open a "Visual Studio .NET Command Prompt" +@echo (64 bit host compiler). The PS4 Orbis SDK must be installed, too. +:END diff --git a/lib/LuaJIT/psvitabuild.bat b/lib/LuaJIT/psvitabuild.bat new file mode 100644 index 0000000..3991dc6 --- /dev/null +++ b/lib/LuaJIT/psvitabuild.bat @@ -0,0 +1,93 @@ +@rem Script to build LuaJIT with the PS Vita SDK. +@rem Donated to the public domain. +@rem +@rem Open a "Visual Studio .NET Command Prompt" (32 bit host compiler) +@rem Then cd to this directory and run this script. + +@if not defined INCLUDE goto :FAIL +@if not defined SCE_PSP2_SDK_DIR goto :FAIL + +@setlocal +@rem ---- Host compiler ---- +@set LJCOMPILE=cl /nologo /c /MD /O2 /W3 /D_CRT_SECURE_NO_DEPRECATE +@set LJLINK=link /nologo +@set LJMT=mt /nologo +@set DASMDIR=..\dynasm +@set DASM=%DASMDIR%\dynasm.lua +@set ALL_LIB=lib_base.c lib_math.c lib_bit.c lib_string.c lib_table.c lib_io.c lib_os.c lib_package.c lib_debug.c lib_jit.c lib_ffi.c + +%LJCOMPILE% host\minilua.c +@if errorlevel 1 goto :BAD +%LJLINK% /out:minilua.exe minilua.obj +@if errorlevel 1 goto :BAD +if exist minilua.exe.manifest^ + %LJMT% -manifest minilua.exe.manifest -outputresource:minilua.exe + +@rem Check for 32 bit host compiler. +@minilua +@if errorlevel 8 goto :FAIL + +@set DASMFLAGS=-D FPU -D HFABI +minilua %DASM% -LN %DASMFLAGS% -o host\buildvm_arch.h vm_arm.dasc +@if errorlevel 1 goto :BAD + +%LJCOMPILE% /I "." /I %DASMDIR% -DLUAJIT_TARGET=LUAJIT_ARCH_ARM -DLUAJIT_OS=LUAJIT_OS_OTHER -DLUAJIT_DISABLE_JIT -DLUAJIT_DISABLE_FFI -DLJ_TARGET_PSVITA=1 host\buildvm*.c +@if errorlevel 1 goto :BAD +%LJLINK% /out:buildvm.exe buildvm*.obj +@if errorlevel 1 goto :BAD +if exist buildvm.exe.manifest^ + %LJMT% -manifest buildvm.exe.manifest -outputresource:buildvm.exe + +buildvm -m elfasm -o lj_vm.s +@if errorlevel 1 goto :BAD +buildvm -m bcdef -o lj_bcdef.h %ALL_LIB% +@if errorlevel 1 goto :BAD +buildvm -m ffdef -o lj_ffdef.h %ALL_LIB% +@if errorlevel 1 goto :BAD +buildvm -m libdef -o lj_libdef.h %ALL_LIB% +@if errorlevel 1 goto :BAD +buildvm -m recdef -o lj_recdef.h %ALL_LIB% +@if errorlevel 1 goto :BAD +buildvm -m vmdef -o jit\vmdef.lua %ALL_LIB% +@if errorlevel 1 goto :BAD +buildvm -m folddef -o lj_folddef.h lj_opt_fold.c +@if errorlevel 1 goto :BAD + +@rem ---- Cross compiler ---- +@set LJCOMPILE="%SCE_PSP2_SDK_DIR%\host_tools\build\bin\psp2snc" -c -w -DLUAJIT_DISABLE_FFI -DLUAJIT_USE_SYSMALLOC +@set LJLIB="%SCE_PSP2_SDK_DIR%\host_tools\build\bin\psp2ld32" -r --output= +@set INCLUDE="" + +"%SCE_PSP2_SDK_DIR%\host_tools\build\bin\psp2as" -o lj_vm.o lj_vm.s + +@if "%1" neq "debug" goto :NODEBUG +@shift +@set LJCOMPILE=%LJCOMPILE% -g -O0 +@set TARGETLIB=libluajitD.a +goto :BUILD +:NODEBUG +@set LJCOMPILE=%LJCOMPILE% -O2 +@set TARGETLIB=libluajit.a +:BUILD +del %TARGETLIB% + +%LJCOMPILE% ljamalg.c +@if errorlevel 1 goto :BAD +%LJLIB%%TARGETLIB% ljamalg.o lj_vm.o +@if errorlevel 1 goto :BAD + +@del *.o *.obj *.manifest minilua.exe buildvm.exe +@echo. +@echo === Successfully built LuaJIT for PS Vita === + +@goto :END +:BAD +@echo. +@echo ******************************************************* +@echo *** Build FAILED -- Please check the error messages *** +@echo ******************************************************* +@goto :END +:FAIL +@echo To run this script you must open a "Visual Studio .NET Command Prompt" +@echo (32 bit host compiler). The PS Vita SDK must be installed, too. +:END diff --git a/lib/LuaJIT/vm_arm.dasc b/lib/LuaJIT/vm_arm.dasc new file mode 100644 index 0000000..780cc16 --- /dev/null +++ b/lib/LuaJIT/vm_arm.dasc @@ -0,0 +1,4593 @@ +|// Low-level VM code for ARM CPUs. +|// Bytecode interpreter, fast functions and helper functions. +|// Copyright (C) 2005-2017 Mike Pall. See Copyright Notice in luajit.h +| +|.arch arm +|.section code_op, code_sub +| +|.actionlist build_actionlist +|.globals GLOB_ +|.globalnames globnames +|.externnames extnames +| +|// Note: The ragged indentation of the instructions is intentional. +|// The starting columns indicate data dependencies. +| +|//----------------------------------------------------------------------- +| +|// Fixed register assignments for the interpreter. +| +|// The following must be C callee-save. +|.define MASKR8, r4 // 255*8 constant for fast bytecode decoding. +|.define KBASE, r5 // Constants of current Lua function. +|.define PC, r6 // Next PC. +|.define DISPATCH, r7 // Opcode dispatch table. +|.define LREG, r8 // Register holding lua_State (also in SAVE_L). +| +|// C callee-save in EABI, but often refetched. Temporary in iOS 3.0+. +|.define BASE, r9 // Base of current Lua stack frame. +| +|// The following temporaries are not saved across C calls, except for RA/RC. +|.define RA, r10 // Callee-save. +|.define RC, r11 // Callee-save. +|.define RB, r12 +|.define OP, r12 // Overlaps RB, must not be lr. +|.define INS, lr +| +|// Calling conventions. Also used as temporaries. +|.define CARG1, r0 +|.define CARG2, r1 +|.define CARG3, r2 +|.define CARG4, r3 +|.define CARG12, r0 // For 1st soft-fp double. +|.define CARG34, r2 // For 2nd soft-fp double. +| +|.define CRET1, r0 +|.define CRET2, r1 +| +|// Stack layout while in interpreter. Must match with lj_frame.h. +|.define SAVE_R4, [sp, #28] +|.define CFRAME_SPACE, #28 +|.define SAVE_ERRF, [sp, #24] +|.define SAVE_NRES, [sp, #20] +|.define SAVE_CFRAME, [sp, #16] +|.define SAVE_L, [sp, #12] +|.define SAVE_PC, [sp, #8] +|.define SAVE_MULTRES, [sp, #4] +|.define ARG5, [sp] +| +|.define TMPDhi, [sp, #4] +|.define TMPDlo, [sp] +|.define TMPD, [sp] +|.define TMPDp, sp +| +|.if FPU +|.macro saveregs +| push {r5, r6, r7, r8, r9, r10, r11, lr} +| vpush {d8-d15} +| sub sp, sp, CFRAME_SPACE+4 +| str r4, SAVE_R4 +|.endmacro +|.macro restoreregs_ret +| ldr r4, SAVE_R4 +| add sp, sp, CFRAME_SPACE+4 +| vpop {d8-d15} +| pop {r5, r6, r7, r8, r9, r10, r11, pc} +|.endmacro +|.else +|.macro saveregs +| push {r4, r5, r6, r7, r8, r9, r10, r11, lr} +| sub sp, sp, CFRAME_SPACE +|.endmacro +|.macro restoreregs_ret +| add sp, sp, CFRAME_SPACE +| pop {r4, r5, r6, r7, r8, r9, r10, r11, pc} +|.endmacro +|.endif +| +|// Type definitions. Some of these are only used for documentation. +|.type L, lua_State, LREG +|.type GL, global_State +|.type TVALUE, TValue +|.type GCOBJ, GCobj +|.type STR, GCstr +|.type TAB, GCtab +|.type LFUNC, GCfuncL +|.type CFUNC, GCfuncC +|.type PROTO, GCproto +|.type UPVAL, GCupval +|.type NODE, Node +|.type NARGS8, int +|.type TRACE, GCtrace +|.type SBUF, SBuf +| +|//----------------------------------------------------------------------- +| +|// Trap for not-yet-implemented parts. +|.macro NYI; ud; .endmacro +| +|//----------------------------------------------------------------------- +| +|// Access to frame relative to BASE. +|.define FRAME_FUNC, #-8 +|.define FRAME_PC, #-4 +| +|.macro decode_RA8, dst, ins; and dst, MASKR8, ins, lsr #5; .endmacro +|.macro decode_RB8, dst, ins; and dst, MASKR8, ins, lsr #21; .endmacro +|.macro decode_RC8, dst, ins; and dst, MASKR8, ins, lsr #13; .endmacro +|.macro decode_RD, dst, ins; lsr dst, ins, #16; .endmacro +|.macro decode_OP, dst, ins; and dst, ins, #255; .endmacro +| +|// Instruction fetch. +|.macro ins_NEXT1 +| ldrb OP, [PC] +|.endmacro +|.macro ins_NEXT2 +| ldr INS, [PC], #4 +|.endmacro +|// Instruction decode+dispatch. +|.macro ins_NEXT3 +| ldr OP, [DISPATCH, OP, lsl #2] +| decode_RA8 RA, INS +| decode_RD RC, INS +| bx OP +|.endmacro +|.macro ins_NEXT +| ins_NEXT1 +| ins_NEXT2 +| ins_NEXT3 +|.endmacro +| +|// Instruction footer. +|.if 1 +| // Replicated dispatch. Less unpredictable branches, but higher I-Cache use. +| .define ins_next, ins_NEXT +| .define ins_next_, ins_NEXT +| .define ins_next1, ins_NEXT1 +| .define ins_next2, ins_NEXT2 +| .define ins_next3, ins_NEXT3 +|.else +| // Common dispatch. Lower I-Cache use, only one (very) unpredictable branch. +| // Affects only certain kinds of benchmarks (and only with -j off). +| .macro ins_next +| b ->ins_next +| .endmacro +| .macro ins_next1 +| .endmacro +| .macro ins_next2 +| .endmacro +| .macro ins_next3 +| b ->ins_next +| .endmacro +| .macro ins_next_ +| ->ins_next: +| ins_NEXT +| .endmacro +|.endif +| +|// Avoid register name substitution for field name. +#define field_pc pc +| +|// Call decode and dispatch. +|.macro ins_callt +| // BASE = new base, CARG3 = LFUNC/CFUNC, RC = nargs*8, FRAME_PC(BASE) = PC +| ldr PC, LFUNC:CARG3->field_pc +| ldrb OP, [PC] // STALL: load PC. early PC. +| ldr INS, [PC], #4 +| ldr OP, [DISPATCH, OP, lsl #2] // STALL: load OP. early OP. +| decode_RA8 RA, INS +| add RA, RA, BASE +| bx OP +|.endmacro +| +|.macro ins_call +| // BASE = new base, CARG3 = LFUNC/CFUNC, RC = nargs*8, PC = caller PC +| str PC, [BASE, FRAME_PC] +| ins_callt // STALL: locked PC. +|.endmacro +| +|//----------------------------------------------------------------------- +| +|// Macros to test operand types. +|.macro checktp, reg, tp; cmn reg, #-tp; .endmacro +|.macro checktpeq, reg, tp; cmneq reg, #-tp; .endmacro +|.macro checktpne, reg, tp; cmnne reg, #-tp; .endmacro +|.macro checkstr, reg, target; checktp reg, LJ_TSTR; bne target; .endmacro +|.macro checktab, reg, target; checktp reg, LJ_TTAB; bne target; .endmacro +|.macro checkfunc, reg, target; checktp reg, LJ_TFUNC; bne target; .endmacro +| +|// Assumes DISPATCH is relative to GL. +#define DISPATCH_GL(field) (GG_DISP2G + (int)offsetof(global_State, field)) +#define DISPATCH_J(field) (GG_DISP2J + (int)offsetof(jit_State, field)) +| +#define PC2PROTO(field) ((int)offsetof(GCproto, field)-(int)sizeof(GCproto)) +| +|.macro hotcheck, delta +| lsr CARG1, PC, #1 +| and CARG1, CARG1, #126 +| sub CARG1, CARG1, #-GG_DISP2HOT +| ldrh CARG2, [DISPATCH, CARG1] +| subs CARG2, CARG2, #delta +| strh CARG2, [DISPATCH, CARG1] +|.endmacro +| +|.macro hotloop +| hotcheck HOTCOUNT_LOOP +| blo ->vm_hotloop +|.endmacro +| +|.macro hotcall +| hotcheck HOTCOUNT_CALL +| blo ->vm_hotcall +|.endmacro +| +|// Set current VM state. +|.macro mv_vmstate, reg, st; mvn reg, #LJ_VMST_..st; .endmacro +|.macro st_vmstate, reg; str reg, [DISPATCH, #DISPATCH_GL(vmstate)]; .endmacro +| +|// Move table write barrier back. Overwrites mark and tmp. +|.macro barrierback, tab, mark, tmp +| ldr tmp, [DISPATCH, #DISPATCH_GL(gc.grayagain)] +| bic mark, mark, #LJ_GC_BLACK // black2gray(tab) +| str tab, [DISPATCH, #DISPATCH_GL(gc.grayagain)] +| strb mark, tab->marked +| str tmp, tab->gclist +|.endmacro +| +|.macro .IOS, a, b +|.if IOS +| a, b +|.endif +|.endmacro +| +|//----------------------------------------------------------------------- + +#if !LJ_DUALNUM +#error "Only dual-number mode supported for ARM target" +#endif + +/* Generate subroutines used by opcodes and other parts of the VM. */ +/* The .code_sub section should be last to help static branch prediction. */ +static void build_subroutines(BuildCtx *ctx) +{ + |.code_sub + | + |//----------------------------------------------------------------------- + |//-- Return handling ---------------------------------------------------- + |//----------------------------------------------------------------------- + | + |->vm_returnp: + | // See vm_return. Also: RB = previous base. + | tst PC, #FRAME_P + | beq ->cont_dispatch + | + | // Return from pcall or xpcall fast func. + | ldr PC, [RB, FRAME_PC] // Fetch PC of previous frame. + | mvn CARG2, #~LJ_TTRUE + | mov BASE, RB + | // Prepending may overwrite the pcall frame, so do it at the end. + | str CARG2, [RA, FRAME_PC] // Prepend true to results. + | sub RA, RA, #8 + | + |->vm_returnc: + | adds RC, RC, #8 // RC = (nresults+1)*8. + | mov CRET1, #LUA_YIELD + | beq ->vm_unwind_c_eh + | str RC, SAVE_MULTRES + | ands CARG1, PC, #FRAME_TYPE + | beq ->BC_RET_Z // Handle regular return to Lua. + | + |->vm_return: + | // BASE = base, RA = resultptr, RC/MULTRES = (nresults+1)*8, PC = return + | // CARG1 = PC & FRAME_TYPE + | bic RB, PC, #FRAME_TYPEP + | cmp CARG1, #FRAME_C + | sub RB, BASE, RB // RB = previous base. + | bne ->vm_returnp + | + | str RB, L->base + | ldr KBASE, SAVE_NRES + | mv_vmstate CARG4, C + | sub BASE, BASE, #8 + | subs CARG3, RC, #8 + | lsl KBASE, KBASE, #3 // KBASE = (nresults_wanted+1)*8 + | st_vmstate CARG4 + | beq >2 + |1: + | subs CARG3, CARG3, #8 + | ldrd CARG12, [RA], #8 + | strd CARG12, [BASE], #8 + | bne <1 + |2: + | cmp KBASE, RC // More/less results wanted? + | bne >6 + |3: + | str BASE, L->top // Store new top. + | + |->vm_leave_cp: + | ldr RC, SAVE_CFRAME // Restore previous C frame. + | mov CRET1, #0 // Ok return status for vm_pcall. + | str RC, L->cframe + | + |->vm_leave_unw: + | restoreregs_ret + | + |6: + | blt >7 // Less results wanted? + | // More results wanted. Check stack size and fill up results with nil. + | ldr CARG3, L->maxstack + | mvn CARG2, #~LJ_TNIL + | cmp BASE, CARG3 + | bhs >8 + | str CARG2, [BASE, #4] + | add RC, RC, #8 + | add BASE, BASE, #8 + | b <2 + | + |7: // Less results wanted. + | sub CARG1, RC, KBASE + | cmp KBASE, #0 // LUA_MULTRET+1 case? + | subne BASE, BASE, CARG1 // Either keep top or shrink it. + | b <3 + | + |8: // Corner case: need to grow stack for filling up results. + | // This can happen if: + | // - A C function grows the stack (a lot). + | // - The GC shrinks the stack in between. + | // - A return back from a lua_call() with (high) nresults adjustment. + | str BASE, L->top // Save current top held in BASE (yes). + | lsr CARG2, KBASE, #3 + | mov CARG1, L + | bl extern lj_state_growstack // (lua_State *L, int n) + | ldr BASE, L->top // Need the (realloced) L->top in BASE. + | b <2 + | + |->vm_unwind_c: // Unwind C stack, return from vm_pcall. + | // (void *cframe, int errcode) + | mov sp, CARG1 + | mov CRET1, CARG2 + |->vm_unwind_c_eh: // Landing pad for external unwinder. + | ldr L, SAVE_L + | mv_vmstate CARG4, C + | ldr GL:CARG3, L->glref + | str CARG4, GL:CARG3->vmstate + | b ->vm_leave_unw + | + |->vm_unwind_ff: // Unwind C stack, return from ff pcall. + | // (void *cframe) + | bic CARG1, CARG1, #~CFRAME_RAWMASK // Use two steps: bic sp is deprecated. + | mov sp, CARG1 + |->vm_unwind_ff_eh: // Landing pad for external unwinder. + | ldr L, SAVE_L + | mov MASKR8, #255 + | mov RC, #16 // 2 results: false + error message. + | lsl MASKR8, MASKR8, #3 // MASKR8 = 255*8. + | ldr BASE, L->base + | ldr DISPATCH, L->glref // Setup pointer to dispatch table. + | mvn CARG1, #~LJ_TFALSE + | sub RA, BASE, #8 // Results start at BASE-8. + | ldr PC, [BASE, FRAME_PC] // Fetch PC of previous frame. + | add DISPATCH, DISPATCH, #GG_G2DISP + | mv_vmstate CARG2, INTERP + | str CARG1, [BASE, #-4] // Prepend false to error message. + | st_vmstate CARG2 + | b ->vm_returnc + | + |->vm_unwind_ext: // Complete external unwind. +#if !LJ_NO_UNWIND + | push {r0, r1, r2, lr} + | bl extern _Unwind_Complete + | ldr r0, [sp] + | bl extern _Unwind_DeleteException + | pop {r0, r1, r2, lr} + | mov r0, r1 + | bx r2 +#endif + | + |//----------------------------------------------------------------------- + |//-- Grow stack for calls ----------------------------------------------- + |//----------------------------------------------------------------------- + | + |->vm_growstack_c: // Grow stack for C function. + | // CARG1 = L + | mov CARG2, #LUA_MINSTACK + | b >2 + | + |->vm_growstack_l: // Grow stack for Lua function. + | // BASE = new base, RA = BASE+framesize*8, RC = nargs*8, PC = first PC + | add RC, BASE, RC + | sub RA, RA, BASE + | mov CARG1, L + | str BASE, L->base + | add PC, PC, #4 // Must point after first instruction. + | str RC, L->top + | lsr CARG2, RA, #3 + |2: + | // L->base = new base, L->top = top + | str PC, SAVE_PC + | bl extern lj_state_growstack // (lua_State *L, int n) + | ldr BASE, L->base + | ldr RC, L->top + | ldr LFUNC:CARG3, [BASE, FRAME_FUNC] + | sub NARGS8:RC, RC, BASE + | // BASE = new base, RB = LFUNC/CFUNC, RC = nargs*8, FRAME_PC(BASE) = PC + | ins_callt // Just retry the call. + | + |//----------------------------------------------------------------------- + |//-- Entry points into the assembler VM --------------------------------- + |//----------------------------------------------------------------------- + | + |->vm_resume: // Setup C frame and resume thread. + | // (lua_State *L, TValue *base, int nres1 = 0, ptrdiff_t ef = 0) + | saveregs + | mov L, CARG1 + | ldr DISPATCH, L:CARG1->glref // Setup pointer to dispatch table. + | mov BASE, CARG2 + | add DISPATCH, DISPATCH, #GG_G2DISP + | str L, SAVE_L + | mov PC, #FRAME_CP + | str CARG3, SAVE_NRES + | add CARG2, sp, #CFRAME_RESUME + | ldrb CARG1, L->status + | str CARG3, SAVE_ERRF + | str L, SAVE_PC // Any value outside of bytecode is ok. + | str CARG3, SAVE_CFRAME + | cmp CARG1, #0 + | str CARG2, L->cframe + | beq >3 + | + | // Resume after yield (like a return). + | str L, [DISPATCH, #DISPATCH_GL(cur_L)] + | mov RA, BASE + | ldr BASE, L->base + | ldr CARG1, L->top + | mov MASKR8, #255 + | strb CARG3, L->status + | sub RC, CARG1, BASE + | ldr PC, [BASE, FRAME_PC] + | lsl MASKR8, MASKR8, #3 // MASKR8 = 255*8. + | mv_vmstate CARG2, INTERP + | add RC, RC, #8 + | ands CARG1, PC, #FRAME_TYPE + | st_vmstate CARG2 + | str RC, SAVE_MULTRES + | beq ->BC_RET_Z + | b ->vm_return + | + |->vm_pcall: // Setup protected C frame and enter VM. + | // (lua_State *L, TValue *base, int nres1, ptrdiff_t ef) + | saveregs + | mov PC, #FRAME_CP + | str CARG4, SAVE_ERRF + | b >1 + | + |->vm_call: // Setup C frame and enter VM. + | // (lua_State *L, TValue *base, int nres1) + | saveregs + | mov PC, #FRAME_C + | + |1: // Entry point for vm_pcall above (PC = ftype). + | ldr RC, L:CARG1->cframe + | str CARG3, SAVE_NRES + | mov L, CARG1 + | str CARG1, SAVE_L + | ldr DISPATCH, L->glref // Setup pointer to dispatch table. + | mov BASE, CARG2 + | str CARG1, SAVE_PC // Any value outside of bytecode is ok. + | str RC, SAVE_CFRAME + | add DISPATCH, DISPATCH, #GG_G2DISP + | str sp, L->cframe // Add our C frame to cframe chain. + | + |3: // Entry point for vm_cpcall/vm_resume (BASE = base, PC = ftype). + | str L, [DISPATCH, #DISPATCH_GL(cur_L)] + | ldr RB, L->base // RB = old base (for vmeta_call). + | ldr CARG1, L->top + | mov MASKR8, #255 + | add PC, PC, BASE + | lsl MASKR8, MASKR8, #3 // MASKR8 = 255*8. + | sub PC, PC, RB // PC = frame delta + frame type + | mv_vmstate CARG2, INTERP + | sub NARGS8:RC, CARG1, BASE + | st_vmstate CARG2 + | + |->vm_call_dispatch: + | // RB = old base, BASE = new base, RC = nargs*8, PC = caller PC + | ldrd CARG34, [BASE, FRAME_FUNC] + | checkfunc CARG4, ->vmeta_call + | + |->vm_call_dispatch_f: + | ins_call + | // BASE = new base, CARG3 = func, RC = nargs*8, PC = caller PC + | + |->vm_cpcall: // Setup protected C frame, call C. + | // (lua_State *L, lua_CFunction func, void *ud, lua_CPFunction cp) + | saveregs + | mov L, CARG1 + | ldr RA, L:CARG1->stack + | str CARG1, SAVE_L + | ldr DISPATCH, L->glref // Setup pointer to dispatch table. + | ldr RB, L->top + | str CARG1, SAVE_PC // Any value outside of bytecode is ok. + | ldr RC, L->cframe + | add DISPATCH, DISPATCH, #GG_G2DISP + | sub RA, RA, RB // Compute -savestack(L, L->top). + | mov RB, #0 + | str RA, SAVE_NRES // Neg. delta means cframe w/o frame. + | str RB, SAVE_ERRF // No error function. + | str RC, SAVE_CFRAME + | str sp, L->cframe // Add our C frame to cframe chain. + | str L, [DISPATCH, #DISPATCH_GL(cur_L)] + | blx CARG4 // (lua_State *L, lua_CFunction func, void *ud) + | movs BASE, CRET1 + | mov PC, #FRAME_CP + | bne <3 // Else continue with the call. + | b ->vm_leave_cp // No base? Just remove C frame. + | + |//----------------------------------------------------------------------- + |//-- Metamethod handling ------------------------------------------------ + |//----------------------------------------------------------------------- + | + |//-- Continuation dispatch ---------------------------------------------- + | + |->cont_dispatch: + | // BASE = meta base, RA = resultptr, RC = (nresults+1)*8 + | ldr LFUNC:CARG3, [RB, FRAME_FUNC] + | ldr CARG1, [BASE, #-16] // Get continuation. + | mov CARG4, BASE + | mov BASE, RB // Restore caller BASE. + |.if FFI + | cmp CARG1, #1 + |.endif + | ldr PC, [CARG4, #-12] // Restore PC from [cont|PC]. + | ldr CARG3, LFUNC:CARG3->field_pc + | mvn INS, #~LJ_TNIL + | add CARG2, RA, RC + | str INS, [CARG2, #-4] // Ensure one valid arg. + |.if FFI + | bls >1 + |.endif + | ldr KBASE, [CARG3, #PC2PROTO(k)] + | // BASE = base, RA = resultptr, CARG4 = meta base + | bx CARG1 + | + |.if FFI + |1: + | beq ->cont_ffi_callback // cont = 1: return from FFI callback. + | // cont = 0: tailcall from C function. + | sub CARG4, CARG4, #16 + | sub RC, CARG4, BASE + | b ->vm_call_tail + |.endif + | + |->cont_cat: // RA = resultptr, CARG4 = meta base + | ldr INS, [PC, #-4] + | sub CARG2, CARG4, #16 + | ldrd CARG34, [RA] + | str BASE, L->base + | decode_RB8 RC, INS + | decode_RA8 RA, INS + | add CARG1, BASE, RC + | subs CARG1, CARG2, CARG1 + | strdne CARG34, [CARG2] + | movne CARG3, CARG1 + | bne ->BC_CAT_Z + | strd CARG34, [BASE, RA] + | b ->cont_nop + | + |//-- Table indexing metamethods ----------------------------------------- + | + |->vmeta_tgets1: + | add CARG2, BASE, RB + | b >2 + | + |->vmeta_tgets: + | sub CARG2, DISPATCH, #-DISPATCH_GL(tmptv) + | mvn CARG4, #~LJ_TTAB + | str TAB:RB, [CARG2] + | str CARG4, [CARG2, #4] + |2: + | mvn CARG4, #~LJ_TSTR + | str STR:RC, TMPDlo + | str CARG4, TMPDhi + | mov CARG3, TMPDp + | b >1 + | + |->vmeta_tgetb: // RC = index + | decode_RB8 RB, INS + | str RC, TMPDlo + | mvn CARG4, #~LJ_TISNUM + | add CARG2, BASE, RB + | str CARG4, TMPDhi + | mov CARG3, TMPDp + | b >1 + | + |->vmeta_tgetv: + | add CARG2, BASE, RB + | add CARG3, BASE, RC + |1: + | str BASE, L->base + | mov CARG1, L + | str PC, SAVE_PC + | bl extern lj_meta_tget // (lua_State *L, TValue *o, TValue *k) + | // Returns TValue * (finished) or NULL (metamethod). + | .IOS ldr BASE, L->base + | cmp CRET1, #0 + | beq >3 + | ldrd CARG34, [CRET1] + | ins_next1 + | ins_next2 + | strd CARG34, [BASE, RA] + | ins_next3 + | + |3: // Call __index metamethod. + | // BASE = base, L->top = new base, stack = cont/func/t/k + | rsb CARG1, BASE, #FRAME_CONT + | ldr BASE, L->top + | mov NARGS8:RC, #16 // 2 args for func(t, k). + | str PC, [BASE, #-12] // [cont|PC] + | add PC, CARG1, BASE + | ldr LFUNC:CARG3, [BASE, FRAME_FUNC] // Guaranteed to be a function here. + | b ->vm_call_dispatch_f + | + |->vmeta_tgetr: + | .IOS mov RC, BASE + | bl extern lj_tab_getinth // (GCtab *t, int32_t key) + | // Returns cTValue * or NULL. + | .IOS mov BASE, RC + | cmp CRET1, #0 + | ldrdne CARG12, [CRET1] + | mvneq CARG2, #~LJ_TNIL + | b ->BC_TGETR_Z + | + |//----------------------------------------------------------------------- + | + |->vmeta_tsets1: + | add CARG2, BASE, RB + | b >2 + | + |->vmeta_tsets: + | sub CARG2, DISPATCH, #-DISPATCH_GL(tmptv) + | mvn CARG4, #~LJ_TTAB + | str TAB:RB, [CARG2] + | str CARG4, [CARG2, #4] + |2: + | mvn CARG4, #~LJ_TSTR + | str STR:RC, TMPDlo + | str CARG4, TMPDhi + | mov CARG3, TMPDp + | b >1 + | + |->vmeta_tsetb: // RC = index + | decode_RB8 RB, INS + | str RC, TMPDlo + | mvn CARG4, #~LJ_TISNUM + | add CARG2, BASE, RB + | str CARG4, TMPDhi + | mov CARG3, TMPDp + | b >1 + | + |->vmeta_tsetv: + | add CARG2, BASE, RB + | add CARG3, BASE, RC + |1: + | str BASE, L->base + | mov CARG1, L + | str PC, SAVE_PC + | bl extern lj_meta_tset // (lua_State *L, TValue *o, TValue *k) + | // Returns TValue * (finished) or NULL (metamethod). + | .IOS ldr BASE, L->base + | cmp CRET1, #0 + | ldrd CARG34, [BASE, RA] + | beq >3 + | ins_next1 + | // NOBARRIER: lj_meta_tset ensures the table is not black. + | strd CARG34, [CRET1] + | ins_next2 + | ins_next3 + | + |3: // Call __newindex metamethod. + | // BASE = base, L->top = new base, stack = cont/func/t/k/(v) + | rsb CARG1, BASE, #FRAME_CONT + | ldr BASE, L->top + | mov NARGS8:RC, #24 // 3 args for func(t, k, v). + | strd CARG34, [BASE, #16] // Copy value to third argument. + | str PC, [BASE, #-12] // [cont|PC] + | add PC, CARG1, BASE + | ldr LFUNC:CARG3, [BASE, FRAME_FUNC] // Guaranteed to be a function here. + | b ->vm_call_dispatch_f + | + |->vmeta_tsetr: + | str BASE, L->base + | .IOS mov RC, BASE + | str PC, SAVE_PC + | bl extern lj_tab_setinth // (lua_State *L, GCtab *t, int32_t key) + | // Returns TValue *. + | .IOS mov BASE, RC + | b ->BC_TSETR_Z + | + |//-- Comparison metamethods --------------------------------------------- + | + |->vmeta_comp: + | mov CARG1, L + | sub PC, PC, #4 + | mov CARG2, RA + | str BASE, L->base + | mov CARG3, RC + | str PC, SAVE_PC + | decode_OP CARG4, INS + | bl extern lj_meta_comp // (lua_State *L, TValue *o1, *o2, int op) + | // Returns 0/1 or TValue * (metamethod). + |3: + | .IOS ldr BASE, L->base + | cmp CRET1, #1 + | bhi ->vmeta_binop + |4: + | ldrh RB, [PC, #2] + | add PC, PC, #4 + | add RB, PC, RB, lsl #2 + | subhs PC, RB, #0x20000 + |->cont_nop: + | ins_next + | + |->cont_ra: // RA = resultptr + | ldr INS, [PC, #-4] + | ldrd CARG12, [RA] + | decode_RA8 CARG3, INS + | strd CARG12, [BASE, CARG3] + | b ->cont_nop + | + |->cont_condt: // RA = resultptr + | ldr CARG2, [RA, #4] + | mvn CARG1, #~LJ_TTRUE + | cmp CARG1, CARG2 // Branch if result is true. + | b <4 + | + |->cont_condf: // RA = resultptr + | ldr CARG2, [RA, #4] + | checktp CARG2, LJ_TFALSE // Branch if result is false. + | b <4 + | + |->vmeta_equal: + | // CARG2, CARG3, CARG4 are already set by BC_ISEQV/BC_ISNEV. + | sub PC, PC, #4 + | str BASE, L->base + | mov CARG1, L + | str PC, SAVE_PC + | bl extern lj_meta_equal // (lua_State *L, GCobj *o1, *o2, int ne) + | // Returns 0/1 or TValue * (metamethod). + | b <3 + | + |->vmeta_equal_cd: + |.if FFI + | sub PC, PC, #4 + | str BASE, L->base + | mov CARG1, L + | mov CARG2, INS + | str PC, SAVE_PC + | bl extern lj_meta_equal_cd // (lua_State *L, BCIns op) + | // Returns 0/1 or TValue * (metamethod). + | b <3 + |.endif + | + |->vmeta_istype: + | sub PC, PC, #4 + | str BASE, L->base + | mov CARG1, L + | lsr CARG2, RA, #3 + | mov CARG3, RC + | str PC, SAVE_PC + | bl extern lj_meta_istype // (lua_State *L, BCReg ra, BCReg tp) + | .IOS ldr BASE, L->base + | b ->cont_nop + | + |//-- Arithmetic metamethods --------------------------------------------- + | + |->vmeta_arith_vn: + | decode_RB8 RB, INS + | decode_RC8 RC, INS + | add CARG3, BASE, RB + | add CARG4, KBASE, RC + | b >1 + | + |->vmeta_arith_nv: + | decode_RB8 RB, INS + | decode_RC8 RC, INS + | add CARG4, BASE, RB + | add CARG3, KBASE, RC + | b >1 + | + |->vmeta_unm: + | ldr INS, [PC, #-8] + | sub PC, PC, #4 + | add CARG3, BASE, RC + | add CARG4, BASE, RC + | b >1 + | + |->vmeta_arith_vv: + | decode_RB8 RB, INS + | decode_RC8 RC, INS + | add CARG3, BASE, RB + | add CARG4, BASE, RC + |1: + | decode_OP OP, INS + | add CARG2, BASE, RA + | str BASE, L->base + | mov CARG1, L + | str PC, SAVE_PC + | str OP, ARG5 + | bl extern lj_meta_arith // (lua_State *L, TValue *ra,*rb,*rc, BCReg op) + | // Returns NULL (finished) or TValue * (metamethod). + | .IOS ldr BASE, L->base + | cmp CRET1, #0 + | beq ->cont_nop + | + | // Call metamethod for binary op. + |->vmeta_binop: + | // BASE = old base, CRET1 = new base, stack = cont/func/o1/o2 + | sub CARG2, CRET1, BASE + | str PC, [CRET1, #-12] // [cont|PC] + | add PC, CARG2, #FRAME_CONT + | mov BASE, CRET1 + | mov NARGS8:RC, #16 // 2 args for func(o1, o2). + | b ->vm_call_dispatch + | + |->vmeta_len: + | add CARG2, BASE, RC + | str BASE, L->base + | mov CARG1, L + | str PC, SAVE_PC + | bl extern lj_meta_len // (lua_State *L, TValue *o) + | // Returns NULL (retry) or TValue * (metamethod base). + | .IOS ldr BASE, L->base +#if LJ_52 + | cmp CRET1, #0 + | bne ->vmeta_binop // Binop call for compatibility. + | ldr TAB:CARG1, [BASE, RC] + | b ->BC_LEN_Z +#else + | b ->vmeta_binop // Binop call for compatibility. +#endif + | + |//-- Call metamethod ---------------------------------------------------- + | + |->vmeta_call: // Resolve and call __call metamethod. + | // RB = old base, BASE = new base, RC = nargs*8 + | mov CARG1, L + | str RB, L->base // This is the callers base! + | sub CARG2, BASE, #8 + | str PC, SAVE_PC + | add CARG3, BASE, NARGS8:RC + | .IOS mov RA, BASE + | bl extern lj_meta_call // (lua_State *L, TValue *func, TValue *top) + | .IOS mov BASE, RA + | ldr LFUNC:CARG3, [BASE, FRAME_FUNC] // Guaranteed to be a function here. + | add NARGS8:RC, NARGS8:RC, #8 // Got one more argument now. + | ins_call + | + |->vmeta_callt: // Resolve __call for BC_CALLT. + | // BASE = old base, RA = new base, RC = nargs*8 + | mov CARG1, L + | str BASE, L->base + | sub CARG2, RA, #8 + | str PC, SAVE_PC + | add CARG3, RA, NARGS8:RC + | bl extern lj_meta_call // (lua_State *L, TValue *func, TValue *top) + | .IOS ldr BASE, L->base + | ldr LFUNC:CARG3, [RA, FRAME_FUNC] // Guaranteed to be a function here. + | ldr PC, [BASE, FRAME_PC] + | add NARGS8:RC, NARGS8:RC, #8 // Got one more argument now. + | b ->BC_CALLT2_Z + | + |//-- Argument coercion for 'for' statement ------------------------------ + | + |->vmeta_for: + | mov CARG1, L + | str BASE, L->base + | mov CARG2, RA + | str PC, SAVE_PC + | bl extern lj_meta_for // (lua_State *L, TValue *base) + | .IOS ldr BASE, L->base + |.if JIT + | ldrb OP, [PC, #-4] + |.endif + | ldr INS, [PC, #-4] + |.if JIT + | cmp OP, #BC_JFORI + |.endif + | decode_RA8 RA, INS + | decode_RD RC, INS + |.if JIT + | beq =>BC_JFORI + |.endif + | b =>BC_FORI + | + |//----------------------------------------------------------------------- + |//-- Fast functions ----------------------------------------------------- + |//----------------------------------------------------------------------- + | + |.macro .ffunc, name + |->ff_ .. name: + |.endmacro + | + |.macro .ffunc_1, name + |->ff_ .. name: + | ldrd CARG12, [BASE] + | cmp NARGS8:RC, #8 + | blo ->fff_fallback + |.endmacro + | + |.macro .ffunc_2, name + |->ff_ .. name: + | ldrd CARG12, [BASE] + | ldrd CARG34, [BASE, #8] + | cmp NARGS8:RC, #16 + | blo ->fff_fallback + |.endmacro + | + |.macro .ffunc_n, name + | .ffunc_1 name + | checktp CARG2, LJ_TISNUM + | bhs ->fff_fallback + |.endmacro + | + |.macro .ffunc_nn, name + | .ffunc_2 name + | checktp CARG2, LJ_TISNUM + | cmnlo CARG4, #-LJ_TISNUM + | bhs ->fff_fallback + |.endmacro + | + |.macro .ffunc_d, name + | .ffunc name + | ldr CARG2, [BASE, #4] + | cmp NARGS8:RC, #8 + | vldr d0, [BASE] + | blo ->fff_fallback + | checktp CARG2, LJ_TISNUM + | bhs ->fff_fallback + |.endmacro + | + |.macro .ffunc_dd, name + | .ffunc name + | ldr CARG2, [BASE, #4] + | ldr CARG4, [BASE, #12] + | cmp NARGS8:RC, #16 + | vldr d0, [BASE] + | vldr d1, [BASE, #8] + | blo ->fff_fallback + | checktp CARG2, LJ_TISNUM + | cmnlo CARG4, #-LJ_TISNUM + | bhs ->fff_fallback + |.endmacro + | + |// Inlined GC threshold check. Caveat: uses CARG1 and CARG2. + |.macro ffgccheck + | ldr CARG1, [DISPATCH, #DISPATCH_GL(gc.total)] + | ldr CARG2, [DISPATCH, #DISPATCH_GL(gc.threshold)] + | cmp CARG1, CARG2 + | blge ->fff_gcstep + |.endmacro + | + |//-- Base library: checks ----------------------------------------------- + | + |.ffunc_1 assert + | checktp CARG2, LJ_TTRUE + | bhi ->fff_fallback + | ldr PC, [BASE, FRAME_PC] + | strd CARG12, [BASE, #-8] + | mov RB, BASE + | subs RA, NARGS8:RC, #8 + | add RC, NARGS8:RC, #8 // Compute (nresults+1)*8. + | beq ->fff_res // Done if exactly 1 argument. + |1: + | ldrd CARG12, [RB, #8] + | subs RA, RA, #8 + | strd CARG12, [RB], #8 + | bne <1 + | b ->fff_res + | + |.ffunc type + | ldr CARG2, [BASE, #4] + | cmp NARGS8:RC, #8 + | blo ->fff_fallback + | checktp CARG2, LJ_TISNUM + | mvnlo CARG2, #~LJ_TISNUM + | rsb CARG4, CARG2, #(int)(offsetof(GCfuncC, upvalue)>>3)-1 + | lsl CARG4, CARG4, #3 + | ldrd CARG12, [CFUNC:CARG3, CARG4] + | b ->fff_restv + | + |//-- Base library: getters and setters --------------------------------- + | + |.ffunc_1 getmetatable + | checktp CARG2, LJ_TTAB + | cmnne CARG2, #-LJ_TUDATA + | bne >6 + |1: // Field metatable must be at same offset for GCtab and GCudata! + | ldr TAB:RB, TAB:CARG1->metatable + |2: + | mvn CARG2, #~LJ_TNIL + | ldr STR:RC, [DISPATCH, #DISPATCH_GL(gcroot[GCROOT_MMNAME+MM_metatable])] + | cmp TAB:RB, #0 + | beq ->fff_restv + | ldr CARG3, TAB:RB->hmask + | ldr CARG4, STR:RC->hash + | ldr NODE:INS, TAB:RB->node + | and CARG3, CARG3, CARG4 // idx = str->hash & tab->hmask + | add CARG3, CARG3, CARG3, lsl #1 + | add NODE:INS, NODE:INS, CARG3, lsl #3 // node = tab->node + idx*3*8 + |3: // Rearranged logic, because we expect _not_ to find the key. + | ldrd CARG34, NODE:INS->key // STALL: early NODE:INS. + | ldrd CARG12, NODE:INS->val + | ldr NODE:INS, NODE:INS->next + | checktp CARG4, LJ_TSTR + | cmpeq CARG3, STR:RC + | beq >5 + | cmp NODE:INS, #0 + | bne <3 + |4: + | mov CARG1, RB // Use metatable as default result. + | mvn CARG2, #~LJ_TTAB + | b ->fff_restv + |5: + | checktp CARG2, LJ_TNIL + | bne ->fff_restv + | b <4 + | + |6: + | checktp CARG2, LJ_TISNUM + | mvnhs CARG2, CARG2 + | movlo CARG2, #~LJ_TISNUM + | add CARG4, DISPATCH, CARG2, lsl #2 + | ldr TAB:RB, [CARG4, #DISPATCH_GL(gcroot[GCROOT_BASEMT])] + | b <2 + | + |.ffunc_2 setmetatable + | // Fast path: no mt for table yet and not clearing the mt. + | checktp CARG2, LJ_TTAB + | ldreq TAB:RB, TAB:CARG1->metatable + | checktpeq CARG4, LJ_TTAB + | ldrbeq CARG4, TAB:CARG1->marked + | cmpeq TAB:RB, #0 + | bne ->fff_fallback + | tst CARG4, #LJ_GC_BLACK // isblack(table) + | str TAB:CARG3, TAB:CARG1->metatable + | beq ->fff_restv + | barrierback TAB:CARG1, CARG4, CARG3 + | b ->fff_restv + | + |.ffunc rawget + | ldrd CARG34, [BASE] + | cmp NARGS8:RC, #16 + | blo ->fff_fallback + | mov CARG2, CARG3 + | checktab CARG4, ->fff_fallback + | mov CARG1, L + | add CARG3, BASE, #8 + | .IOS mov RA, BASE + | bl extern lj_tab_get // (lua_State *L, GCtab *t, cTValue *key) + | // Returns cTValue *. + | .IOS mov BASE, RA + | ldrd CARG12, [CRET1] + | b ->fff_restv + | + |//-- Base library: conversions ------------------------------------------ + | + |.ffunc tonumber + | // Only handles the number case inline (without a base argument). + | ldrd CARG12, [BASE] + | cmp NARGS8:RC, #8 + | bne ->fff_fallback + | checktp CARG2, LJ_TISNUM + | bls ->fff_restv + | b ->fff_fallback + | + |.ffunc_1 tostring + | // Only handles the string or number case inline. + | checktp CARG2, LJ_TSTR + | // A __tostring method in the string base metatable is ignored. + | beq ->fff_restv + | // Handle numbers inline, unless a number base metatable is present. + | ldr CARG4, [DISPATCH, #DISPATCH_GL(gcroot[GCROOT_BASEMT_NUM])] + | str BASE, L->base + | checktp CARG2, LJ_TISNUM + | cmpls CARG4, #0 + | str PC, SAVE_PC // Redundant (but a defined value). + | bhi ->fff_fallback + | ffgccheck + | mov CARG1, L + | mov CARG2, BASE + | bl extern lj_strfmt_number // (lua_State *L, cTValue *o) + | // Returns GCstr *. + | ldr BASE, L->base + | mvn CARG2, #~LJ_TSTR + | b ->fff_restv + | + |//-- Base library: iterators ------------------------------------------- + | + |.ffunc_1 next + | mvn CARG4, #~LJ_TNIL + | checktab CARG2, ->fff_fallback + | strd CARG34, [BASE, NARGS8:RC] // Set missing 2nd arg to nil. + | ldr PC, [BASE, FRAME_PC] + | mov CARG2, CARG1 + | str BASE, L->base // Add frame since C call can throw. + | mov CARG1, L + | str BASE, L->top // Dummy frame length is ok. + | add CARG3, BASE, #8 + | str PC, SAVE_PC + | bl extern lj_tab_next // (lua_State *L, GCtab *t, TValue *key) + | // Returns 0 at end of traversal. + | .IOS ldr BASE, L->base + | cmp CRET1, #0 + | mvneq CRET2, #~LJ_TNIL + | beq ->fff_restv // End of traversal: return nil. + | ldrd CARG12, [BASE, #8] // Copy key and value to results. + | ldrd CARG34, [BASE, #16] + | mov RC, #(2+1)*8 + | strd CARG12, [BASE, #-8] + | strd CARG34, [BASE] + | b ->fff_res + | + |.ffunc_1 pairs + | checktab CARG2, ->fff_fallback +#if LJ_52 + | ldr TAB:RB, TAB:CARG1->metatable +#endif + | ldrd CFUNC:CARG34, CFUNC:CARG3->upvalue[0] + | ldr PC, [BASE, FRAME_PC] +#if LJ_52 + | cmp TAB:RB, #0 + | bne ->fff_fallback +#endif + | mvn CARG2, #~LJ_TNIL + | mov RC, #(3+1)*8 + | strd CFUNC:CARG34, [BASE, #-8] + | str CARG2, [BASE, #12] + | b ->fff_res + | + |.ffunc_2 ipairs_aux + | checktp CARG2, LJ_TTAB + | checktpeq CARG4, LJ_TISNUM + | bne ->fff_fallback + | ldr RB, TAB:CARG1->asize + | ldr RC, TAB:CARG1->array + | add CARG3, CARG3, #1 + | ldr PC, [BASE, FRAME_PC] + | cmp CARG3, RB + | add RC, RC, CARG3, lsl #3 + | strd CARG34, [BASE, #-8] + | ldrdlo CARG12, [RC] + | mov RC, #(0+1)*8 + | bhs >2 // Not in array part? + |1: + | checktp CARG2, LJ_TNIL + | movne RC, #(2+1)*8 + | strdne CARG12, [BASE] + | b ->fff_res + |2: // Check for empty hash part first. Otherwise call C function. + | ldr RB, TAB:CARG1->hmask + | mov CARG2, CARG3 + | cmp RB, #0 + | beq ->fff_res + | .IOS mov RA, BASE + | bl extern lj_tab_getinth // (GCtab *t, int32_t key) + | // Returns cTValue * or NULL. + | .IOS mov BASE, RA + | cmp CRET1, #0 + | beq ->fff_res + | ldrd CARG12, [CRET1] + | b <1 + | + |.ffunc_1 ipairs + | checktab CARG2, ->fff_fallback +#if LJ_52 + | ldr TAB:RB, TAB:CARG1->metatable +#endif + | ldrd CFUNC:CARG34, CFUNC:CARG3->upvalue[0] + | ldr PC, [BASE, FRAME_PC] +#if LJ_52 + | cmp TAB:RB, #0 + | bne ->fff_fallback +#endif + | mov CARG1, #0 + | mvn CARG2, #~LJ_TISNUM + | mov RC, #(3+1)*8 + | strd CFUNC:CARG34, [BASE, #-8] + | strd CARG12, [BASE, #8] + | b ->fff_res + | + |//-- Base library: catch errors ---------------------------------------- + | + |.ffunc pcall + | ldrb RA, [DISPATCH, #DISPATCH_GL(hookmask)] + | cmp NARGS8:RC, #8 + | blo ->fff_fallback + | tst RA, #HOOK_ACTIVE // Remember active hook before pcall. + | mov RB, BASE + | add BASE, BASE, #8 + | moveq PC, #8+FRAME_PCALL + | movne PC, #8+FRAME_PCALLH + | sub NARGS8:RC, NARGS8:RC, #8 + | b ->vm_call_dispatch + | + |.ffunc_2 xpcall + | ldrb RA, [DISPATCH, #DISPATCH_GL(hookmask)] + | checkfunc CARG4, ->fff_fallback // Traceback must be a function. + | mov RB, BASE + | strd CARG12, [BASE, #8] // Swap function and traceback. + | strd CARG34, [BASE] + | tst RA, #HOOK_ACTIVE // Remember active hook before pcall. + | add BASE, BASE, #16 + | moveq PC, #16+FRAME_PCALL + | movne PC, #16+FRAME_PCALLH + | sub NARGS8:RC, NARGS8:RC, #16 + | b ->vm_call_dispatch + | + |//-- Coroutine library -------------------------------------------------- + | + |.macro coroutine_resume_wrap, resume + |.if resume + |.ffunc_1 coroutine_resume + | checktp CARG2, LJ_TTHREAD + | bne ->fff_fallback + |.else + |.ffunc coroutine_wrap_aux + | ldr L:CARG1, CFUNC:CARG3->upvalue[0].gcr + |.endif + | ldr PC, [BASE, FRAME_PC] + | str BASE, L->base + | ldr CARG2, L:CARG1->top + | ldrb RA, L:CARG1->status + | ldr RB, L:CARG1->base + | add CARG3, CARG2, NARGS8:RC + | add CARG4, CARG2, RA + | str PC, SAVE_PC + | cmp CARG4, RB + | beq ->fff_fallback + | ldr CARG4, L:CARG1->maxstack + | ldr RB, L:CARG1->cframe + | cmp RA, #LUA_YIELD + | cmpls CARG3, CARG4 + | cmpls RB, #0 + | bhi ->fff_fallback + |1: + |.if resume + | sub CARG3, CARG3, #8 // Keep resumed thread in stack for GC. + | add BASE, BASE, #8 + | sub NARGS8:RC, NARGS8:RC, #8 + |.endif + | str CARG3, L:CARG1->top + | str BASE, L->top + |2: // Move args to coroutine. + | ldrd CARG34, [BASE, RB] + | cmp RB, NARGS8:RC + | strdne CARG34, [CARG2, RB] + | add RB, RB, #8 + | bne <2 + | + | mov CARG3, #0 + | mov L:RA, L:CARG1 + | mov CARG4, #0 + | bl ->vm_resume // (lua_State *L, TValue *base, 0, 0) + | // Returns thread status. + |4: + | ldr CARG3, L:RA->base + | mv_vmstate CARG2, INTERP + | ldr CARG4, L:RA->top + | cmp CRET1, #LUA_YIELD + | ldr BASE, L->base + | str L, [DISPATCH, #DISPATCH_GL(cur_L)] + | st_vmstate CARG2 + | bhi >8 + | subs RC, CARG4, CARG3 + | ldr CARG1, L->maxstack + | add CARG2, BASE, RC + | beq >6 // No results? + | cmp CARG2, CARG1 + | mov RB, #0 + | bhi >9 // Need to grow stack? + | + | sub CARG4, RC, #8 + | str CARG3, L:RA->top // Clear coroutine stack. + |5: // Move results from coroutine. + | ldrd CARG12, [CARG3, RB] + | cmp RB, CARG4 + | strd CARG12, [BASE, RB] + | add RB, RB, #8 + | bne <5 + |6: + |.if resume + | mvn CARG3, #~LJ_TTRUE + | add RC, RC, #16 + |7: + | str CARG3, [BASE, #-4] // Prepend true/false to results. + | sub RA, BASE, #8 + |.else + | mov RA, BASE + | add RC, RC, #8 + |.endif + | ands CARG1, PC, #FRAME_TYPE + | str PC, SAVE_PC + | str RC, SAVE_MULTRES + | beq ->BC_RET_Z + | b ->vm_return + | + |8: // Coroutine returned with error (at co->top-1). + |.if resume + | ldrd CARG12, [CARG4, #-8]! + | mvn CARG3, #~LJ_TFALSE + | mov RC, #(2+1)*8 + | str CARG4, L:RA->top // Remove error from coroutine stack. + | strd CARG12, [BASE] // Copy error message. + | b <7 + |.else + | mov CARG1, L + | mov CARG2, L:RA + | bl extern lj_ffh_coroutine_wrap_err // (lua_State *L, lua_State *co) + | // Never returns. + |.endif + | + |9: // Handle stack expansion on return from yield. + | mov CARG1, L + | lsr CARG2, RC, #3 + | bl extern lj_state_growstack // (lua_State *L, int n) + | mov CRET1, #0 + | b <4 + |.endmacro + | + | coroutine_resume_wrap 1 // coroutine.resume + | coroutine_resume_wrap 0 // coroutine.wrap + | + |.ffunc coroutine_yield + | ldr CARG1, L->cframe + | add CARG2, BASE, NARGS8:RC + | str BASE, L->base + | tst CARG1, #CFRAME_RESUME + | str CARG2, L->top + | mov CRET1, #LUA_YIELD + | mov CARG3, #0 + | beq ->fff_fallback + | str CARG3, L->cframe + | strb CRET1, L->status + | b ->vm_leave_unw + | + |//-- Math library ------------------------------------------------------- + | + |.macro math_round, func + | .ffunc_1 math_ .. func + | checktp CARG2, LJ_TISNUM + | beq ->fff_restv + | bhi ->fff_fallback + | // Round FP value and normalize result. + | lsl CARG3, CARG2, #1 + | adds RB, CARG3, #0x00200000 + | bpl >2 // |x| < 1? + | mvn CARG4, #0x3e0 + | subs RB, CARG4, RB, asr #21 + | lsl CARG4, CARG2, #11 + | lsl CARG3, CARG1, #11 + | orr CARG4, CARG4, #0x80000000 + | rsb INS, RB, #32 + | orr CARG4, CARG4, CARG1, lsr #21 + | bls >3 // |x| >= 2^31? + | orr CARG3, CARG3, CARG4, lsl INS + | lsr CARG1, CARG4, RB + |.if "func" == "floor" + | tst CARG3, CARG2, asr #31 + | addne CARG1, CARG1, #1 + |.else + | bics CARG3, CARG3, CARG2, asr #31 + | addsne CARG1, CARG1, #1 + | ldrdvs CARG12, >9 + | bvs ->fff_restv + |.endif + | cmp CARG2, #0 + | rsblt CARG1, CARG1, #0 + |1: + | mvn CARG2, #~LJ_TISNUM + | b ->fff_restv + | + |2: // |x| < 1 + | bcs ->fff_restv // |x| is not finite. + | orr CARG3, CARG3, CARG1 // ztest = abs(hi) | lo + |.if "func" == "floor" + | tst CARG3, CARG2, asr #31 // return (ztest & sign) == 0 ? 0 : -1 + | moveq CARG1, #0 + | mvnne CARG1, #0 + |.else + | bics CARG3, CARG3, CARG2, asr #31 // return (ztest & ~sign) == 0 ? 0 : 1 + | moveq CARG1, #0 + | movne CARG1, #1 + |.endif + | mvn CARG2, #~LJ_TISNUM + | b ->fff_restv + | + |3: // |x| >= 2^31. Check for x == -(2^31). + | cmpeq CARG4, #0x80000000 + |.if "func" == "floor" + | cmpeq CARG3, #0 + |.endif + | bne >4 + | cmp CARG2, #0 + | movmi CARG1, #0x80000000 + | bmi <1 + |4: + | bl ->vm_..func.._sf + | b ->fff_restv + |.endmacro + | + | math_round floor + | math_round ceil + | + |.align 8 + |9: + | .long 0x00000000, 0x41e00000 // 2^31. + | + |.ffunc_1 math_abs + | checktp CARG2, LJ_TISNUM + | bhi ->fff_fallback + | bicne CARG2, CARG2, #0x80000000 + | bne ->fff_restv + | cmp CARG1, #0 + | rsbslt CARG1, CARG1, #0 + | ldrdvs CARG12, <9 + | // Fallthrough. + | + |->fff_restv: + | // CARG12 = TValue result. + | ldr PC, [BASE, FRAME_PC] + | strd CARG12, [BASE, #-8] + |->fff_res1: + | // PC = return. + | mov RC, #(1+1)*8 + |->fff_res: + | // RC = (nresults+1)*8, PC = return. + | ands CARG1, PC, #FRAME_TYPE + | ldreq INS, [PC, #-4] + | str RC, SAVE_MULTRES + | sub RA, BASE, #8 + | bne ->vm_return + | decode_RB8 RB, INS + |5: + | cmp RB, RC // More results expected? + | bhi >6 + | decode_RA8 CARG1, INS + | ins_next1 + | ins_next2 + | // Adjust BASE. KBASE is assumed to be set for the calling frame. + | sub BASE, RA, CARG1 + | ins_next3 + | + |6: // Fill up results with nil. + | add CARG2, RA, RC + | mvn CARG1, #~LJ_TNIL + | add RC, RC, #8 + | str CARG1, [CARG2, #-4] + | b <5 + | + |.macro math_extern, func + |.if HFABI + | .ffunc_d math_ .. func + |.else + | .ffunc_n math_ .. func + |.endif + | .IOS mov RA, BASE + | bl extern func + | .IOS mov BASE, RA + |.if HFABI + | b ->fff_resd + |.else + | b ->fff_restv + |.endif + |.endmacro + | + |.macro math_extern2, func + |.if HFABI + | .ffunc_dd math_ .. func + |.else + | .ffunc_nn math_ .. func + |.endif + | .IOS mov RA, BASE + | bl extern func + | .IOS mov BASE, RA + |.if HFABI + | b ->fff_resd + |.else + | b ->fff_restv + |.endif + |.endmacro + | + |.if FPU + | .ffunc_d math_sqrt + | vsqrt.f64 d0, d0 + |->fff_resd: + | ldr PC, [BASE, FRAME_PC] + | vstr d0, [BASE, #-8] + | b ->fff_res1 + |.else + | math_extern sqrt + |.endif + | + |.ffunc math_log + |.if HFABI + | ldr CARG2, [BASE, #4] + | cmp NARGS8:RC, #8 // Need exactly 1 argument. + | vldr d0, [BASE] + | bne ->fff_fallback + |.else + | ldrd CARG12, [BASE] + | cmp NARGS8:RC, #8 // Need exactly 1 argument. + | bne ->fff_fallback + |.endif + | checktp CARG2, LJ_TISNUM + | bhs ->fff_fallback + | .IOS mov RA, BASE + | bl extern log + | .IOS mov BASE, RA + |.if HFABI + | b ->fff_resd + |.else + | b ->fff_restv + |.endif + | + | math_extern log10 + | math_extern exp + | math_extern sin + | math_extern cos + | math_extern tan + | math_extern asin + | math_extern acos + | math_extern atan + | math_extern sinh + | math_extern cosh + | math_extern tanh + | math_extern2 pow + | math_extern2 atan2 + | math_extern2 fmod + | + |.if HFABI + | .ffunc math_ldexp + | ldr CARG4, [BASE, #4] + | ldrd CARG12, [BASE, #8] + | cmp NARGS8:RC, #16 + | blo ->fff_fallback + | vldr d0, [BASE] + | checktp CARG4, LJ_TISNUM + | bhs ->fff_fallback + | checktp CARG2, LJ_TISNUM + | bne ->fff_fallback + | .IOS mov RA, BASE + | bl extern ldexp // (double x, int exp) + | .IOS mov BASE, RA + | b ->fff_resd + |.else + |.ffunc_2 math_ldexp + | checktp CARG2, LJ_TISNUM + | bhs ->fff_fallback + | checktp CARG4, LJ_TISNUM + | bne ->fff_fallback + | .IOS mov RA, BASE + | bl extern ldexp // (double x, int exp) + | .IOS mov BASE, RA + | b ->fff_restv + |.endif + | + |.if HFABI + |.ffunc_d math_frexp + | mov CARG1, sp + | .IOS mov RA, BASE + | bl extern frexp + | .IOS mov BASE, RA + | ldr CARG3, [sp] + | mvn CARG4, #~LJ_TISNUM + | ldr PC, [BASE, FRAME_PC] + | vstr d0, [BASE, #-8] + | mov RC, #(2+1)*8 + | strd CARG34, [BASE] + | b ->fff_res + |.else + |.ffunc_n math_frexp + | mov CARG3, sp + | .IOS mov RA, BASE + | bl extern frexp + | .IOS mov BASE, RA + | ldr CARG3, [sp] + | mvn CARG4, #~LJ_TISNUM + | ldr PC, [BASE, FRAME_PC] + | strd CARG12, [BASE, #-8] + | mov RC, #(2+1)*8 + | strd CARG34, [BASE] + | b ->fff_res + |.endif + | + |.if HFABI + |.ffunc_d math_modf + | sub CARG1, BASE, #8 + | ldr PC, [BASE, FRAME_PC] + | .IOS mov RA, BASE + | bl extern modf + | .IOS mov BASE, RA + | mov RC, #(2+1)*8 + | vstr d0, [BASE] + | b ->fff_res + |.else + |.ffunc_n math_modf + | sub CARG3, BASE, #8 + | ldr PC, [BASE, FRAME_PC] + | .IOS mov RA, BASE + | bl extern modf + | .IOS mov BASE, RA + | mov RC, #(2+1)*8 + | strd CARG12, [BASE] + | b ->fff_res + |.endif + | + |.macro math_minmax, name, cond, fcond + |.if FPU + | .ffunc_1 name + | add RB, BASE, RC + | checktp CARG2, LJ_TISNUM + | add RA, BASE, #8 + | bne >4 + |1: // Handle integers. + | ldrd CARG34, [RA] + | cmp RA, RB + | bhs ->fff_restv + | checktp CARG4, LJ_TISNUM + | bne >3 + | cmp CARG1, CARG3 + | add RA, RA, #8 + | mov..cond CARG1, CARG3 + | b <1 + |3: // Convert intermediate result to number and continue below. + | vmov s4, CARG1 + | bhi ->fff_fallback + | vldr d1, [RA] + | vcvt.f64.s32 d0, s4 + | b >6 + | + |4: + | vldr d0, [BASE] + | bhi ->fff_fallback + |5: // Handle numbers. + | ldrd CARG34, [RA] + | vldr d1, [RA] + | cmp RA, RB + | bhs ->fff_resd + | checktp CARG4, LJ_TISNUM + | bhs >7 + |6: + | vcmp.f64 d0, d1 + | vmrs + | add RA, RA, #8 + | vmov..fcond.f64 d0, d1 + | b <5 + |7: // Convert integer to number and continue above. + | vmov s4, CARG3 + | bhi ->fff_fallback + | vcvt.f64.s32 d1, s4 + | b <6 + | + |.else + | + | .ffunc_1 name + | checktp CARG2, LJ_TISNUM + | mov RA, #8 + | bne >4 + |1: // Handle integers. + | ldrd CARG34, [BASE, RA] + | cmp RA, RC + | bhs ->fff_restv + | checktp CARG4, LJ_TISNUM + | bne >3 + | cmp CARG1, CARG3 + | add RA, RA, #8 + | mov..cond CARG1, CARG3 + | b <1 + |3: // Convert intermediate result to number and continue below. + | bhi ->fff_fallback + | bl extern __aeabi_i2d + | ldrd CARG34, [BASE, RA] + | b >6 + | + |4: + | bhi ->fff_fallback + |5: // Handle numbers. + | ldrd CARG34, [BASE, RA] + | cmp RA, RC + | bhs ->fff_restv + | checktp CARG4, LJ_TISNUM + | bhs >7 + |6: + | bl extern __aeabi_cdcmple + | add RA, RA, #8 + | mov..fcond CARG1, CARG3 + | mov..fcond CARG2, CARG4 + | b <5 + |7: // Convert integer to number and continue above. + | bhi ->fff_fallback + | strd CARG12, TMPD + | mov CARG1, CARG3 + | bl extern __aeabi_i2d + | ldrd CARG34, TMPD + | b <6 + |.endif + |.endmacro + | + | math_minmax math_min, gt, hi + | math_minmax math_max, lt, lo + | + |//-- String library ----------------------------------------------------- + | + |.ffunc string_byte // Only handle the 1-arg case here. + | ldrd CARG12, [BASE] + | ldr PC, [BASE, FRAME_PC] + | cmp NARGS8:RC, #8 + | checktpeq CARG2, LJ_TSTR // Need exactly 1 argument. + | bne ->fff_fallback + | ldr CARG3, STR:CARG1->len + | ldrb CARG1, STR:CARG1[1] // Access is always ok (NUL at end). + | mvn CARG2, #~LJ_TISNUM + | cmp CARG3, #0 + | moveq RC, #(0+1)*8 + | movne RC, #(1+1)*8 + | strd CARG12, [BASE, #-8] + | b ->fff_res + | + |.ffunc string_char // Only handle the 1-arg case here. + | ffgccheck + | ldrd CARG12, [BASE] + | ldr PC, [BASE, FRAME_PC] + | cmp NARGS8:RC, #8 // Need exactly 1 argument. + | checktpeq CARG2, LJ_TISNUM + | bicseq CARG4, CARG1, #255 + | mov CARG3, #1 + | bne ->fff_fallback + | str CARG1, TMPD + | mov CARG2, TMPDp // Points to stack. Little-endian. + |->fff_newstr: + | // CARG2 = str, CARG3 = len. + | str BASE, L->base + | mov CARG1, L + | str PC, SAVE_PC + | bl extern lj_str_new // (lua_State *L, char *str, size_t l) + |->fff_resstr: + | // Returns GCstr *. + | ldr BASE, L->base + | mvn CARG2, #~LJ_TSTR + | b ->fff_restv + | + |.ffunc string_sub + | ffgccheck + | ldrd CARG12, [BASE] + | ldrd CARG34, [BASE, #16] + | cmp NARGS8:RC, #16 + | mvn RB, #0 + | beq >1 + | blo ->fff_fallback + | checktp CARG4, LJ_TISNUM + | mov RB, CARG3 + | bne ->fff_fallback + |1: + | ldrd CARG34, [BASE, #8] + | checktp CARG2, LJ_TSTR + | ldreq CARG2, STR:CARG1->len + | checktpeq CARG4, LJ_TISNUM + | bne ->fff_fallback + | // CARG1 = str, CARG2 = str->len, CARG3 = start, RB = end + | add CARG4, CARG2, #1 + | cmp CARG3, #0 // if (start < 0) start += len+1 + | addlt CARG3, CARG3, CARG4 + | cmp CARG3, #1 // if (start < 1) start = 1 + | movlt CARG3, #1 + | cmp RB, #0 // if (end < 0) end += len+1 + | addlt RB, RB, CARG4 + | bic RB, RB, RB, asr #31 // if (end < 0) end = 0 + | cmp RB, CARG2 // if (end > len) end = len + | add CARG1, STR:CARG1, #sizeof(GCstr)-1 + | movgt RB, CARG2 + | add CARG2, CARG1, CARG3 + | subs CARG3, RB, CARG3 // len = end - start + | add CARG3, CARG3, #1 // len += 1 + | bge ->fff_newstr + |->fff_emptystr: + | sub STR:CARG1, DISPATCH, #-DISPATCH_GL(strempty) + | mvn CARG2, #~LJ_TSTR + | b ->fff_restv + | + |.macro ffstring_op, name + | .ffunc string_ .. name + | ffgccheck + | ldr CARG3, [BASE, #4] + | cmp NARGS8:RC, #8 + | ldr STR:CARG2, [BASE] + | blo ->fff_fallback + | sub SBUF:CARG1, DISPATCH, #-DISPATCH_GL(tmpbuf) + | checkstr CARG3, ->fff_fallback + | ldr CARG4, SBUF:CARG1->b + | str BASE, L->base + | str PC, SAVE_PC + | str L, SBUF:CARG1->L + | str CARG4, SBUF:CARG1->p + | bl extern lj_buf_putstr_ .. name + | bl extern lj_buf_tostr + | b ->fff_resstr + |.endmacro + | + |ffstring_op reverse + |ffstring_op lower + |ffstring_op upper + | + |//-- Bit library -------------------------------------------------------- + | + |// FP number to bit conversion for soft-float. Clobbers r0-r3. + |->vm_tobit_fb: + | bhi ->fff_fallback + |->vm_tobit: + | lsl RB, CARG2, #1 + | adds RB, RB, #0x00200000 + | movpl CARG1, #0 // |x| < 1? + | bxpl lr + | mvn CARG4, #0x3e0 + | subs RB, CARG4, RB, asr #21 + | bmi >1 // |x| >= 2^32? + | lsl CARG4, CARG2, #11 + | orr CARG4, CARG4, #0x80000000 + | orr CARG4, CARG4, CARG1, lsr #21 + | cmp CARG2, #0 + | lsr CARG1, CARG4, RB + | rsblt CARG1, CARG1, #0 + | bx lr + |1: + | add RB, RB, #21 + | lsr CARG4, CARG1, RB + | rsb RB, RB, #20 + | lsl CARG1, CARG2, #12 + | cmp CARG2, #0 + | orr CARG1, CARG4, CARG1, lsl RB + | rsblt CARG1, CARG1, #0 + | bx lr + | + |.macro .ffunc_bit, name + | .ffunc_1 bit_..name + | checktp CARG2, LJ_TISNUM + | blne ->vm_tobit_fb + |.endmacro + | + |.ffunc_bit tobit + | mvn CARG2, #~LJ_TISNUM + | b ->fff_restv + | + |.macro .ffunc_bit_op, name, ins + | .ffunc_bit name + | mov CARG3, CARG1 + | mov RA, #8 + |1: + | ldrd CARG12, [BASE, RA] + | cmp RA, NARGS8:RC + | add RA, RA, #8 + | bge >2 + | checktp CARG2, LJ_TISNUM + | blne ->vm_tobit_fb + | ins CARG3, CARG3, CARG1 + | b <1 + |.endmacro + | + |.ffunc_bit_op band, and + |.ffunc_bit_op bor, orr + |.ffunc_bit_op bxor, eor + | + |2: + | mvn CARG4, #~LJ_TISNUM + | ldr PC, [BASE, FRAME_PC] + | strd CARG34, [BASE, #-8] + | b ->fff_res1 + | + |.ffunc_bit bswap + | eor CARG3, CARG1, CARG1, ror #16 + | bic CARG3, CARG3, #0x00ff0000 + | ror CARG1, CARG1, #8 + | mvn CARG2, #~LJ_TISNUM + | eor CARG1, CARG1, CARG3, lsr #8 + | b ->fff_restv + | + |.ffunc_bit bnot + | mvn CARG1, CARG1 + | mvn CARG2, #~LJ_TISNUM + | b ->fff_restv + | + |.macro .ffunc_bit_sh, name, ins, shmod + | .ffunc bit_..name + | ldrd CARG12, [BASE, #8] + | cmp NARGS8:RC, #16 + | blo ->fff_fallback + | checktp CARG2, LJ_TISNUM + | blne ->vm_tobit_fb + |.if shmod == 0 + | and RA, CARG1, #31 + |.else + | rsb RA, CARG1, #0 + |.endif + | ldrd CARG12, [BASE] + | checktp CARG2, LJ_TISNUM + | blne ->vm_tobit_fb + | ins CARG1, CARG1, RA + | mvn CARG2, #~LJ_TISNUM + | b ->fff_restv + |.endmacro + | + |.ffunc_bit_sh lshift, lsl, 0 + |.ffunc_bit_sh rshift, lsr, 0 + |.ffunc_bit_sh arshift, asr, 0 + |.ffunc_bit_sh rol, ror, 1 + |.ffunc_bit_sh ror, ror, 0 + | + |//----------------------------------------------------------------------- + | + |->fff_fallback: // Call fast function fallback handler. + | // BASE = new base, RC = nargs*8 + | ldr CARG3, [BASE, FRAME_FUNC] + | ldr CARG2, L->maxstack + | add CARG1, BASE, NARGS8:RC + | ldr PC, [BASE, FRAME_PC] // Fallback may overwrite PC. + | str CARG1, L->top + | ldr CARG3, CFUNC:CARG3->f + | str BASE, L->base + | add CARG1, CARG1, #8*LUA_MINSTACK + | str PC, SAVE_PC // Redundant (but a defined value). + | cmp CARG1, CARG2 + | mov CARG1, L + | bhi >5 // Need to grow stack. + | blx CARG3 // (lua_State *L) + | // Either throws an error, or recovers and returns -1, 0 or nresults+1. + | ldr BASE, L->base + | cmp CRET1, #0 + | lsl RC, CRET1, #3 + | sub RA, BASE, #8 + | bgt ->fff_res // Returned nresults+1? + |1: // Returned 0 or -1: retry fast path. + | ldr CARG1, L->top + | ldr LFUNC:CARG3, [BASE, FRAME_FUNC] + | sub NARGS8:RC, CARG1, BASE + | bne ->vm_call_tail // Returned -1? + | ins_callt // Returned 0: retry fast path. + | + |// Reconstruct previous base for vmeta_call during tailcall. + |->vm_call_tail: + | ands CARG1, PC, #FRAME_TYPE + | bic CARG2, PC, #FRAME_TYPEP + | ldreq INS, [PC, #-4] + | andeq CARG2, MASKR8, INS, lsr #5 // Conditional decode_RA8. + | addeq CARG2, CARG2, #8 + | sub RB, BASE, CARG2 + | b ->vm_call_dispatch // Resolve again for tailcall. + | + |5: // Grow stack for fallback handler. + | mov CARG2, #LUA_MINSTACK + | bl extern lj_state_growstack // (lua_State *L, int n) + | ldr BASE, L->base + | cmp CARG1, CARG1 // Set zero-flag to force retry. + | b <1 + | + |->fff_gcstep: // Call GC step function. + | // BASE = new base, RC = nargs*8 + | mov RA, lr + | str BASE, L->base + | add CARG2, BASE, NARGS8:RC + | str PC, SAVE_PC // Redundant (but a defined value). + | str CARG2, L->top + | mov CARG1, L + | bl extern lj_gc_step // (lua_State *L) + | ldr BASE, L->base + | mov lr, RA // Help return address predictor. + | ldr CFUNC:CARG3, [BASE, FRAME_FUNC] + | bx lr + | + |//----------------------------------------------------------------------- + |//-- Special dispatch targets ------------------------------------------- + |//----------------------------------------------------------------------- + | + |->vm_record: // Dispatch target for recording phase. + |.if JIT + | ldrb CARG1, [DISPATCH, #DISPATCH_GL(hookmask)] + | tst CARG1, #HOOK_VMEVENT // No recording while in vmevent. + | bne >5 + | // Decrement the hookcount for consistency, but always do the call. + | ldr CARG2, [DISPATCH, #DISPATCH_GL(hookcount)] + | tst CARG1, #HOOK_ACTIVE + | bne >1 + | sub CARG2, CARG2, #1 + | tst CARG1, #LUA_MASKLINE|LUA_MASKCOUNT + | strne CARG2, [DISPATCH, #DISPATCH_GL(hookcount)] + | b >1 + |.endif + | + |->vm_rethook: // Dispatch target for return hooks. + | ldrb CARG1, [DISPATCH, #DISPATCH_GL(hookmask)] + | tst CARG1, #HOOK_ACTIVE // Hook already active? + | beq >1 + |5: // Re-dispatch to static ins. + | decode_OP OP, INS + | add OP, DISPATCH, OP, lsl #2 + | ldr pc, [OP, #GG_DISP2STATIC] + | + |->vm_inshook: // Dispatch target for instr/line hooks. + | ldrb CARG1, [DISPATCH, #DISPATCH_GL(hookmask)] + | ldr CARG2, [DISPATCH, #DISPATCH_GL(hookcount)] + | tst CARG1, #HOOK_ACTIVE // Hook already active? + | bne <5 + | tst CARG1, #LUA_MASKLINE|LUA_MASKCOUNT + | beq <5 + | subs CARG2, CARG2, #1 + | str CARG2, [DISPATCH, #DISPATCH_GL(hookcount)] + | beq >1 + | tst CARG1, #LUA_MASKLINE + | beq <5 + |1: + | mov CARG1, L + | str BASE, L->base + | mov CARG2, PC + | // SAVE_PC must hold the _previous_ PC. The callee updates it with PC. + | bl extern lj_dispatch_ins // (lua_State *L, const BCIns *pc) + |3: + | ldr BASE, L->base + |4: // Re-dispatch to static ins. + | ldrb OP, [PC, #-4] + | ldr INS, [PC, #-4] + | add OP, DISPATCH, OP, lsl #2 + | ldr OP, [OP, #GG_DISP2STATIC] + | decode_RA8 RA, INS + | decode_RD RC, INS + | bx OP + | + |->cont_hook: // Continue from hook yield. + | ldr CARG1, [CARG4, #-24] + | add PC, PC, #4 + | str CARG1, SAVE_MULTRES // Restore MULTRES for *M ins. + | b <4 + | + |->vm_hotloop: // Hot loop counter underflow. + |.if JIT + | ldr LFUNC:CARG3, [BASE, FRAME_FUNC] // Same as curr_topL(L). + | sub CARG1, DISPATCH, #-GG_DISP2J + | str PC, SAVE_PC + | ldr CARG3, LFUNC:CARG3->field_pc + | mov CARG2, PC + | str L, [DISPATCH, #DISPATCH_J(L)] + | ldrb CARG3, [CARG3, #PC2PROTO(framesize)] + | str BASE, L->base + | add CARG3, BASE, CARG3, lsl #3 + | str CARG3, L->top + | bl extern lj_trace_hot // (jit_State *J, const BCIns *pc) + | b <3 + |.endif + | + |->vm_callhook: // Dispatch target for call hooks. + | mov CARG2, PC + |.if JIT + | b >1 + |.endif + | + |->vm_hotcall: // Hot call counter underflow. + |.if JIT + | orr CARG2, PC, #1 + |1: + |.endif + | add CARG4, BASE, RC + | str PC, SAVE_PC + | mov CARG1, L + | str BASE, L->base + | sub RA, RA, BASE + | str CARG4, L->top + | bl extern lj_dispatch_call // (lua_State *L, const BCIns *pc) + | // Returns ASMFunction. + | ldr BASE, L->base + | ldr CARG4, L->top + | mov CARG2, #0 + | add RA, BASE, RA + | sub NARGS8:RC, CARG4, BASE + | str CARG2, SAVE_PC // Invalidate for subsequent line hook. + | ldr LFUNC:CARG3, [BASE, FRAME_FUNC] + | ldr INS, [PC, #-4] + | bx CRET1 + | + |->cont_stitch: // Trace stitching. + |.if JIT + | // RA = resultptr, CARG4 = meta base + | ldr RB, SAVE_MULTRES + | ldr INS, [PC, #-4] + | ldr TRACE:CARG3, [CARG4, #-24] // Save previous trace. + | subs RB, RB, #8 + | decode_RA8 RC, INS // Call base. + | beq >2 + |1: // Move results down. + | ldrd CARG12, [RA] + | add RA, RA, #8 + | subs RB, RB, #8 + | strd CARG12, [BASE, RC] + | add RC, RC, #8 + | bne <1 + |2: + | decode_RA8 RA, INS + | decode_RB8 RB, INS + | add RA, RA, RB + |3: + | cmp RA, RC + | mvn CARG2, #~LJ_TNIL + | bhi >9 // More results wanted? + | + | ldrh RA, TRACE:CARG3->traceno + | ldrh RC, TRACE:CARG3->link + | cmp RC, RA + | beq ->cont_nop // Blacklisted. + | cmp RC, #0 + | bne =>BC_JLOOP // Jump to stitched trace. + | + | // Stitch a new trace to the previous trace. + | str RA, [DISPATCH, #DISPATCH_J(exitno)] + | str L, [DISPATCH, #DISPATCH_J(L)] + | str BASE, L->base + | sub CARG1, DISPATCH, #-GG_DISP2J + | mov CARG2, PC + | bl extern lj_dispatch_stitch // (jit_State *J, const BCIns *pc) + | ldr BASE, L->base + | b ->cont_nop + | + |9: // Fill up results with nil. + | strd CARG12, [BASE, RC] + | add RC, RC, #8 + | b <3 + |.endif + | + |->vm_profhook: // Dispatch target for profiler hook. +#if LJ_HASPROFILE + | mov CARG1, L + | str BASE, L->base + | mov CARG2, PC + | bl extern lj_dispatch_profile // (lua_State *L, const BCIns *pc) + | // HOOK_PROFILE is off again, so re-dispatch to dynamic instruction. + | ldr BASE, L->base + | sub PC, PC, #4 + | b ->cont_nop +#endif + | + |//----------------------------------------------------------------------- + |//-- Trace exit handler ------------------------------------------------- + |//----------------------------------------------------------------------- + | + |->vm_exit_handler: + |.if JIT + | sub sp, sp, #12 + | push {r0,r1,r2,r3,r4,r5,r6,r7,r8,r9,r10,r11,r12} + | ldr CARG1, [sp, #64] // Load original value of lr. + | ldr DISPATCH, [lr] // Load DISPATCH. + | add CARG3, sp, #64 // Recompute original value of sp. + | mv_vmstate CARG4, EXIT + | str CARG3, [sp, #52] // Store sp in RID_SP + | st_vmstate CARG4 + | ldr CARG2, [CARG1, #-4]! // Get exit instruction. + | str CARG1, [sp, #56] // Store exit pc in RID_LR and RID_PC. + | str CARG1, [sp, #60] + |.if FPU + | vpush {d0-d15} + |.endif + | lsl CARG2, CARG2, #8 + | add CARG1, CARG1, CARG2, asr #6 + | ldr CARG2, [lr, #4] // Load exit stub group offset. + | sub CARG1, CARG1, lr + | ldr L, [DISPATCH, #DISPATCH_GL(cur_L)] + | add CARG1, CARG2, CARG1, lsr #2 // Compute exit number. + | ldr BASE, [DISPATCH, #DISPATCH_GL(jit_base)] + | str CARG1, [DISPATCH, #DISPATCH_J(exitno)] + | mov CARG4, #0 + | str BASE, L->base + | str L, [DISPATCH, #DISPATCH_J(L)] + | str CARG4, [DISPATCH, #DISPATCH_GL(jit_base)] + | sub CARG1, DISPATCH, #-GG_DISP2J + | mov CARG2, sp + | bl extern lj_trace_exit // (jit_State *J, ExitState *ex) + | // Returns MULTRES (unscaled) or negated error code. + | ldr CARG2, L->cframe + | ldr BASE, L->base + | bic CARG2, CARG2, #~CFRAME_RAWMASK // Use two steps: bic sp is deprecated. + | mov sp, CARG2 + | ldr PC, SAVE_PC // Get SAVE_PC. + | str L, SAVE_L // Set SAVE_L (on-trace resume/yield). + | b >1 + |.endif + |->vm_exit_interp: + | // CARG1 = MULTRES or negated error code, BASE, PC and DISPATCH set. + |.if JIT + | ldr L, SAVE_L + |1: + | cmp CARG1, #0 + | blt >9 // Check for error from exit. + | lsl RC, CARG1, #3 + | ldr LFUNC:CARG2, [BASE, FRAME_FUNC] + | str RC, SAVE_MULTRES + | mov CARG3, #0 + | str BASE, L->base + | ldr CARG2, LFUNC:CARG2->field_pc + | str CARG3, [DISPATCH, #DISPATCH_GL(jit_base)] + | mv_vmstate CARG4, INTERP + | ldr KBASE, [CARG2, #PC2PROTO(k)] + | // Modified copy of ins_next which handles function header dispatch, too. + | ldrb OP, [PC] + | mov MASKR8, #255 + | ldr INS, [PC], #4 + | lsl MASKR8, MASKR8, #3 // MASKR8 = 255*8. + | st_vmstate CARG4 + | cmp OP, #BC_FUNCC+2 // Fast function? + | bhs >4 + |2: + | cmp OP, #BC_FUNCF // Function header? + | ldr OP, [DISPATCH, OP, lsl #2] + | decode_RA8 RA, INS + | lsrlo RC, INS, #16 // No: Decode operands A*8 and D. + | subhs RC, RC, #8 + | addhs RA, RA, BASE // Yes: RA = BASE+framesize*8, RC = nargs*8 + | ldrhs CARG3, [BASE, FRAME_FUNC] + | bx OP + | + |4: // Check frame below fast function. + | ldr CARG1, [BASE, FRAME_PC] + | ands CARG2, CARG1, #FRAME_TYPE + | bne <2 // Trace stitching continuation? + | // Otherwise set KBASE for Lua function below fast function. + | ldr CARG3, [CARG1, #-4] + | decode_RA8 CARG1, CARG3 + | sub CARG2, BASE, CARG1 + | ldr LFUNC:CARG3, [CARG2, #-16] + | ldr CARG3, LFUNC:CARG3->field_pc + | ldr KBASE, [CARG3, #PC2PROTO(k)] + | b <2 + | + |9: // Rethrow error from the right C frame. + | rsb CARG2, CARG1, #0 + | mov CARG1, L + | bl extern lj_err_throw // (lua_State *L, int errcode) + |.endif + | + |//----------------------------------------------------------------------- + |//-- Math helper functions ---------------------------------------------- + |//----------------------------------------------------------------------- + | + |// FP value rounding. Called from JIT code. + |// + |// double lj_vm_floor/ceil/trunc(double x); + |.macro vm_round, func, hf + |.if hf == 1 + | vmov CARG1, CARG2, d0 + |.endif + | lsl CARG3, CARG2, #1 + | adds RB, CARG3, #0x00200000 + | bpl >2 // |x| < 1? + | mvn CARG4, #0x3cc + | subs RB, CARG4, RB, asr #21 // 2^0: RB = 51, 2^51: RB = 0. + | bxlo lr // |x| >= 2^52: done. + | mvn CARG4, #1 + | bic CARG3, CARG1, CARG4, lsl RB // ztest = lo & ~lomask + | and CARG1, CARG1, CARG4, lsl RB // lo &= lomask + | subs RB, RB, #32 + | bicpl CARG4, CARG2, CARG4, lsl RB // |x| <= 2^20: ztest |= hi & ~himask + | orrpl CARG3, CARG3, CARG4 + | mvnpl CARG4, #1 + | andpl CARG2, CARG2, CARG4, lsl RB // |x| <= 2^20: hi &= himask + |.if "func" == "floor" + | tst CARG3, CARG2, asr #31 // iszero = ((ztest & signmask) == 0) + |.else + | bics CARG3, CARG3, CARG2, asr #31 // iszero = ((ztest & ~signmask) == 0) + |.endif + |.if hf == 1 + | vmoveq d0, CARG1, CARG2 + |.endif + | bxeq lr // iszero: done. + | mvn CARG4, #1 + | cmp RB, #0 + | lslpl CARG3, CARG4, RB + | mvnmi CARG3, #0 + | add RB, RB, #32 + | subs CARG1, CARG1, CARG4, lsl RB // lo = lo-lomask + | sbc CARG2, CARG2, CARG3 // hi = hi-himask+carry + |.if hf == 1 + | vmov d0, CARG1, CARG2 + |.endif + | bx lr + | + |2: // |x| < 1: + | bxcs lr // |x| is not finite. + | orr CARG3, CARG3, CARG1 // ztest = (2*hi) | lo + |.if "func" == "floor" + | tst CARG3, CARG2, asr #31 // iszero = ((ztest & signmask) == 0) + |.else + | bics CARG3, CARG3, CARG2, asr #31 // iszero = ((ztest & ~signmask) == 0) + |.endif + | mov CARG1, #0 // lo = 0 + | and CARG2, CARG2, #0x80000000 + | ldrne CARG4, <9 // hi = sign(x) | (iszero ? 0.0 : 1.0) + | orrne CARG2, CARG2, CARG4 + |.if hf == 1 + | vmov d0, CARG1, CARG2 + |.endif + | bx lr + |.endmacro + | + |9: + | .long 0x3ff00000 // hiword(+1.0) + | + |->vm_floor: + |.if HFABI + | vm_round floor, 1 + |.endif + |->vm_floor_sf: + | vm_round floor, 0 + | + |->vm_ceil: + |.if HFABI + | vm_round ceil, 1 + |.endif + |->vm_ceil_sf: + | vm_round ceil, 0 + | + |.macro vm_trunc, hf + |.if JIT + |.if hf == 1 + | vmov CARG1, CARG2, d0 + |.endif + | lsl CARG3, CARG2, #1 + | adds RB, CARG3, #0x00200000 + | andpl CARG2, CARG2, #0x80000000 // |x| < 1? hi = sign(x), lo = 0. + | movpl CARG1, #0 + |.if hf == 1 + | vmovpl d0, CARG1, CARG2 + |.endif + | bxpl lr + | mvn CARG4, #0x3cc + | subs RB, CARG4, RB, asr #21 // 2^0: RB = 51, 2^51: RB = 0. + | bxlo lr // |x| >= 2^52: already done. + | mvn CARG4, #1 + | and CARG1, CARG1, CARG4, lsl RB // lo &= lomask + | subs RB, RB, #32 + | andpl CARG2, CARG2, CARG4, lsl RB // |x| <= 2^20: hi &= himask + |.if hf == 1 + | vmov d0, CARG1, CARG2 + |.endif + | bx lr + |.endif + |.endmacro + | + |->vm_trunc: + |.if HFABI + | vm_trunc 1 + |.endif + |->vm_trunc_sf: + | vm_trunc 0 + | + | // double lj_vm_mod(double dividend, double divisor); + |->vm_mod: + |.if FPU + | // Special calling convention. Also, RC (r11) is not preserved. + | vdiv.f64 d0, d6, d7 + | mov RC, lr + | vmov CARG1, CARG2, d0 + | bl ->vm_floor_sf + | vmov d0, CARG1, CARG2 + | vmul.f64 d0, d0, d7 + | mov lr, RC + | vsub.f64 d6, d6, d0 + | bx lr + |.else + | push {r0, r1, r2, r3, r4, lr} + | bl extern __aeabi_ddiv + | bl ->vm_floor_sf + | ldrd CARG34, [sp, #8] + | bl extern __aeabi_dmul + | ldrd CARG34, [sp] + | eor CARG2, CARG2, #0x80000000 + | bl extern __aeabi_dadd + | add sp, sp, #20 + | pop {pc} + |.endif + | + | // int lj_vm_modi(int dividend, int divisor); + |->vm_modi: + | ands RB, CARG1, #0x80000000 + | rsbmi CARG1, CARG1, #0 // a = |dividend| + | eor RB, RB, CARG2, asr #1 // Keep signdiff and sign(divisor). + | cmp CARG2, #0 + | rsbmi CARG2, CARG2, #0 // b = |divisor| + | subs CARG4, CARG2, #1 + | cmpne CARG1, CARG2 + | moveq CARG1, #0 // if (b == 1 || a == b) a = 0 + | tsthi CARG2, CARG4 + | andeq CARG1, CARG1, CARG4 // else if ((b & (b-1)) == 0) a &= b-1 + | bls >1 + | // Use repeated subtraction to get the remainder. + | clz CARG3, CARG1 + | clz CARG4, CARG2 + | sub CARG4, CARG4, CARG3 + | rsbs CARG3, CARG4, #31 // entry = (31-(clz(b)-clz(a)))*8 + | addne pc, pc, CARG3, lsl #3 // Duff's device. + | nop + { + int i; + for (i = 31; i >= 0; i--) { + | cmp CARG1, CARG2, lsl #i + | subhs CARG1, CARG1, CARG2, lsl #i + } + } + |1: + | cmp CARG1, #0 + | cmpne RB, #0 + | submi CARG1, CARG1, CARG2 // if (y != 0 && signdiff) y = y - b + | eors CARG2, CARG1, RB, lsl #1 + | rsbmi CARG1, CARG1, #0 // if (sign(divisor) != sign(y)) y = -y + | bx lr + | + |//----------------------------------------------------------------------- + |//-- Miscellaneous functions -------------------------------------------- + |//----------------------------------------------------------------------- + | + |//----------------------------------------------------------------------- + |//-- FFI helper functions ----------------------------------------------- + |//----------------------------------------------------------------------- + | + |// Handler for callback functions. + |// Saveregs already performed. Callback slot number in [sp], g in r12. + |->vm_ffi_callback: + |.if FFI + |.type CTSTATE, CTState, PC + | ldr CTSTATE, GL:r12->ctype_state + | add DISPATCH, r12, #GG_G2DISP + |.if FPU + | str r4, SAVE_R4 + | add r4, sp, CFRAME_SPACE+4+8*8 + | vstmdb r4!, {d8-d15} + |.endif + |.if HFABI + | add r12, CTSTATE, #offsetof(CTState, cb.fpr[8]) + |.endif + | strd CARG34, CTSTATE->cb.gpr[2] + | strd CARG12, CTSTATE->cb.gpr[0] + |.if HFABI + | vstmdb r12!, {d0-d7} + |.endif + | ldr CARG4, [sp] + | add CARG3, sp, #CFRAME_SIZE + | mov CARG1, CTSTATE + | lsr CARG4, CARG4, #3 + | str CARG3, CTSTATE->cb.stack + | mov CARG2, sp + | str CARG4, CTSTATE->cb.slot + | str CTSTATE, SAVE_PC // Any value outside of bytecode is ok. + | bl extern lj_ccallback_enter // (CTState *cts, void *cf) + | // Returns lua_State *. + | ldr BASE, L:CRET1->base + | mv_vmstate CARG2, INTERP + | ldr RC, L:CRET1->top + | mov MASKR8, #255 + | ldr LFUNC:CARG3, [BASE, FRAME_FUNC] + | mov L, CRET1 + | sub RC, RC, BASE + | lsl MASKR8, MASKR8, #3 // MASKR8 = 255*8. + | st_vmstate CARG2 + | ins_callt + |.endif + | + |->cont_ffi_callback: // Return from FFI callback. + |.if FFI + | ldr CTSTATE, [DISPATCH, #DISPATCH_GL(ctype_state)] + | str BASE, L->base + | str CARG4, L->top + | str L, CTSTATE->L + | mov CARG1, CTSTATE + | mov CARG2, RA + | bl extern lj_ccallback_leave // (CTState *cts, TValue *o) + | ldrd CARG12, CTSTATE->cb.gpr[0] + |.if HFABI + | vldr d0, CTSTATE->cb.fpr[0] + |.endif + | b ->vm_leave_unw + |.endif + | + |->vm_ffi_call: // Call C function via FFI. + | // Caveat: needs special frame unwinding, see below. + |.if FFI + | .type CCSTATE, CCallState, r4 + | push {CCSTATE, r5, r11, lr} + | mov CCSTATE, CARG1 + | ldr CARG1, CCSTATE:CARG1->spadj + | ldrb CARG2, CCSTATE->nsp + | add CARG3, CCSTATE, #offsetof(CCallState, stack) + |.if HFABI + | add RB, CCSTATE, #offsetof(CCallState, fpr[0]) + |.endif + | mov r11, sp + | sub sp, sp, CARG1 // Readjust stack. + | subs CARG2, CARG2, #1 + |.if HFABI + | vldm RB, {d0-d7} + |.endif + | ldr RB, CCSTATE->func + | bmi >2 + |1: // Copy stack slots. + | ldr CARG4, [CARG3, CARG2, lsl #2] + | str CARG4, [sp, CARG2, lsl #2] + | subs CARG2, CARG2, #1 + | bpl <1 + |2: + | ldrd CARG12, CCSTATE->gpr[0] + | ldrd CARG34, CCSTATE->gpr[2] + | blx RB + | mov sp, r11 + |.if HFABI + | add r12, CCSTATE, #offsetof(CCallState, fpr[4]) + |.endif + | strd CRET1, CCSTATE->gpr[0] + |.if HFABI + | vstmdb r12!, {d0-d3} + |.endif + | pop {CCSTATE, r5, r11, pc} + |.endif + |// Note: vm_ffi_call must be the last function in this object file! + | + |//----------------------------------------------------------------------- +} + +/* Generate the code for a single instruction. */ +static void build_ins(BuildCtx *ctx, BCOp op, int defop) +{ + int vk = 0; + |=>defop: + + switch (op) { + + /* -- Comparison ops ---------------------------------------------------- */ + + /* Remember: all ops branch for a true comparison, fall through otherwise. */ + + case BC_ISLT: case BC_ISGE: case BC_ISLE: case BC_ISGT: + | // RA = src1*8, RC = src2, JMP with RC = target + | lsl RC, RC, #3 + | ldrd CARG12, [RA, BASE]! + | ldrh RB, [PC, #2] + | ldrd CARG34, [RC, BASE]! + | add PC, PC, #4 + | add RB, PC, RB, lsl #2 + | checktp CARG2, LJ_TISNUM + | bne >3 + | checktp CARG4, LJ_TISNUM + | bne >4 + | cmp CARG1, CARG3 + if (op == BC_ISLT) { + | sublt PC, RB, #0x20000 + } else if (op == BC_ISGE) { + | subge PC, RB, #0x20000 + } else if (op == BC_ISLE) { + | suble PC, RB, #0x20000 + } else { + | subgt PC, RB, #0x20000 + } + |1: + | ins_next + | + |3: // CARG12 is not an integer. + |.if FPU + | vldr d0, [RA] + | bhi ->vmeta_comp + | // d0 is a number. + | checktp CARG4, LJ_TISNUM + | vldr d1, [RC] + | blo >5 + | bhi ->vmeta_comp + | // d0 is a number, CARG3 is an integer. + | vmov s4, CARG3 + | vcvt.f64.s32 d1, s4 + | b >5 + |4: // CARG1 is an integer, CARG34 is not an integer. + | vldr d1, [RC] + | bhi ->vmeta_comp + | // CARG1 is an integer, d1 is a number. + | vmov s4, CARG1 + | vcvt.f64.s32 d0, s4 + |5: // d0 and d1 are numbers. + | vcmp.f64 d0, d1 + | vmrs + | // To preserve NaN semantics GE/GT branch on unordered, but LT/LE don't. + if (op == BC_ISLT) { + | sublo PC, RB, #0x20000 + } else if (op == BC_ISGE) { + | subhs PC, RB, #0x20000 + } else if (op == BC_ISLE) { + | subls PC, RB, #0x20000 + } else { + | subhi PC, RB, #0x20000 + } + | b <1 + |.else + | bhi ->vmeta_comp + | // CARG12 is a number. + | checktp CARG4, LJ_TISNUM + | movlo RA, RB // Save RB. + | blo >5 + | bhi ->vmeta_comp + | // CARG12 is a number, CARG3 is an integer. + | mov CARG1, CARG3 + | mov RC, RA + | mov RA, RB // Save RB. + | bl extern __aeabi_i2d + | mov CARG3, CARG1 + | mov CARG4, CARG2 + | ldrd CARG12, [RC] // Restore first operand. + | b >5 + |4: // CARG1 is an integer, CARG34 is not an integer. + | bhi ->vmeta_comp + | // CARG1 is an integer, CARG34 is a number. + | mov RA, RB // Save RB. + | bl extern __aeabi_i2d + | ldrd CARG34, [RC] // Restore second operand. + |5: // CARG12 and CARG34 are numbers. + | bl extern __aeabi_cdcmple + | // To preserve NaN semantics GE/GT branch on unordered, but LT/LE don't. + if (op == BC_ISLT) { + | sublo PC, RA, #0x20000 + } else if (op == BC_ISGE) { + | subhs PC, RA, #0x20000 + } else if (op == BC_ISLE) { + | subls PC, RA, #0x20000 + } else { + | subhi PC, RA, #0x20000 + } + | b <1 + |.endif + break; + + case BC_ISEQV: case BC_ISNEV: + vk = op == BC_ISEQV; + | // RA = src1*8, RC = src2, JMP with RC = target + | lsl RC, RC, #3 + | ldrd CARG12, [RA, BASE]! + | ldrh RB, [PC, #2] + | ldrd CARG34, [RC, BASE]! + | add PC, PC, #4 + | add RB, PC, RB, lsl #2 + | checktp CARG2, LJ_TISNUM + | cmnls CARG4, #-LJ_TISNUM + if (vk) { + | bls ->BC_ISEQN_Z + } else { + | bls ->BC_ISNEN_Z + } + | // Either or both types are not numbers. + |.if FFI + | checktp CARG2, LJ_TCDATA + | checktpne CARG4, LJ_TCDATA + | beq ->vmeta_equal_cd + |.endif + | cmp CARG2, CARG4 // Compare types. + | bne >2 // Not the same type? + | checktp CARG2, LJ_TISPRI + | bhs >1 // Same type and primitive type? + | + | // Same types and not a primitive type. Compare GCobj or pvalue. + | cmp CARG1, CARG3 + if (vk) { + | bne >3 // Different GCobjs or pvalues? + |1: // Branch if same. + | sub PC, RB, #0x20000 + |2: // Different. + | ins_next + |3: + | checktp CARG2, LJ_TISTABUD + | bhi <2 // Different objects and not table/ud? + } else { + | beq >1 // Same GCobjs or pvalues? + | checktp CARG2, LJ_TISTABUD + | bhi >2 // Different objects and not table/ud? + } + | // Different tables or userdatas. Need to check __eq metamethod. + | // Field metatable must be at same offset for GCtab and GCudata! + | ldr TAB:RA, TAB:CARG1->metatable + | cmp TAB:RA, #0 + if (vk) { + | beq <2 // No metatable? + } else { + | beq >2 // No metatable? + } + | ldrb RA, TAB:RA->nomm + | mov CARG4, #1-vk // ne = 0 or 1. + | mov CARG2, CARG1 + | tst RA, #1<vmeta_equal // 'no __eq' flag not set? + if (vk) { + | b <2 + } else { + |2: // Branch if different. + | sub PC, RB, #0x20000 + |1: // Same. + | ins_next + } + break; + + case BC_ISEQS: case BC_ISNES: + vk = op == BC_ISEQS; + | // RA = src*8, RC = str_const (~), JMP with RC = target + | mvn RC, RC + | ldrd CARG12, [BASE, RA] + | ldrh RB, [PC, #2] + | ldr STR:CARG3, [KBASE, RC, lsl #2] + | add PC, PC, #4 + | add RB, PC, RB, lsl #2 + | checktp CARG2, LJ_TSTR + |.if FFI + | bne >7 + | cmp CARG1, CARG3 + |.else + | cmpeq CARG1, CARG3 + |.endif + if (vk) { + | subeq PC, RB, #0x20000 + |1: + } else { + |1: + | subne PC, RB, #0x20000 + } + | ins_next + | + |.if FFI + |7: + | checktp CARG2, LJ_TCDATA + | bne <1 + | b ->vmeta_equal_cd + |.endif + break; + + case BC_ISEQN: case BC_ISNEN: + vk = op == BC_ISEQN; + | // RA = src*8, RC = num_const (~), JMP with RC = target + | lsl RC, RC, #3 + | ldrd CARG12, [RA, BASE]! + | ldrh RB, [PC, #2] + | ldrd CARG34, [RC, KBASE]! + | add PC, PC, #4 + | add RB, PC, RB, lsl #2 + if (vk) { + |->BC_ISEQN_Z: + } else { + |->BC_ISNEN_Z: + } + | checktp CARG2, LJ_TISNUM + | bne >3 + | checktp CARG4, LJ_TISNUM + | bne >4 + | cmp CARG1, CARG3 + if (vk) { + | subeq PC, RB, #0x20000 + |1: + } else { + |1: + | subne PC, RB, #0x20000 + } + |2: + | ins_next + | + |3: // CARG12 is not an integer. + |.if FFI + | bhi >7 + |.else + if (!vk) { + | subhi PC, RB, #0x20000 + } + | bhi <2 + |.endif + |.if FPU + | checktp CARG4, LJ_TISNUM + | vmov s4, CARG3 + | vldr d0, [RA] + | vldrlo d1, [RC] + | vcvths.f64.s32 d1, s4 + | b >5 + |4: // CARG1 is an integer, d1 is a number. + | vmov s4, CARG1 + | vldr d1, [RC] + | vcvt.f64.s32 d0, s4 + |5: // d0 and d1 are numbers. + | vcmp.f64 d0, d1 + | vmrs + if (vk) { + | subeq PC, RB, #0x20000 + } else { + | subne PC, RB, #0x20000 + } + | b <2 + |.else + | // CARG12 is a number. + | checktp CARG4, LJ_TISNUM + | movlo RA, RB // Save RB. + | blo >5 + | // CARG12 is a number, CARG3 is an integer. + | mov CARG1, CARG3 + | mov RC, RA + |4: // CARG1 is an integer, CARG34 is a number. + | mov RA, RB // Save RB. + | bl extern __aeabi_i2d + | ldrd CARG34, [RC] // Restore other operand. + |5: // CARG12 and CARG34 are numbers. + | bl extern __aeabi_cdcmpeq + if (vk) { + | subeq PC, RA, #0x20000 + } else { + | subne PC, RA, #0x20000 + } + | b <2 + |.endif + | + |.if FFI + |7: + | checktp CARG2, LJ_TCDATA + | bne <1 + | b ->vmeta_equal_cd + |.endif + break; + + case BC_ISEQP: case BC_ISNEP: + vk = op == BC_ISEQP; + | // RA = src*8, RC = primitive_type (~), JMP with RC = target + | ldrd CARG12, [BASE, RA] + | ldrh RB, [PC, #2] + | add PC, PC, #4 + | mvn RC, RC + | add RB, PC, RB, lsl #2 + |.if FFI + | checktp CARG2, LJ_TCDATA + | beq ->vmeta_equal_cd + |.endif + | cmp CARG2, RC + if (vk) { + | subeq PC, RB, #0x20000 + } else { + | subne PC, RB, #0x20000 + } + | ins_next + break; + + /* -- Unary test and copy ops ------------------------------------------- */ + + case BC_ISTC: case BC_ISFC: case BC_IST: case BC_ISF: + | // RA = dst*8 or unused, RC = src, JMP with RC = target + | add RC, BASE, RC, lsl #3 + | ldrh RB, [PC, #2] + | ldrd CARG12, [RC] + | add PC, PC, #4 + | add RB, PC, RB, lsl #2 + | checktp CARG2, LJ_TTRUE + if (op == BC_ISTC || op == BC_IST) { + | subls PC, RB, #0x20000 + if (op == BC_ISTC) { + | strdls CARG12, [BASE, RA] + } + } else { + | subhi PC, RB, #0x20000 + if (op == BC_ISFC) { + | strdhi CARG12, [BASE, RA] + } + } + | ins_next + break; + + case BC_ISTYPE: + | // RA = src*8, RC = -type + | ldrd CARG12, [BASE, RA] + | ins_next1 + | cmn CARG2, RC + | ins_next2 + | bne ->vmeta_istype + | ins_next3 + break; + case BC_ISNUM: + | // RA = src*8, RC = -(TISNUM-1) + | ldrd CARG12, [BASE, RA] + | ins_next1 + | checktp CARG2, LJ_TISNUM + | ins_next2 + | bhs ->vmeta_istype + | ins_next3 + break; + + /* -- Unary ops --------------------------------------------------------- */ + + case BC_MOV: + | // RA = dst*8, RC = src + | lsl RC, RC, #3 + | ins_next1 + | ldrd CARG12, [BASE, RC] + | ins_next2 + | strd CARG12, [BASE, RA] + | ins_next3 + break; + case BC_NOT: + | // RA = dst*8, RC = src + | add RC, BASE, RC, lsl #3 + | ins_next1 + | ldr CARG1, [RC, #4] + | add RA, BASE, RA + | ins_next2 + | checktp CARG1, LJ_TTRUE + | mvnls CARG2, #~LJ_TFALSE + | mvnhi CARG2, #~LJ_TTRUE + | str CARG2, [RA, #4] + | ins_next3 + break; + case BC_UNM: + | // RA = dst*8, RC = src + | lsl RC, RC, #3 + | ldrd CARG12, [BASE, RC] + | ins_next1 + | ins_next2 + | checktp CARG2, LJ_TISNUM + | bhi ->vmeta_unm + | eorne CARG2, CARG2, #0x80000000 + | bne >5 + | rsbseq CARG1, CARG1, #0 + | ldrdvs CARG12, >9 + |5: + | strd CARG12, [BASE, RA] + | ins_next3 + | + |.align 8 + |9: + | .long 0x00000000, 0x41e00000 // 2^31. + break; + case BC_LEN: + | // RA = dst*8, RC = src + | lsl RC, RC, #3 + | ldrd CARG12, [BASE, RC] + | checkstr CARG2, >2 + | ldr CARG1, STR:CARG1->len + |1: + | mvn CARG2, #~LJ_TISNUM + | ins_next1 + | ins_next2 + | strd CARG12, [BASE, RA] + | ins_next3 + |2: + | checktab CARG2, ->vmeta_len +#if LJ_52 + | ldr TAB:CARG3, TAB:CARG1->metatable + | cmp TAB:CARG3, #0 + | bne >9 + |3: +#endif + |->BC_LEN_Z: + | .IOS mov RC, BASE + | bl extern lj_tab_len // (GCtab *t) + | // Returns uint32_t (but less than 2^31). + | .IOS mov BASE, RC + | b <1 +#if LJ_52 + |9: + | ldrb CARG4, TAB:CARG3->nomm + | tst CARG4, #1<vmeta_len +#endif + break; + + /* -- Binary ops -------------------------------------------------------- */ + + |.macro ins_arithcheck, cond, ncond, target + ||if (vk == 1) { + | cmn CARG4, #-LJ_TISNUM + | cmn..cond CARG2, #-LJ_TISNUM + ||} else { + | cmn CARG2, #-LJ_TISNUM + | cmn..cond CARG4, #-LJ_TISNUM + ||} + | b..ncond target + |.endmacro + |.macro ins_arithcheck_int, target + | ins_arithcheck eq, ne, target + |.endmacro + |.macro ins_arithcheck_num, target + | ins_arithcheck lo, hs, target + |.endmacro + | + |.macro ins_arithpre + | decode_RB8 RB, INS + | decode_RC8 RC, INS + | // RA = dst*8, RB = src1*8, RC = src2*8 | num_const*8 + ||vk = ((int)op - BC_ADDVN) / (BC_ADDNV-BC_ADDVN); + ||switch (vk) { + ||case 0: + | .if FPU + | ldrd CARG12, [RB, BASE]! + | ldrd CARG34, [RC, KBASE]! + | .else + | ldrd CARG12, [BASE, RB] + | ldrd CARG34, [KBASE, RC] + | .endif + || break; + ||case 1: + | .if FPU + | ldrd CARG34, [RB, BASE]! + | ldrd CARG12, [RC, KBASE]! + | .else + | ldrd CARG34, [BASE, RB] + | ldrd CARG12, [KBASE, RC] + | .endif + || break; + ||default: + | .if FPU + | ldrd CARG12, [RB, BASE]! + | ldrd CARG34, [RC, BASE]! + | .else + | ldrd CARG12, [BASE, RB] + | ldrd CARG34, [BASE, RC] + | .endif + || break; + ||} + |.endmacro + | + |.macro ins_arithpre_fpu, reg1, reg2 + |.if FPU + ||if (vk == 1) { + | vldr reg2, [RB] + | vldr reg1, [RC] + ||} else { + | vldr reg1, [RB] + | vldr reg2, [RC] + ||} + |.endif + |.endmacro + | + |.macro ins_arithpost_fpu, reg + | ins_next1 + | add RA, BASE, RA + | ins_next2 + | vstr reg, [RA] + | ins_next3 + |.endmacro + | + |.macro ins_arithfallback, ins + ||switch (vk) { + ||case 0: + | ins ->vmeta_arith_vn + || break; + ||case 1: + | ins ->vmeta_arith_nv + || break; + ||default: + | ins ->vmeta_arith_vv + || break; + ||} + |.endmacro + | + |.macro ins_arithdn, intins, fpins, fpcall + | ins_arithpre + |.if "intins" ~= "vm_modi" and not FPU + | ins_next1 + |.endif + | ins_arithcheck_int >5 + |.if "intins" == "smull" + | smull CARG1, RC, CARG3, CARG1 + | cmp RC, CARG1, asr #31 + | ins_arithfallback bne + |.elif "intins" == "vm_modi" + | movs CARG2, CARG3 + | ins_arithfallback beq + | bl ->vm_modi + | mvn CARG2, #~LJ_TISNUM + |.else + | intins CARG1, CARG1, CARG3 + | ins_arithfallback bvs + |.endif + |4: + |.if "intins" == "vm_modi" or FPU + | ins_next1 + |.endif + | ins_next2 + | strd CARG12, [BASE, RA] + | ins_next3 + |5: // FP variant. + | ins_arithpre_fpu d6, d7 + | ins_arithfallback ins_arithcheck_num + |.if FPU + |.if "intins" == "vm_modi" + | bl fpcall + |.else + | fpins d6, d6, d7 + |.endif + | ins_arithpost_fpu d6 + |.else + | bl fpcall + |.if "intins" ~= "vm_modi" + | ins_next1 + |.endif + | b <4 + |.endif + |.endmacro + | + |.macro ins_arithfp, fpins, fpcall + | ins_arithpre + |.if "fpins" ~= "extern" or HFABI + | ins_arithpre_fpu d0, d1 + |.endif + | ins_arithfallback ins_arithcheck_num + |.if "fpins" == "extern" + | .IOS mov RC, BASE + | bl fpcall + | .IOS mov BASE, RC + |.elif FPU + | fpins d0, d0, d1 + |.else + | bl fpcall + |.endif + |.if ("fpins" ~= "extern" or HFABI) and FPU + | ins_arithpost_fpu d0 + |.else + | ins_next1 + | ins_next2 + | strd CARG12, [BASE, RA] + | ins_next3 + |.endif + |.endmacro + + case BC_ADDVN: case BC_ADDNV: case BC_ADDVV: + | ins_arithdn adds, vadd.f64, extern __aeabi_dadd + break; + case BC_SUBVN: case BC_SUBNV: case BC_SUBVV: + | ins_arithdn subs, vsub.f64, extern __aeabi_dsub + break; + case BC_MULVN: case BC_MULNV: case BC_MULVV: + | ins_arithdn smull, vmul.f64, extern __aeabi_dmul + break; + case BC_DIVVN: case BC_DIVNV: case BC_DIVVV: + | ins_arithfp vdiv.f64, extern __aeabi_ddiv + break; + case BC_MODVN: case BC_MODNV: case BC_MODVV: + | ins_arithdn vm_modi, vm_mod, ->vm_mod + break; + case BC_POW: + | // NYI: (partial) integer arithmetic. + | ins_arithfp extern, extern pow + break; + + case BC_CAT: + | decode_RB8 RC, INS + | decode_RC8 RB, INS + | // RA = dst*8, RC = src_start*8, RB = src_end*8 (note: RB/RC swapped!) + | sub CARG3, RB, RC + | str BASE, L->base + | add CARG2, BASE, RB + |->BC_CAT_Z: + | // RA = dst*8, RC = src_start*8, CARG2 = top-1 + | mov CARG1, L + | str PC, SAVE_PC + | lsr CARG3, CARG3, #3 + | bl extern lj_meta_cat // (lua_State *L, TValue *top, int left) + | // Returns NULL (finished) or TValue * (metamethod). + | ldr BASE, L->base + | cmp CRET1, #0 + | bne ->vmeta_binop + | ldrd CARG34, [BASE, RC] + | ins_next1 + | ins_next2 + | strd CARG34, [BASE, RA] // Copy result to RA. + | ins_next3 + break; + + /* -- Constant ops ------------------------------------------------------ */ + + case BC_KSTR: + | // RA = dst*8, RC = str_const (~) + | mvn RC, RC + | ins_next1 + | ldr CARG1, [KBASE, RC, lsl #2] + | mvn CARG2, #~LJ_TSTR + | ins_next2 + | strd CARG12, [BASE, RA] + | ins_next3 + break; + case BC_KCDATA: + |.if FFI + | // RA = dst*8, RC = cdata_const (~) + | mvn RC, RC + | ins_next1 + | ldr CARG1, [KBASE, RC, lsl #2] + | mvn CARG2, #~LJ_TCDATA + | ins_next2 + | strd CARG12, [BASE, RA] + | ins_next3 + |.endif + break; + case BC_KSHORT: + | // RA = dst*8, (RC = int16_literal) + | mov CARG1, INS, asr #16 // Refetch sign-extended reg. + | mvn CARG2, #~LJ_TISNUM + | ins_next1 + | ins_next2 + | strd CARG12, [BASE, RA] + | ins_next3 + break; + case BC_KNUM: + | // RA = dst*8, RC = num_const + | lsl RC, RC, #3 + | ins_next1 + | ldrd CARG12, [KBASE, RC] + | ins_next2 + | strd CARG12, [BASE, RA] + | ins_next3 + break; + case BC_KPRI: + | // RA = dst*8, RC = primitive_type (~) + | add RA, BASE, RA + | mvn RC, RC + | ins_next1 + | ins_next2 + | str RC, [RA, #4] + | ins_next3 + break; + case BC_KNIL: + | // RA = base*8, RC = end + | add RA, BASE, RA + | add RC, BASE, RC, lsl #3 + | mvn CARG1, #~LJ_TNIL + | str CARG1, [RA, #4] + | add RA, RA, #8 + |1: + | str CARG1, [RA, #4] + | cmp RA, RC + | add RA, RA, #8 + | blt <1 + | ins_next_ + break; + + /* -- Upvalue and function ops ------------------------------------------ */ + + case BC_UGET: + | // RA = dst*8, RC = uvnum + | ldr LFUNC:CARG2, [BASE, FRAME_FUNC] + | lsl RC, RC, #2 + | add RC, RC, #offsetof(GCfuncL, uvptr) + | ldr UPVAL:CARG2, [LFUNC:CARG2, RC] + | ldr CARG2, UPVAL:CARG2->v + | ldrd CARG34, [CARG2] + | ins_next1 + | ins_next2 + | strd CARG34, [BASE, RA] + | ins_next3 + break; + case BC_USETV: + | // RA = uvnum*8, RC = src + | ldr LFUNC:CARG2, [BASE, FRAME_FUNC] + | lsr RA, RA, #1 + | add RA, RA, #offsetof(GCfuncL, uvptr) + | lsl RC, RC, #3 + | ldr UPVAL:CARG2, [LFUNC:CARG2, RA] + | ldrd CARG34, [BASE, RC] + | ldrb RB, UPVAL:CARG2->marked + | ldrb RC, UPVAL:CARG2->closed + | ldr CARG2, UPVAL:CARG2->v + | tst RB, #LJ_GC_BLACK // isblack(uv) + | add RB, CARG4, #-LJ_TISGCV + | cmpne RC, #0 + | strd CARG34, [CARG2] + | bne >2 // Upvalue is closed and black? + |1: + | ins_next + | + |2: // Check if new value is collectable. + | cmn RB, #-(LJ_TNUMX - LJ_TISGCV) + | ldrbhi RC, GCOBJ:CARG3->gch.marked + | bls <1 // tvisgcv(v) + | sub CARG1, DISPATCH, #-GG_DISP2G + | tst RC, #LJ_GC_WHITES + | // Crossed a write barrier. Move the barrier forward. + |.if IOS + | beq <1 + | mov RC, BASE + | bl extern lj_gc_barrieruv // (global_State *g, TValue *tv) + | mov BASE, RC + |.else + | blne extern lj_gc_barrieruv // (global_State *g, TValue *tv) + |.endif + | b <1 + break; + case BC_USETS: + | // RA = uvnum*8, RC = str_const (~) + | ldr LFUNC:CARG2, [BASE, FRAME_FUNC] + | lsr RA, RA, #1 + | add RA, RA, #offsetof(GCfuncL, uvptr) + | mvn RC, RC + | ldr UPVAL:CARG2, [LFUNC:CARG2, RA] + | ldr STR:CARG3, [KBASE, RC, lsl #2] + | ldrb RB, UPVAL:CARG2->marked + | ldrb RC, UPVAL:CARG2->closed + | ldr CARG2, UPVAL:CARG2->v + | mvn CARG4, #~LJ_TSTR + | tst RB, #LJ_GC_BLACK // isblack(uv) + | ldrb RB, STR:CARG3->marked + | strd CARG34, [CARG2] + | bne >2 + |1: + | ins_next + | + |2: // Check if string is white and ensure upvalue is closed. + | tst RB, #LJ_GC_WHITES // iswhite(str) + | cmpne RC, #0 + | sub CARG1, DISPATCH, #-GG_DISP2G + | // Crossed a write barrier. Move the barrier forward. + |.if IOS + | beq <1 + | mov RC, BASE + | bl extern lj_gc_barrieruv // (global_State *g, TValue *tv) + | mov BASE, RC + |.else + | blne extern lj_gc_barrieruv // (global_State *g, TValue *tv) + |.endif + | b <1 + break; + case BC_USETN: + | // RA = uvnum*8, RC = num_const + | ldr LFUNC:CARG2, [BASE, FRAME_FUNC] + | lsr RA, RA, #1 + | add RA, RA, #offsetof(GCfuncL, uvptr) + | lsl RC, RC, #3 + | ldr UPVAL:CARG2, [LFUNC:CARG2, RA] + | ldrd CARG34, [KBASE, RC] + | ldr CARG2, UPVAL:CARG2->v + | ins_next1 + | ins_next2 + | strd CARG34, [CARG2] + | ins_next3 + break; + case BC_USETP: + | // RA = uvnum*8, RC = primitive_type (~) + | ldr LFUNC:CARG2, [BASE, FRAME_FUNC] + | lsr RA, RA, #1 + | add RA, RA, #offsetof(GCfuncL, uvptr) + | ldr UPVAL:CARG2, [LFUNC:CARG2, RA] + | mvn RC, RC + | ldr CARG2, UPVAL:CARG2->v + | ins_next1 + | ins_next2 + | str RC, [CARG2, #4] + | ins_next3 + break; + + case BC_UCLO: + | // RA = level*8, RC = target + | ldr CARG3, L->openupval + | add RC, PC, RC, lsl #2 + | str BASE, L->base + | cmp CARG3, #0 + | sub PC, RC, #0x20000 + | beq >1 + | mov CARG1, L + | add CARG2, BASE, RA + | bl extern lj_func_closeuv // (lua_State *L, TValue *level) + | ldr BASE, L->base + |1: + | ins_next + break; + + case BC_FNEW: + | // RA = dst*8, RC = proto_const (~) (holding function prototype) + | mvn RC, RC + | str BASE, L->base + | ldr CARG2, [KBASE, RC, lsl #2] + | str PC, SAVE_PC + | ldr CARG3, [BASE, FRAME_FUNC] + | mov CARG1, L + | // (lua_State *L, GCproto *pt, GCfuncL *parent) + | bl extern lj_func_newL_gc + | // Returns GCfuncL *. + | ldr BASE, L->base + | mvn CARG2, #~LJ_TFUNC + | ins_next1 + | ins_next2 + | strd CARG12, [BASE, RA] + | ins_next3 + break; + + /* -- Table ops --------------------------------------------------------- */ + + case BC_TNEW: + case BC_TDUP: + | // RA = dst*8, RC = (hbits|asize) | tab_const (~) + if (op == BC_TDUP) { + | mvn RC, RC + } + | ldr CARG3, [DISPATCH, #DISPATCH_GL(gc.total)] + | ldr CARG4, [DISPATCH, #DISPATCH_GL(gc.threshold)] + | str BASE, L->base + | str PC, SAVE_PC + | cmp CARG3, CARG4 + | mov CARG1, L + | bhs >5 + |1: + if (op == BC_TNEW) { + | lsl CARG2, RC, #21 + | lsr CARG3, RC, #11 + | asr RC, CARG2, #21 + | lsr CARG2, CARG2, #21 + | cmn RC, #1 + | addeq CARG2, CARG2, #2 + | bl extern lj_tab_new // (lua_State *L, int32_t asize, uint32_t hbits) + | // Returns GCtab *. + } else { + | ldr CARG2, [KBASE, RC, lsl #2] + | bl extern lj_tab_dup // (lua_State *L, Table *kt) + | // Returns GCtab *. + } + | ldr BASE, L->base + | mvn CARG2, #~LJ_TTAB + | ins_next1 + | ins_next2 + | strd CARG12, [BASE, RA] + | ins_next3 + |5: + | bl extern lj_gc_step_fixtop // (lua_State *L) + | mov CARG1, L + | b <1 + break; + + case BC_GGET: + | // RA = dst*8, RC = str_const (~) + case BC_GSET: + | // RA = dst*8, RC = str_const (~) + | ldr LFUNC:CARG2, [BASE, FRAME_FUNC] + | mvn RC, RC + | ldr TAB:CARG1, LFUNC:CARG2->env + | ldr STR:RC, [KBASE, RC, lsl #2] + if (op == BC_GGET) { + | b ->BC_TGETS_Z + } else { + | b ->BC_TSETS_Z + } + break; + + case BC_TGETV: + | decode_RB8 RB, INS + | decode_RC8 RC, INS + | // RA = dst*8, RB = table*8, RC = key*8 + | ldrd TAB:CARG12, [BASE, RB] + | ldrd CARG34, [BASE, RC] + | checktab CARG2, ->vmeta_tgetv // STALL: load CARG12. + | checktp CARG4, LJ_TISNUM // Integer key? + | ldreq CARG4, TAB:CARG1->array + | ldreq CARG2, TAB:CARG1->asize + | bne >9 + | + | add CARG4, CARG4, CARG3, lsl #3 + | cmp CARG3, CARG2 // In array part? + | ldrdlo CARG34, [CARG4] + | bhs ->vmeta_tgetv + | ins_next1 // Overwrites RB! + | checktp CARG4, LJ_TNIL + | beq >5 + |1: + | ins_next2 + | strd CARG34, [BASE, RA] + | ins_next3 + | + |5: // Check for __index if table value is nil. + | ldr TAB:CARG2, TAB:CARG1->metatable + | cmp TAB:CARG2, #0 + | beq <1 // No metatable: done. + | ldrb CARG2, TAB:CARG2->nomm + | tst CARG2, #1<vmeta_tgetv + | + |9: + | checktp CARG4, LJ_TSTR // String key? + | moveq STR:RC, CARG3 + | beq ->BC_TGETS_Z + | b ->vmeta_tgetv + break; + case BC_TGETS: + | decode_RB8 RB, INS + | and RC, RC, #255 + | // RA = dst*8, RB = table*8, RC = str_const (~) + | ldrd CARG12, [BASE, RB] + | mvn RC, RC + | ldr STR:RC, [KBASE, RC, lsl #2] // STALL: early RC. + | checktab CARG2, ->vmeta_tgets1 + |->BC_TGETS_Z: + | // (TAB:RB =) TAB:CARG1 = GCtab *, STR:RC = GCstr *, RA = dst*8 + | ldr CARG3, TAB:CARG1->hmask + | ldr CARG4, STR:RC->hash + | ldr NODE:INS, TAB:CARG1->node + | mov TAB:RB, TAB:CARG1 + | and CARG3, CARG3, CARG4 // idx = str->hash & tab->hmask + | add CARG3, CARG3, CARG3, lsl #1 + | add NODE:INS, NODE:INS, CARG3, lsl #3 // node = tab->node + idx*3*8 + |1: + | ldrd CARG12, NODE:INS->key // STALL: early NODE:INS. + | ldrd CARG34, NODE:INS->val + | ldr NODE:INS, NODE:INS->next + | checktp CARG2, LJ_TSTR + | cmpeq CARG1, STR:RC + | bne >4 + | checktp CARG4, LJ_TNIL + | beq >5 + |3: + | ins_next1 + | ins_next2 + | strd CARG34, [BASE, RA] + | ins_next3 + | + |4: // Follow hash chain. + | cmp NODE:INS, #0 + | bne <1 + | // End of hash chain: key not found, nil result. + | + |5: // Check for __index if table value is nil. + | ldr TAB:CARG1, TAB:RB->metatable + | mov CARG3, #0 // Optional clear of undef. value (during load stall). + | mvn CARG4, #~LJ_TNIL + | cmp TAB:CARG1, #0 + | beq <3 // No metatable: done. + | ldrb CARG2, TAB:CARG1->nomm + | tst CARG2, #1<vmeta_tgets + break; + case BC_TGETB: + | decode_RB8 RB, INS + | and RC, RC, #255 + | // RA = dst*8, RB = table*8, RC = index + | ldrd CARG12, [BASE, RB] + | checktab CARG2, ->vmeta_tgetb // STALL: load CARG12. + | ldr CARG3, TAB:CARG1->asize + | ldr CARG4, TAB:CARG1->array + | lsl CARG2, RC, #3 + | cmp RC, CARG3 + | ldrdlo CARG34, [CARG4, CARG2] + | bhs ->vmeta_tgetb + | ins_next1 // Overwrites RB! + | checktp CARG4, LJ_TNIL + | beq >5 + |1: + | ins_next2 + | strd CARG34, [BASE, RA] + | ins_next3 + | + |5: // Check for __index if table value is nil. + | ldr TAB:CARG2, TAB:CARG1->metatable + | cmp TAB:CARG2, #0 + | beq <1 // No metatable: done. + | ldrb CARG2, TAB:CARG2->nomm + | tst CARG2, #1<vmeta_tgetb + break; + case BC_TGETR: + | decode_RB8 RB, INS + | decode_RC8 RC, INS + | // RA = dst*8, RB = table*8, RC = key*8 + | ldr TAB:CARG1, [BASE, RB] + | ldr CARG2, [BASE, RC] + | ldr CARG4, TAB:CARG1->array + | ldr CARG3, TAB:CARG1->asize + | add CARG4, CARG4, CARG2, lsl #3 + | cmp CARG2, CARG3 // In array part? + | bhs ->vmeta_tgetr + | ldrd CARG12, [CARG4] + |->BC_TGETR_Z: + | ins_next1 + | ins_next2 + | strd CARG12, [BASE, RA] + | ins_next3 + break; + + case BC_TSETV: + | decode_RB8 RB, INS + | decode_RC8 RC, INS + | // RA = src*8, RB = table*8, RC = key*8 + | ldrd TAB:CARG12, [BASE, RB] + | ldrd CARG34, [BASE, RC] + | checktab CARG2, ->vmeta_tsetv // STALL: load CARG12. + | checktp CARG4, LJ_TISNUM // Integer key? + | ldreq CARG2, TAB:CARG1->array + | ldreq CARG4, TAB:CARG1->asize + | bne >9 + | + | add CARG2, CARG2, CARG3, lsl #3 + | cmp CARG3, CARG4 // In array part? + | ldrlo INS, [CARG2, #4] + | bhs ->vmeta_tsetv + | ins_next1 // Overwrites RB! + | checktp INS, LJ_TNIL + | ldrb INS, TAB:CARG1->marked + | ldrd CARG34, [BASE, RA] + | beq >5 + |1: + | tst INS, #LJ_GC_BLACK // isblack(table) + | strd CARG34, [CARG2] + | bne >7 + |2: + | ins_next2 + | ins_next3 + | + |5: // Check for __newindex if previous value is nil. + | ldr TAB:RA, TAB:CARG1->metatable + | cmp TAB:RA, #0 + | beq <1 // No metatable: done. + | ldrb RA, TAB:RA->nomm + | tst RA, #1<vmeta_tsetv + | + |7: // Possible table write barrier for the value. Skip valiswhite check. + | barrierback TAB:CARG1, INS, CARG3 + | b <2 + | + |9: + | checktp CARG4, LJ_TSTR // String key? + | moveq STR:RC, CARG3 + | beq ->BC_TSETS_Z + | b ->vmeta_tsetv + break; + case BC_TSETS: + | decode_RB8 RB, INS + | and RC, RC, #255 + | // RA = src*8, RB = table*8, RC = str_const (~) + | ldrd CARG12, [BASE, RB] + | mvn RC, RC + | ldr STR:RC, [KBASE, RC, lsl #2] // STALL: early RC. + | checktab CARG2, ->vmeta_tsets1 + |->BC_TSETS_Z: + | // (TAB:RB =) TAB:CARG1 = GCtab *, STR:RC = GCstr *, RA = dst*8 + | ldr CARG3, TAB:CARG1->hmask + | ldr CARG4, STR:RC->hash + | ldr NODE:INS, TAB:CARG1->node + | mov TAB:RB, TAB:CARG1 + | and CARG3, CARG3, CARG4 // idx = str->hash & tab->hmask + | add CARG3, CARG3, CARG3, lsl #1 + | mov CARG4, #0 + | add NODE:INS, NODE:INS, CARG3, lsl #3 // node = tab->node + idx*3*8 + | strb CARG4, TAB:RB->nomm // Clear metamethod cache. + |1: + | ldrd CARG12, NODE:INS->key + | ldr CARG4, NODE:INS->val.it + | ldr NODE:CARG3, NODE:INS->next + | checktp CARG2, LJ_TSTR + | cmpeq CARG1, STR:RC + | bne >5 + | ldrb CARG2, TAB:RB->marked + | checktp CARG4, LJ_TNIL // Key found, but nil value? + | ldrd CARG34, [BASE, RA] + | beq >4 + |2: + | tst CARG2, #LJ_GC_BLACK // isblack(table) + | strd CARG34, NODE:INS->val + | bne >7 + |3: + | ins_next + | + |4: // Check for __newindex if previous value is nil. + | ldr TAB:CARG1, TAB:RB->metatable + | cmp TAB:CARG1, #0 + | beq <2 // No metatable: done. + | ldrb CARG1, TAB:CARG1->nomm + | tst CARG1, #1<vmeta_tsets + | + |5: // Follow hash chain. + | movs NODE:INS, NODE:CARG3 + | bne <1 + | // End of hash chain: key not found, add a new one. + | + | // But check for __newindex first. + | ldr TAB:CARG1, TAB:RB->metatable + | mov CARG3, TMPDp + | str PC, SAVE_PC + | cmp TAB:CARG1, #0 // No metatable: continue. + | str BASE, L->base + | ldrbne CARG2, TAB:CARG1->nomm + | mov CARG1, L + | beq >6 + | tst CARG2, #1<vmeta_tsets // 'no __newindex' flag NOT set: check. + |6: + | mvn CARG4, #~LJ_TSTR + | str STR:RC, TMPDlo + | mov CARG2, TAB:RB + | str CARG4, TMPDhi + | bl extern lj_tab_newkey // (lua_State *L, GCtab *t, TValue *k) + | // Returns TValue *. + | ldr BASE, L->base + | ldrd CARG34, [BASE, RA] + | strd CARG34, [CRET1] + | b <3 // No 2nd write barrier needed. + | + |7: // Possible table write barrier for the value. Skip valiswhite check. + | barrierback TAB:RB, CARG2, CARG3 + | b <3 + break; + case BC_TSETB: + | decode_RB8 RB, INS + | and RC, RC, #255 + | // RA = src*8, RB = table*8, RC = index + | ldrd CARG12, [BASE, RB] + | checktab CARG2, ->vmeta_tsetb // STALL: load CARG12. + | ldr CARG3, TAB:CARG1->asize + | ldr RB, TAB:CARG1->array + | lsl CARG2, RC, #3 + | cmp RC, CARG3 + | ldrdlo CARG34, [CARG2, RB]! + | bhs ->vmeta_tsetb + | ins_next1 // Overwrites RB! + | checktp CARG4, LJ_TNIL + | ldrb INS, TAB:CARG1->marked + | ldrd CARG34, [BASE, RA] + | beq >5 + |1: + | tst INS, #LJ_GC_BLACK // isblack(table) + | strd CARG34, [CARG2] + | bne >7 + |2: + | ins_next2 + | ins_next3 + | + |5: // Check for __newindex if previous value is nil. + | ldr TAB:RA, TAB:CARG1->metatable + | cmp TAB:RA, #0 + | beq <1 // No metatable: done. + | ldrb RA, TAB:RA->nomm + | tst RA, #1<vmeta_tsetb + | + |7: // Possible table write barrier for the value. Skip valiswhite check. + | barrierback TAB:CARG1, INS, CARG3 + | b <2 + break; + case BC_TSETR: + | decode_RB8 RB, INS + | decode_RC8 RC, INS + | // RA = src*8, RB = table*8, RC = key*8 + | ldr TAB:CARG2, [BASE, RB] + | ldr CARG3, [BASE, RC] + | ldrb INS, TAB:CARG2->marked + | ldr CARG1, TAB:CARG2->array + | ldr CARG4, TAB:CARG2->asize + | tst INS, #LJ_GC_BLACK // isblack(table) + | add CARG1, CARG1, CARG3, lsl #3 + | bne >7 + |2: + | cmp CARG3, CARG4 // In array part? + | bhs ->vmeta_tsetr + |->BC_TSETR_Z: + | ldrd CARG34, [BASE, RA] + | ins_next1 + | ins_next2 + | strd CARG34, [CARG1] + | ins_next3 + | + |7: // Possible table write barrier for the value. Skip valiswhite check. + | barrierback TAB:CARG2, INS, RB + | b <2 + break; + + case BC_TSETM: + | // RA = base*8 (table at base-1), RC = num_const (start index) + | add RA, BASE, RA + |1: + | ldr RB, SAVE_MULTRES + | ldr TAB:CARG2, [RA, #-8] // Guaranteed to be a table. + | ldr CARG1, [KBASE, RC, lsl #3] // Integer constant is in lo-word. + | subs RB, RB, #8 + | ldr CARG4, TAB:CARG2->asize + | beq >4 // Nothing to copy? + | add CARG3, CARG1, RB, lsr #3 + | cmp CARG3, CARG4 + | ldr CARG4, TAB:CARG2->array + | add RB, RA, RB + | bhi >5 + | add INS, CARG4, CARG1, lsl #3 + | ldrb CARG1, TAB:CARG2->marked + |3: // Copy result slots to table. + | ldrd CARG34, [RA], #8 + | strd CARG34, [INS], #8 + | cmp RA, RB + | blo <3 + | tst CARG1, #LJ_GC_BLACK // isblack(table) + | bne >7 + |4: + | ins_next + | + |5: // Need to resize array part. + | str BASE, L->base + | mov CARG1, L + | str PC, SAVE_PC + | bl extern lj_tab_reasize // (lua_State *L, GCtab *t, int nasize) + | // Must not reallocate the stack. + | .IOS ldr BASE, L->base + | b <1 + | + |7: // Possible table write barrier for any value. Skip valiswhite check. + | barrierback TAB:CARG2, CARG1, CARG3 + | b <4 + break; + + /* -- Calls and vararg handling ----------------------------------------- */ + + case BC_CALLM: + | // RA = base*8, (RB = nresults+1,) RC = extra_nargs + | ldr CARG1, SAVE_MULTRES + | decode_RC8 NARGS8:RC, INS + | add NARGS8:RC, NARGS8:RC, CARG1 + | b ->BC_CALL_Z + break; + case BC_CALL: + | decode_RC8 NARGS8:RC, INS + | // RA = base*8, (RB = nresults+1,) RC = (nargs+1)*8 + |->BC_CALL_Z: + | mov RB, BASE // Save old BASE for vmeta_call. + | ldrd CARG34, [BASE, RA]! + | sub NARGS8:RC, NARGS8:RC, #8 + | add BASE, BASE, #8 + | checkfunc CARG4, ->vmeta_call + | ins_call + break; + + case BC_CALLMT: + | // RA = base*8, (RB = 0,) RC = extra_nargs + | ldr CARG1, SAVE_MULTRES + | add NARGS8:RC, CARG1, RC, lsl #3 + | b ->BC_CALLT1_Z + break; + case BC_CALLT: + | lsl NARGS8:RC, RC, #3 + | // RA = base*8, (RB = 0,) RC = (nargs+1)*8 + |->BC_CALLT1_Z: + | ldrd LFUNC:CARG34, [RA, BASE]! + | sub NARGS8:RC, NARGS8:RC, #8 + | add RA, RA, #8 + | checkfunc CARG4, ->vmeta_callt + | ldr PC, [BASE, FRAME_PC] + |->BC_CALLT2_Z: + | mov RB, #0 + | ldrb CARG4, LFUNC:CARG3->ffid + | tst PC, #FRAME_TYPE + | bne >7 + |1: + | str LFUNC:CARG3, [BASE, FRAME_FUNC] // Copy function down, but keep PC. + | cmp NARGS8:RC, #0 + | beq >3 + |2: + | ldrd CARG12, [RA, RB] + | add INS, RB, #8 + | cmp INS, NARGS8:RC + | strd CARG12, [BASE, RB] + | mov RB, INS + | bne <2 + |3: + | cmp CARG4, #1 // (> FF_C) Calling a fast function? + | bhi >5 + |4: + | ins_callt + | + |5: // Tailcall to a fast function with a Lua frame below. + | ldr INS, [PC, #-4] + | decode_RA8 RA, INS + | sub CARG1, BASE, RA + | ldr LFUNC:CARG1, [CARG1, #-16] + | ldr CARG1, LFUNC:CARG1->field_pc + | ldr KBASE, [CARG1, #PC2PROTO(k)] + | b <4 + | + |7: // Tailcall from a vararg function. + | eor PC, PC, #FRAME_VARG + | tst PC, #FRAME_TYPEP // Vararg frame below? + | movne CARG4, #0 // Clear ffid if no Lua function below. + | bne <1 + | sub BASE, BASE, PC + | ldr PC, [BASE, FRAME_PC] + | tst PC, #FRAME_TYPE + | movne CARG4, #0 // Clear ffid if no Lua function below. + | b <1 + break; + + case BC_ITERC: + | // RA = base*8, (RB = nresults+1, RC = nargs+1 (2+1)) + | add RA, BASE, RA + | mov RB, BASE // Save old BASE for vmeta_call. + | ldrd CARG34, [RA, #-16] + | ldrd CARG12, [RA, #-8] + | add BASE, RA, #8 + | strd CARG34, [RA, #8] // Copy state. + | strd CARG12, [RA, #16] // Copy control var. + | // STALL: locked CARG34. + | ldrd LFUNC:CARG34, [RA, #-24] + | mov NARGS8:RC, #16 // Iterators get 2 arguments. + | // STALL: load CARG34. + | strd LFUNC:CARG34, [RA] // Copy callable. + | checkfunc CARG4, ->vmeta_call + | ins_call + break; + + case BC_ITERN: + | // RA = base*8, (RB = nresults+1, RC = nargs+1 (2+1)) + |.if JIT + | // NYI: add hotloop, record BC_ITERN. + |.endif + | add RA, BASE, RA + | ldr TAB:RB, [RA, #-16] + | ldr CARG1, [RA, #-8] // Get index from control var. + | ldr INS, TAB:RB->asize + | ldr CARG2, TAB:RB->array + | add PC, PC, #4 + |1: // Traverse array part. + | subs RC, CARG1, INS + | add CARG3, CARG2, CARG1, lsl #3 + | bhs >5 // Index points after array part? + | ldrd CARG34, [CARG3] + | checktp CARG4, LJ_TNIL + | addeq CARG1, CARG1, #1 // Skip holes in array part. + | beq <1 + | ldrh RC, [PC, #-2] + | mvn CARG2, #~LJ_TISNUM + | strd CARG34, [RA, #8] + | add RC, PC, RC, lsl #2 + | add RB, CARG1, #1 + | strd CARG12, [RA] + | sub PC, RC, #0x20000 + | str RB, [RA, #-8] // Update control var. + |3: + | ins_next + | + |5: // Traverse hash part. + | ldr CARG4, TAB:RB->hmask + | ldr NODE:RB, TAB:RB->node + |6: + | add CARG1, RC, RC, lsl #1 + | cmp RC, CARG4 // End of iteration? Branch to ITERL+1. + | add NODE:CARG3, NODE:RB, CARG1, lsl #3 // node = tab->node + idx*3*8 + | bhi <3 + | ldrd CARG12, NODE:CARG3->val + | checktp CARG2, LJ_TNIL + | add RC, RC, #1 + | beq <6 // Skip holes in hash part. + | ldrh RB, [PC, #-2] + | add RC, RC, INS + | ldrd CARG34, NODE:CARG3->key + | str RC, [RA, #-8] // Update control var. + | strd CARG12, [RA, #8] + | add RC, PC, RB, lsl #2 + | sub PC, RC, #0x20000 + | strd CARG34, [RA] + | b <3 + break; + + case BC_ISNEXT: + | // RA = base*8, RC = target (points to ITERN) + | add RA, BASE, RA + | add RC, PC, RC, lsl #2 + | ldrd CFUNC:CARG12, [RA, #-24] + | ldr CARG3, [RA, #-12] + | ldr CARG4, [RA, #-4] + | checktp CARG2, LJ_TFUNC + | ldrbeq CARG1, CFUNC:CARG1->ffid + | checktpeq CARG3, LJ_TTAB + | checktpeq CARG4, LJ_TNIL + | cmpeq CARG1, #FF_next_N + | subeq PC, RC, #0x20000 + | bne >5 + | ins_next1 + | ins_next2 + | mov CARG1, #0 + | mvn CARG2, #0x00018000 + | strd CARG1, [RA, #-8] // Initialize control var. + |1: + | ins_next3 + |5: // Despecialize bytecode if any of the checks fail. + | mov CARG1, #BC_JMP + | mov OP, #BC_ITERC + | strb CARG1, [PC, #-4] + | sub PC, RC, #0x20000 + | strb OP, [PC] // Subsumes ins_next1. + | ins_next2 + | b <1 + break; + + case BC_VARG: + | decode_RB8 RB, INS + | decode_RC8 RC, INS + | // RA = base*8, RB = (nresults+1)*8, RC = numparams*8 + | ldr CARG1, [BASE, FRAME_PC] + | add RC, BASE, RC + | add RA, BASE, RA + | add RC, RC, #FRAME_VARG + | add CARG4, RA, RB + | sub CARG3, BASE, #8 // CARG3 = vtop + | sub RC, RC, CARG1 // RC = vbase + | // Note: RC may now be even _above_ BASE if nargs was < numparams. + | cmp RB, #0 + | sub CARG1, CARG3, RC + | beq >5 // Copy all varargs? + | sub CARG4, CARG4, #16 + |1: // Copy vararg slots to destination slots. + | cmp RC, CARG3 + | ldrdlo CARG12, [RC], #8 + | mvnhs CARG2, #~LJ_TNIL + | cmp RA, CARG4 + | strd CARG12, [RA], #8 + | blo <1 + |2: + | ins_next + | + |5: // Copy all varargs. + | ldr CARG4, L->maxstack + | cmp CARG1, #0 + | movle RB, #8 // MULTRES = (0+1)*8 + | addgt RB, CARG1, #8 + | add CARG2, RA, CARG1 + | str RB, SAVE_MULTRES + | ble <2 + | cmp CARG2, CARG4 + | bhi >7 + |6: + | ldrd CARG12, [RC], #8 + | strd CARG12, [RA], #8 + | cmp RC, CARG3 + | blo <6 + | b <2 + | + |7: // Grow stack for varargs. + | lsr CARG2, CARG1, #3 + | str RA, L->top + | mov CARG1, L + | str BASE, L->base + | sub RC, RC, BASE // Need delta, because BASE may change. + | str PC, SAVE_PC + | sub RA, RA, BASE + | bl extern lj_state_growstack // (lua_State *L, int n) + | ldr BASE, L->base + | add RA, BASE, RA + | add RC, BASE, RC + | sub CARG3, BASE, #8 + | b <6 + break; + + /* -- Returns ----------------------------------------------------------- */ + + case BC_RETM: + | // RA = results*8, RC = extra results + | ldr CARG1, SAVE_MULTRES + | ldr PC, [BASE, FRAME_PC] + | add RA, BASE, RA + | add RC, CARG1, RC, lsl #3 + | b ->BC_RETM_Z + break; + + case BC_RET: + | // RA = results*8, RC = nresults+1 + | ldr PC, [BASE, FRAME_PC] + | lsl RC, RC, #3 + | add RA, BASE, RA + |->BC_RETM_Z: + | str RC, SAVE_MULTRES + |1: + | ands CARG1, PC, #FRAME_TYPE + | eor CARG2, PC, #FRAME_VARG + | bne ->BC_RETV2_Z + | + |->BC_RET_Z: + | // BASE = base, RA = resultptr, RC = (nresults+1)*8, PC = return + | ldr INS, [PC, #-4] + | subs CARG4, RC, #8 + | sub CARG3, BASE, #8 + | beq >3 + |2: + | ldrd CARG12, [RA], #8 + | add BASE, BASE, #8 + | subs CARG4, CARG4, #8 + | strd CARG12, [BASE, #-16] + | bne <2 + |3: + | decode_RA8 RA, INS + | sub CARG4, CARG3, RA + | decode_RB8 RB, INS + | ldr LFUNC:CARG1, [CARG4, FRAME_FUNC] + |5: + | cmp RB, RC // More results expected? + | bhi >6 + | mov BASE, CARG4 + | ldr CARG2, LFUNC:CARG1->field_pc + | ins_next1 + | ins_next2 + | ldr KBASE, [CARG2, #PC2PROTO(k)] + | ins_next3 + | + |6: // Fill up results with nil. + | mvn CARG2, #~LJ_TNIL + | add BASE, BASE, #8 + | add RC, RC, #8 + | str CARG2, [BASE, #-12] + | b <5 + | + |->BC_RETV1_Z: // Non-standard return case. + | add RA, BASE, RA + |->BC_RETV2_Z: + | tst CARG2, #FRAME_TYPEP + | bne ->vm_return + | // Return from vararg function: relocate BASE down. + | sub BASE, BASE, CARG2 + | ldr PC, [BASE, FRAME_PC] + | b <1 + break; + + case BC_RET0: case BC_RET1: + | // RA = results*8, RC = nresults+1 + | ldr PC, [BASE, FRAME_PC] + | lsl RC, RC, #3 + | str RC, SAVE_MULTRES + | ands CARG1, PC, #FRAME_TYPE + | eor CARG2, PC, #FRAME_VARG + | ldreq INS, [PC, #-4] + | bne ->BC_RETV1_Z + if (op == BC_RET1) { + | ldrd CARG12, [BASE, RA] + } + | sub CARG4, BASE, #8 + | decode_RA8 RA, INS + if (op == BC_RET1) { + | strd CARG12, [CARG4] + } + | sub BASE, CARG4, RA + | decode_RB8 RB, INS + | ldr LFUNC:CARG1, [BASE, FRAME_FUNC] + |5: + | cmp RB, RC + | bhi >6 + | ldr CARG2, LFUNC:CARG1->field_pc + | ins_next1 + | ins_next2 + | ldr KBASE, [CARG2, #PC2PROTO(k)] + | ins_next3 + | + |6: // Fill up results with nil. + | sub CARG2, CARG4, #4 + | mvn CARG3, #~LJ_TNIL + | str CARG3, [CARG2, RC] + | add RC, RC, #8 + | b <5 + break; + + /* -- Loops and branches ------------------------------------------------ */ + + |.define FOR_IDX, [RA]; .define FOR_TIDX, [RA, #4] + |.define FOR_STOP, [RA, #8]; .define FOR_TSTOP, [RA, #12] + |.define FOR_STEP, [RA, #16]; .define FOR_TSTEP, [RA, #20] + |.define FOR_EXT, [RA, #24]; .define FOR_TEXT, [RA, #28] + + case BC_FORL: + |.if JIT + | hotloop + |.endif + | // Fall through. Assumes BC_IFORL follows. + break; + + case BC_JFORI: + case BC_JFORL: +#if !LJ_HASJIT + break; +#endif + case BC_FORI: + case BC_IFORL: + | // RA = base*8, RC = target (after end of loop or start of loop) + vk = (op == BC_IFORL || op == BC_JFORL); + | ldrd CARG12, [RA, BASE]! + if (op != BC_JFORL) { + | add RC, PC, RC, lsl #2 + } + if (!vk) { + | ldrd CARG34, FOR_STOP + | checktp CARG2, LJ_TISNUM + | ldr RB, FOR_TSTEP + | bne >5 + | checktp CARG4, LJ_TISNUM + | ldr CARG4, FOR_STEP + | checktpeq RB, LJ_TISNUM + | bne ->vmeta_for + | cmp CARG4, #0 + | blt >4 + | cmp CARG1, CARG3 + } else { + | ldrd CARG34, FOR_STEP + | checktp CARG2, LJ_TISNUM + | bne >5 + | adds CARG1, CARG1, CARG3 + | ldr CARG4, FOR_STOP + if (op == BC_IFORL) { + | addvs RC, PC, #0x20000 // Overflow: prevent branch. + } else { + | bvs >2 // Overflow: do not enter mcode. + } + | cmp CARG3, #0 + | blt >4 + | cmp CARG1, CARG4 + } + |1: + if (op == BC_FORI) { + | subgt PC, RC, #0x20000 + } else if (op == BC_JFORI) { + | sub PC, RC, #0x20000 + | ldrhle RC, [PC, #-2] + } else if (op == BC_IFORL) { + | suble PC, RC, #0x20000 + } + if (vk) { + | strd CARG12, FOR_IDX + } + |2: + | ins_next1 + | ins_next2 + | strd CARG12, FOR_EXT + if (op == BC_JFORI || op == BC_JFORL) { + | ble =>BC_JLOOP + } + |3: + | ins_next3 + | + |4: // Invert check for negative step. + if (!vk) { + | cmp CARG3, CARG1 + } else { + | cmp CARG4, CARG1 + } + | b <1 + | + |5: // FP loop. + if (!vk) { + | cmnlo CARG4, #-LJ_TISNUM + | cmnlo RB, #-LJ_TISNUM + | bhs ->vmeta_for + |.if FPU + | vldr d0, FOR_IDX + | vldr d1, FOR_STOP + | cmp RB, #0 + | vstr d0, FOR_EXT + |.else + | cmp RB, #0 + | strd CARG12, FOR_EXT + | blt >8 + |.endif + } else { + |.if FPU + | vldr d0, FOR_IDX + | vldr d2, FOR_STEP + | vldr d1, FOR_STOP + | cmp CARG4, #0 + | vadd.f64 d0, d0, d2 + |.else + | cmp CARG4, #0 + | blt >8 + | bl extern __aeabi_dadd + | strd CARG12, FOR_IDX + | ldrd CARG34, FOR_STOP + | strd CARG12, FOR_EXT + |.endif + } + |6: + |.if FPU + | vcmpge.f64 d0, d1 + | vcmplt.f64 d1, d0 + | vmrs + |.else + | bl extern __aeabi_cdcmple + |.endif + if (vk) { + |.if FPU + | vstr d0, FOR_IDX + | vstr d0, FOR_EXT + |.endif + } + if (op == BC_FORI) { + | subhi PC, RC, #0x20000 + } else if (op == BC_JFORI) { + | sub PC, RC, #0x20000 + | ldrhls RC, [PC, #-2] + | bls =>BC_JLOOP + } else if (op == BC_IFORL) { + | subls PC, RC, #0x20000 + } else { + | bls =>BC_JLOOP + } + | ins_next1 + | ins_next2 + | b <3 + | + |.if not FPU + |8: // Invert check for negative step. + if (vk) { + | bl extern __aeabi_dadd + | strd CARG12, FOR_IDX + | strd CARG12, FOR_EXT + } + | mov CARG3, CARG1 + | mov CARG4, CARG2 + | ldrd CARG12, FOR_STOP + | b <6 + |.endif + break; + + case BC_ITERL: + |.if JIT + | hotloop + |.endif + | // Fall through. Assumes BC_IITERL follows. + break; + + case BC_JITERL: +#if !LJ_HASJIT + break; +#endif + case BC_IITERL: + | // RA = base*8, RC = target + | ldrd CARG12, [RA, BASE]! + if (op == BC_JITERL) { + | cmn CARG2, #-LJ_TNIL // Stop if iterator returned nil. + | strdne CARG12, [RA, #-8] + | bne =>BC_JLOOP + } else { + | add RC, PC, RC, lsl #2 + | // STALL: load CARG12. + | cmn CARG2, #-LJ_TNIL // Stop if iterator returned nil. + | subne PC, RC, #0x20000 // Otherwise save control var + branch. + | strdne CARG12, [RA, #-8] + } + | ins_next + break; + + case BC_LOOP: + | // RA = base*8, RC = target (loop extent) + | // Note: RA/RC is only used by trace recorder to determine scope/extent + | // This opcode does NOT jump, it's only purpose is to detect a hot loop. + |.if JIT + | hotloop + |.endif + | // Fall through. Assumes BC_ILOOP follows. + break; + + case BC_ILOOP: + | // RA = base*8, RC = target (loop extent) + | ins_next + break; + + case BC_JLOOP: + |.if JIT + | // RA = base (ignored), RC = traceno + | ldr CARG1, [DISPATCH, #DISPATCH_J(trace)] + | mov CARG2, #0 // Traces on ARM don't store the trace number, so use 0. + | ldr TRACE:RC, [CARG1, RC, lsl #2] + | st_vmstate CARG2 + | ldr RA, TRACE:RC->mcode + | str BASE, [DISPATCH, #DISPATCH_GL(jit_base)] + | str L, [DISPATCH, #DISPATCH_GL(tmpbuf.L)] + | bx RA + |.endif + break; + + case BC_JMP: + | // RA = base*8 (only used by trace recorder), RC = target + | add RC, PC, RC, lsl #2 + | sub PC, RC, #0x20000 + | ins_next + break; + + /* -- Function headers -------------------------------------------------- */ + + case BC_FUNCF: + |.if JIT + | hotcall + |.endif + case BC_FUNCV: /* NYI: compiled vararg functions. */ + | // Fall through. Assumes BC_IFUNCF/BC_IFUNCV follow. + break; + + case BC_JFUNCF: +#if !LJ_HASJIT + break; +#endif + case BC_IFUNCF: + | // BASE = new base, RA = BASE+framesize*8, CARG3 = LFUNC, RC = nargs*8 + | ldr CARG1, L->maxstack + | ldrb CARG2, [PC, #-4+PC2PROTO(numparams)] + | ldr KBASE, [PC, #-4+PC2PROTO(k)] + | cmp RA, CARG1 + | bhi ->vm_growstack_l + if (op != BC_JFUNCF) { + | ins_next1 + | ins_next2 + } + |2: + | cmp NARGS8:RC, CARG2, lsl #3 // Check for missing parameters. + | mvn CARG4, #~LJ_TNIL + | blo >3 + if (op == BC_JFUNCF) { + | decode_RD RC, INS + | b =>BC_JLOOP + } else { + | ins_next3 + } + | + |3: // Clear missing parameters. + | strd CARG34, [BASE, NARGS8:RC] + | add NARGS8:RC, NARGS8:RC, #8 + | b <2 + break; + + case BC_JFUNCV: +#if !LJ_HASJIT + break; +#endif + | NYI // NYI: compiled vararg functions + break; /* NYI: compiled vararg functions. */ + + case BC_IFUNCV: + | // BASE = new base, RA = BASE+framesize*8, CARG3 = LFUNC, RC = nargs*8 + | ldr CARG1, L->maxstack + | add CARG4, BASE, RC + | add RA, RA, RC + | str LFUNC:CARG3, [CARG4] // Store copy of LFUNC. + | add CARG2, RC, #8+FRAME_VARG + | ldr KBASE, [PC, #-4+PC2PROTO(k)] + | cmp RA, CARG1 + | str CARG2, [CARG4, #4] // Store delta + FRAME_VARG. + | bhs ->vm_growstack_l + | ldrb RB, [PC, #-4+PC2PROTO(numparams)] + | mov RA, BASE + | mov RC, CARG4 + | cmp RB, #0 + | add BASE, CARG4, #8 + | beq >3 + | mvn CARG3, #~LJ_TNIL + |1: + | cmp RA, RC // Less args than parameters? + | ldrdlo CARG12, [RA], #8 + | movhs CARG2, CARG3 + | strlo CARG3, [RA, #-4] // Clear old fixarg slot (help the GC). + |2: + | subs RB, RB, #1 + | strd CARG12, [CARG4, #8]! + | bne <1 + |3: + | ins_next + break; + + case BC_FUNCC: + case BC_FUNCCW: + | // BASE = new base, RA = BASE+framesize*8, CARG3 = CFUNC, RC = nargs*8 + if (op == BC_FUNCC) { + | ldr CARG4, CFUNC:CARG3->f + } else { + | ldr CARG4, [DISPATCH, #DISPATCH_GL(wrapf)] + } + | add CARG2, RA, NARGS8:RC + | ldr CARG1, L->maxstack + | add RC, BASE, NARGS8:RC + | str BASE, L->base + | cmp CARG2, CARG1 + | str RC, L->top + if (op == BC_FUNCCW) { + | ldr CARG2, CFUNC:CARG3->f + } + | mv_vmstate CARG3, C + | mov CARG1, L + | bhi ->vm_growstack_c // Need to grow stack. + | st_vmstate CARG3 + | blx CARG4 // (lua_State *L [, lua_CFunction f]) + | // Returns nresults. + | ldr BASE, L->base + | mv_vmstate CARG3, INTERP + | ldr CRET2, L->top + | str L, [DISPATCH, #DISPATCH_GL(cur_L)] + | lsl RC, CRET1, #3 + | st_vmstate CARG3 + | ldr PC, [BASE, FRAME_PC] + | sub RA, CRET2, RC // RA = L->top - nresults*8 + | b ->vm_returnc + break; + + /* ---------------------------------------------------------------------- */ + + default: + fprintf(stderr, "Error: undefined opcode BC_%s\n", bc_names[op]); + exit(2); + break; + } +} + +static int build_backend(BuildCtx *ctx) +{ + int op; + + dasm_growpc(Dst, BC__MAX); + + build_subroutines(ctx); + + |.code_op + for (op = 0; op < BC__MAX; op++) + build_ins(ctx, (BCOp)op, op); + + return BC__MAX; +} + +/* Emit pseudo frame-info for all assembler functions. */ +static void emit_asm_debug(BuildCtx *ctx) +{ + int fcofs = (int)((uint8_t *)ctx->glob[GLOB_vm_ffi_call] - ctx->code); + int i; + switch (ctx->mode) { + case BUILD_elfasm: + fprintf(ctx->fp, "\t.section .debug_frame,\"\",%%progbits\n"); + fprintf(ctx->fp, + ".Lframe0:\n" + "\t.long .LECIE0-.LSCIE0\n" + ".LSCIE0:\n" + "\t.long 0xffffffff\n" + "\t.byte 0x1\n" + "\t.string \"\"\n" + "\t.uleb128 0x1\n" + "\t.sleb128 -4\n" + "\t.byte 0xe\n" /* Return address is in lr. */ + "\t.byte 0xc\n\t.uleb128 0xd\n\t.uleb128 0\n" /* def_cfa sp */ + "\t.align 2\n" + ".LECIE0:\n\n"); + fprintf(ctx->fp, + ".LSFDE0:\n" + "\t.long .LEFDE0-.LASFDE0\n" + ".LASFDE0:\n" + "\t.long .Lframe0\n" + "\t.long .Lbegin\n" + "\t.long %d\n" + "\t.byte 0xe\n\t.uleb128 %d\n" /* def_cfa_offset */ + "\t.byte 0x8e\n\t.uleb128 1\n", /* offset lr */ + fcofs, CFRAME_SIZE); + for (i = 11; i >= (LJ_ARCH_HASFPU ? 5 : 4); i--) /* offset r4-r11 */ + fprintf(ctx->fp, "\t.byte %d\n\t.uleb128 %d\n", 0x80+i, 2+(11-i)); +#if LJ_ARCH_HASFPU + for (i = 15; i >= 8; i--) /* offset d8-d15 */ + fprintf(ctx->fp, "\t.byte 5\n\t.uleb128 %d, %d\n", + 64+2*i, 10+2*(15-i)); + fprintf(ctx->fp, "\t.byte 0x84\n\t.uleb128 %d\n", 25); /* offset r4 */ +#endif + fprintf(ctx->fp, + "\t.align 2\n" + ".LEFDE0:\n\n"); +#if LJ_HASFFI + fprintf(ctx->fp, + ".LSFDE1:\n" + "\t.long .LEFDE1-.LASFDE1\n" + ".LASFDE1:\n" + "\t.long .Lframe0\n" + "\t.long lj_vm_ffi_call\n" + "\t.long %d\n" + "\t.byte 0xe\n\t.uleb128 16\n" /* def_cfa_offset */ + "\t.byte 0x8e\n\t.uleb128 1\n" /* offset lr */ + "\t.byte 0x8b\n\t.uleb128 2\n" /* offset r11 */ + "\t.byte 0x85\n\t.uleb128 3\n" /* offset r5 */ + "\t.byte 0x84\n\t.uleb128 4\n" /* offset r4 */ + "\t.byte 0xd\n\t.uleb128 0xb\n" /* def_cfa_register r11 */ + "\t.align 2\n" + ".LEFDE1:\n\n", (int)ctx->codesz - fcofs); +#endif + break; + default: + break; + } +} + diff --git a/lib/LuaJIT/vm_arm64.dasc b/lib/LuaJIT/vm_arm64.dasc new file mode 100644 index 0000000..fb226e3 --- /dev/null +++ b/lib/LuaJIT/vm_arm64.dasc @@ -0,0 +1,3988 @@ +|// Low-level VM code for ARM64 CPUs. +|// Bytecode interpreter, fast functions and helper functions. +|// Copyright (C) 2005-2017 Mike Pall. See Copyright Notice in luajit.h +| +|.arch arm64 +|.section code_op, code_sub +| +|.actionlist build_actionlist +|.globals GLOB_ +|.globalnames globnames +|.externnames extnames +| +|// Note: The ragged indentation of the instructions is intentional. +|// The starting columns indicate data dependencies. +| +|//----------------------------------------------------------------------- +| +|// ARM64 registers and the AAPCS64 ABI 1.0 at a glance: +|// +|// x0-x17 temp, x19-x28 callee-saved, x29 fp, x30 lr +|// x18 is reserved on most platforms. Don't use it, save it or restore it. +|// x31 doesn't exist. Register number 31 either means xzr/wzr (zero) or sp, +|// depending on the instruction. +|// v0-v7 temp, v8-v15 callee-saved (only d8-d15 preserved), v16-v31 temp +|// +|// x0-x7/v0-v7 hold parameters and results. +| +|// Fixed register assignments for the interpreter. +| +|// The following must be C callee-save. +|.define BASE, x19 // Base of current Lua stack frame. +|.define KBASE, x20 // Constants of current Lua function. +|.define PC, x21 // Next PC. +|.define GLREG, x22 // Global state. +|.define LREG, x23 // Register holding lua_State (also in SAVE_L). +|.define TISNUM, x24 // Constant LJ_TISNUM << 47. +|.define TISNUMhi, x25 // Constant LJ_TISNUM << 15. +|.define TISNIL, x26 // Constant -1LL. +|.define fp, x29 // Yes, we have to maintain a frame pointer. +| +|.define ST_INTERP, w26 // Constant -1. +| +|// The following temporaries are not saved across C calls, except for RA/RC. +|.define RA, x27 +|.define RC, x28 +|.define RB, x17 +|.define RAw, w27 +|.define RCw, w28 +|.define RBw, w17 +|.define INS, x16 +|.define INSw, w16 +|.define ITYPE, x15 +|.define TMP0, x8 +|.define TMP1, x9 +|.define TMP2, x10 +|.define TMP3, x11 +|.define TMP0w, w8 +|.define TMP1w, w9 +|.define TMP2w, w10 +|.define TMP3w, w11 +| +|// Calling conventions. Also used as temporaries. +|.define CARG1, x0 +|.define CARG2, x1 +|.define CARG3, x2 +|.define CARG4, x3 +|.define CARG5, x4 +|.define CARG1w, w0 +|.define CARG2w, w1 +|.define CARG3w, w2 +|.define CARG4w, w3 +|.define CARG5w, w4 +| +|.define FARG1, d0 +|.define FARG2, d1 +| +|.define CRET1, x0 +|.define CRET1w, w0 +| +|// Stack layout while in interpreter. Must match with lj_frame.h. +| +|.define CFRAME_SPACE, 208 +|//----- 16 byte aligned, <-- sp entering interpreter +|// Unused [sp, #204] // 32 bit values +|.define SAVE_NRES, [sp, #200] +|.define SAVE_ERRF, [sp, #196] +|.define SAVE_MULTRES, [sp, #192] +|.define TMPD, [sp, #184] // 64 bit values +|.define SAVE_L, [sp, #176] +|.define SAVE_PC, [sp, #168] +|.define SAVE_CFRAME, [sp, #160] +|.define SAVE_FPR_, 96 // 96+8*8: 64 bit FPR saves +|.define SAVE_GPR_, 16 // 16+10*8: 64 bit GPR saves +|.define SAVE_LR, [sp, #8] +|.define SAVE_FP, [sp] +|//----- 16 byte aligned, <-- sp while in interpreter. +| +|.define TMPDofs, #184 +| +|.macro save_, gpr1, gpr2, fpr1, fpr2 +| stp d..fpr1, d..fpr2, [sp, # SAVE_FPR_+(fpr1-8)*8] +| stp x..gpr1, x..gpr2, [sp, # SAVE_GPR_+(gpr1-19)*8] +|.endmacro +|.macro rest_, gpr1, gpr2, fpr1, fpr2 +| ldp d..fpr1, d..fpr2, [sp, # SAVE_FPR_+(fpr1-8)*8] +| ldp x..gpr1, x..gpr2, [sp, # SAVE_GPR_+(gpr1-19)*8] +|.endmacro +| +|.macro saveregs +| stp fp, lr, [sp, #-CFRAME_SPACE]! +| add fp, sp, #0 +| stp x19, x20, [sp, # SAVE_GPR_] +| save_ 21, 22, 8, 9 +| save_ 23, 24, 10, 11 +| save_ 25, 26, 12, 13 +| save_ 27, 28, 14, 15 +|.endmacro +|.macro restoreregs +| ldp x19, x20, [sp, # SAVE_GPR_] +| rest_ 21, 22, 8, 9 +| rest_ 23, 24, 10, 11 +| rest_ 25, 26, 12, 13 +| rest_ 27, 28, 14, 15 +| ldp fp, lr, [sp], # CFRAME_SPACE +|.endmacro +| +|// Type definitions. Some of these are only used for documentation. +|.type L, lua_State, LREG +|.type GL, global_State, GLREG +|.type TVALUE, TValue +|.type GCOBJ, GCobj +|.type STR, GCstr +|.type TAB, GCtab +|.type LFUNC, GCfuncL +|.type CFUNC, GCfuncC +|.type PROTO, GCproto +|.type UPVAL, GCupval +|.type NODE, Node +|.type NARGS8, int +|.type TRACE, GCtrace +|.type SBUF, SBuf +| +|//----------------------------------------------------------------------- +| +|// Trap for not-yet-implemented parts. +|.macro NYI; brk; .endmacro +| +|//----------------------------------------------------------------------- +| +|// Access to frame relative to BASE. +|.define FRAME_FUNC, #-16 +|.define FRAME_PC, #-8 +| +|// Endian-specific defines. +|.if ENDIAN_LE +|.define LO, 0 +|.define OFS_RD, 2 +|.define OFS_RB, 3 +|.define OFS_RA, 1 +|.define OFS_OP, 0 +|.else +|.define LO, 4 +|.define OFS_RD, 0 +|.define OFS_RB, 0 +|.define OFS_RA, 2 +|.define OFS_OP, 3 +|.endif +| +|.macro decode_RA, dst, ins; ubfx dst, ins, #8, #8; .endmacro +|.macro decode_RB, dst, ins; ubfx dst, ins, #24, #8; .endmacro +|.macro decode_RC, dst, ins; ubfx dst, ins, #16, #8; .endmacro +|.macro decode_RD, dst, ins; ubfx dst, ins, #16, #16; .endmacro +|.macro decode_RC8RD, dst, src; ubfiz dst, src, #3, #8; .endmacro +| +|// Instruction decode+dispatch. +|.macro ins_NEXT +| ldr INSw, [PC], #4 +| add TMP1, GL, INS, uxtb #3 +| decode_RA RA, INS +| ldr TMP0, [TMP1, #GG_G2DISP] +| decode_RD RC, INS +| br TMP0 +|.endmacro +| +|// Instruction footer. +|.if 1 +| // Replicated dispatch. Less unpredictable branches, but higher I-Cache use. +| .define ins_next, ins_NEXT +| .define ins_next_, ins_NEXT +|.else +| // Common dispatch. Lower I-Cache use, only one (very) unpredictable branch. +| // Affects only certain kinds of benchmarks (and only with -j off). +| .macro ins_next +| b ->ins_next +| .endmacro +| .macro ins_next_ +| ->ins_next: +| ins_NEXT +| .endmacro +|.endif +| +|// Call decode and dispatch. +|.macro ins_callt +| // BASE = new base, CARG3 = LFUNC/CFUNC, RC = nargs*8, FRAME_PC(BASE) = PC +| ldr PC, LFUNC:CARG3->pc +| ldr INSw, [PC], #4 +| add TMP1, GL, INS, uxtb #3 +| decode_RA RA, INS +| ldr TMP0, [TMP1, #GG_G2DISP] +| add RA, BASE, RA, lsl #3 +| br TMP0 +|.endmacro +| +|.macro ins_call +| // BASE = new base, CARG3 = LFUNC/CFUNC, RC = nargs*8, PC = caller PC +| str PC, [BASE, FRAME_PC] +| ins_callt +|.endmacro +| +|//----------------------------------------------------------------------- +| +|// Macros to check the TValue type and extract the GCobj. Branch on failure. +|.macro checktp, reg, tp, target +| asr ITYPE, reg, #47 +| cmn ITYPE, #-tp +| and reg, reg, #LJ_GCVMASK +| bne target +|.endmacro +|.macro checktp, dst, reg, tp, target +| asr ITYPE, reg, #47 +| cmn ITYPE, #-tp +| and dst, reg, #LJ_GCVMASK +| bne target +|.endmacro +|.macro checkstr, reg, target; checktp reg, LJ_TSTR, target; .endmacro +|.macro checktab, reg, target; checktp reg, LJ_TTAB, target; .endmacro +|.macro checkfunc, reg, target; checktp reg, LJ_TFUNC, target; .endmacro +|.macro checkint, reg, target +| cmp TISNUMhi, reg, lsr #32 +| bne target +|.endmacro +|.macro checknum, reg, target +| cmp TISNUMhi, reg, lsr #32 +| bls target +|.endmacro +|.macro checknumber, reg, target +| cmp TISNUMhi, reg, lsr #32 +| blo target +|.endmacro +| +|.macro mov_false, reg; movn reg, #0x8000, lsl #32; .endmacro +|.macro mov_true, reg; movn reg, #0x0001, lsl #48; .endmacro +| +#define GL_J(field) (GG_G2J + (int)offsetof(jit_State, field)) +| +#define PC2PROTO(field) ((int)offsetof(GCproto, field)-(int)sizeof(GCproto)) +| +|.macro hotcheck, delta +| lsr CARG1, PC, #1 +| and CARG1, CARG1, #126 +| add CARG1, CARG1, #GG_G2DISP+GG_DISP2HOT +| ldrh CARG2w, [GL, CARG1] +| subs CARG2, CARG2, #delta +| strh CARG2w, [GL, CARG1] +|.endmacro +| +|.macro hotloop +| hotcheck HOTCOUNT_LOOP +| blo ->vm_hotloop +|.endmacro +| +|.macro hotcall +| hotcheck HOTCOUNT_CALL +| blo ->vm_hotcall +|.endmacro +| +|// Set current VM state. +|.macro mv_vmstate, reg, st; movn reg, #LJ_VMST_..st; .endmacro +|.macro st_vmstate, reg; str reg, GL->vmstate; .endmacro +| +|// Move table write barrier back. Overwrites mark and tmp. +|.macro barrierback, tab, mark, tmp +| ldr tmp, GL->gc.grayagain +| and mark, mark, #~LJ_GC_BLACK // black2gray(tab) +| str tab, GL->gc.grayagain +| strb mark, tab->marked +| str tmp, tab->gclist +|.endmacro +| +|//----------------------------------------------------------------------- + +#if !LJ_DUALNUM +#error "Only dual-number mode supported for ARM64 target" +#endif + +/* Generate subroutines used by opcodes and other parts of the VM. */ +/* The .code_sub section should be last to help static branch prediction. */ +static void build_subroutines(BuildCtx *ctx) +{ + |.code_sub + | + |//----------------------------------------------------------------------- + |//-- Return handling ---------------------------------------------------- + |//----------------------------------------------------------------------- + | + |->vm_returnp: + | // See vm_return. Also: RB = previous base. + | tbz PC, #2, ->cont_dispatch // (PC & FRAME_P) == 0? + | + | // Return from pcall or xpcall fast func. + | ldr PC, [RB, FRAME_PC] // Fetch PC of previous frame. + | mov_true TMP0 + | mov BASE, RB + | // Prepending may overwrite the pcall frame, so do it at the end. + | str TMP0, [RA, #-8]! // Prepend true to results. + | + |->vm_returnc: + | adds RC, RC, #8 // RC = (nresults+1)*8. + | mov CRET1, #LUA_YIELD + | beq ->vm_unwind_c_eh + | str RCw, SAVE_MULTRES + | ands CARG1, PC, #FRAME_TYPE + | beq ->BC_RET_Z // Handle regular return to Lua. + | + |->vm_return: + | // BASE = base, RA = resultptr, RC/MULTRES = (nresults+1)*8, PC = return + | // CARG1 = PC & FRAME_TYPE + | and RB, PC, #~FRAME_TYPEP + | cmp CARG1, #FRAME_C + | sub RB, BASE, RB // RB = previous base. + | bne ->vm_returnp + | + | str RB, L->base + | ldrsw CARG2, SAVE_NRES // CARG2 = nresults+1. + | mv_vmstate TMP0w, C + | sub BASE, BASE, #16 + | subs TMP2, RC, #8 + | st_vmstate TMP0w + | beq >2 + |1: + | subs TMP2, TMP2, #8 + | ldr TMP0, [RA], #8 + | str TMP0, [BASE], #8 + | bne <1 + |2: + | cmp RC, CARG2, lsl #3 // More/less results wanted? + | bne >6 + |3: + | str BASE, L->top // Store new top. + | + |->vm_leave_cp: + | ldr RC, SAVE_CFRAME // Restore previous C frame. + | mov CRET1, #0 // Ok return status for vm_pcall. + | str RC, L->cframe + | + |->vm_leave_unw: + | restoreregs + | ret + | + |6: + | bgt >7 // Less results wanted? + | // More results wanted. Check stack size and fill up results with nil. + | ldr CARG3, L->maxstack + | cmp BASE, CARG3 + | bhs >8 + | str TISNIL, [BASE], #8 + | add RC, RC, #8 + | b <2 + | + |7: // Less results wanted. + | cbz CARG2, <3 // LUA_MULTRET+1 case? + | sub CARG1, RC, CARG2, lsl #3 + | sub BASE, BASE, CARG1 // Shrink top. + | b <3 + | + |8: // Corner case: need to grow stack for filling up results. + | // This can happen if: + | // - A C function grows the stack (a lot). + | // - The GC shrinks the stack in between. + | // - A return back from a lua_call() with (high) nresults adjustment. + | str BASE, L->top // Save current top held in BASE (yes). + | mov CARG1, L + | bl extern lj_state_growstack // (lua_State *L, int n) + | ldr BASE, L->top // Need the (realloced) L->top in BASE. + | ldrsw CARG2, SAVE_NRES + | b <2 + | + |->vm_unwind_c: // Unwind C stack, return from vm_pcall. + | // (void *cframe, int errcode) + | mov sp, CARG1 + | mov CRET1, CARG2 + |->vm_unwind_c_eh: // Landing pad for external unwinder. + | ldr L, SAVE_L + | mv_vmstate TMP0w, C + | ldr GL, L->glref + | st_vmstate TMP0w + | b ->vm_leave_unw + | + |->vm_unwind_ff: // Unwind C stack, return from ff pcall. + | // (void *cframe) + | and sp, CARG1, #CFRAME_RAWMASK + |->vm_unwind_ff_eh: // Landing pad for external unwinder. + | ldr L, SAVE_L + | movz TISNUM, #(LJ_TISNUM>>1)&0xffff, lsl #48 + | movz TISNUMhi, #(LJ_TISNUM>>1)&0xffff, lsl #16 + | movn TISNIL, #0 + | mov RC, #16 // 2 results: false + error message. + | ldr BASE, L->base + | ldr GL, L->glref // Setup pointer to global state. + | mov_false TMP0 + | sub RA, BASE, #8 // Results start at BASE-8. + | ldr PC, [BASE, FRAME_PC] // Fetch PC of previous frame. + | str TMP0, [BASE, #-8] // Prepend false to error message. + | st_vmstate ST_INTERP + | b ->vm_returnc + | + |//----------------------------------------------------------------------- + |//-- Grow stack for calls ----------------------------------------------- + |//----------------------------------------------------------------------- + | + |->vm_growstack_c: // Grow stack for C function. + | // CARG1 = L + | mov CARG2, #LUA_MINSTACK + | b >2 + | + |->vm_growstack_l: // Grow stack for Lua function. + | // BASE = new base, RA = BASE+framesize*8, RC = nargs*8, PC = first PC + | add RC, BASE, RC + | sub RA, RA, BASE + | mov CARG1, L + | stp BASE, RC, L->base + | add PC, PC, #4 // Must point after first instruction. + | lsr CARG2, RA, #3 + |2: + | // L->base = new base, L->top = top + | str PC, SAVE_PC + | bl extern lj_state_growstack // (lua_State *L, int n) + | ldp BASE, RC, L->base + | ldr LFUNC:CARG3, [BASE, FRAME_FUNC] + | sub NARGS8:RC, RC, BASE + | and LFUNC:CARG3, CARG3, #LJ_GCVMASK + | // BASE = new base, RB = LFUNC/CFUNC, RC = nargs*8, FRAME_PC(BASE) = PC + | ins_callt // Just retry the call. + | + |//----------------------------------------------------------------------- + |//-- Entry points into the assembler VM --------------------------------- + |//----------------------------------------------------------------------- + | + |->vm_resume: // Setup C frame and resume thread. + | // (lua_State *L, TValue *base, int nres1 = 0, ptrdiff_t ef = 0) + | saveregs + | mov L, CARG1 + | ldr GL, L->glref // Setup pointer to global state. + | mov BASE, CARG2 + | str L, SAVE_L + | mov PC, #FRAME_CP + | str wzr, SAVE_NRES + | add TMP0, sp, #CFRAME_RESUME + | ldrb TMP1w, L->status + | str wzr, SAVE_ERRF + | str L, SAVE_PC // Any value outside of bytecode is ok. + | str xzr, SAVE_CFRAME + | str TMP0, L->cframe + | cbz TMP1w, >3 + | + | // Resume after yield (like a return). + | str L, GL->cur_L + | mov RA, BASE + | ldp BASE, CARG1, L->base + | movz TISNUM, #(LJ_TISNUM>>1)&0xffff, lsl #48 + | movz TISNUMhi, #(LJ_TISNUM>>1)&0xffff, lsl #16 + | ldr PC, [BASE, FRAME_PC] + | strb wzr, L->status + | movn TISNIL, #0 + | sub RC, CARG1, BASE + | ands CARG1, PC, #FRAME_TYPE + | add RC, RC, #8 + | st_vmstate ST_INTERP + | str RCw, SAVE_MULTRES + | beq ->BC_RET_Z + | b ->vm_return + | + |->vm_pcall: // Setup protected C frame and enter VM. + | // (lua_State *L, TValue *base, int nres1, ptrdiff_t ef) + | saveregs + | mov PC, #FRAME_CP + | str CARG4w, SAVE_ERRF + | b >1 + | + |->vm_call: // Setup C frame and enter VM. + | // (lua_State *L, TValue *base, int nres1) + | saveregs + | mov PC, #FRAME_C + | + |1: // Entry point for vm_pcall above (PC = ftype). + | ldr RC, L:CARG1->cframe + | str CARG3w, SAVE_NRES + | mov L, CARG1 + | str CARG1, SAVE_L + | ldr GL, L->glref // Setup pointer to global state. + | mov BASE, CARG2 + | str CARG1, SAVE_PC // Any value outside of bytecode is ok. + | str RC, SAVE_CFRAME + | str fp, L->cframe // Add our C frame to cframe chain. + | + |3: // Entry point for vm_cpcall/vm_resume (BASE = base, PC = ftype). + | str L, GL->cur_L + | ldp RB, CARG1, L->base // RB = old base (for vmeta_call). + | movz TISNUM, #(LJ_TISNUM>>1)&0xffff, lsl #48 + | movz TISNUMhi, #(LJ_TISNUM>>1)&0xffff, lsl #16 + | add PC, PC, BASE + | movn TISNIL, #0 + | sub PC, PC, RB // PC = frame delta + frame type + | sub NARGS8:RC, CARG1, BASE + | st_vmstate ST_INTERP + | + |->vm_call_dispatch: + | // RB = old base, BASE = new base, RC = nargs*8, PC = caller PC + | ldr CARG3, [BASE, FRAME_FUNC] + | checkfunc CARG3, ->vmeta_call + | + |->vm_call_dispatch_f: + | ins_call + | // BASE = new base, CARG3 = func, RC = nargs*8, PC = caller PC + | + |->vm_cpcall: // Setup protected C frame, call C. + | // (lua_State *L, lua_CFunction func, void *ud, lua_CPFunction cp) + | saveregs + | mov L, CARG1 + | ldr RA, L:CARG1->stack + | str CARG1, SAVE_L + | ldr GL, L->glref // Setup pointer to global state. + | ldr RB, L->top + | str CARG1, SAVE_PC // Any value outside of bytecode is ok. + | ldr RC, L->cframe + | sub RA, RA, RB // Compute -savestack(L, L->top). + | str RAw, SAVE_NRES // Neg. delta means cframe w/o frame. + | str wzr, SAVE_ERRF // No error function. + | str RC, SAVE_CFRAME + | str fp, L->cframe // Add our C frame to cframe chain. + | str L, GL->cur_L + | blr CARG4 // (lua_State *L, lua_CFunction func, void *ud) + | mov BASE, CRET1 + | mov PC, #FRAME_CP + | cbnz BASE, <3 // Else continue with the call. + | b ->vm_leave_cp // No base? Just remove C frame. + | + |//----------------------------------------------------------------------- + |//-- Metamethod handling ------------------------------------------------ + |//----------------------------------------------------------------------- + | + |//-- Continuation dispatch ---------------------------------------------- + | + |->cont_dispatch: + | // BASE = meta base, RA = resultptr, RC = (nresults+1)*8 + | ldr LFUNC:CARG3, [RB, FRAME_FUNC] + | ldr CARG1, [BASE, #-32] // Get continuation. + | mov CARG4, BASE + | mov BASE, RB // Restore caller BASE. + | and LFUNC:CARG3, CARG3, #LJ_GCVMASK + |.if FFI + | cmp CARG1, #1 + |.endif + | ldr PC, [CARG4, #-24] // Restore PC from [cont|PC]. + | ldr CARG3, LFUNC:CARG3->pc + | add TMP0, RA, RC + | str TISNIL, [TMP0, #-8] // Ensure one valid arg. + |.if FFI + | bls >1 + |.endif + | ldr KBASE, [CARG3, #PC2PROTO(k)] + | // BASE = base, RA = resultptr, CARG4 = meta base + | br CARG1 + | + |.if FFI + |1: + | beq ->cont_ffi_callback // cont = 1: return from FFI callback. + | // cont = 0: tailcall from C function. + | sub CARG4, CARG4, #32 + | sub RC, CARG4, BASE + | b ->vm_call_tail + |.endif + | + |->cont_cat: // RA = resultptr, CARG4 = meta base + | ldr INSw, [PC, #-4] + | sub CARG2, CARG4, #32 + | ldr TMP0, [RA] + | str BASE, L->base + | decode_RB RB, INS + | decode_RA RA, INS + | add TMP1, BASE, RB, lsl #3 + | subs TMP1, CARG2, TMP1 + | beq >1 + | str TMP0, [CARG2] + | lsr CARG3, TMP1, #3 + | b ->BC_CAT_Z + | + |1: + | str TMP0, [BASE, RA, lsl #3] + | b ->cont_nop + | + |//-- Table indexing metamethods ----------------------------------------- + | + |->vmeta_tgets1: + | movn CARG4, #~LJ_TSTR + | add CARG2, BASE, RB, lsl #3 + | add CARG4, STR:RC, CARG4, lsl #47 + | b >2 + | + |->vmeta_tgets: + | movk CARG2, #(LJ_TTAB>>1)&0xffff, lsl #48 + | str CARG2, GL->tmptv + | add CARG2, GL, #offsetof(global_State, tmptv) + |2: + | add CARG3, sp, TMPDofs + | str CARG4, TMPD + | b >1 + | + |->vmeta_tgetb: // RB = table, RC = index + | add RC, RC, TISNUM + | add CARG2, BASE, RB, lsl #3 + | add CARG3, sp, TMPDofs + | str RC, TMPD + | b >1 + | + |->vmeta_tgetv: // RB = table, RC = key + | add CARG2, BASE, RB, lsl #3 + | add CARG3, BASE, RC, lsl #3 + |1: + | str BASE, L->base + | mov CARG1, L + | str PC, SAVE_PC + | bl extern lj_meta_tget // (lua_State *L, TValue *o, TValue *k) + | // Returns TValue * (finished) or NULL (metamethod). + | cbz CRET1, >3 + | ldr TMP0, [CRET1] + | str TMP0, [BASE, RA, lsl #3] + | ins_next + | + |3: // Call __index metamethod. + | // BASE = base, L->top = new base, stack = cont/func/t/k + | sub TMP1, BASE, #FRAME_CONT + | ldr BASE, L->top + | mov NARGS8:RC, #16 // 2 args for func(t, k). + | ldr LFUNC:CARG3, [BASE, FRAME_FUNC] // Guaranteed to be a function here. + | str PC, [BASE, #-24] // [cont|PC] + | sub PC, BASE, TMP1 + | and LFUNC:CARG3, CARG3, #LJ_GCVMASK + | b ->vm_call_dispatch_f + | + |->vmeta_tgetr: + | sxtw CARG2, TMP1w + | bl extern lj_tab_getinth // (GCtab *t, int32_t key) + | // Returns cTValue * or NULL. + | mov TMP0, TISNIL + | cbz CRET1, ->BC_TGETR_Z + | ldr TMP0, [CRET1] + | b ->BC_TGETR_Z + | + |//----------------------------------------------------------------------- + | + |->vmeta_tsets1: + | movn CARG4, #~LJ_TSTR + | add CARG2, BASE, RB, lsl #3 + | add CARG4, STR:RC, CARG4, lsl #47 + | b >2 + | + |->vmeta_tsets: + | movk CARG2, #(LJ_TTAB>>1)&0xffff, lsl #48 + | str CARG2, GL->tmptv + | add CARG2, GL, #offsetof(global_State, tmptv) + |2: + | add CARG3, sp, TMPDofs + | str CARG4, TMPD + | b >1 + | + |->vmeta_tsetb: // RB = table, RC = index + | add RC, RC, TISNUM + | add CARG2, BASE, RB, lsl #3 + | add CARG3, sp, TMPDofs + | str RC, TMPD + | b >1 + | + |->vmeta_tsetv: + | add CARG2, BASE, RB, lsl #3 + | add CARG3, BASE, RC, lsl #3 + |1: + | str BASE, L->base + | mov CARG1, L + | str PC, SAVE_PC + | bl extern lj_meta_tset // (lua_State *L, TValue *o, TValue *k) + | // Returns TValue * (finished) or NULL (metamethod). + | ldr TMP0, [BASE, RA, lsl #3] + | cbz CRET1, >3 + | // NOBARRIER: lj_meta_tset ensures the table is not black. + | str TMP0, [CRET1] + | ins_next + | + |3: // Call __newindex metamethod. + | // BASE = base, L->top = new base, stack = cont/func/t/k/(v) + | sub TMP1, BASE, #FRAME_CONT + | ldr BASE, L->top + | mov NARGS8:RC, #24 // 3 args for func(t, k, v). + | ldr LFUNC:CARG3, [BASE, FRAME_FUNC] // Guaranteed to be a function here. + | str TMP0, [BASE, #16] // Copy value to third argument. + | str PC, [BASE, #-24] // [cont|PC] + | sub PC, BASE, TMP1 + | and LFUNC:CARG3, CARG3, #LJ_GCVMASK + | b ->vm_call_dispatch_f + | + |->vmeta_tsetr: + | sxtw CARG3, TMP1w + | str BASE, L->base + | str PC, SAVE_PC + | bl extern lj_tab_setinth // (lua_State *L, GCtab *t, int32_t key) + | // Returns TValue *. + | b ->BC_TSETR_Z + | + |//-- Comparison metamethods --------------------------------------------- + | + |->vmeta_comp: + | add CARG2, BASE, RA, lsl #3 + | sub PC, PC, #4 + | add CARG3, BASE, RC, lsl #3 + | str BASE, L->base + | mov CARG1, L + | str PC, SAVE_PC + | uxtb CARG4w, INSw + | bl extern lj_meta_comp // (lua_State *L, TValue *o1, *o2, int op) + | // Returns 0/1 or TValue * (metamethod). + |3: + | cmp CRET1, #1 + | bhi ->vmeta_binop + |4: + | ldrh RBw, [PC, # OFS_RD] + | add PC, PC, #4 + | add RB, PC, RB, lsl #2 + | sub RB, RB, #0x20000 + | csel PC, PC, RB, lo + |->cont_nop: + | ins_next + | + |->cont_ra: // RA = resultptr + | ldr INSw, [PC, #-4] + | ldr TMP0, [RA] + | decode_RA TMP1, INS + | str TMP0, [BASE, TMP1, lsl #3] + | b ->cont_nop + | + |->cont_condt: // RA = resultptr + | ldr TMP0, [RA] + | mov_true TMP1 + | cmp TMP1, TMP0 // Branch if result is true. + | b <4 + | + |->cont_condf: // RA = resultptr + | ldr TMP0, [RA] + | mov_false TMP1 + | cmp TMP0, TMP1 // Branch if result is false. + | b <4 + | + |->vmeta_equal: + | // CARG2, CARG3, CARG4 are already set by BC_ISEQV/BC_ISNEV. + | and TAB:CARG3, CARG3, #LJ_GCVMASK + | sub PC, PC, #4 + | str BASE, L->base + | mov CARG1, L + | str PC, SAVE_PC + | bl extern lj_meta_equal // (lua_State *L, GCobj *o1, *o2, int ne) + | // Returns 0/1 or TValue * (metamethod). + | b <3 + | + |->vmeta_equal_cd: + |.if FFI + | sub PC, PC, #4 + | str BASE, L->base + | mov CARG1, L + | mov CARG2, INS + | str PC, SAVE_PC + | bl extern lj_meta_equal_cd // (lua_State *L, BCIns op) + | // Returns 0/1 or TValue * (metamethod). + | b <3 + |.endif + | + |->vmeta_istype: + | sub PC, PC, #4 + | str BASE, L->base + | mov CARG1, L + | mov CARG2, RA + | mov CARG3, RC + | str PC, SAVE_PC + | bl extern lj_meta_istype // (lua_State *L, BCReg ra, BCReg tp) + | b ->cont_nop + | + |//-- Arithmetic metamethods --------------------------------------------- + | + |->vmeta_arith_vn: + | add CARG3, BASE, RB, lsl #3 + | add CARG4, KBASE, RC, lsl #3 + | b >1 + | + |->vmeta_arith_nv: + | add CARG4, BASE, RB, lsl #3 + | add CARG3, KBASE, RC, lsl #3 + | b >1 + | + |->vmeta_unm: + | add CARG3, BASE, RC, lsl #3 + | mov CARG4, CARG3 + | b >1 + | + |->vmeta_arith_vv: + | add CARG3, BASE, RB, lsl #3 + | add CARG4, BASE, RC, lsl #3 + |1: + | uxtb CARG5w, INSw + | add CARG2, BASE, RA, lsl #3 + | str BASE, L->base + | mov CARG1, L + | str PC, SAVE_PC + | bl extern lj_meta_arith // (lua_State *L, TValue *ra,*rb,*rc, BCReg op) + | // Returns NULL (finished) or TValue * (metamethod). + | cbz CRET1, ->cont_nop + | + | // Call metamethod for binary op. + |->vmeta_binop: + | // BASE = old base, CRET1 = new base, stack = cont/func/o1/o2 + | sub TMP1, CRET1, BASE + | str PC, [CRET1, #-24] // [cont|PC] + | add PC, TMP1, #FRAME_CONT + | mov BASE, CRET1 + | mov NARGS8:RC, #16 // 2 args for func(o1, o2). + | b ->vm_call_dispatch + | + |->vmeta_len: + | add CARG2, BASE, RC, lsl #3 +#if LJ_52 + | mov TAB:RC, TAB:CARG1 // Save table (ignored for other types). +#endif + | str BASE, L->base + | mov CARG1, L + | str PC, SAVE_PC + | bl extern lj_meta_len // (lua_State *L, TValue *o) + | // Returns NULL (retry) or TValue * (metamethod base). +#if LJ_52 + | cbnz CRET1, ->vmeta_binop // Binop call for compatibility. + | mov TAB:CARG1, TAB:RC + | b ->BC_LEN_Z +#else + | b ->vmeta_binop // Binop call for compatibility. +#endif + | + |//-- Call metamethod ---------------------------------------------------- + | + |->vmeta_call: // Resolve and call __call metamethod. + | // RB = old base, BASE = new base, RC = nargs*8 + | mov CARG1, L + | str RB, L->base // This is the callers base! + | sub CARG2, BASE, #16 + | str PC, SAVE_PC + | add CARG3, BASE, NARGS8:RC + | bl extern lj_meta_call // (lua_State *L, TValue *func, TValue *top) + | ldr LFUNC:CARG3, [BASE, FRAME_FUNC] // Guaranteed to be a function here. + | add NARGS8:RC, NARGS8:RC, #8 // Got one more argument now. + | and LFUNC:CARG3, CARG3, #LJ_GCVMASK + | ins_call + | + |->vmeta_callt: // Resolve __call for BC_CALLT. + | // BASE = old base, RA = new base, RC = nargs*8 + | mov CARG1, L + | str BASE, L->base + | sub CARG2, RA, #16 + | str PC, SAVE_PC + | add CARG3, RA, NARGS8:RC + | bl extern lj_meta_call // (lua_State *L, TValue *func, TValue *top) + | ldr TMP1, [RA, FRAME_FUNC] // Guaranteed to be a function here. + | ldr PC, [BASE, FRAME_PC] + | add NARGS8:RC, NARGS8:RC, #8 // Got one more argument now. + | and LFUNC:CARG3, TMP1, #LJ_GCVMASK + | b ->BC_CALLT2_Z + | + |//-- Argument coercion for 'for' statement ------------------------------ + | + |->vmeta_for: + | mov CARG1, L + | str BASE, L->base + | mov CARG2, RA + | str PC, SAVE_PC + | bl extern lj_meta_for // (lua_State *L, TValue *base) + | ldr INSw, [PC, #-4] + |.if JIT + | uxtb TMP0w, INSw + |.endif + | decode_RA RA, INS + | decode_RD RC, INS + |.if JIT + | cmp TMP0, #BC_JFORI + | beq =>BC_JFORI + |.endif + | b =>BC_FORI + | + |//----------------------------------------------------------------------- + |//-- Fast functions ----------------------------------------------------- + |//----------------------------------------------------------------------- + | + |.macro .ffunc, name + |->ff_ .. name: + |.endmacro + | + |.macro .ffunc_1, name + |->ff_ .. name: + | ldr CARG1, [BASE] + | cmp NARGS8:RC, #8 + | blo ->fff_fallback + |.endmacro + | + |.macro .ffunc_2, name + |->ff_ .. name: + | ldp CARG1, CARG2, [BASE] + | cmp NARGS8:RC, #16 + | blo ->fff_fallback + |.endmacro + | + |.macro .ffunc_n, name + | .ffunc name + | ldr CARG1, [BASE] + | cmp NARGS8:RC, #8 + | ldr FARG1, [BASE] + | blo ->fff_fallback + | checknum CARG1, ->fff_fallback + |.endmacro + | + |.macro .ffunc_nn, name + | .ffunc name + | ldp CARG1, CARG2, [BASE] + | cmp NARGS8:RC, #16 + | ldp FARG1, FARG2, [BASE] + | blo ->fff_fallback + | checknum CARG1, ->fff_fallback + | checknum CARG2, ->fff_fallback + |.endmacro + | + |// Inlined GC threshold check. Caveat: uses CARG1 and CARG2. + |.macro ffgccheck + | ldp CARG1, CARG2, GL->gc.total // Assumes threshold follows total. + | cmp CARG1, CARG2 + | blt >1 + | bl ->fff_gcstep + |1: + |.endmacro + | + |//-- Base library: checks ----------------------------------------------- + | + |.ffunc_1 assert + | ldr PC, [BASE, FRAME_PC] + | mov_false TMP1 + | cmp CARG1, TMP1 + | bhs ->fff_fallback + | str CARG1, [BASE, #-16] + | sub RB, BASE, #8 + | subs RA, NARGS8:RC, #8 + | add RC, NARGS8:RC, #8 // Compute (nresults+1)*8. + | cbz RA, ->fff_res // Done if exactly 1 argument. + |1: + | ldr CARG1, [RB, #16] + | sub RA, RA, #8 + | str CARG1, [RB], #8 + | cbnz RA, <1 + | b ->fff_res + | + |.ffunc_1 type + | mov TMP0, #~LJ_TISNUM + | asr ITYPE, CARG1, #47 + | cmn ITYPE, #~LJ_TISNUM + | csinv TMP1, TMP0, ITYPE, lo + | add TMP1, TMP1, #offsetof(GCfuncC, upvalue)/8 + | ldr CARG1, [CFUNC:CARG3, TMP1, lsl #3] + | b ->fff_restv + | + |//-- Base library: getters and setters --------------------------------- + | + |.ffunc_1 getmetatable + | asr ITYPE, CARG1, #47 + | cmn ITYPE, #-LJ_TTAB + | ccmn ITYPE, #-LJ_TUDATA, #4, ne + | and TAB:CARG1, CARG1, #LJ_GCVMASK + | bne >6 + |1: // Field metatable must be at same offset for GCtab and GCudata! + | ldr TAB:RB, TAB:CARG1->metatable + |2: + | mov CARG1, TISNIL + | ldr STR:RC, GL->gcroot[GCROOT_MMNAME+MM_metatable] + | cbz TAB:RB, ->fff_restv + | ldr TMP1w, TAB:RB->hmask + | ldr TMP2w, STR:RC->hash + | ldr NODE:CARG3, TAB:RB->node + | and TMP1w, TMP1w, TMP2w // idx = str->hash & tab->hmask + | add TMP1, TMP1, TMP1, lsl #1 + | movn CARG4, #~LJ_TSTR + | add NODE:CARG3, NODE:CARG3, TMP1, lsl #3 // node = tab->node + idx*3*8 + | add CARG4, STR:RC, CARG4, lsl #47 // Tagged key to look for. + |3: // Rearranged logic, because we expect _not_ to find the key. + | ldp CARG1, TMP0, NODE:CARG3->val + | ldr NODE:CARG3, NODE:CARG3->next + | cmp TMP0, CARG4 + | beq >5 + | cbnz NODE:CARG3, <3 + |4: + | mov CARG1, RB // Use metatable as default result. + | movk CARG1, #(LJ_TTAB>>1)&0xffff, lsl #48 + | b ->fff_restv + |5: + | cmp TMP0, TISNIL + | bne ->fff_restv + | b <4 + | + |6: + | movn TMP0, #~LJ_TISNUM + | cmp ITYPE, TMP0 + | csel ITYPE, ITYPE, TMP0, hs + | sub TMP1, GL, ITYPE, lsl #3 + | ldr TAB:RB, [TMP1, #offsetof(global_State, gcroot[GCROOT_BASEMT])-8] + | b <2 + | + |.ffunc_2 setmetatable + | // Fast path: no mt for table yet and not clearing the mt. + | checktp TMP1, CARG1, LJ_TTAB, ->fff_fallback + | ldr TAB:TMP0, TAB:TMP1->metatable + | asr ITYPE, CARG2, #47 + | ldrb TMP2w, TAB:TMP1->marked + | cmn ITYPE, #-LJ_TTAB + | and TAB:CARG2, CARG2, #LJ_GCVMASK + | ccmp TAB:TMP0, #0, #0, eq + | bne ->fff_fallback + | str TAB:CARG2, TAB:TMP1->metatable + | tbz TMP2w, #2, ->fff_restv // isblack(table) + | barrierback TAB:TMP1, TMP2w, TMP0 + | b ->fff_restv + | + |.ffunc rawget + | ldr CARG2, [BASE] + | cmp NARGS8:RC, #16 + | blo ->fff_fallback + | checktab CARG2, ->fff_fallback + | mov CARG1, L + | add CARG3, BASE, #8 + | bl extern lj_tab_get // (lua_State *L, GCtab *t, cTValue *key) + | // Returns cTValue *. + | ldr CARG1, [CRET1] + | b ->fff_restv + | + |//-- Base library: conversions ------------------------------------------ + | + |.ffunc tonumber + | // Only handles the number case inline (without a base argument). + | ldr CARG1, [BASE] + | cmp NARGS8:RC, #8 + | bne ->fff_fallback + | checknumber CARG1, ->fff_fallback + | b ->fff_restv + | + |.ffunc_1 tostring + | // Only handles the string or number case inline. + | asr ITYPE, CARG1, #47 + | cmn ITYPE, #-LJ_TSTR + | // A __tostring method in the string base metatable is ignored. + | beq ->fff_restv + | // Handle numbers inline, unless a number base metatable is present. + | ldr TMP1, GL->gcroot[GCROOT_BASEMT_NUM] + | str BASE, L->base + | cmn ITYPE, #-LJ_TISNUM + | ccmp TMP1, #0, #0, ls + | str PC, SAVE_PC // Redundant (but a defined value). + | bne ->fff_fallback + | ffgccheck + | mov CARG1, L + | mov CARG2, BASE + | bl extern lj_strfmt_number // (lua_State *L, cTValue *o) + | // Returns GCstr *. + | movn TMP1, #~LJ_TSTR + | ldr BASE, L->base + | add CARG1, CARG1, TMP1, lsl #47 + | b ->fff_restv + | + |//-- Base library: iterators ------------------------------------------- + | + |.ffunc_1 next + | checktp CARG2, CARG1, LJ_TTAB, ->fff_fallback + | str TISNIL, [BASE, NARGS8:RC] // Set missing 2nd arg to nil. + | ldr PC, [BASE, FRAME_PC] + | stp BASE, BASE, L->base // Add frame since C call can throw. + | mov CARG1, L + | add CARG3, BASE, #8 + | str PC, SAVE_PC + | bl extern lj_tab_next // (lua_State *L, GCtab *t, TValue *key) + | // Returns 0 at end of traversal. + | str TISNIL, [BASE, #-16] + | cbz CRET1, ->fff_res1 // End of traversal: return nil. + | ldp CARG1, CARG2, [BASE, #8] // Copy key and value to results. + | mov RC, #(2+1)*8 + | stp CARG1, CARG2, [BASE, #-16] + | b ->fff_res + | + |.ffunc_1 pairs + | checktp TMP1, CARG1, LJ_TTAB, ->fff_fallback +#if LJ_52 + | ldr TAB:CARG2, TAB:TMP1->metatable +#endif + | ldr CFUNC:CARG4, CFUNC:CARG3->upvalue[0] + | ldr PC, [BASE, FRAME_PC] +#if LJ_52 + | cbnz TAB:CARG2, ->fff_fallback +#endif + | mov RC, #(3+1)*8 + | stp CARG1, TISNIL, [BASE, #-8] + | str CFUNC:CARG4, [BASE, #-16] + | b ->fff_res + | + |.ffunc_2 ipairs_aux + | checktab CARG1, ->fff_fallback + | checkint CARG2, ->fff_fallback + | ldr TMP1w, TAB:CARG1->asize + | ldr CARG3, TAB:CARG1->array + | ldr TMP0w, TAB:CARG1->hmask + | add CARG2w, CARG2w, #1 + | cmp CARG2w, TMP1w + | ldr PC, [BASE, FRAME_PC] + | add TMP2, CARG2, TISNUM + | mov RC, #(0+1)*8 + | str TMP2, [BASE, #-16] + | bhs >2 // Not in array part? + | ldr TMP0, [CARG3, CARG2, lsl #3] + |1: + | mov TMP1, #(2+1)*8 + | cmp TMP0, TISNIL + | str TMP0, [BASE, #-8] + | csel RC, RC, TMP1, eq + | b ->fff_res + |2: // Check for empty hash part first. Otherwise call C function. + | cbz TMP0w, ->fff_res + | bl extern lj_tab_getinth // (GCtab *t, int32_t key) + | // Returns cTValue * or NULL. + | cbz CRET1, ->fff_res + | ldr TMP0, [CRET1] + | b <1 + | + |.ffunc_1 ipairs + | checktp TMP1, CARG1, LJ_TTAB, ->fff_fallback +#if LJ_52 + | ldr TAB:CARG2, TAB:TMP1->metatable +#endif + | ldr CFUNC:CARG4, CFUNC:CARG3->upvalue[0] + | ldr PC, [BASE, FRAME_PC] +#if LJ_52 + | cbnz TAB:CARG2, ->fff_fallback +#endif + | mov RC, #(3+1)*8 + | stp CARG1, TISNUM, [BASE, #-8] + | str CFUNC:CARG4, [BASE, #-16] + | b ->fff_res + | + |//-- Base library: catch errors ---------------------------------------- + | + |.ffunc pcall + | ldrb TMP0w, GL->hookmask + | subs NARGS8:RC, NARGS8:RC, #8 + | blo ->fff_fallback + | mov RB, BASE + | add BASE, BASE, #16 + | ubfx TMP0w, TMP0w, #HOOK_ACTIVE_SHIFT, #1 + | add PC, TMP0, #16+FRAME_PCALL + | beq ->vm_call_dispatch + |1: + | add TMP2, BASE, NARGS8:RC + |2: + | ldr TMP0, [TMP2, #-16] + | str TMP0, [TMP2, #-8]! + | cmp TMP2, BASE + | bne <2 + | b ->vm_call_dispatch + | + |.ffunc xpcall + | ldp CARG1, CARG2, [BASE] + | ldrb TMP0w, GL->hookmask + | subs NARGS8:TMP1, NARGS8:RC, #16 + | blo ->fff_fallback + | mov RB, BASE + | asr ITYPE, CARG2, #47 + | ubfx TMP0w, TMP0w, #HOOK_ACTIVE_SHIFT, #1 + | cmn ITYPE, #-LJ_TFUNC + | add PC, TMP0, #24+FRAME_PCALL + | bne ->fff_fallback // Traceback must be a function. + | mov NARGS8:RC, NARGS8:TMP1 + | add BASE, BASE, #24 + | stp CARG2, CARG1, [RB] // Swap function and traceback. + | cbz NARGS8:RC, ->vm_call_dispatch + | b <1 + | + |//-- Coroutine library -------------------------------------------------- + | + |.macro coroutine_resume_wrap, resume + |.if resume + |.ffunc_1 coroutine_resume + | checktp CARG1, LJ_TTHREAD, ->fff_fallback + |.else + |.ffunc coroutine_wrap_aux + | ldr L:CARG1, CFUNC:CARG3->upvalue[0].gcr + | and L:CARG1, CARG1, #LJ_GCVMASK + |.endif + | ldr PC, [BASE, FRAME_PC] + | str BASE, L->base + | ldp RB, CARG2, L:CARG1->base + | ldrb TMP1w, L:CARG1->status + | add TMP0, CARG2, TMP1 + | str PC, SAVE_PC + | cmp TMP0, RB + | beq ->fff_fallback + | cmp TMP1, #LUA_YIELD + | add TMP0, CARG2, #8 + | csel CARG2, CARG2, TMP0, hs + | ldr CARG4, L:CARG1->maxstack + | add CARG3, CARG2, NARGS8:RC + | ldr RB, L:CARG1->cframe + | ccmp CARG3, CARG4, #2, ls + | ccmp RB, #0, #2, ls + | bhi ->fff_fallback + |.if resume + | sub CARG3, CARG3, #8 // Keep resumed thread in stack for GC. + | add BASE, BASE, #8 + | sub NARGS8:RC, NARGS8:RC, #8 + |.endif + | str CARG3, L:CARG1->top + | str BASE, L->top + | cbz NARGS8:RC, >3 + |2: // Move args to coroutine. + | ldr TMP0, [BASE, RB] + | cmp RB, NARGS8:RC + | str TMP0, [CARG2, RB] + | add RB, RB, #8 + | bne <2 + |3: + | mov CARG3, #0 + | mov L:RA, L:CARG1 + | mov CARG4, #0 + | bl ->vm_resume // (lua_State *L, TValue *base, 0, 0) + | // Returns thread status. + |4: + | ldp CARG3, CARG4, L:RA->base + | cmp CRET1, #LUA_YIELD + | ldr BASE, L->base + | str L, GL->cur_L + | st_vmstate ST_INTERP + | bhi >8 + | sub RC, CARG4, CARG3 + | ldr CARG1, L->maxstack + | add CARG2, BASE, RC + | cbz RC, >6 // No results? + | cmp CARG2, CARG1 + | mov RB, #0 + | bhi >9 // Need to grow stack? + | + | sub CARG4, RC, #8 + | str CARG3, L:RA->top // Clear coroutine stack. + |5: // Move results from coroutine. + | ldr TMP0, [CARG3, RB] + | cmp RB, CARG4 + | str TMP0, [BASE, RB] + | add RB, RB, #8 + | bne <5 + |6: + |.if resume + | mov_true TMP1 + | add RC, RC, #16 + |7: + | str TMP1, [BASE, #-8] // Prepend true/false to results. + | sub RA, BASE, #8 + |.else + | mov RA, BASE + | add RC, RC, #8 + |.endif + | ands CARG1, PC, #FRAME_TYPE + | str PC, SAVE_PC + | str RCw, SAVE_MULTRES + | beq ->BC_RET_Z + | b ->vm_return + | + |8: // Coroutine returned with error (at co->top-1). + |.if resume + | ldr TMP0, [CARG4, #-8]! + | mov_false TMP1 + | mov RC, #(2+1)*8 + | str CARG4, L:RA->top // Remove error from coroutine stack. + | str TMP0, [BASE] // Copy error message. + | b <7 + |.else + | mov CARG1, L + | mov CARG2, L:RA + | bl extern lj_ffh_coroutine_wrap_err // (lua_State *L, lua_State *co) + | // Never returns. + |.endif + | + |9: // Handle stack expansion on return from yield. + | mov CARG1, L + | lsr CARG2, RC, #3 + | bl extern lj_state_growstack // (lua_State *L, int n) + | mov CRET1, #0 + | b <4 + |.endmacro + | + | coroutine_resume_wrap 1 // coroutine.resume + | coroutine_resume_wrap 0 // coroutine.wrap + | + |.ffunc coroutine_yield + | ldr TMP0, L->cframe + | add TMP1, BASE, NARGS8:RC + | mov CRET1, #LUA_YIELD + | stp BASE, TMP1, L->base + | tbz TMP0, #0, ->fff_fallback + | str xzr, L->cframe + | strb CRET1w, L->status + | b ->vm_leave_unw + | + |//-- Math library ------------------------------------------------------- + | + |.macro math_round, func, round + | .ffunc math_ .. func + | ldr CARG1, [BASE] + | cmp NARGS8:RC, #8 + | ldr d0, [BASE] + | blo ->fff_fallback + | cmp TISNUMhi, CARG1, lsr #32 + | beq ->fff_restv + | blo ->fff_fallback + | round d0, d0 + | b ->fff_resn + |.endmacro + | + | math_round floor, frintm + | math_round ceil, frintp + | + |.ffunc_1 math_abs + | checknumber CARG1, ->fff_fallback + | and CARG1, CARG1, #U64x(7fffffff,ffffffff) + | bne ->fff_restv + | eor CARG2w, CARG1w, CARG1w, asr #31 + | movz CARG3, #0x41e0, lsl #48 // 2^31. + | subs CARG1w, CARG2w, CARG1w, asr #31 + | add CARG1, CARG1, TISNUM + | csel CARG1, CARG1, CARG3, pl + | // Fallthrough. + | + |->fff_restv: + | // CARG1 = TValue result. + | ldr PC, [BASE, FRAME_PC] + | str CARG1, [BASE, #-16] + |->fff_res1: + | // PC = return. + | mov RC, #(1+1)*8 + |->fff_res: + | // RC = (nresults+1)*8, PC = return. + | ands CARG1, PC, #FRAME_TYPE + | str RCw, SAVE_MULTRES + | sub RA, BASE, #16 + | bne ->vm_return + | ldr INSw, [PC, #-4] + | decode_RB RB, INS + |5: + | cmp RC, RB, lsl #3 // More results expected? + | blo >6 + | decode_RA TMP1, INS + | // Adjust BASE. KBASE is assumed to be set for the calling frame. + | sub BASE, RA, TMP1, lsl #3 + | ins_next + | + |6: // Fill up results with nil. + | add TMP1, RA, RC + | add RC, RC, #8 + | str TISNIL, [TMP1, #-8] + | b <5 + | + |.macro math_extern, func + | .ffunc_n math_ .. func + | bl extern func + | b ->fff_resn + |.endmacro + | + |.macro math_extern2, func + | .ffunc_nn math_ .. func + | bl extern func + | b ->fff_resn + |.endmacro + | + |.ffunc_n math_sqrt + | fsqrt d0, d0 + |->fff_resn: + | ldr PC, [BASE, FRAME_PC] + | str d0, [BASE, #-16] + | b ->fff_res1 + | + |.ffunc math_log + | ldr CARG1, [BASE] + | cmp NARGS8:RC, #8 + | ldr FARG1, [BASE] + | bne ->fff_fallback // Need exactly 1 argument. + | checknum CARG1, ->fff_fallback + | bl extern log + | b ->fff_resn + | + | math_extern log10 + | math_extern exp + | math_extern sin + | math_extern cos + | math_extern tan + | math_extern asin + | math_extern acos + | math_extern atan + | math_extern sinh + | math_extern cosh + | math_extern tanh + | math_extern2 pow + | math_extern2 atan2 + | math_extern2 fmod + | + |.ffunc_2 math_ldexp + | ldr FARG1, [BASE] + | checknum CARG1, ->fff_fallback + | checkint CARG2, ->fff_fallback + | sxtw CARG1, CARG2w + | bl extern ldexp // (double x, int exp) + | b ->fff_resn + | + |.ffunc_n math_frexp + | add CARG1, sp, TMPDofs + | bl extern frexp + | ldr CARG2w, TMPD + | ldr PC, [BASE, FRAME_PC] + | str d0, [BASE, #-16] + | mov RC, #(2+1)*8 + | add CARG2, CARG2, TISNUM + | str CARG2, [BASE, #-8] + | b ->fff_res + | + |.ffunc_n math_modf + | sub CARG1, BASE, #16 + | ldr PC, [BASE, FRAME_PC] + | bl extern modf + | mov RC, #(2+1)*8 + | str d0, [BASE, #-8] + | b ->fff_res + | + |.macro math_minmax, name, cond, fcond + | .ffunc_1 name + | add RB, BASE, RC + | add RA, BASE, #8 + | checkint CARG1, >4 + |1: // Handle integers. + | ldr CARG2, [RA] + | cmp RA, RB + | bhs ->fff_restv + | checkint CARG2, >3 + | cmp CARG1w, CARG2w + | add RA, RA, #8 + | csel CARG1, CARG2, CARG1, cond + | b <1 + |3: // Convert intermediate result to number and continue below. + | scvtf d0, CARG1w + | blo ->fff_fallback + | ldr d1, [RA] + | b >6 + | + |4: + | ldr d0, [BASE] + | blo ->fff_fallback + |5: // Handle numbers. + | ldr CARG2, [RA] + | ldr d1, [RA] + | cmp RA, RB + | bhs ->fff_resn + | checknum CARG2, >7 + |6: + | fcmp d0, d1 + | add RA, RA, #8 + | fcsel d0, d1, d0, fcond + | b <5 + |7: // Convert integer to number and continue above. + | scvtf d1, CARG2w + | blo ->fff_fallback + | b <6 + |.endmacro + | + | math_minmax math_min, gt, hi + | math_minmax math_max, lt, lo + | + |//-- String library ----------------------------------------------------- + | + |.ffunc string_byte // Only handle the 1-arg case here. + | ldp PC, CARG1, [BASE, FRAME_PC] + | cmp NARGS8:RC, #8 + | asr ITYPE, CARG1, #47 + | ccmn ITYPE, #-LJ_TSTR, #0, eq + | and STR:CARG1, CARG1, #LJ_GCVMASK + | bne ->fff_fallback + | ldrb TMP0w, STR:CARG1[1] // Access is always ok (NUL at end). + | ldr CARG3w, STR:CARG1->len + | add TMP0, TMP0, TISNUM + | str TMP0, [BASE, #-16] + | mov RC, #(0+1)*8 + | cbz CARG3, ->fff_res + | b ->fff_res1 + | + |.ffunc string_char // Only handle the 1-arg case here. + | ffgccheck + | ldp PC, CARG1, [BASE, FRAME_PC] + | cmp CARG1w, #255 + | ccmp NARGS8:RC, #8, #0, ls // Need exactly 1 argument. + | bne ->fff_fallback + | checkint CARG1, ->fff_fallback + | mov CARG3, #1 + | // Point to the char inside the integer in the stack slot. + |.if ENDIAN_LE + | mov CARG2, BASE + |.else + | add CARG2, BASE, #7 + |.endif + |->fff_newstr: + | // CARG2 = str, CARG3 = len. + | str BASE, L->base + | mov CARG1, L + | str PC, SAVE_PC + | bl extern lj_str_new // (lua_State *L, char *str, size_t l) + |->fff_resstr: + | // Returns GCstr *. + | ldr BASE, L->base + | movn TMP1, #~LJ_TSTR + | add CARG1, CARG1, TMP1, lsl #47 + | b ->fff_restv + | + |.ffunc string_sub + | ffgccheck + | ldr CARG1, [BASE] + | ldr CARG3, [BASE, #16] + | cmp NARGS8:RC, #16 + | movn RB, #0 + | beq >1 + | blo ->fff_fallback + | checkint CARG3, ->fff_fallback + | sxtw RB, CARG3w + |1: + | ldr CARG2, [BASE, #8] + | checkstr CARG1, ->fff_fallback + | ldr TMP1w, STR:CARG1->len + | checkint CARG2, ->fff_fallback + | sxtw CARG2, CARG2w + | // CARG1 = str, TMP1 = str->len, CARG2 = start, RB = end + | add TMP2, RB, TMP1 + | cmp RB, #0 + | add TMP0, CARG2, TMP1 + | csinc RB, RB, TMP2, ge // if (end < 0) end += len+1 + | cmp CARG2, #0 + | csinc CARG2, CARG2, TMP0, ge // if (start < 0) start += len+1 + | cmp RB, #0 + | csel RB, RB, xzr, ge // if (end < 0) end = 0 + | cmp CARG2, #1 + | csinc CARG2, CARG2, xzr, ge // if (start < 1) start = 1 + | cmp RB, TMP1 + | csel RB, RB, TMP1, le // if (end > len) end = len + | add CARG1, STR:CARG1, #sizeof(GCstr)-1 + | subs CARG3, RB, CARG2 // len = end - start + | add CARG2, CARG1, CARG2 + | add CARG3, CARG3, #1 // len += 1 + | bge ->fff_newstr + | add STR:CARG1, GL, #offsetof(global_State, strempty) + | movn TMP1, #~LJ_TSTR + | add CARG1, CARG1, TMP1, lsl #47 + | b ->fff_restv + | + |.macro ffstring_op, name + | .ffunc string_ .. name + | ffgccheck + | ldr CARG2, [BASE] + | cmp NARGS8:RC, #8 + | asr ITYPE, CARG2, #47 + | ccmn ITYPE, #-LJ_TSTR, #0, hs + | and STR:CARG2, CARG2, #LJ_GCVMASK + | bne ->fff_fallback + | ldr TMP0, GL->tmpbuf.b + | add SBUF:CARG1, GL, #offsetof(global_State, tmpbuf) + | str BASE, L->base + | str PC, SAVE_PC + | str L, GL->tmpbuf.L + | str TMP0, GL->tmpbuf.p + | bl extern lj_buf_putstr_ .. name + | bl extern lj_buf_tostr + | b ->fff_resstr + |.endmacro + | + |ffstring_op reverse + |ffstring_op lower + |ffstring_op upper + | + |//-- Bit library -------------------------------------------------------- + | + |// FP number to bit conversion for soft-float. Clobbers CARG1-CARG3 + |->vm_tobit_fb: + | bls ->fff_fallback + | add CARG2, CARG1, CARG1 + | mov CARG3, #1076 + | sub CARG3, CARG3, CARG2, lsr #53 + | cmp CARG3, #53 + | bhi >1 + | and CARG2, CARG2, #U64x(001fffff,ffffffff) + | orr CARG2, CARG2, #U64x(00200000,00000000) + | cmp CARG1, #0 + | lsr CARG2, CARG2, CARG3 + | cneg CARG1w, CARG2w, mi + | br lr + |1: + | mov CARG1w, #0 + | br lr + | + |.macro .ffunc_bit, name + | .ffunc_1 bit_..name + | adr lr, >1 + | checkint CARG1, ->vm_tobit_fb + |1: + |.endmacro + | + |.macro .ffunc_bit_op, name, ins + | .ffunc_bit name + | mov RA, #8 + | mov TMP0w, CARG1w + | adr lr, >2 + |1: + | ldr CARG1, [BASE, RA] + | cmp RA, NARGS8:RC + | add RA, RA, #8 + | bge >9 + | checkint CARG1, ->vm_tobit_fb + |2: + | ins TMP0w, TMP0w, CARG1w + | b <1 + |.endmacro + | + |.ffunc_bit_op band, and + |.ffunc_bit_op bor, orr + |.ffunc_bit_op bxor, eor + | + |.ffunc_bit tobit + | mov TMP0w, CARG1w + |9: // Label reused by .ffunc_bit_op users. + | add CARG1, TMP0, TISNUM + | b ->fff_restv + | + |.ffunc_bit bswap + | rev TMP0w, CARG1w + | add CARG1, TMP0, TISNUM + | b ->fff_restv + | + |.ffunc_bit bnot + | mvn TMP0w, CARG1w + | add CARG1, TMP0, TISNUM + | b ->fff_restv + | + |.macro .ffunc_bit_sh, name, ins, shmod + | .ffunc bit_..name + | ldp TMP0, CARG1, [BASE] + | cmp NARGS8:RC, #16 + | blo ->fff_fallback + | adr lr, >1 + | checkint CARG1, ->vm_tobit_fb + |1: + |.if shmod == 0 + | mov TMP1, CARG1 + |.else + | neg TMP1, CARG1 + |.endif + | mov CARG1, TMP0 + | adr lr, >2 + | checkint CARG1, ->vm_tobit_fb + |2: + | ins TMP0w, CARG1w, TMP1w + | add CARG1, TMP0, TISNUM + | b ->fff_restv + |.endmacro + | + |.ffunc_bit_sh lshift, lsl, 0 + |.ffunc_bit_sh rshift, lsr, 0 + |.ffunc_bit_sh arshift, asr, 0 + |.ffunc_bit_sh rol, ror, 1 + |.ffunc_bit_sh ror, ror, 0 + | + |//----------------------------------------------------------------------- + | + |->fff_fallback: // Call fast function fallback handler. + | // BASE = new base, RC = nargs*8 + | ldp CFUNC:CARG3, PC, [BASE, FRAME_FUNC] // Fallback may overwrite PC. + | ldr TMP2, L->maxstack + | add TMP1, BASE, NARGS8:RC + | stp BASE, TMP1, L->base + | and CFUNC:CARG3, CARG3, #LJ_GCVMASK + | add TMP1, TMP1, #8*LUA_MINSTACK + | ldr CARG3, CFUNC:CARG3->f + | str PC, SAVE_PC // Redundant (but a defined value). + | cmp TMP1, TMP2 + | mov CARG1, L + | bhi >5 // Need to grow stack. + | blr CARG3 // (lua_State *L) + | // Either throws an error, or recovers and returns -1, 0 or nresults+1. + | ldr BASE, L->base + | cmp CRET1w, #0 + | lsl RC, CRET1, #3 + | sub RA, BASE, #16 + | bgt ->fff_res // Returned nresults+1? + |1: // Returned 0 or -1: retry fast path. + | ldr CARG1, L->top + | ldr CFUNC:CARG3, [BASE, FRAME_FUNC] + | sub NARGS8:RC, CARG1, BASE + | bne ->vm_call_tail // Returned -1? + | and CFUNC:CARG3, CARG3, #LJ_GCVMASK + | ins_callt // Returned 0: retry fast path. + | + |// Reconstruct previous base for vmeta_call during tailcall. + |->vm_call_tail: + | ands TMP0, PC, #FRAME_TYPE + | and TMP1, PC, #~FRAME_TYPEP + | bne >3 + | ldrb RAw, [PC, #-4+OFS_RA] + | lsl RA, RA, #3 + | add TMP1, RA, #16 + |3: + | sub RB, BASE, TMP1 + | b ->vm_call_dispatch // Resolve again for tailcall. + | + |5: // Grow stack for fallback handler. + | mov CARG2, #LUA_MINSTACK + | bl extern lj_state_growstack // (lua_State *L, int n) + | ldr BASE, L->base + | cmp CARG1, CARG1 // Set zero-flag to force retry. + | b <1 + | + |->fff_gcstep: // Call GC step function. + | // BASE = new base, RC = nargs*8 + | add CARG2, BASE, NARGS8:RC // Calculate L->top. + | mov RA, lr + | stp BASE, CARG2, L->base + | str PC, SAVE_PC // Redundant (but a defined value). + | mov CARG1, L + | bl extern lj_gc_step // (lua_State *L) + | ldp BASE, CARG2, L->base + | ldr CFUNC:CARG3, [BASE, FRAME_FUNC] + | mov lr, RA // Help return address predictor. + | sub NARGS8:RC, CARG2, BASE // Calculate nargs*8. + | and CFUNC:CARG3, CARG3, #LJ_GCVMASK + | ret + | + |//----------------------------------------------------------------------- + |//-- Special dispatch targets ------------------------------------------- + |//----------------------------------------------------------------------- + | + |->vm_record: // Dispatch target for recording phase. + |.if JIT + | ldrb CARG1w, GL->hookmask + | tst CARG1, #HOOK_VMEVENT // No recording while in vmevent. + | bne >5 + | // Decrement the hookcount for consistency, but always do the call. + | ldr CARG2w, GL->hookcount + | tst CARG1, #HOOK_ACTIVE + | bne >1 + | sub CARG2w, CARG2w, #1 + | tst CARG1, #LUA_MASKLINE|LUA_MASKCOUNT + | beq >1 + | str CARG2w, GL->hookcount + | b >1 + |.endif + | + |->vm_rethook: // Dispatch target for return hooks. + | ldrb TMP2w, GL->hookmask + | tbz TMP2w, #HOOK_ACTIVE_SHIFT, >1 // Hook already active? + |5: // Re-dispatch to static ins. + | ldr TMP0, [TMP1, #GG_G2DISP+GG_DISP2STATIC] + | br TMP0 + | + |->vm_inshook: // Dispatch target for instr/line hooks. + | ldrb TMP2w, GL->hookmask + | ldr TMP3w, GL->hookcount + | tbnz TMP2w, #HOOK_ACTIVE_SHIFT, <5 // Hook already active? + | tst TMP2w, #LUA_MASKLINE|LUA_MASKCOUNT + | beq <5 + | sub TMP3w, TMP3w, #1 + | str TMP3w, GL->hookcount + | cbz TMP3w, >1 + | tbz TMP2w, #LUA_HOOKLINE, <5 + |1: + | mov CARG1, L + | str BASE, L->base + | mov CARG2, PC + | // SAVE_PC must hold the _previous_ PC. The callee updates it with PC. + | bl extern lj_dispatch_ins // (lua_State *L, const BCIns *pc) + |3: + | ldr BASE, L->base + |4: // Re-dispatch to static ins. + | ldr INSw, [PC, #-4] + | add TMP1, GL, INS, uxtb #3 + | decode_RA RA, INS + | ldr TMP0, [TMP1, #GG_G2DISP+GG_DISP2STATIC] + | decode_RD RC, INS + | br TMP0 + | + |->cont_hook: // Continue from hook yield. + | ldr CARG1, [CARG4, #-40] + | add PC, PC, #4 + | str CARG1w, SAVE_MULTRES // Restore MULTRES for *M ins. + | b <4 + | + |->vm_hotloop: // Hot loop counter underflow. + |.if JIT + | ldr LFUNC:CARG3, [BASE, FRAME_FUNC] // Same as curr_topL(L). + | add CARG1, GL, #GG_G2DISP+GG_DISP2J + | and LFUNC:CARG3, CARG3, #LJ_GCVMASK + | str PC, SAVE_PC + | ldr CARG3, LFUNC:CARG3->pc + | mov CARG2, PC + | str L, [GL, #GL_J(L)] + | ldrb CARG3w, [CARG3, #PC2PROTO(framesize)] + | str BASE, L->base + | add CARG3, BASE, CARG3, lsl #3 + | str CARG3, L->top + | bl extern lj_trace_hot // (jit_State *J, const BCIns *pc) + | b <3 + |.endif + | + |->vm_callhook: // Dispatch target for call hooks. + | mov CARG2, PC + |.if JIT + | b >1 + |.endif + | + |->vm_hotcall: // Hot call counter underflow. + |.if JIT + | orr CARG2, PC, #1 + |1: + |.endif + | add TMP1, BASE, NARGS8:RC + | str PC, SAVE_PC + | mov CARG1, L + | sub RA, RA, BASE + | stp BASE, TMP1, L->base + | bl extern lj_dispatch_call // (lua_State *L, const BCIns *pc) + | // Returns ASMFunction. + | ldp BASE, TMP1, L->base + | str xzr, SAVE_PC // Invalidate for subsequent line hook. + | ldr LFUNC:CARG3, [BASE, FRAME_FUNC] + | add RA, BASE, RA + | sub NARGS8:RC, TMP1, BASE + | ldr INSw, [PC, #-4] + | and LFUNC:CARG3, CARG3, #LJ_GCVMASK + | br CRET1 + | + |->cont_stitch: // Trace stitching. + |.if JIT + | // RA = resultptr, CARG4 = meta base + | ldr RBw, SAVE_MULTRES + | ldr INSw, [PC, #-4] + | ldr TRACE:CARG3, [CARG4, #-40] // Save previous trace. + | subs RB, RB, #8 + | decode_RA RC, INS // Call base. + | and CARG3, CARG3, #LJ_GCVMASK + | beq >2 + |1: // Move results down. + | ldr CARG1, [RA] + | add RA, RA, #8 + | subs RB, RB, #8 + | str CARG1, [BASE, RC, lsl #3] + | add RC, RC, #1 + | bne <1 + |2: + | decode_RA RA, INS + | decode_RB RB, INS + | add RA, RA, RB + |3: + | cmp RA, RC + | bhi >9 // More results wanted? + | + | ldrh RAw, TRACE:CARG3->traceno + | ldrh RCw, TRACE:CARG3->link + | cmp RCw, RAw + | beq ->cont_nop // Blacklisted. + | cmp RCw, #0 + | bne =>BC_JLOOP // Jump to stitched trace. + | + | // Stitch a new trace to the previous trace. + | mov CARG1, #GL_J(exitno) + | str RAw, [GL, CARG1] + | mov CARG1, #GL_J(L) + | str L, [GL, CARG1] + | str BASE, L->base + | add CARG1, GL, #GG_G2J + | mov CARG2, PC + | bl extern lj_dispatch_stitch // (jit_State *J, const BCIns *pc) + | ldr BASE, L->base + | b ->cont_nop + | + |9: // Fill up results with nil. + | str TISNIL, [BASE, RC, lsl #3] + | add RC, RC, #1 + | b <3 + |.endif + | + |->vm_profhook: // Dispatch target for profiler hook. +#if LJ_HASPROFILE + | mov CARG1, L + | str BASE, L->base + | mov CARG2, PC + | bl extern lj_dispatch_profile // (lua_State *L, const BCIns *pc) + | // HOOK_PROFILE is off again, so re-dispatch to dynamic instruction. + | ldr BASE, L->base + | sub PC, PC, #4 + | b ->cont_nop +#endif + | + |//----------------------------------------------------------------------- + |//-- Trace exit handler ------------------------------------------------- + |//----------------------------------------------------------------------- + | + |.macro savex_, a, b + | stp d..a, d..b, [sp, #a*8] + | stp x..a, x..b, [sp, #32*8+a*8] + |.endmacro + | + |->vm_exit_handler: + |.if JIT + | sub sp, sp, #(64*8) + | savex_, 0, 1 + | savex_, 2, 3 + | savex_, 4, 5 + | savex_, 6, 7 + | savex_, 8, 9 + | savex_, 10, 11 + | savex_, 12, 13 + | savex_, 14, 15 + | savex_, 16, 17 + | savex_, 18, 19 + | savex_, 20, 21 + | savex_, 22, 23 + | savex_, 24, 25 + | savex_, 26, 27 + | savex_, 28, 29 + | stp d30, d31, [sp, #30*8] + | ldr CARG1, [sp, #64*8] // Load original value of lr. + | add CARG3, sp, #64*8 // Recompute original value of sp. + | mv_vmstate CARG4w, EXIT + | stp xzr, CARG3, [sp, #62*8] // Store 0/sp in RID_LR/RID_SP. + | sub CARG1, CARG1, lr + | ldr L, GL->cur_L + | lsr CARG1, CARG1, #2 + | ldr BASE, GL->jit_base + | sub CARG1, CARG1, #2 + | ldr CARG2w, [lr] // Load trace number. + | st_vmstate CARG4w + |.if ENDIAN_BE + | rev32 CARG2, CARG2 + |.endif + | str BASE, L->base + | ubfx CARG2w, CARG2w, #5, #16 + | str CARG1w, [GL, #GL_J(exitno)] + | str CARG2w, [GL, #GL_J(parent)] + | str L, [GL, #GL_J(L)] + | str xzr, GL->jit_base + | add CARG1, GL, #GG_G2J + | mov CARG2, sp + | bl extern lj_trace_exit // (jit_State *J, ExitState *ex) + | // Returns MULTRES (unscaled) or negated error code. + | ldr CARG2, L->cframe + | ldr BASE, L->base + | and sp, CARG2, #CFRAME_RAWMASK + | ldr PC, SAVE_PC // Get SAVE_PC. + | str L, SAVE_L // Set SAVE_L (on-trace resume/yield). + | b >1 + |.endif + | + |->vm_exit_interp: + | // CARG1 = MULTRES or negated error code, BASE, PC and GL set. + |.if JIT + | ldr L, SAVE_L + |1: + | cmp CARG1w, #0 + | blt >9 // Check for error from exit. + | lsl RC, CARG1, #3 + | ldr LFUNC:CARG2, [BASE, FRAME_FUNC] + | movz TISNUM, #(LJ_TISNUM>>1)&0xffff, lsl #48 + | movz TISNUMhi, #(LJ_TISNUM>>1)&0xffff, lsl #16 + | movn TISNIL, #0 + | and LFUNC:CARG2, CARG2, #LJ_GCVMASK + | str RCw, SAVE_MULTRES + | str BASE, L->base + | ldr CARG2, LFUNC:CARG2->pc + | str xzr, GL->jit_base + | mv_vmstate CARG4w, INTERP + | ldr KBASE, [CARG2, #PC2PROTO(k)] + | // Modified copy of ins_next which handles function header dispatch, too. + | ldrb RBw, [PC, # OFS_OP] + | ldr INSw, [PC], #4 + | st_vmstate CARG4w + | cmp RBw, #BC_FUNCC+2 // Fast function? + | add TMP1, GL, INS, uxtb #3 + | bhs >4 + |2: + | cmp RBw, #BC_FUNCF // Function header? + | add TMP0, GL, RB, uxtb #3 + | ldr RB, [TMP0, #GG_G2DISP] + | decode_RA RA, INS + | lsr TMP0, INS, #16 + | csel RC, TMP0, RC, lo + | blo >5 + | ldr CARG3, [BASE, FRAME_FUNC] + | sub RC, RC, #8 + | add RA, BASE, RA, lsl #3 // Yes: RA = BASE+framesize*8, RC = nargs*8 + | and LFUNC:CARG3, CARG3, #LJ_GCVMASK + |5: + | br RB + | + |4: // Check frame below fast function. + | ldr CARG1, [BASE, FRAME_PC] + | ands CARG2, CARG1, #FRAME_TYPE + | bne <2 // Trace stitching continuation? + | // Otherwise set KBASE for Lua function below fast function. + | ldr CARG3w, [CARG1, #-4] + | decode_RA CARG1, CARG3 + | sub CARG2, BASE, CARG1, lsl #3 + | ldr LFUNC:CARG3, [CARG2, #-32] + | and LFUNC:CARG3, CARG3, #LJ_GCVMASK + | ldr CARG3, LFUNC:CARG3->pc + | ldr KBASE, [CARG3, #PC2PROTO(k)] + | b <2 + | + |9: // Rethrow error from the right C frame. + | neg CARG2, CARG1 + | mov CARG1, L + | bl extern lj_err_throw // (lua_State *L, int errcode) + |.endif + | + |//----------------------------------------------------------------------- + |//-- Math helper functions ---------------------------------------------- + |//----------------------------------------------------------------------- + | + | // int lj_vm_modi(int dividend, int divisor); + |->vm_modi: + | eor CARG4w, CARG1w, CARG2w + | cmp CARG4w, #0 + | eor CARG3w, CARG1w, CARG1w, asr #31 + | eor CARG4w, CARG2w, CARG2w, asr #31 + | sub CARG3w, CARG3w, CARG1w, asr #31 + | sub CARG4w, CARG4w, CARG2w, asr #31 + | udiv CARG1w, CARG3w, CARG4w + | msub CARG1w, CARG1w, CARG4w, CARG3w + | ccmp CARG1w, #0, #4, mi + | sub CARG3w, CARG1w, CARG4w + | csel CARG1w, CARG1w, CARG3w, eq + | eor CARG3w, CARG1w, CARG2w + | cmp CARG3w, #0 + | cneg CARG1w, CARG1w, mi + | ret + | + |//----------------------------------------------------------------------- + |//-- Miscellaneous functions -------------------------------------------- + |//----------------------------------------------------------------------- + | + |//----------------------------------------------------------------------- + |//-- FFI helper functions ----------------------------------------------- + |//----------------------------------------------------------------------- + | + |// Handler for callback functions. + |// Saveregs already performed. Callback slot number in [sp], g in r12. + |->vm_ffi_callback: + |.if FFI + |.type CTSTATE, CTState, PC + | saveregs + | ldr CTSTATE, GL:x10->ctype_state + | mov GL, x10 + | add x10, sp, # CFRAME_SPACE + | str w9, CTSTATE->cb.slot + | stp x0, x1, CTSTATE->cb.gpr[0] + | stp d0, d1, CTSTATE->cb.fpr[0] + | stp x2, x3, CTSTATE->cb.gpr[2] + | stp d2, d3, CTSTATE->cb.fpr[2] + | stp x4, x5, CTSTATE->cb.gpr[4] + | stp d4, d5, CTSTATE->cb.fpr[4] + | stp x6, x7, CTSTATE->cb.gpr[6] + | stp d6, d7, CTSTATE->cb.fpr[6] + | str x10, CTSTATE->cb.stack + | mov CARG1, CTSTATE + | str CTSTATE, SAVE_PC // Any value outside of bytecode is ok. + | mov CARG2, sp + | bl extern lj_ccallback_enter // (CTState *cts, void *cf) + | // Returns lua_State *. + | ldp BASE, RC, L:CRET1->base + | movz TISNUM, #(LJ_TISNUM>>1)&0xffff, lsl #48 + | movz TISNUMhi, #(LJ_TISNUM>>1)&0xffff, lsl #16 + | movn TISNIL, #0 + | mov L, CRET1 + | ldr LFUNC:CARG3, [BASE, FRAME_FUNC] + | sub RC, RC, BASE + | st_vmstate ST_INTERP + | and LFUNC:CARG3, CARG3, #LJ_GCVMASK + | ins_callt + |.endif + | + |->cont_ffi_callback: // Return from FFI callback. + |.if FFI + | ldr CTSTATE, GL->ctype_state + | stp BASE, CARG4, L->base + | str L, CTSTATE->L + | mov CARG1, CTSTATE + | mov CARG2, RA + | bl extern lj_ccallback_leave // (CTState *cts, TValue *o) + | ldp x0, x1, CTSTATE->cb.gpr[0] + | ldp d0, d1, CTSTATE->cb.fpr[0] + | b ->vm_leave_unw + |.endif + | + |->vm_ffi_call: // Call C function via FFI. + | // Caveat: needs special frame unwinding, see below. + |.if FFI + | .type CCSTATE, CCallState, x19 + | stp fp, lr, [sp, #-32]! + | add fp, sp, #0 + | str CCSTATE, [sp, #16] + | mov CCSTATE, x0 + | ldr TMP0w, CCSTATE:x0->spadj + | ldrb TMP1w, CCSTATE->nsp + | add TMP2, CCSTATE, #offsetof(CCallState, stack) + | subs TMP1, TMP1, #1 + | ldr TMP3, CCSTATE->func + | sub sp, fp, TMP0 + | bmi >2 + |1: // Copy stack slots + | ldr TMP0, [TMP2, TMP1, lsl #3] + | str TMP0, [sp, TMP1, lsl #3] + | subs TMP1, TMP1, #1 + | bpl <1 + |2: + | ldp x0, x1, CCSTATE->gpr[0] + | ldp d0, d1, CCSTATE->fpr[0] + | ldp x2, x3, CCSTATE->gpr[2] + | ldp d2, d3, CCSTATE->fpr[2] + | ldp x4, x5, CCSTATE->gpr[4] + | ldp d4, d5, CCSTATE->fpr[4] + | ldp x6, x7, CCSTATE->gpr[6] + | ldp d6, d7, CCSTATE->fpr[6] + | ldr x8, CCSTATE->retp + | blr TMP3 + | mov sp, fp + | stp x0, x1, CCSTATE->gpr[0] + | stp d0, d1, CCSTATE->fpr[0] + | stp d2, d3, CCSTATE->fpr[2] + | ldr CCSTATE, [sp, #16] + | ldp fp, lr, [sp], #32 + | ret + |.endif + |// Note: vm_ffi_call must be the last function in this object file! + | + |//----------------------------------------------------------------------- +} + +/* Generate the code for a single instruction. */ +static void build_ins(BuildCtx *ctx, BCOp op, int defop) +{ + int vk = 0; + |=>defop: + + switch (op) { + + /* -- Comparison ops ---------------------------------------------------- */ + + /* Remember: all ops branch for a true comparison, fall through otherwise. */ + + case BC_ISLT: case BC_ISGE: case BC_ISLE: case BC_ISGT: + | // RA = src1, RC = src2, JMP with RC = target + | ldr CARG1, [BASE, RA, lsl #3] + | ldrh RBw, [PC, # OFS_RD] + | ldr CARG2, [BASE, RC, lsl #3] + | add PC, PC, #4 + | add RB, PC, RB, lsl #2 + | sub RB, RB, #0x20000 + | checkint CARG1, >3 + | checkint CARG2, >4 + | cmp CARG1w, CARG2w + if (op == BC_ISLT) { + | csel PC, RB, PC, lt + } else if (op == BC_ISGE) { + | csel PC, RB, PC, ge + } else if (op == BC_ISLE) { + | csel PC, RB, PC, le + } else { + | csel PC, RB, PC, gt + } + |1: + | ins_next + | + |3: // RA not int. + | ldr FARG1, [BASE, RA, lsl #3] + | blo ->vmeta_comp + | ldr FARG2, [BASE, RC, lsl #3] + | cmp TISNUMhi, CARG2, lsr #32 + | bhi >5 + | bne ->vmeta_comp + | // RA number, RC int. + | scvtf FARG2, CARG2w + | b >5 + | + |4: // RA int, RC not int + | ldr FARG2, [BASE, RC, lsl #3] + | blo ->vmeta_comp + | // RA int, RC number. + | scvtf FARG1, CARG1w + | + |5: // RA number, RC number + | fcmp FARG1, FARG2 + | // To preserve NaN semantics GE/GT branch on unordered, but LT/LE don't. + if (op == BC_ISLT) { + | csel PC, RB, PC, lo + } else if (op == BC_ISGE) { + | csel PC, RB, PC, hs + } else if (op == BC_ISLE) { + | csel PC, RB, PC, ls + } else { + | csel PC, RB, PC, hi + } + | b <1 + break; + + case BC_ISEQV: case BC_ISNEV: + vk = op == BC_ISEQV; + | // RA = src1, RC = src2, JMP with RC = target + | ldr CARG1, [BASE, RA, lsl #3] + | add RC, BASE, RC, lsl #3 + | ldrh RBw, [PC, # OFS_RD] + | ldr CARG3, [RC] + | add PC, PC, #4 + | add RB, PC, RB, lsl #2 + | sub RB, RB, #0x20000 + | asr ITYPE, CARG3, #47 + | cmn ITYPE, #-LJ_TISNUM + if (vk) { + | bls ->BC_ISEQN_Z + } else { + | bls ->BC_ISNEN_Z + } + | // RC is not a number. + | asr TMP0, CARG1, #47 + |.if FFI + | // Check if RC or RA is a cdata. + | cmn ITYPE, #-LJ_TCDATA + | ccmn TMP0, #-LJ_TCDATA, #4, ne + | beq ->vmeta_equal_cd + |.endif + | cmp CARG1, CARG3 + | bne >2 + | // Tag and value are equal. + if (vk) { + |->BC_ISEQV_Z: + | mov PC, RB // Perform branch. + } + |1: + | ins_next + | + |2: // Check if the tags are the same and it's a table or userdata. + | cmp ITYPE, TMP0 + | ccmn ITYPE, #-LJ_TISTABUD, #2, eq + if (vk) { + | bhi <1 + } else { + | bhi ->BC_ISEQV_Z // Reuse code from opposite instruction. + } + | // Different tables or userdatas. Need to check __eq metamethod. + | // Field metatable must be at same offset for GCtab and GCudata! + | and TAB:CARG2, CARG1, #LJ_GCVMASK + | ldr TAB:TMP2, TAB:CARG2->metatable + if (vk) { + | cbz TAB:TMP2, <1 // No metatable? + | ldrb TMP1w, TAB:TMP2->nomm + | mov CARG4, #0 // ne = 0 + | tbnz TMP1w, #MM_eq, <1 // 'no __eq' flag set: done. + } else { + | cbz TAB:TMP2, ->BC_ISEQV_Z // No metatable? + | ldrb TMP1w, TAB:TMP2->nomm + | mov CARG4, #1 // ne = 1. + | tbnz TMP1w, #MM_eq, ->BC_ISEQV_Z // 'no __eq' flag set: done. + } + | b ->vmeta_equal + break; + + case BC_ISEQS: case BC_ISNES: + vk = op == BC_ISEQS; + | // RA = src, RC = str_const (~), JMP with RC = target + | ldr CARG1, [BASE, RA, lsl #3] + | mvn RC, RC + | ldrh RBw, [PC, # OFS_RD] + | ldr CARG2, [KBASE, RC, lsl #3] + | add PC, PC, #4 + | movn TMP0, #~LJ_TSTR + |.if FFI + | asr ITYPE, CARG1, #47 + |.endif + | add RB, PC, RB, lsl #2 + | add CARG2, CARG2, TMP0, lsl #47 + | sub RB, RB, #0x20000 + |.if FFI + | cmn ITYPE, #-LJ_TCDATA + | beq ->vmeta_equal_cd + |.endif + | cmp CARG1, CARG2 + if (vk) { + | csel PC, RB, PC, eq + } else { + | csel PC, RB, PC, ne + } + | ins_next + break; + + case BC_ISEQN: case BC_ISNEN: + vk = op == BC_ISEQN; + | // RA = src, RC = num_const (~), JMP with RC = target + | ldr CARG1, [BASE, RA, lsl #3] + | add RC, KBASE, RC, lsl #3 + | ldrh RBw, [PC, # OFS_RD] + | ldr CARG3, [RC] + | add PC, PC, #4 + | add RB, PC, RB, lsl #2 + | sub RB, RB, #0x20000 + if (vk) { + |->BC_ISEQN_Z: + } else { + |->BC_ISNEN_Z: + } + | checkint CARG1, >4 + | checkint CARG3, >6 + | cmp CARG1w, CARG3w + |1: + if (vk) { + | csel PC, RB, PC, eq + |2: + } else { + |2: + | csel PC, RB, PC, ne + } + |3: + | ins_next + | + |4: // RA not int. + |.if FFI + | blo >7 + |.else + | blo <2 + |.endif + | ldr FARG1, [BASE, RA, lsl #3] + | ldr FARG2, [RC] + | cmp TISNUMhi, CARG3, lsr #32 + | bne >5 + | // RA number, RC int. + | scvtf FARG2, CARG3w + |5: + | // RA number, RC number. + | fcmp FARG1, FARG2 + | b <1 + | + |6: // RA int, RC number + | ldr FARG2, [RC] + | scvtf FARG1, CARG1w + | fcmp FARG1, FARG2 + | b <1 + | + |.if FFI + |7: + | asr ITYPE, CARG1, #47 + | cmn ITYPE, #-LJ_TCDATA + | bne <2 + | b ->vmeta_equal_cd + |.endif + break; + + case BC_ISEQP: case BC_ISNEP: + vk = op == BC_ISEQP; + | // RA = src, RC = primitive_type (~), JMP with RC = target + | ldr TMP0, [BASE, RA, lsl #3] + | ldrh RBw, [PC, # OFS_RD] + | add PC, PC, #4 + | add RC, RC, #1 + | add RB, PC, RB, lsl #2 + |.if FFI + | asr ITYPE, TMP0, #47 + | cmn ITYPE, #-LJ_TCDATA + | beq ->vmeta_equal_cd + | cmn RC, ITYPE + |.else + | cmn RC, TMP0, asr #47 + |.endif + | sub RB, RB, #0x20000 + if (vk) { + | csel PC, RB, PC, eq + } else { + | csel PC, RB, PC, ne + } + | ins_next + break; + + /* -- Unary test and copy ops ------------------------------------------- */ + + case BC_ISTC: case BC_ISFC: case BC_IST: case BC_ISF: + | // RA = dst or unused, RC = src, JMP with RC = target + | ldrh RBw, [PC, # OFS_RD] + | ldr TMP0, [BASE, RC, lsl #3] + | add PC, PC, #4 + | mov_false TMP1 + | add RB, PC, RB, lsl #2 + | cmp TMP0, TMP1 + | sub RB, RB, #0x20000 + if (op == BC_ISTC || op == BC_IST) { + if (op == BC_ISTC) { + | csel RA, RA, RC, lo + } + | csel PC, RB, PC, lo + } else { + if (op == BC_ISFC) { + | csel RA, RA, RC, hs + } + | csel PC, RB, PC, hs + } + if (op == BC_ISTC || op == BC_ISFC) { + | str TMP0, [BASE, RA, lsl #3] + } + | ins_next + break; + + case BC_ISTYPE: + | // RA = src, RC = -type + | ldr TMP0, [BASE, RA, lsl #3] + | cmn RC, TMP0, asr #47 + | bne ->vmeta_istype + | ins_next + break; + case BC_ISNUM: + | // RA = src, RC = -(TISNUM-1) + | ldr TMP0, [BASE, RA] + | checknum TMP0, ->vmeta_istype + | ins_next + break; + + /* -- Unary ops --------------------------------------------------------- */ + + case BC_MOV: + | // RA = dst, RC = src + | ldr TMP0, [BASE, RC, lsl #3] + | str TMP0, [BASE, RA, lsl #3] + | ins_next + break; + case BC_NOT: + | // RA = dst, RC = src + | ldr TMP0, [BASE, RC, lsl #3] + | mov_false TMP1 + | mov_true TMP2 + | cmp TMP0, TMP1 + | csel TMP0, TMP1, TMP2, lo + | str TMP0, [BASE, RA, lsl #3] + | ins_next + break; + case BC_UNM: + | // RA = dst, RC = src + | ldr TMP0, [BASE, RC, lsl #3] + | asr ITYPE, TMP0, #47 + | cmn ITYPE, #-LJ_TISNUM + | bhi ->vmeta_unm + | eor TMP0, TMP0, #U64x(80000000,00000000) + | bne >5 + | negs TMP0w, TMP0w + | movz CARG3, #0x41e0, lsl #48 // 2^31. + | add TMP0, TMP0, TISNUM + | csel TMP0, TMP0, CARG3, vc + |5: + | str TMP0, [BASE, RA, lsl #3] + | ins_next + break; + case BC_LEN: + | // RA = dst, RC = src + | ldr CARG1, [BASE, RC, lsl #3] + | asr ITYPE, CARG1, #47 + | cmn ITYPE, #-LJ_TSTR + | and CARG1, CARG1, #LJ_GCVMASK + | bne >2 + | ldr CARG1w, STR:CARG1->len + |1: + | add CARG1, CARG1, TISNUM + | str CARG1, [BASE, RA, lsl #3] + | ins_next + | + |2: + | cmn ITYPE, #-LJ_TTAB + | bne ->vmeta_len +#if LJ_52 + | ldr TAB:CARG2, TAB:CARG1->metatable + | cbnz TAB:CARG2, >9 + |3: +#endif + |->BC_LEN_Z: + | bl extern lj_tab_len // (GCtab *t) + | // Returns uint32_t (but less than 2^31). + | b <1 + | +#if LJ_52 + |9: + | ldrb TMP1w, TAB:CARG2->nomm + | tbnz TMP1w, #MM_len, <3 // 'no __len' flag set: done. + | b ->vmeta_len +#endif + break; + + /* -- Binary ops -------------------------------------------------------- */ + + |.macro ins_arithcheck_int, target + | checkint CARG1, target + | checkint CARG2, target + |.endmacro + | + |.macro ins_arithcheck_num, target + | checknum CARG1, target + | checknum CARG2, target + |.endmacro + | + |.macro ins_arithcheck_nzdiv, target + | cbz CARG2w, target + |.endmacro + | + |.macro ins_arithhead + ||vk = ((int)op - BC_ADDVN) / (BC_ADDNV-BC_ADDVN); + ||if (vk == 1) { + | and RC, RC, #255 + | decode_RB RB, INS + ||} else { + | decode_RB RB, INS + | and RC, RC, #255 + ||} + |.endmacro + | + |.macro ins_arithload, reg1, reg2 + | // RA = dst, RB = src1, RC = src2 | num_const + ||switch (vk) { + ||case 0: + | ldr reg1, [BASE, RB, lsl #3] + | ldr reg2, [KBASE, RC, lsl #3] + || break; + ||case 1: + | ldr reg1, [KBASE, RC, lsl #3] + | ldr reg2, [BASE, RB, lsl #3] + || break; + ||default: + | ldr reg1, [BASE, RB, lsl #3] + | ldr reg2, [BASE, RC, lsl #3] + || break; + ||} + |.endmacro + | + |.macro ins_arithfallback, ins + ||switch (vk) { + ||case 0: + | ins ->vmeta_arith_vn + || break; + ||case 1: + | ins ->vmeta_arith_nv + || break; + ||default: + | ins ->vmeta_arith_vv + || break; + ||} + |.endmacro + | + |.macro ins_arithmod, res, reg1, reg2 + | fdiv d2, reg1, reg2 + | frintm d2, d2 + | fmsub res, d2, reg2, reg1 + |.endmacro + | + |.macro ins_arithdn, intins, fpins + | ins_arithhead + | ins_arithload CARG1, CARG2 + | ins_arithcheck_int >5 + |.if "intins" == "smull" + | smull CARG1, CARG1w, CARG2w + | cmp CARG1, CARG1, sxtw + | mov CARG1w, CARG1w + | ins_arithfallback bne + |.elif "intins" == "ins_arithmodi" + | ins_arithfallback ins_arithcheck_nzdiv + | bl ->vm_modi + |.else + | intins CARG1w, CARG1w, CARG2w + | ins_arithfallback bvs + |.endif + | add CARG1, CARG1, TISNUM + | str CARG1, [BASE, RA, lsl #3] + |4: + | ins_next + | + |5: // FP variant. + | ins_arithload FARG1, FARG2 + | ins_arithfallback ins_arithcheck_num + | fpins FARG1, FARG1, FARG2 + | str FARG1, [BASE, RA, lsl #3] + | b <4 + |.endmacro + | + |.macro ins_arithfp, fpins + | ins_arithhead + | ins_arithload CARG1, CARG2 + | ins_arithload FARG1, FARG2 + | ins_arithfallback ins_arithcheck_num + |.if "fpins" == "fpow" + | bl extern pow + |.else + | fpins FARG1, FARG1, FARG2 + |.endif + | str FARG1, [BASE, RA, lsl #3] + | ins_next + |.endmacro + + case BC_ADDVN: case BC_ADDNV: case BC_ADDVV: + | ins_arithdn adds, fadd + break; + case BC_SUBVN: case BC_SUBNV: case BC_SUBVV: + | ins_arithdn subs, fsub + break; + case BC_MULVN: case BC_MULNV: case BC_MULVV: + | ins_arithdn smull, fmul + break; + case BC_DIVVN: case BC_DIVNV: case BC_DIVVV: + | ins_arithfp fdiv + break; + case BC_MODVN: case BC_MODNV: case BC_MODVV: + | ins_arithdn ins_arithmodi, ins_arithmod + break; + case BC_POW: + | // NYI: (partial) integer arithmetic. + | ins_arithfp fpow + break; + + case BC_CAT: + | decode_RB RB, INS + | and RC, RC, #255 + | // RA = dst, RB = src_start, RC = src_end + | str BASE, L->base + | sub CARG3, RC, RB + | add CARG2, BASE, RC, lsl #3 + |->BC_CAT_Z: + | // RA = dst, CARG2 = top-1, CARG3 = left + | mov CARG1, L + | str PC, SAVE_PC + | bl extern lj_meta_cat // (lua_State *L, TValue *top, int left) + | // Returns NULL (finished) or TValue * (metamethod). + | ldrb RBw, [PC, #-4+OFS_RB] + | ldr BASE, L->base + | cbnz CRET1, ->vmeta_binop + | ldr TMP0, [BASE, RB, lsl #3] + | str TMP0, [BASE, RA, lsl #3] // Copy result to RA. + | ins_next + break; + + /* -- Constant ops ------------------------------------------------------ */ + + case BC_KSTR: + | // RA = dst, RC = str_const (~) + | mvn RC, RC + | ldr TMP0, [KBASE, RC, lsl #3] + | movn TMP1, #~LJ_TSTR + | add TMP0, TMP0, TMP1, lsl #47 + | str TMP0, [BASE, RA, lsl #3] + | ins_next + break; + case BC_KCDATA: + |.if FFI + | // RA = dst, RC = cdata_const (~) + | mvn RC, RC + | ldr TMP0, [KBASE, RC, lsl #3] + | movn TMP1, #~LJ_TCDATA + | add TMP0, TMP0, TMP1, lsl #47 + | str TMP0, [BASE, RA, lsl #3] + | ins_next + |.endif + break; + case BC_KSHORT: + | // RA = dst, RC = int16_literal + | sxth RCw, RCw + | add TMP0, RC, TISNUM + | str TMP0, [BASE, RA, lsl #3] + | ins_next + break; + case BC_KNUM: + | // RA = dst, RC = num_const + | ldr TMP0, [KBASE, RC, lsl #3] + | str TMP0, [BASE, RA, lsl #3] + | ins_next + break; + case BC_KPRI: + | // RA = dst, RC = primitive_type (~) + | mvn TMP0, RC, lsl #47 + | str TMP0, [BASE, RA, lsl #3] + | ins_next + break; + case BC_KNIL: + | // RA = base, RC = end + | add RA, BASE, RA, lsl #3 + | add RC, BASE, RC, lsl #3 + | str TISNIL, [RA], #8 + |1: + | cmp RA, RC + | str TISNIL, [RA], #8 + | blt <1 + | ins_next_ + break; + + /* -- Upvalue and function ops ------------------------------------------ */ + + case BC_UGET: + | // RA = dst, RC = uvnum + | ldr LFUNC:CARG2, [BASE, FRAME_FUNC] + | add RC, RC, #offsetof(GCfuncL, uvptr)/8 + | and LFUNC:CARG2, CARG2, #LJ_GCVMASK + | ldr UPVAL:CARG2, [LFUNC:CARG2, RC, lsl #3] + | ldr CARG2, UPVAL:CARG2->v + | ldr TMP0, [CARG2] + | str TMP0, [BASE, RA, lsl #3] + | ins_next + break; + case BC_USETV: + | // RA = uvnum, RC = src + | ldr LFUNC:CARG2, [BASE, FRAME_FUNC] + | add RA, RA, #offsetof(GCfuncL, uvptr)/8 + | and LFUNC:CARG2, CARG2, #LJ_GCVMASK + | ldr UPVAL:CARG1, [LFUNC:CARG2, RA, lsl #3] + | ldr CARG3, [BASE, RC, lsl #3] + | ldr CARG2, UPVAL:CARG1->v + | ldrb TMP2w, UPVAL:CARG1->marked + | ldrb TMP0w, UPVAL:CARG1->closed + | asr ITYPE, CARG3, #47 + | str CARG3, [CARG2] + | add ITYPE, ITYPE, #-LJ_TISGCV + | tst TMP2w, #LJ_GC_BLACK // isblack(uv) + | ccmp TMP0w, #0, #4, ne // && uv->closed + | ccmn ITYPE, #-(LJ_TNUMX - LJ_TISGCV), #0, ne // && tvisgcv(v) + | bhi >2 + |1: + | ins_next + | + |2: // Check if new value is white. + | and GCOBJ:CARG3, CARG3, #LJ_GCVMASK + | ldrb TMP1w, GCOBJ:CARG3->gch.marked + | tst TMP1w, #LJ_GC_WHITES // iswhite(str) + | beq <1 + | // Crossed a write barrier. Move the barrier forward. + | mov CARG1, GL + | bl extern lj_gc_barrieruv // (global_State *g, TValue *tv) + | b <1 + break; + case BC_USETS: + | // RA = uvnum, RC = str_const (~) + | ldr LFUNC:CARG2, [BASE, FRAME_FUNC] + | add RA, RA, #offsetof(GCfuncL, uvptr)/8 + | mvn RC, RC + | and LFUNC:CARG2, CARG2, #LJ_GCVMASK + | ldr UPVAL:CARG1, [LFUNC:CARG2, RA, lsl #3] + | ldr STR:CARG3, [KBASE, RC, lsl #3] + | movn TMP0, #~LJ_TSTR + | ldr CARG2, UPVAL:CARG1->v + | ldrb TMP2w, UPVAL:CARG1->marked + | add TMP0, STR:CARG3, TMP0, lsl #47 + | ldrb TMP1w, STR:CARG3->marked + | str TMP0, [CARG2] + | tbnz TMP2w, #2, >2 // isblack(uv) + |1: + | ins_next + | + |2: // Check if string is white and ensure upvalue is closed. + | ldrb TMP0w, UPVAL:CARG1->closed + | tst TMP1w, #LJ_GC_WHITES // iswhite(str) + | ccmp TMP0w, #0, #4, ne + | beq <1 + | // Crossed a write barrier. Move the barrier forward. + | mov CARG1, GL + | bl extern lj_gc_barrieruv // (global_State *g, TValue *tv) + | b <1 + break; + case BC_USETN: + | // RA = uvnum, RC = num_const + | ldr LFUNC:CARG2, [BASE, FRAME_FUNC] + | add RA, RA, #offsetof(GCfuncL, uvptr)/8 + | and LFUNC:CARG2, CARG2, #LJ_GCVMASK + | ldr UPVAL:CARG2, [LFUNC:CARG2, RA, lsl #3] + | ldr TMP0, [KBASE, RC, lsl #3] + | ldr CARG2, UPVAL:CARG2->v + | str TMP0, [CARG2] + | ins_next + break; + case BC_USETP: + | // RA = uvnum, RC = primitive_type (~) + | ldr LFUNC:CARG2, [BASE, FRAME_FUNC] + | add RA, RA, #offsetof(GCfuncL, uvptr)/8 + | and LFUNC:CARG2, CARG2, #LJ_GCVMASK + | ldr UPVAL:CARG2, [LFUNC:CARG2, RA, lsl #3] + | mvn TMP0, RC, lsl #47 + | ldr CARG2, UPVAL:CARG2->v + | str TMP0, [CARG2] + | ins_next + break; + + case BC_UCLO: + | // RA = level, RC = target + | ldr CARG3, L->openupval + | add RC, PC, RC, lsl #2 + | str BASE, L->base + | sub PC, RC, #0x20000 + | cbz CARG3, >1 + | mov CARG1, L + | add CARG2, BASE, RA, lsl #3 + | bl extern lj_func_closeuv // (lua_State *L, TValue *level) + | ldr BASE, L->base + |1: + | ins_next + break; + + case BC_FNEW: + | // RA = dst, RC = proto_const (~) (holding function prototype) + | mvn RC, RC + | str BASE, L->base + | ldr LFUNC:CARG3, [BASE, FRAME_FUNC] + | str PC, SAVE_PC + | ldr CARG2, [KBASE, RC, lsl #3] + | mov CARG1, L + | and LFUNC:CARG3, CARG3, #LJ_GCVMASK + | // (lua_State *L, GCproto *pt, GCfuncL *parent) + | bl extern lj_func_newL_gc + | // Returns GCfuncL *. + | ldr BASE, L->base + | movn TMP0, #~LJ_TFUNC + | add CRET1, CRET1, TMP0, lsl #47 + | str CRET1, [BASE, RA, lsl #3] + | ins_next + break; + + /* -- Table ops --------------------------------------------------------- */ + + case BC_TNEW: + case BC_TDUP: + | // RA = dst, RC = (hbits|asize) | tab_const (~) + | ldp CARG3, CARG4, GL->gc.total // Assumes threshold follows total. + | str BASE, L->base + | str PC, SAVE_PC + | mov CARG1, L + | cmp CARG3, CARG4 + | bhs >5 + |1: + if (op == BC_TNEW) { + | and CARG2, RC, #0x7ff + | lsr CARG3, RC, #11 + | cmp CARG2, #0x7ff + | mov TMP0, #0x801 + | csel CARG2, CARG2, TMP0, ne + | bl extern lj_tab_new // (lua_State *L, int32_t asize, uint32_t hbits) + | // Returns GCtab *. + } else { + | mvn RC, RC + | ldr CARG2, [KBASE, RC, lsl #3] + | bl extern lj_tab_dup // (lua_State *L, Table *kt) + | // Returns GCtab *. + } + | ldr BASE, L->base + | movk CRET1, #(LJ_TTAB>>1)&0xffff, lsl #48 + | str CRET1, [BASE, RA, lsl #3] + | ins_next + | + |5: + | bl extern lj_gc_step_fixtop // (lua_State *L) + | mov CARG1, L + | b <1 + break; + + case BC_GGET: + | // RA = dst, RC = str_const (~) + case BC_GSET: + | // RA = dst, RC = str_const (~) + | ldr LFUNC:CARG1, [BASE, FRAME_FUNC] + | mvn RC, RC + | and LFUNC:CARG1, CARG1, #LJ_GCVMASK + | ldr TAB:CARG2, LFUNC:CARG1->env + | ldr STR:RC, [KBASE, RC, lsl #3] + if (op == BC_GGET) { + | b ->BC_TGETS_Z + } else { + | b ->BC_TSETS_Z + } + break; + + case BC_TGETV: + | decode_RB RB, INS + | and RC, RC, #255 + | // RA = dst, RB = table, RC = key + | ldr CARG2, [BASE, RB, lsl #3] + | ldr TMP1, [BASE, RC, lsl #3] + | checktab CARG2, ->vmeta_tgetv + | checkint TMP1, >9 // Integer key? + | ldr CARG3, TAB:CARG2->array + | ldr CARG1w, TAB:CARG2->asize + | add CARG3, CARG3, TMP1, uxtw #3 + | cmp TMP1w, CARG1w // In array part? + | bhs ->vmeta_tgetv + | ldr TMP0, [CARG3] + | cmp TMP0, TISNIL + | beq >5 + |1: + | str TMP0, [BASE, RA, lsl #3] + | ins_next + | + |5: // Check for __index if table value is nil. + | ldr TAB:CARG1, TAB:CARG2->metatable + | cbz TAB:CARG1, <1 // No metatable: done. + | ldrb TMP1w, TAB:CARG1->nomm + | tbnz TMP1w, #MM_index, <1 // 'no __index' flag set: done. + | b ->vmeta_tgetv + | + |9: + | asr ITYPE, TMP1, #47 + | cmn ITYPE, #-LJ_TSTR // String key? + | bne ->vmeta_tgetv + | and STR:RC, TMP1, #LJ_GCVMASK + | b ->BC_TGETS_Z + break; + case BC_TGETS: + | decode_RB RB, INS + | and RC, RC, #255 + | // RA = dst, RB = table, RC = str_const (~) + | ldr CARG2, [BASE, RB, lsl #3] + | mvn RC, RC + | ldr STR:RC, [KBASE, RC, lsl #3] + | checktab CARG2, ->vmeta_tgets1 + |->BC_TGETS_Z: + | // TAB:CARG2 = GCtab *, STR:RC = GCstr *, RA = dst + | ldr TMP1w, TAB:CARG2->hmask + | ldr TMP2w, STR:RC->hash + | ldr NODE:CARG3, TAB:CARG2->node + | and TMP1w, TMP1w, TMP2w // idx = str->hash & tab->hmask + | add TMP1, TMP1, TMP1, lsl #1 + | movn CARG4, #~LJ_TSTR + | add NODE:CARG3, NODE:CARG3, TMP1, lsl #3 // node = tab->node + idx*3*8 + | add CARG4, STR:RC, CARG4, lsl #47 // Tagged key to look for. + |1: + | ldp TMP0, CARG1, NODE:CARG3->val + | ldr NODE:CARG3, NODE:CARG3->next + | cmp CARG1, CARG4 + | bne >4 + | cmp TMP0, TISNIL + | beq >5 + |3: + | str TMP0, [BASE, RA, lsl #3] + | ins_next + | + |4: // Follow hash chain. + | cbnz NODE:CARG3, <1 + | // End of hash chain: key not found, nil result. + | mov TMP0, TISNIL + | + |5: // Check for __index if table value is nil. + | ldr TAB:CARG1, TAB:CARG2->metatable + | cbz TAB:CARG1, <3 // No metatable: done. + | ldrb TMP1w, TAB:CARG1->nomm + | tbnz TMP1w, #MM_index, <3 // 'no __index' flag set: done. + | b ->vmeta_tgets + break; + case BC_TGETB: + | decode_RB RB, INS + | and RC, RC, #255 + | // RA = dst, RB = table, RC = index + | ldr CARG2, [BASE, RB, lsl #3] + | checktab CARG2, ->vmeta_tgetb + | ldr CARG3, TAB:CARG2->array + | ldr CARG1w, TAB:CARG2->asize + | add CARG3, CARG3, RC, lsl #3 + | cmp RCw, CARG1w // In array part? + | bhs ->vmeta_tgetb + | ldr TMP0, [CARG3] + | cmp TMP0, TISNIL + | beq >5 + |1: + | str TMP0, [BASE, RA, lsl #3] + | ins_next + | + |5: // Check for __index if table value is nil. + | ldr TAB:CARG1, TAB:CARG2->metatable + | cbz TAB:CARG1, <1 // No metatable: done. + | ldrb TMP1w, TAB:CARG1->nomm + | tbnz TMP1w, #MM_index, <1 // 'no __index' flag set: done. + | b ->vmeta_tgetb + break; + case BC_TGETR: + | decode_RB RB, INS + | and RC, RC, #255 + | // RA = dst, RB = table, RC = key + | ldr CARG1, [BASE, RB, lsl #3] + | ldr TMP1, [BASE, RC, lsl #3] + | and TAB:CARG1, CARG1, #LJ_GCVMASK + | ldr CARG3, TAB:CARG1->array + | ldr TMP2w, TAB:CARG1->asize + | add CARG3, CARG3, TMP1w, uxtw #3 + | cmp TMP1w, TMP2w // In array part? + | bhs ->vmeta_tgetr + | ldr TMP0, [CARG3] + |->BC_TGETR_Z: + | str TMP0, [BASE, RA, lsl #3] + | ins_next + break; + + case BC_TSETV: + | decode_RB RB, INS + | and RC, RC, #255 + | // RA = src, RB = table, RC = key + | ldr CARG2, [BASE, RB, lsl #3] + | ldr TMP1, [BASE, RC, lsl #3] + | checktab CARG2, ->vmeta_tsetv + | checkint TMP1, >9 // Integer key? + | ldr CARG3, TAB:CARG2->array + | ldr CARG1w, TAB:CARG2->asize + | add CARG3, CARG3, TMP1, uxtw #3 + | cmp TMP1w, CARG1w // In array part? + | bhs ->vmeta_tsetv + | ldr TMP1, [CARG3] + | ldr TMP0, [BASE, RA, lsl #3] + | ldrb TMP2w, TAB:CARG2->marked + | cmp TMP1, TISNIL // Previous value is nil? + | beq >5 + |1: + | str TMP0, [CARG3] + | tbnz TMP2w, #2, >7 // isblack(table) + |2: + | ins_next + | + |5: // Check for __newindex if previous value is nil. + | ldr TAB:CARG1, TAB:CARG2->metatable + | cbz TAB:CARG1, <1 // No metatable: done. + | ldrb TMP1w, TAB:CARG1->nomm + | tbnz TMP1w, #MM_newindex, <1 // 'no __newindex' flag set: done. + | b ->vmeta_tsetv + | + |7: // Possible table write barrier for the value. Skip valiswhite check. + | barrierback TAB:CARG2, TMP2w, TMP1 + | b <2 + | + |9: + | asr ITYPE, TMP1, #47 + | cmn ITYPE, #-LJ_TSTR // String key? + | bne ->vmeta_tsetv + | and STR:RC, TMP1, #LJ_GCVMASK + | b ->BC_TSETS_Z + break; + case BC_TSETS: + | decode_RB RB, INS + | and RC, RC, #255 + | // RA = dst, RB = table, RC = str_const (~) + | ldr CARG2, [BASE, RB, lsl #3] + | mvn RC, RC + | ldr STR:RC, [KBASE, RC, lsl #3] + | checktab CARG2, ->vmeta_tsets1 + |->BC_TSETS_Z: + | // TAB:CARG2 = GCtab *, STR:RC = GCstr *, RA = src + | ldr TMP1w, TAB:CARG2->hmask + | ldr TMP2w, STR:RC->hash + | ldr NODE:CARG3, TAB:CARG2->node + | and TMP1w, TMP1w, TMP2w // idx = str->hash & tab->hmask + | add TMP1, TMP1, TMP1, lsl #1 + | movn CARG4, #~LJ_TSTR + | add NODE:CARG3, NODE:CARG3, TMP1, lsl #3 // node = tab->node + idx*3*8 + | add CARG4, STR:RC, CARG4, lsl #47 // Tagged key to look for. + | strb wzr, TAB:CARG2->nomm // Clear metamethod cache. + |1: + | ldp TMP1, CARG1, NODE:CARG3->val + | ldr NODE:TMP3, NODE:CARG3->next + | ldrb TMP2w, TAB:CARG2->marked + | cmp CARG1, CARG4 + | bne >5 + | ldr TMP0, [BASE, RA, lsl #3] + | cmp TMP1, TISNIL // Previous value is nil? + | beq >4 + |2: + | str TMP0, NODE:CARG3->val + | tbnz TMP2w, #2, >7 // isblack(table) + |3: + | ins_next + | + |4: // Check for __newindex if previous value is nil. + | ldr TAB:CARG1, TAB:CARG2->metatable + | cbz TAB:CARG1, <2 // No metatable: done. + | ldrb TMP1w, TAB:CARG1->nomm + | tbnz TMP1w, #MM_newindex, <2 // 'no __newindex' flag set: done. + | b ->vmeta_tsets + | + |5: // Follow hash chain. + | mov NODE:CARG3, NODE:TMP3 + | cbnz NODE:TMP3, <1 + | // End of hash chain: key not found, add a new one. + | + | // But check for __newindex first. + | ldr TAB:CARG1, TAB:CARG2->metatable + | cbz TAB:CARG1, >6 // No metatable: continue. + | ldrb TMP1w, TAB:CARG1->nomm + | // 'no __newindex' flag NOT set: check. + | tbz TMP1w, #MM_newindex, ->vmeta_tsets + |6: + | movn TMP1, #~LJ_TSTR + | str PC, SAVE_PC + | add TMP0, STR:RC, TMP1, lsl #47 + | str BASE, L->base + | mov CARG1, L + | str TMP0, TMPD + | add CARG3, sp, TMPDofs + | bl extern lj_tab_newkey // (lua_State *L, GCtab *t, TValue *k) + | // Returns TValue *. + | ldr BASE, L->base + | ldr TMP0, [BASE, RA, lsl #3] + | str TMP0, [CRET1] + | b <3 // No 2nd write barrier needed. + | + |7: // Possible table write barrier for the value. Skip valiswhite check. + | barrierback TAB:CARG2, TMP2w, TMP1 + | b <3 + break; + case BC_TSETB: + | decode_RB RB, INS + | and RC, RC, #255 + | // RA = src, RB = table, RC = index + | ldr CARG2, [BASE, RB, lsl #3] + | checktab CARG2, ->vmeta_tsetb + | ldr CARG3, TAB:CARG2->array + | ldr CARG1w, TAB:CARG2->asize + | add CARG3, CARG3, RC, lsl #3 + | cmp RCw, CARG1w // In array part? + | bhs ->vmeta_tsetb + | ldr TMP1, [CARG3] + | ldr TMP0, [BASE, RA, lsl #3] + | ldrb TMP2w, TAB:CARG2->marked + | cmp TMP1, TISNIL // Previous value is nil? + | beq >5 + |1: + | str TMP0, [CARG3] + | tbnz TMP2w, #2, >7 // isblack(table) + |2: + | ins_next + | + |5: // Check for __newindex if previous value is nil. + | ldr TAB:CARG1, TAB:CARG2->metatable + | cbz TAB:CARG1, <1 // No metatable: done. + | ldrb TMP1w, TAB:CARG1->nomm + | tbnz TMP1w, #MM_newindex, <1 // 'no __newindex' flag set: done. + | b ->vmeta_tsetb + | + |7: // Possible table write barrier for the value. Skip valiswhite check. + | barrierback TAB:CARG2, TMP2w, TMP1 + | b <2 + break; + case BC_TSETR: + | decode_RB RB, INS + | and RC, RC, #255 + | // RA = src, RB = table, RC = key + | ldr CARG2, [BASE, RB, lsl #3] + | ldr TMP1, [BASE, RC, lsl #3] + | and TAB:CARG2, CARG2, #LJ_GCVMASK + | ldr CARG1, TAB:CARG2->array + | ldrb TMP2w, TAB:CARG2->marked + | ldr CARG4w, TAB:CARG2->asize + | add CARG1, CARG1, TMP1, uxtw #3 + | tbnz TMP2w, #2, >7 // isblack(table) + |2: + | cmp TMP1w, CARG4w // In array part? + | bhs ->vmeta_tsetr + |->BC_TSETR_Z: + | ldr TMP0, [BASE, RA, lsl #3] + | str TMP0, [CARG1] + | ins_next + | + |7: // Possible table write barrier for the value. Skip valiswhite check. + | barrierback TAB:CARG2, TMP2w, TMP0 + | b <2 + break; + + case BC_TSETM: + | // RA = base (table at base-1), RC = num_const (start index) + | add RA, BASE, RA, lsl #3 + |1: + | ldr RBw, SAVE_MULTRES + | ldr TAB:CARG2, [RA, #-8] // Guaranteed to be a table. + | ldr TMP1, [KBASE, RC, lsl #3] // Integer constant is in lo-word. + | sub RB, RB, #8 + | cbz RB, >4 // Nothing to copy? + | and TAB:CARG2, CARG2, #LJ_GCVMASK + | ldr CARG1w, TAB:CARG2->asize + | add CARG3w, TMP1w, RBw, lsr #3 + | ldr CARG4, TAB:CARG2->array + | cmp CARG3, CARG1 + | add RB, RA, RB + | bhi >5 + | add TMP1, CARG4, TMP1w, uxtw #3 + | ldrb TMP2w, TAB:CARG2->marked + |3: // Copy result slots to table. + | ldr TMP0, [RA], #8 + | str TMP0, [TMP1], #8 + | cmp RA, RB + | blo <3 + | tbnz TMP2w, #2, >7 // isblack(table) + |4: + | ins_next + | + |5: // Need to resize array part. + | str BASE, L->base + | mov CARG1, L + | str PC, SAVE_PC + | bl extern lj_tab_reasize // (lua_State *L, GCtab *t, int nasize) + | // Must not reallocate the stack. + | b <1 + | + |7: // Possible table write barrier for any value. Skip valiswhite check. + | barrierback TAB:CARG2, TMP2w, TMP1 + | b <4 + break; + + /* -- Calls and vararg handling ----------------------------------------- */ + + case BC_CALLM: + | // RA = base, (RB = nresults+1,) RC = extra_nargs + | ldr TMP0w, SAVE_MULTRES + | decode_RC8RD NARGS8:RC, RC + | add NARGS8:RC, NARGS8:RC, TMP0 + | b ->BC_CALL_Z + break; + case BC_CALL: + | decode_RC8RD NARGS8:RC, RC + | // RA = base, (RB = nresults+1,) RC = (nargs+1)*8 + |->BC_CALL_Z: + | mov RB, BASE // Save old BASE for vmeta_call. + | add BASE, BASE, RA, lsl #3 + | ldr CARG3, [BASE] + | sub NARGS8:RC, NARGS8:RC, #8 + | add BASE, BASE, #16 + | checkfunc CARG3, ->vmeta_call + | ins_call + break; + + case BC_CALLMT: + | // RA = base, (RB = 0,) RC = extra_nargs + | ldr TMP0w, SAVE_MULTRES + | add NARGS8:RC, TMP0, RC, lsl #3 + | b ->BC_CALLT1_Z + break; + case BC_CALLT: + | lsl NARGS8:RC, RC, #3 + | // RA = base, (RB = 0,) RC = (nargs+1)*8 + |->BC_CALLT1_Z: + | add RA, BASE, RA, lsl #3 + | ldr TMP1, [RA] + | sub NARGS8:RC, NARGS8:RC, #8 + | add RA, RA, #16 + | checktp CARG3, TMP1, LJ_TFUNC, ->vmeta_callt + | ldr PC, [BASE, FRAME_PC] + |->BC_CALLT2_Z: + | mov RB, #0 + | ldrb TMP2w, LFUNC:CARG3->ffid + | tst PC, #FRAME_TYPE + | bne >7 + |1: + | str TMP1, [BASE, FRAME_FUNC] // Copy function down, but keep PC. + | cbz NARGS8:RC, >3 + |2: + | ldr TMP0, [RA, RB] + | add TMP1, RB, #8 + | cmp TMP1, NARGS8:RC + | str TMP0, [BASE, RB] + | mov RB, TMP1 + | bne <2 + |3: + | cmp TMP2, #1 // (> FF_C) Calling a fast function? + | bhi >5 + |4: + | ins_callt + | + |5: // Tailcall to a fast function with a Lua frame below. + | ldrb RAw, [PC, #-4+OFS_RA] + | sub CARG1, BASE, RA, lsl #3 + | ldr LFUNC:CARG1, [CARG1, #-32] + | and LFUNC:CARG1, CARG1, #LJ_GCVMASK + | ldr CARG1, LFUNC:CARG1->pc + | ldr KBASE, [CARG1, #PC2PROTO(k)] + | b <4 + | + |7: // Tailcall from a vararg function. + | eor PC, PC, #FRAME_VARG + | tst PC, #FRAME_TYPEP // Vararg frame below? + | csel TMP2, RB, TMP2, ne // Clear ffid if no Lua function below. + | bne <1 + | sub BASE, BASE, PC + | ldr PC, [BASE, FRAME_PC] + | tst PC, #FRAME_TYPE + | csel TMP2, RB, TMP2, ne // Clear ffid if no Lua function below. + | b <1 + break; + + case BC_ITERC: + | // RA = base, (RB = nresults+1, RC = nargs+1 (2+1)) + | add RA, BASE, RA, lsl #3 + | ldr CARG3, [RA, #-24] + | mov RB, BASE // Save old BASE for vmeta_call. + | ldp CARG1, CARG2, [RA, #-16] + | add BASE, RA, #16 + | mov NARGS8:RC, #16 // Iterators get 2 arguments. + | str CARG3, [RA] // Copy callable. + | stp CARG1, CARG2, [RA, #16] // Copy state and control var. + | checkfunc CARG3, ->vmeta_call + | ins_call + break; + + case BC_ITERN: + | // RA = base, (RB = nresults+1, RC = nargs+1 (2+1)) + |.if JIT + | // NYI: add hotloop, record BC_ITERN. + |.endif + | add RA, BASE, RA, lsl #3 + | ldr TAB:RB, [RA, #-16] + | ldrh TMP3w, [PC, # OFS_RD] + | ldr CARG1w, [RA, #-8+LO] // Get index from control var. + | add PC, PC, #4 + | add TMP3, PC, TMP3, lsl #2 + | and TAB:RB, RB, #LJ_GCVMASK + | sub TMP3, TMP3, #0x20000 + | ldr TMP1w, TAB:RB->asize + | ldr CARG2, TAB:RB->array + |1: // Traverse array part. + | subs RC, CARG1, TMP1 + | add CARG3, CARG2, CARG1, lsl #3 + | bhs >5 // Index points after array part? + | ldr TMP0, [CARG3] + | cmp TMP0, TISNIL + | cinc CARG1, CARG1, eq // Skip holes in array part. + | beq <1 + | add CARG1, CARG1, TISNUM + | stp CARG1, TMP0, [RA] + | add CARG1, CARG1, #1 + |3: + | str CARG1w, [RA, #-8+LO] // Update control var. + | mov PC, TMP3 + |4: + | ins_next + | + |5: // Traverse hash part. + | ldr TMP2w, TAB:RB->hmask + | ldr NODE:RB, TAB:RB->node + |6: + | add CARG1, RC, RC, lsl #1 + | cmp RC, TMP2 // End of iteration? Branch to ITERN+1. + | add NODE:CARG3, NODE:RB, CARG1, lsl #3 // node = tab->node + idx*3*8 + | bhi <4 + | ldp TMP0, CARG1, NODE:CARG3->val + | cmp TMP0, TISNIL + | add RC, RC, #1 + | beq <6 // Skip holes in hash part. + | stp CARG1, TMP0, [RA] + | add CARG1, RC, TMP1 + | b <3 + break; + + case BC_ISNEXT: + | // RA = base, RC = target (points to ITERN) + | add RA, BASE, RA, lsl #3 + | ldr CFUNC:CARG1, [RA, #-24] + | add RC, PC, RC, lsl #2 + | ldp TAB:CARG3, CARG4, [RA, #-16] + | sub RC, RC, #0x20000 + | checkfunc CFUNC:CARG1, >5 + | asr TMP0, TAB:CARG3, #47 + | ldrb TMP1w, CFUNC:CARG1->ffid + | cmn TMP0, #-LJ_TTAB + | ccmp CARG4, TISNIL, #0, eq + | ccmp TMP1w, #FF_next_N, #0, eq + | bne >5 + | mov TMP0w, #0xfffe7fff + | lsl TMP0, TMP0, #32 + | str TMP0, [RA, #-8] // Initialize control var. + |1: + | mov PC, RC + | ins_next + | + |5: // Despecialize bytecode if any of the checks fail. + | mov TMP0, #BC_JMP + | mov TMP1, #BC_ITERC + | strb TMP0w, [PC, #-4+OFS_OP] + | strb TMP1w, [RC, # OFS_OP] + | b <1 + break; + + case BC_VARG: + | decode_RB RB, INS + | and RC, RC, #255 + | // RA = base, RB = (nresults+1), RC = numparams + | ldr TMP1, [BASE, FRAME_PC] + | add RC, BASE, RC, lsl #3 + | add RA, BASE, RA, lsl #3 + | add RC, RC, #FRAME_VARG + | add TMP2, RA, RB, lsl #3 + | sub RC, RC, TMP1 // RC = vbase + | // Note: RC may now be even _above_ BASE if nargs was < numparams. + | sub TMP3, BASE, #16 // TMP3 = vtop + | cbz RB, >5 + | sub TMP2, TMP2, #16 + |1: // Copy vararg slots to destination slots. + | cmp RC, TMP3 + | ldr TMP0, [RC], #8 + | csel TMP0, TMP0, TISNIL, lo + | cmp RA, TMP2 + | str TMP0, [RA], #8 + | blo <1 + |2: + | ins_next + | + |5: // Copy all varargs. + | ldr TMP0, L->maxstack + | subs TMP2, TMP3, RC + | csel RB, xzr, TMP2, le // MULTRES = (max(vtop-vbase,0)+1)*8 + | add RB, RB, #8 + | add TMP1, RA, TMP2 + | str RBw, SAVE_MULTRES + | ble <2 // Nothing to copy. + | cmp TMP1, TMP0 + | bhi >7 + |6: + | ldr TMP0, [RC], #8 + | str TMP0, [RA], #8 + | cmp RC, TMP3 + | blo <6 + | b <2 + | + |7: // Grow stack for varargs. + | lsr CARG2, TMP2, #3 + | stp BASE, RA, L->base + | mov CARG1, L + | sub RC, RC, BASE // Need delta, because BASE may change. + | str PC, SAVE_PC + | bl extern lj_state_growstack // (lua_State *L, int n) + | ldp BASE, RA, L->base + | add RC, BASE, RC + | sub TMP3, BASE, #16 + | b <6 + break; + + /* -- Returns ----------------------------------------------------------- */ + + case BC_RETM: + | // RA = results, RC = extra results + | ldr TMP0w, SAVE_MULTRES + | ldr PC, [BASE, FRAME_PC] + | add RA, BASE, RA, lsl #3 + | add RC, TMP0, RC, lsl #3 + | b ->BC_RETM_Z + break; + + case BC_RET: + | // RA = results, RC = nresults+1 + | ldr PC, [BASE, FRAME_PC] + | lsl RC, RC, #3 + | add RA, BASE, RA, lsl #3 + |->BC_RETM_Z: + | str RCw, SAVE_MULTRES + |1: + | ands CARG1, PC, #FRAME_TYPE + | eor CARG2, PC, #FRAME_VARG + | bne ->BC_RETV2_Z + | + |->BC_RET_Z: + | // BASE = base, RA = resultptr, RC = (nresults+1)*8, PC = return + | ldr INSw, [PC, #-4] + | subs TMP1, RC, #8 + | sub CARG3, BASE, #16 + | beq >3 + |2: + | ldr TMP0, [RA], #8 + | add BASE, BASE, #8 + | sub TMP1, TMP1, #8 + | str TMP0, [BASE, #-24] + | cbnz TMP1, <2 + |3: + | decode_RA RA, INS + | sub CARG4, CARG3, RA, lsl #3 + | decode_RB RB, INS + | ldr LFUNC:CARG1, [CARG4, FRAME_FUNC] + |5: + | cmp RC, RB, lsl #3 // More results expected? + | blo >6 + | and LFUNC:CARG1, CARG1, #LJ_GCVMASK + | mov BASE, CARG4 + | ldr CARG2, LFUNC:CARG1->pc + | ldr KBASE, [CARG2, #PC2PROTO(k)] + | ins_next + | + |6: // Fill up results with nil. + | add BASE, BASE, #8 + | add RC, RC, #8 + | str TISNIL, [BASE, #-24] + | b <5 + | + |->BC_RETV1_Z: // Non-standard return case. + | add RA, BASE, RA, lsl #3 + |->BC_RETV2_Z: + | tst CARG2, #FRAME_TYPEP + | bne ->vm_return + | // Return from vararg function: relocate BASE down. + | sub BASE, BASE, CARG2 + | ldr PC, [BASE, FRAME_PC] + | b <1 + break; + + case BC_RET0: case BC_RET1: + | // RA = results, RC = nresults+1 + | ldr PC, [BASE, FRAME_PC] + | lsl RC, RC, #3 + | str RCw, SAVE_MULTRES + | ands CARG1, PC, #FRAME_TYPE + | eor CARG2, PC, #FRAME_VARG + | bne ->BC_RETV1_Z + | ldr INSw, [PC, #-4] + if (op == BC_RET1) { + | ldr TMP0, [BASE, RA, lsl #3] + } + | sub CARG4, BASE, #16 + | decode_RA RA, INS + | sub BASE, CARG4, RA, lsl #3 + if (op == BC_RET1) { + | str TMP0, [CARG4], #8 + } + | decode_RB RB, INS + | ldr LFUNC:CARG1, [BASE, FRAME_FUNC] + |5: + | cmp RC, RB, lsl #3 + | blo >6 + | and LFUNC:CARG1, CARG1, #LJ_GCVMASK + | ldr CARG2, LFUNC:CARG1->pc + | ldr KBASE, [CARG2, #PC2PROTO(k)] + | ins_next + | + |6: // Fill up results with nil. + | add RC, RC, #8 + | str TISNIL, [CARG4], #8 + | b <5 + break; + + /* -- Loops and branches ------------------------------------------------ */ + + |.define FOR_IDX, [RA]; .define FOR_TIDX, [RA, #4] + |.define FOR_STOP, [RA, #8]; .define FOR_TSTOP, [RA, #12] + |.define FOR_STEP, [RA, #16]; .define FOR_TSTEP, [RA, #20] + |.define FOR_EXT, [RA, #24]; .define FOR_TEXT, [RA, #28] + + case BC_FORL: + |.if JIT + | hotloop + |.endif + | // Fall through. Assumes BC_IFORL follows. + break; + + case BC_JFORI: + case BC_JFORL: +#if !LJ_HASJIT + break; +#endif + case BC_FORI: + case BC_IFORL: + | // RA = base, RC = target (after end of loop or start of loop) + vk = (op == BC_IFORL || op == BC_JFORL); + | add RA, BASE, RA, lsl #3 + | ldp CARG1, CARG2, FOR_IDX // CARG1 = IDX, CARG2 = STOP + | ldr CARG3, FOR_STEP // CARG3 = STEP + if (op != BC_JFORL) { + | add RC, PC, RC, lsl #2 + | sub RC, RC, #0x20000 + } + | checkint CARG1, >5 + if (!vk) { + | checkint CARG2, ->vmeta_for + | checkint CARG3, ->vmeta_for + | tbnz CARG3w, #31, >4 + | cmp CARG1w, CARG2w + } else { + | adds CARG1w, CARG1w, CARG3w + | bvs >2 + | add TMP0, CARG1, TISNUM + | tbnz CARG3w, #31, >4 + | cmp CARG1w, CARG2w + } + |1: + if (op == BC_FORI) { + | csel PC, RC, PC, gt + } else if (op == BC_JFORI) { + | mov PC, RC + | ldrh RCw, [RC, #-4+OFS_RD] + } else if (op == BC_IFORL) { + | csel PC, RC, PC, le + } + if (vk) { + | str TMP0, FOR_IDX + | str TMP0, FOR_EXT + } else { + | str CARG1, FOR_EXT + } + if (op == BC_JFORI || op == BC_JFORL) { + | ble =>BC_JLOOP + } + |2: + | ins_next + | + |4: // Invert check for negative step. + | cmp CARG2w, CARG1w + | b <1 + | + |5: // FP loop. + | ldp d0, d1, FOR_IDX + | blo ->vmeta_for + if (!vk) { + | checknum CARG2, ->vmeta_for + | checknum CARG3, ->vmeta_for + | str d0, FOR_EXT + } else { + | ldr d2, FOR_STEP + | fadd d0, d0, d2 + } + | tbnz CARG3, #63, >7 + | fcmp d0, d1 + |6: + if (vk) { + | str d0, FOR_IDX + | str d0, FOR_EXT + } + if (op == BC_FORI) { + | csel PC, RC, PC, hi + } else if (op == BC_JFORI) { + | ldrh RCw, [RC, #-4+OFS_RD] + | bls =>BC_JLOOP + } else if (op == BC_IFORL) { + | csel PC, RC, PC, ls + } else { + | bls =>BC_JLOOP + } + | b <2 + | + |7: // Invert check for negative step. + | fcmp d1, d0 + | b <6 + break; + + case BC_ITERL: + |.if JIT + | hotloop + |.endif + | // Fall through. Assumes BC_IITERL follows. + break; + + case BC_JITERL: +#if !LJ_HASJIT + break; +#endif + case BC_IITERL: + | // RA = base, RC = target + | ldr CARG1, [BASE, RA, lsl #3] + | add TMP1, BASE, RA, lsl #3 + | cmp CARG1, TISNIL + | beq >1 // Stop if iterator returned nil. + if (op == BC_JITERL) { + | str CARG1, [TMP1, #-8] + | b =>BC_JLOOP + } else { + | add TMP0, PC, RC, lsl #2 // Otherwise save control var + branch. + | sub PC, TMP0, #0x20000 + | str CARG1, [TMP1, #-8] + } + |1: + | ins_next + break; + + case BC_LOOP: + | // RA = base, RC = target (loop extent) + | // Note: RA/RC is only used by trace recorder to determine scope/extent + | // This opcode does NOT jump, it's only purpose is to detect a hot loop. + |.if JIT + | hotloop + |.endif + | // Fall through. Assumes BC_ILOOP follows. + break; + + case BC_ILOOP: + | // RA = base, RC = target (loop extent) + | ins_next + break; + + case BC_JLOOP: + |.if JIT + | // RA = base (ignored), RC = traceno + | ldr CARG1, [GL, #GL_J(trace)] + | mov CARG2w, #0 // Traces on ARM64 don't store the trace #, so use 0. + | ldr TRACE:RC, [CARG1, RC, lsl #3] + | st_vmstate CARG2w + | ldr RA, TRACE:RC->mcode + | str BASE, GL->jit_base + | str L, GL->tmpbuf.L + | sub sp, sp, #16 // See SPS_FIXED. Avoids sp adjust in every root trace. + | br RA + |.endif + break; + + case BC_JMP: + | // RA = base (only used by trace recorder), RC = target + | add RC, PC, RC, lsl #2 + | sub PC, RC, #0x20000 + | ins_next + break; + + /* -- Function headers -------------------------------------------------- */ + + case BC_FUNCF: + |.if JIT + | hotcall + |.endif + case BC_FUNCV: /* NYI: compiled vararg functions. */ + | // Fall through. Assumes BC_IFUNCF/BC_IFUNCV follow. + break; + + case BC_JFUNCF: +#if !LJ_HASJIT + break; +#endif + case BC_IFUNCF: + | // BASE = new base, RA = BASE+framesize*8, CARG3 = LFUNC, RC = nargs*8 + | ldr CARG1, L->maxstack + | ldrb TMP1w, [PC, #-4+PC2PROTO(numparams)] + | ldr KBASE, [PC, #-4+PC2PROTO(k)] + | cmp RA, CARG1 + | bhi ->vm_growstack_l + |2: + | cmp NARGS8:RC, TMP1, lsl #3 // Check for missing parameters. + | blo >3 + if (op == BC_JFUNCF) { + | decode_RD RC, INS + | b =>BC_JLOOP + } else { + | ins_next + } + | + |3: // Clear missing parameters. + | str TISNIL, [BASE, NARGS8:RC] + | add NARGS8:RC, NARGS8:RC, #8 + | b <2 + break; + + case BC_JFUNCV: +#if !LJ_HASJIT + break; +#endif + | NYI // NYI: compiled vararg functions + break; /* NYI: compiled vararg functions. */ + + case BC_IFUNCV: + | // BASE = new base, RA = BASE+framesize*8, CARG3 = LFUNC, RC = nargs*8 + | ldr CARG1, L->maxstack + | movn TMP0, #~LJ_TFUNC + | add TMP2, BASE, RC + | add LFUNC:CARG3, CARG3, TMP0, lsl #47 + | add RA, RA, RC + | add TMP0, RC, #16+FRAME_VARG + | str LFUNC:CARG3, [TMP2], #8 // Store (tagged) copy of LFUNC. + | ldr KBASE, [PC, #-4+PC2PROTO(k)] + | cmp RA, CARG1 + | str TMP0, [TMP2], #8 // Store delta + FRAME_VARG. + | bhs ->vm_growstack_l + | sub RC, TMP2, #16 + | ldrb TMP1w, [PC, #-4+PC2PROTO(numparams)] + | mov RA, BASE + | mov BASE, TMP2 + | cbz TMP1, >2 + |1: + | cmp RA, RC // Less args than parameters? + | bhs >3 + | ldr TMP0, [RA] + | sub TMP1, TMP1, #1 + | str TISNIL, [RA], #8 // Clear old fixarg slot (help the GC). + | str TMP0, [TMP2], #8 + | cbnz TMP1, <1 + |2: + | ins_next + | + |3: + | sub TMP1, TMP1, #1 + | str TISNIL, [TMP2], #8 + | cbz TMP1, <2 + | b <3 + break; + + case BC_FUNCC: + case BC_FUNCCW: + | // BASE = new base, RA = BASE+framesize*8, CARG3 = CFUNC, RC = nargs*8 + if (op == BC_FUNCC) { + | ldr CARG4, CFUNC:CARG3->f + } else { + | ldr CARG4, GL->wrapf + } + | add CARG2, RA, NARGS8:RC + | ldr CARG1, L->maxstack + | add RC, BASE, NARGS8:RC + | cmp CARG2, CARG1 + | stp BASE, RC, L->base + if (op == BC_FUNCCW) { + | ldr CARG2, CFUNC:CARG3->f + } + | mv_vmstate TMP0w, C + | mov CARG1, L + | bhi ->vm_growstack_c // Need to grow stack. + | st_vmstate TMP0w + | blr CARG4 // (lua_State *L [, lua_CFunction f]) + | // Returns nresults. + | ldp BASE, TMP1, L->base + | str L, GL->cur_L + | sbfiz RC, CRET1, #3, #32 + | st_vmstate ST_INTERP + | ldr PC, [BASE, FRAME_PC] + | sub RA, TMP1, RC // RA = L->top - nresults*8 + | b ->vm_returnc + break; + + /* ---------------------------------------------------------------------- */ + + default: + fprintf(stderr, "Error: undefined opcode BC_%s\n", bc_names[op]); + exit(2); + break; + } +} + +static int build_backend(BuildCtx *ctx) +{ + int op; + + dasm_growpc(Dst, BC__MAX); + + build_subroutines(ctx); + + |.code_op + for (op = 0; op < BC__MAX; op++) + build_ins(ctx, (BCOp)op, op); + + return BC__MAX; +} + +/* Emit pseudo frame-info for all assembler functions. */ +static void emit_asm_debug(BuildCtx *ctx) +{ + int fcofs = (int)((uint8_t *)ctx->glob[GLOB_vm_ffi_call] - ctx->code); + int i, cf = CFRAME_SIZE >> 3; + switch (ctx->mode) { + case BUILD_elfasm: + fprintf(ctx->fp, "\t.section .debug_frame,\"\",%%progbits\n"); + fprintf(ctx->fp, + ".Lframe0:\n" + "\t.long .LECIE0-.LSCIE0\n" + ".LSCIE0:\n" + "\t.long 0xffffffff\n" + "\t.byte 0x1\n" + "\t.string \"\"\n" + "\t.uleb128 0x1\n" + "\t.sleb128 -8\n" + "\t.byte 30\n" /* Return address is in lr. */ + "\t.byte 0xc\n\t.uleb128 31\n\t.uleb128 0\n" /* def_cfa sp */ + "\t.align 3\n" + ".LECIE0:\n\n"); + fprintf(ctx->fp, + ".LSFDE0:\n" + "\t.long .LEFDE0-.LASFDE0\n" + ".LASFDE0:\n" + "\t.long .Lframe0\n" + "\t.quad .Lbegin\n" + "\t.quad %d\n" + "\t.byte 0xe\n\t.uleb128 %d\n" /* def_cfa_offset */ + "\t.byte 0x9d\n\t.uleb128 %d\n" /* offset fp */ + "\t.byte 0x9e\n\t.uleb128 %d\n", /* offset lr */ + fcofs, CFRAME_SIZE, cf, cf-1); + for (i = 19; i <= 28; i++) /* offset x19-x28 */ + fprintf(ctx->fp, "\t.byte 0x%x\n\t.uleb128 %d\n", 0x80+i, cf-i+17); + for (i = 8; i <= 15; i++) /* offset d8-d15 */ + fprintf(ctx->fp, "\t.byte 5\n\t.uleb128 0x%x\n\t.uleb128 %d\n", + 64+i, cf-i-4); + fprintf(ctx->fp, + "\t.align 3\n" + ".LEFDE0:\n\n"); +#if LJ_HASFFI + fprintf(ctx->fp, + ".LSFDE1:\n" + "\t.long .LEFDE1-.LASFDE1\n" + ".LASFDE1:\n" + "\t.long .Lframe0\n" + "\t.quad lj_vm_ffi_call\n" + "\t.quad %d\n" + "\t.byte 0xe\n\t.uleb128 32\n" /* def_cfa_offset */ + "\t.byte 0x9d\n\t.uleb128 4\n" /* offset fp */ + "\t.byte 0x9e\n\t.uleb128 3\n" /* offset lr */ + "\t.byte 0x93\n\t.uleb128 2\n" /* offset x19 */ + "\t.align 3\n" + ".LEFDE1:\n\n", (int)ctx->codesz - fcofs); +#endif + fprintf(ctx->fp, "\t.section .eh_frame,\"a\",%%progbits\n"); + fprintf(ctx->fp, + ".Lframe1:\n" + "\t.long .LECIE1-.LSCIE1\n" + ".LSCIE1:\n" + "\t.long 0\n" + "\t.byte 0x1\n" + "\t.string \"zPR\"\n" + "\t.uleb128 0x1\n" + "\t.sleb128 -8\n" + "\t.byte 30\n" /* Return address is in lr. */ + "\t.uleb128 6\n" /* augmentation length */ + "\t.byte 0x1b\n" /* pcrel|sdata4 */ + "\t.long lj_err_unwind_dwarf-.\n" + "\t.byte 0x1b\n" /* pcrel|sdata4 */ + "\t.byte 0xc\n\t.uleb128 31\n\t.uleb128 0\n" /* def_cfa sp */ + "\t.align 3\n" + ".LECIE1:\n\n"); + fprintf(ctx->fp, + ".LSFDE2:\n" + "\t.long .LEFDE2-.LASFDE2\n" + ".LASFDE2:\n" + "\t.long .LASFDE2-.Lframe1\n" + "\t.long .Lbegin-.\n" + "\t.long %d\n" + "\t.uleb128 0\n" /* augmentation length */ + "\t.byte 0xe\n\t.uleb128 %d\n" /* def_cfa_offset */ + "\t.byte 0x9d\n\t.uleb128 %d\n" /* offset fp */ + "\t.byte 0x9e\n\t.uleb128 %d\n", /* offset lr */ + fcofs, CFRAME_SIZE, cf, cf-1); + for (i = 19; i <= 28; i++) /* offset x19-x28 */ + fprintf(ctx->fp, "\t.byte 0x%x\n\t.uleb128 %d\n", 0x80+i, cf-i+17); + for (i = 8; i <= 15; i++) /* offset d8-d15 */ + fprintf(ctx->fp, "\t.byte 5\n\t.uleb128 0x%x\n\t.uleb128 %d\n", + 64+i, cf-i-4); + fprintf(ctx->fp, + "\t.align 3\n" + ".LEFDE2:\n\n"); +#if LJ_HASFFI + fprintf(ctx->fp, + ".Lframe2:\n" + "\t.long .LECIE2-.LSCIE2\n" + ".LSCIE2:\n" + "\t.long 0\n" + "\t.byte 0x1\n" + "\t.string \"zR\"\n" + "\t.uleb128 0x1\n" + "\t.sleb128 -8\n" + "\t.byte 30\n" /* Return address is in lr. */ + "\t.uleb128 1\n" /* augmentation length */ + "\t.byte 0x1b\n" /* pcrel|sdata4 */ + "\t.byte 0xc\n\t.uleb128 31\n\t.uleb128 0\n" /* def_cfa sp */ + "\t.align 3\n" + ".LECIE2:\n\n"); + fprintf(ctx->fp, + ".LSFDE3:\n" + "\t.long .LEFDE3-.LASFDE3\n" + ".LASFDE3:\n" + "\t.long .LASFDE3-.Lframe2\n" + "\t.long lj_vm_ffi_call-.\n" + "\t.long %d\n" + "\t.uleb128 0\n" /* augmentation length */ + "\t.byte 0xe\n\t.uleb128 32\n" /* def_cfa_offset */ + "\t.byte 0x9d\n\t.uleb128 4\n" /* offset fp */ + "\t.byte 0x9e\n\t.uleb128 3\n" /* offset lr */ + "\t.byte 0x93\n\t.uleb128 2\n" /* offset x19 */ + "\t.align 3\n" + ".LEFDE3:\n\n", (int)ctx->codesz - fcofs); +#endif + break; + default: + break; + } +} + diff --git a/lib/LuaJIT/vm_mips.dasc b/lib/LuaJIT/vm_mips.dasc new file mode 100644 index 0000000..f324812 --- /dev/null +++ b/lib/LuaJIT/vm_mips.dasc @@ -0,0 +1,5264 @@ +|// Low-level VM code for MIPS CPUs. +|// Bytecode interpreter, fast functions and helper functions. +|// Copyright (C) 2005-2017 Mike Pall. See Copyright Notice in luajit.h +|// +|// MIPS soft-float support contributed by Djordje Kovacevic and +|// Stefan Pejic from RT-RK.com, sponsored by Cisco Systems, Inc. +| +|.arch mips +|.section code_op, code_sub +| +|.actionlist build_actionlist +|.globals GLOB_ +|.globalnames globnames +|.externnames extnames +| +|// Note: The ragged indentation of the instructions is intentional. +|// The starting columns indicate data dependencies. +| +|//----------------------------------------------------------------------- +| +|// Fixed register assignments for the interpreter. +|// Don't use: r0 = 0, r26/r27 = reserved, r28 = gp, r29 = sp, r31 = ra +| +|.macro .FPU, a, b +|.if FPU +| a, b +|.endif +|.endmacro +| +|// The following must be C callee-save (but BASE is often refetched). +|.define BASE, r16 // Base of current Lua stack frame. +|.define KBASE, r17 // Constants of current Lua function. +|.define PC, r18 // Next PC. +|.define DISPATCH, r19 // Opcode dispatch table. +|.define LREG, r20 // Register holding lua_State (also in SAVE_L). +|.define MULTRES, r21 // Size of multi-result: (nresults+1)*8. +| +|.define JGL, r30 // On-trace: global_State + 32768. +| +|// Constants for type-comparisons, stores and conversions. C callee-save. +|.define TISNUM, r22 +|.define TISNIL, r30 +|.if FPU +|.define TOBIT, f30 // 2^52 + 2^51. +|.endif +| +|// The following temporaries are not saved across C calls, except for RA. +|.define RA, r23 // Callee-save. +|.define RB, r8 +|.define RC, r9 +|.define RD, r10 +|.define INS, r11 +| +|.define AT, r1 // Assembler temporary. +|.define TMP0, r12 +|.define TMP1, r13 +|.define TMP2, r14 +|.define TMP3, r15 +| +|// MIPS o32 calling convention. +|.define CFUNCADDR, r25 +|.define CARG1, r4 +|.define CARG2, r5 +|.define CARG3, r6 +|.define CARG4, r7 +| +|.define CRET1, r2 +|.define CRET2, r3 +| +|.if ENDIAN_LE +|.define SFRETLO, CRET1 +|.define SFRETHI, CRET2 +|.define SFARG1LO, CARG1 +|.define SFARG1HI, CARG2 +|.define SFARG2LO, CARG3 +|.define SFARG2HI, CARG4 +|.else +|.define SFRETLO, CRET2 +|.define SFRETHI, CRET1 +|.define SFARG1LO, CARG2 +|.define SFARG1HI, CARG1 +|.define SFARG2LO, CARG4 +|.define SFARG2HI, CARG3 +|.endif +| +|.if FPU +|.define FARG1, f12 +|.define FARG2, f14 +| +|.define FRET1, f0 +|.define FRET2, f2 +|.endif +| +|// Stack layout while in interpreter. Must match with lj_frame.h. +|.if FPU // MIPS32 hard-float. +| +|.define CFRAME_SPACE, 112 // Delta for sp. +| +|.define SAVE_ERRF, 124(sp) // 32 bit C frame info. +|.define SAVE_NRES, 120(sp) +|.define SAVE_CFRAME, 116(sp) +|.define SAVE_L, 112(sp) +|//----- 8 byte aligned, ^^^^ 16 byte register save area, owned by interpreter. +|.define SAVE_GPR_, 72 // .. 72+10*4: 32 bit GPR saves. +|.define SAVE_FPR_, 24 // .. 24+6*8: 64 bit FPR saves. +| +|.else // MIPS32 soft-float +| +|.define CFRAME_SPACE, 64 // Delta for sp. +| +|.define SAVE_ERRF, 76(sp) // 32 bit C frame info. +|.define SAVE_NRES, 72(sp) +|.define SAVE_CFRAME, 68(sp) +|.define SAVE_L, 64(sp) +|//----- 8 byte aligned, ^^^^ 16 byte register save area, owned by interpreter. +|.define SAVE_GPR_, 24 // .. 24+10*4: 32 bit GPR saves. +| +|.endif +| +|.define SAVE_PC, 20(sp) +|.define ARG5, 16(sp) +|.define CSAVE_4, 12(sp) +|.define CSAVE_3, 8(sp) +|.define CSAVE_2, 4(sp) +|.define CSAVE_1, 0(sp) +|//----- 8 byte aligned, ^^^^ 16 byte register save area, owned by callee. +| +|.define ARG5_OFS, 16 +|.define SAVE_MULTRES, ARG5 +| +|//----------------------------------------------------------------------- +| +|.macro saveregs +| addiu sp, sp, -CFRAME_SPACE +| sw ra, SAVE_GPR_+9*4(sp) +| sw r30, SAVE_GPR_+8*4(sp) +| .FPU sdc1 f30, SAVE_FPR_+5*8(sp) +| sw r23, SAVE_GPR_+7*4(sp) +| sw r22, SAVE_GPR_+6*4(sp) +| .FPU sdc1 f28, SAVE_FPR_+4*8(sp) +| sw r21, SAVE_GPR_+5*4(sp) +| sw r20, SAVE_GPR_+4*4(sp) +| .FPU sdc1 f26, SAVE_FPR_+3*8(sp) +| sw r19, SAVE_GPR_+3*4(sp) +| sw r18, SAVE_GPR_+2*4(sp) +| .FPU sdc1 f24, SAVE_FPR_+2*8(sp) +| sw r17, SAVE_GPR_+1*4(sp) +| sw r16, SAVE_GPR_+0*4(sp) +| .FPU sdc1 f22, SAVE_FPR_+1*8(sp) +| .FPU sdc1 f20, SAVE_FPR_+0*8(sp) +|.endmacro +| +|.macro restoreregs_ret +| lw ra, SAVE_GPR_+9*4(sp) +| lw r30, SAVE_GPR_+8*4(sp) +| .FPU ldc1 f30, SAVE_FPR_+5*8(sp) +| lw r23, SAVE_GPR_+7*4(sp) +| lw r22, SAVE_GPR_+6*4(sp) +| .FPU ldc1 f28, SAVE_FPR_+4*8(sp) +| lw r21, SAVE_GPR_+5*4(sp) +| lw r20, SAVE_GPR_+4*4(sp) +| .FPU ldc1 f26, SAVE_FPR_+3*8(sp) +| lw r19, SAVE_GPR_+3*4(sp) +| lw r18, SAVE_GPR_+2*4(sp) +| .FPU ldc1 f24, SAVE_FPR_+2*8(sp) +| lw r17, SAVE_GPR_+1*4(sp) +| lw r16, SAVE_GPR_+0*4(sp) +| .FPU ldc1 f22, SAVE_FPR_+1*8(sp) +| .FPU ldc1 f20, SAVE_FPR_+0*8(sp) +| jr ra +| addiu sp, sp, CFRAME_SPACE +|.endmacro +| +|// Type definitions. Some of these are only used for documentation. +|.type L, lua_State, LREG +|.type GL, global_State +|.type TVALUE, TValue +|.type GCOBJ, GCobj +|.type STR, GCstr +|.type TAB, GCtab +|.type LFUNC, GCfuncL +|.type CFUNC, GCfuncC +|.type PROTO, GCproto +|.type UPVAL, GCupval +|.type NODE, Node +|.type NARGS8, int +|.type TRACE, GCtrace +|.type SBUF, SBuf +| +|//----------------------------------------------------------------------- +| +|// Trap for not-yet-implemented parts. +|.macro NYI; .long 0xf0f0f0f0; .endmacro +| +|// Macros to mark delay slots. +|.macro ., a; a; .endmacro +|.macro ., a,b; a,b; .endmacro +|.macro ., a,b,c; a,b,c; .endmacro +| +|//----------------------------------------------------------------------- +| +|// Endian-specific defines. +|.if ENDIAN_LE +|.define FRAME_PC, -4 +|.define FRAME_FUNC, -8 +|.define HI, 4 +|.define LO, 0 +|.define OFS_RD, 2 +|.define OFS_RA, 1 +|.define OFS_OP, 0 +|.else +|.define FRAME_PC, -8 +|.define FRAME_FUNC, -4 +|.define HI, 0 +|.define LO, 4 +|.define OFS_RD, 0 +|.define OFS_RA, 2 +|.define OFS_OP, 3 +|.endif +| +|// Instruction decode. +|.macro decode_OP1, dst, ins; andi dst, ins, 0xff; .endmacro +|.macro decode_OP4a, dst, ins; andi dst, ins, 0xff; .endmacro +|.macro decode_OP4b, dst; sll dst, dst, 2; .endmacro +|.macro decode_RC4a, dst, ins; srl dst, ins, 14; .endmacro +|.macro decode_RC4b, dst; andi dst, dst, 0x3fc; .endmacro +|.macro decode_RD4b, dst; sll dst, dst, 2; .endmacro +|.macro decode_RA8a, dst, ins; srl dst, ins, 5; .endmacro +|.macro decode_RA8b, dst; andi dst, dst, 0x7f8; .endmacro +|.macro decode_RB8a, dst, ins; srl dst, ins, 21; .endmacro +|.macro decode_RB8b, dst; andi dst, dst, 0x7f8; .endmacro +|.macro decode_RD8a, dst, ins; srl dst, ins, 16; .endmacro +|.macro decode_RD8b, dst; sll dst, dst, 3; .endmacro +|.macro decode_RDtoRC8, dst, src; andi dst, src, 0x7f8; .endmacro +| +|// Instruction fetch. +|.macro ins_NEXT1 +| lw INS, 0(PC) +| addiu PC, PC, 4 +|.endmacro +|// Instruction decode+dispatch. +|.macro ins_NEXT2 +| decode_OP4a TMP1, INS +| decode_OP4b TMP1 +| addu TMP0, DISPATCH, TMP1 +| decode_RD8a RD, INS +| lw AT, 0(TMP0) +| decode_RA8a RA, INS +| decode_RD8b RD +| jr AT +| decode_RA8b RA +|.endmacro +|.macro ins_NEXT +| ins_NEXT1 +| ins_NEXT2 +|.endmacro +| +|// Instruction footer. +|.if 1 +| // Replicated dispatch. Less unpredictable branches, but higher I-Cache use. +| .define ins_next, ins_NEXT +| .define ins_next_, ins_NEXT +| .define ins_next1, ins_NEXT1 +| .define ins_next2, ins_NEXT2 +|.else +| // Common dispatch. Lower I-Cache use, only one (very) unpredictable branch. +| // Affects only certain kinds of benchmarks (and only with -j off). +| .macro ins_next +| b ->ins_next +| .endmacro +| .macro ins_next1 +| .endmacro +| .macro ins_next2 +| b ->ins_next +| .endmacro +| .macro ins_next_ +| ->ins_next: +| ins_NEXT +| .endmacro +|.endif +| +|// Call decode and dispatch. +|.macro ins_callt +| // BASE = new base, RB = LFUNC/CFUNC, RC = nargs*8, FRAME_PC(BASE) = PC +| lw PC, LFUNC:RB->pc +| lw INS, 0(PC) +| addiu PC, PC, 4 +| decode_OP4a TMP1, INS +| decode_RA8a RA, INS +| decode_OP4b TMP1 +| decode_RA8b RA +| addu TMP0, DISPATCH, TMP1 +| lw TMP0, 0(TMP0) +| jr TMP0 +| addu RA, RA, BASE +|.endmacro +| +|.macro ins_call +| // BASE = new base, RB = LFUNC/CFUNC, RC = nargs*8, PC = caller PC +| sw PC, FRAME_PC(BASE) +| ins_callt +|.endmacro +| +|//----------------------------------------------------------------------- +| +|.macro branch_RD +| srl TMP0, RD, 1 +| lui AT, (-(BCBIAS_J*4 >> 16) & 65535) +| addu TMP0, TMP0, AT +| addu PC, PC, TMP0 +|.endmacro +| +|// Assumes DISPATCH is relative to GL. +#define DISPATCH_GL(field) (GG_DISP2G + (int)offsetof(global_State, field)) +#define DISPATCH_J(field) (GG_DISP2J + (int)offsetof(jit_State, field)) +#define GG_DISP2GOT (GG_OFS(got) - GG_OFS(dispatch)) +#define DISPATCH_GOT(name) (GG_DISP2GOT + 4*LJ_GOT_##name) +| +#define PC2PROTO(field) ((int)offsetof(GCproto, field)-(int)sizeof(GCproto)) +| +|.macro load_got, func +| lw CFUNCADDR, DISPATCH_GOT(func)(DISPATCH) +|.endmacro +|// Much faster. Sadly, there's no easy way to force the required code layout. +|// .macro call_intern, func; bal extern func; .endmacro +|.macro call_intern, func; jalr CFUNCADDR; .endmacro +|.macro call_extern; jalr CFUNCADDR; .endmacro +|.macro jmp_extern; jr CFUNCADDR; .endmacro +| +|.macro hotcheck, delta, target +| srl TMP1, PC, 1 +| andi TMP1, TMP1, 126 +| addu TMP1, TMP1, DISPATCH +| lhu TMP2, GG_DISP2HOT(TMP1) +| addiu TMP2, TMP2, -delta +| bltz TMP2, target +|. sh TMP2, GG_DISP2HOT(TMP1) +|.endmacro +| +|.macro hotloop +| hotcheck HOTCOUNT_LOOP, ->vm_hotloop +|.endmacro +| +|.macro hotcall +| hotcheck HOTCOUNT_CALL, ->vm_hotcall +|.endmacro +| +|// Set current VM state. Uses TMP0. +|.macro li_vmstate, st; li TMP0, ~LJ_VMST_..st; .endmacro +|.macro st_vmstate; sw TMP0, DISPATCH_GL(vmstate)(DISPATCH); .endmacro +| +|// Move table write barrier back. Overwrites mark and tmp. +|.macro barrierback, tab, mark, tmp, target +| lw tmp, DISPATCH_GL(gc.grayagain)(DISPATCH) +| andi mark, mark, ~LJ_GC_BLACK & 255 // black2gray(tab) +| sw tab, DISPATCH_GL(gc.grayagain)(DISPATCH) +| sb mark, tab->marked +| b target +|. sw tmp, tab->gclist +|.endmacro +| +|//----------------------------------------------------------------------- + +/* Generate subroutines used by opcodes and other parts of the VM. */ +/* The .code_sub section should be last to help static branch prediction. */ +static void build_subroutines(BuildCtx *ctx) +{ + |.code_sub + | + |//----------------------------------------------------------------------- + |//-- Return handling ---------------------------------------------------- + |//----------------------------------------------------------------------- + | + |->vm_returnp: + | // See vm_return. Also: TMP2 = previous base. + | andi AT, PC, FRAME_P + | beqz AT, ->cont_dispatch + |. li TMP1, LJ_TTRUE + | + | // Return from pcall or xpcall fast func. + | lw PC, FRAME_PC(TMP2) // Fetch PC of previous frame. + | move BASE, TMP2 // Restore caller base. + | // Prepending may overwrite the pcall frame, so do it at the end. + | sw TMP1, FRAME_PC(RA) // Prepend true to results. + | addiu RA, RA, -8 + | + |->vm_returnc: + | addiu RD, RD, 8 // RD = (nresults+1)*8. + | andi TMP0, PC, FRAME_TYPE + | beqz RD, ->vm_unwind_c_eh + |. li CRET1, LUA_YIELD + | beqz TMP0, ->BC_RET_Z // Handle regular return to Lua. + |. move MULTRES, RD + | + |->vm_return: + | // BASE = base, RA = resultptr, RD/MULTRES = (nresults+1)*8, PC = return + | // TMP0 = PC & FRAME_TYPE + | li TMP2, -8 + | xori AT, TMP0, FRAME_C + | and TMP2, PC, TMP2 + | bnez AT, ->vm_returnp + | subu TMP2, BASE, TMP2 // TMP2 = previous base. + | + | addiu TMP1, RD, -8 + | sw TMP2, L->base + | li_vmstate C + | lw TMP2, SAVE_NRES + | addiu BASE, BASE, -8 + | st_vmstate + | beqz TMP1, >2 + |. sll TMP2, TMP2, 3 + |1: + | addiu TMP1, TMP1, -8 + | lw SFRETHI, HI(RA) + | lw SFRETLO, LO(RA) + | addiu RA, RA, 8 + | sw SFRETHI, HI(BASE) + | sw SFRETLO, LO(BASE) + | bnez TMP1, <1 + |. addiu BASE, BASE, 8 + | + |2: + | bne TMP2, RD, >6 + |3: + |. sw BASE, L->top // Store new top. + | + |->vm_leave_cp: + | lw TMP0, SAVE_CFRAME // Restore previous C frame. + | move CRET1, r0 // Ok return status for vm_pcall. + | sw TMP0, L->cframe + | + |->vm_leave_unw: + | restoreregs_ret + | + |6: + | lw TMP1, L->maxstack + | slt AT, TMP2, RD + | bnez AT, >7 // Less results wanted? + | // More results wanted. Check stack size and fill up results with nil. + |. slt AT, BASE, TMP1 + | beqz AT, >8 + |. nop + | sw TISNIL, HI(BASE) + | addiu RD, RD, 8 + | b <2 + |. addiu BASE, BASE, 8 + | + |7: // Less results wanted. + | subu TMP0, RD, TMP2 + | subu TMP0, BASE, TMP0 // Either keep top or shrink it. + | b <3 + |. movn BASE, TMP0, TMP2 // LUA_MULTRET+1 case? + | + |8: // Corner case: need to grow stack for filling up results. + | // This can happen if: + | // - A C function grows the stack (a lot). + | // - The GC shrinks the stack in between. + | // - A return back from a lua_call() with (high) nresults adjustment. + | load_got lj_state_growstack + | move MULTRES, RD + | srl CARG2, TMP2, 3 + | call_intern lj_state_growstack // (lua_State *L, int n) + |. move CARG1, L + | lw TMP2, SAVE_NRES + | lw BASE, L->top // Need the (realloced) L->top in BASE. + | move RD, MULTRES + | b <2 + |. sll TMP2, TMP2, 3 + | + |->vm_unwind_c: // Unwind C stack, return from vm_pcall. + | // (void *cframe, int errcode) + | move sp, CARG1 + | move CRET1, CARG2 + |->vm_unwind_c_eh: // Landing pad for external unwinder. + | lw L, SAVE_L + | li TMP0, ~LJ_VMST_C + | lw GL:TMP1, L->glref + | b ->vm_leave_unw + |. sw TMP0, GL:TMP1->vmstate + | + |->vm_unwind_ff: // Unwind C stack, return from ff pcall. + | // (void *cframe) + | li AT, -4 + | and sp, CARG1, AT + |->vm_unwind_ff_eh: // Landing pad for external unwinder. + | lw L, SAVE_L + | .FPU lui TMP3, 0x59c0 // TOBIT = 2^52 + 2^51 (float). + | li TISNUM, LJ_TISNUM // Setup type comparison constants. + | li TISNIL, LJ_TNIL + | lw BASE, L->base + | lw DISPATCH, L->glref // Setup pointer to dispatch table. + | .FPU mtc1 TMP3, TOBIT + | li TMP1, LJ_TFALSE + | li_vmstate INTERP + | lw PC, FRAME_PC(BASE) // Fetch PC of previous frame. + | .FPU cvt.d.s TOBIT, TOBIT + | addiu RA, BASE, -8 // Results start at BASE-8. + | addiu DISPATCH, DISPATCH, GG_G2DISP + | sw TMP1, HI(RA) // Prepend false to error message. + | st_vmstate + | b ->vm_returnc + |. li RD, 16 // 2 results: false + error message. + | + |//----------------------------------------------------------------------- + |//-- Grow stack for calls ----------------------------------------------- + |//----------------------------------------------------------------------- + | + |->vm_growstack_c: // Grow stack for C function. + | b >2 + |. li CARG2, LUA_MINSTACK + | + |->vm_growstack_l: // Grow stack for Lua function. + | // BASE = new base, RA = BASE+framesize*8, RC = nargs*8, PC = first PC + | addu RC, BASE, RC + | subu RA, RA, BASE + | sw BASE, L->base + | addiu PC, PC, 4 // Must point after first instruction. + | sw RC, L->top + | srl CARG2, RA, 3 + |2: + | // L->base = new base, L->top = top + | load_got lj_state_growstack + | sw PC, SAVE_PC + | call_intern lj_state_growstack // (lua_State *L, int n) + |. move CARG1, L + | lw BASE, L->base + | lw RC, L->top + | lw LFUNC:RB, FRAME_FUNC(BASE) + | subu RC, RC, BASE + | // BASE = new base, RB = LFUNC/CFUNC, RC = nargs*8, FRAME_PC(BASE) = PC + | ins_callt // Just retry the call. + | + |//----------------------------------------------------------------------- + |//-- Entry points into the assembler VM --------------------------------- + |//----------------------------------------------------------------------- + | + |->vm_resume: // Setup C frame and resume thread. + | // (lua_State *L, TValue *base, int nres1 = 0, ptrdiff_t ef = 0) + | saveregs + | move L, CARG1 + | lw DISPATCH, L->glref // Setup pointer to dispatch table. + | move BASE, CARG2 + | lbu TMP1, L->status + | sw L, SAVE_L + | li PC, FRAME_CP + | addiu TMP0, sp, CFRAME_RESUME + | addiu DISPATCH, DISPATCH, GG_G2DISP + | sw r0, SAVE_NRES + | sw r0, SAVE_ERRF + | sw CARG1, SAVE_PC // Any value outside of bytecode is ok. + | sw r0, SAVE_CFRAME + | beqz TMP1, >3 + |. sw TMP0, L->cframe + | + | // Resume after yield (like a return). + | sw L, DISPATCH_GL(cur_L)(DISPATCH) + | move RA, BASE + | lw BASE, L->base + | li TISNUM, LJ_TISNUM // Setup type comparison constants. + | lw TMP1, L->top + | lw PC, FRAME_PC(BASE) + | .FPU lui TMP3, 0x59c0 // TOBIT = 2^52 + 2^51 (float). + | subu RD, TMP1, BASE + | .FPU mtc1 TMP3, TOBIT + | sb r0, L->status + | .FPU cvt.d.s TOBIT, TOBIT + | li_vmstate INTERP + | addiu RD, RD, 8 + | st_vmstate + | move MULTRES, RD + | andi TMP0, PC, FRAME_TYPE + | beqz TMP0, ->BC_RET_Z + |. li TISNIL, LJ_TNIL + | b ->vm_return + |. nop + | + |->vm_pcall: // Setup protected C frame and enter VM. + | // (lua_State *L, TValue *base, int nres1, ptrdiff_t ef) + | saveregs + | sw CARG4, SAVE_ERRF + | b >1 + |. li PC, FRAME_CP + | + |->vm_call: // Setup C frame and enter VM. + | // (lua_State *L, TValue *base, int nres1) + | saveregs + | li PC, FRAME_C + | + |1: // Entry point for vm_pcall above (PC = ftype). + | lw TMP1, L:CARG1->cframe + | move L, CARG1 + | sw CARG3, SAVE_NRES + | lw DISPATCH, L->glref // Setup pointer to dispatch table. + | sw CARG1, SAVE_L + | move BASE, CARG2 + | addiu DISPATCH, DISPATCH, GG_G2DISP + | sw CARG1, SAVE_PC // Any value outside of bytecode is ok. + | sw TMP1, SAVE_CFRAME + | sw sp, L->cframe // Add our C frame to cframe chain. + | + |3: // Entry point for vm_cpcall/vm_resume (BASE = base, PC = ftype). + | sw L, DISPATCH_GL(cur_L)(DISPATCH) + | lw TMP2, L->base // TMP2 = old base (used in vmeta_call). + | li TISNUM, LJ_TISNUM // Setup type comparison constants. + | .FPU lui TMP3, 0x59c0 // TOBIT = 2^52 + 2^51 (float). + | lw TMP1, L->top + | .FPU mtc1 TMP3, TOBIT + | addu PC, PC, BASE + | subu NARGS8:RC, TMP1, BASE + | subu PC, PC, TMP2 // PC = frame delta + frame type + | .FPU cvt.d.s TOBIT, TOBIT + | li_vmstate INTERP + | li TISNIL, LJ_TNIL + | st_vmstate + | + |->vm_call_dispatch: + | // TMP2 = old base, BASE = new base, RC = nargs*8, PC = caller PC + | lw TMP0, FRAME_PC(BASE) + | li AT, LJ_TFUNC + | bne TMP0, AT, ->vmeta_call + |. lw LFUNC:RB, FRAME_FUNC(BASE) + | + |->vm_call_dispatch_f: + | ins_call + | // BASE = new base, RB = func, RC = nargs*8, PC = caller PC + | + |->vm_cpcall: // Setup protected C frame, call C. + | // (lua_State *L, lua_CFunction func, void *ud, lua_CPFunction cp) + | saveregs + | move L, CARG1 + | lw TMP0, L:CARG1->stack + | sw CARG1, SAVE_L + | lw TMP1, L->top + | lw DISPATCH, L->glref // Setup pointer to dispatch table. + | sw CARG1, SAVE_PC // Any value outside of bytecode is ok. + | subu TMP0, TMP0, TMP1 // Compute -savestack(L, L->top). + | lw TMP1, L->cframe + | addiu DISPATCH, DISPATCH, GG_G2DISP + | sw TMP0, SAVE_NRES // Neg. delta means cframe w/o frame. + | sw r0, SAVE_ERRF // No error function. + | sw TMP1, SAVE_CFRAME + | sw sp, L->cframe // Add our C frame to cframe chain. + | sw L, DISPATCH_GL(cur_L)(DISPATCH) + | jalr CARG4 // (lua_State *L, lua_CFunction func, void *ud) + |. move CFUNCADDR, CARG4 + | move BASE, CRET1 + | bnez CRET1, <3 // Else continue with the call. + |. li PC, FRAME_CP + | b ->vm_leave_cp // No base? Just remove C frame. + |. nop + | + |//----------------------------------------------------------------------- + |//-- Metamethod handling ------------------------------------------------ + |//----------------------------------------------------------------------- + | + |// The lj_meta_* functions (except for lj_meta_cat) don't reallocate the + |// stack, so BASE doesn't need to be reloaded across these calls. + | + |//-- Continuation dispatch ---------------------------------------------- + | + |->cont_dispatch: + | // BASE = meta base, RA = resultptr, RD = (nresults+1)*8 + | lw TMP0, -16+LO(BASE) // Continuation. + | move RB, BASE + | move BASE, TMP2 // Restore caller BASE. + | lw LFUNC:TMP1, FRAME_FUNC(TMP2) + |.if FFI + | sltiu AT, TMP0, 2 + |.endif + | lw PC, -16+HI(RB) // Restore PC from [cont|PC]. + | addu TMP2, RA, RD + | lw TMP1, LFUNC:TMP1->pc + |.if FFI + | bnez AT, >1 + |.endif + |. sw TISNIL, -8+HI(TMP2) // Ensure one valid arg. + | // BASE = base, RA = resultptr, RB = meta base + | jr TMP0 // Jump to continuation. + |. lw KBASE, PC2PROTO(k)(TMP1) + | + |.if FFI + |1: + | bnez TMP0, ->cont_ffi_callback // cont = 1: return from FFI callback. + | // cont = 0: tailcall from C function. + |. addiu TMP1, RB, -16 + | b ->vm_call_tail + |. subu RC, TMP1, BASE + |.endif + | + |->cont_cat: // RA = resultptr, RB = meta base + | lw INS, -4(PC) + | addiu CARG2, RB, -16 + | lw SFRETHI, HI(RA) + | lw SFRETLO, LO(RA) + | decode_RB8a MULTRES, INS + | decode_RA8a RA, INS + | decode_RB8b MULTRES + | decode_RA8b RA + | addu TMP1, BASE, MULTRES + | sw BASE, L->base + | subu CARG3, CARG2, TMP1 + | sw SFRETHI, HI(CARG2) + | bne TMP1, CARG2, ->BC_CAT_Z + |. sw SFRETLO, LO(CARG2) + | addu RA, BASE, RA + | sw SFRETHI, HI(RA) + | b ->cont_nop + |. sw SFRETLO, LO(RA) + | + |//-- Table indexing metamethods ----------------------------------------- + | + |->vmeta_tgets1: + | addiu CARG3, DISPATCH, DISPATCH_GL(tmptv) + | li TMP0, LJ_TSTR + | sw STR:RC, LO(CARG3) + | b >1 + |. sw TMP0, HI(CARG3) + | + |->vmeta_tgets: + | addiu CARG2, DISPATCH, DISPATCH_GL(tmptv) + | li TMP0, LJ_TTAB + | sw TAB:RB, LO(CARG2) + | addiu CARG3, DISPATCH, DISPATCH_GL(tmptv2) + | sw TMP0, HI(CARG2) + | li TMP1, LJ_TSTR + | sw STR:RC, LO(CARG3) + | b >1 + |. sw TMP1, HI(CARG3) + | + |->vmeta_tgetb: // TMP0 = index + | addiu CARG3, DISPATCH, DISPATCH_GL(tmptv) + | sw TMP0, LO(CARG3) + | sw TISNUM, HI(CARG3) + | + |->vmeta_tgetv: + |1: + | load_got lj_meta_tget + | sw BASE, L->base + | sw PC, SAVE_PC + | call_intern lj_meta_tget // (lua_State *L, TValue *o, TValue *k) + |. move CARG1, L + | // Returns TValue * (finished) or NULL (metamethod). + | beqz CRET1, >3 + |. addiu TMP1, BASE, -FRAME_CONT + | lw SFARG1HI, HI(CRET1) + | lw SFARG2HI, LO(CRET1) + | ins_next1 + | sw SFARG1HI, HI(RA) + | sw SFARG2HI, LO(RA) + | ins_next2 + | + |3: // Call __index metamethod. + | // BASE = base, L->top = new base, stack = cont/func/t/k + | lw BASE, L->top + | sw PC, -16+HI(BASE) // [cont|PC] + | subu PC, BASE, TMP1 + | lw LFUNC:RB, FRAME_FUNC(BASE) // Guaranteed to be a function here. + | b ->vm_call_dispatch_f + |. li NARGS8:RC, 16 // 2 args for func(t, k). + | + |->vmeta_tgetr: + | load_got lj_tab_getinth + | call_intern lj_tab_getinth // (GCtab *t, int32_t key) + |. nop + | // Returns cTValue * or NULL. + | beqz CRET1, ->BC_TGETR_Z + |. move SFARG2HI, TISNIL + | lw SFARG2HI, HI(CRET1) + | b ->BC_TGETR_Z + |. lw SFARG2LO, LO(CRET1) + | + |//----------------------------------------------------------------------- + | + |->vmeta_tsets1: + | addiu CARG3, DISPATCH, DISPATCH_GL(tmptv) + | li TMP0, LJ_TSTR + | sw STR:RC, LO(CARG3) + | b >1 + |. sw TMP0, HI(CARG3) + | + |->vmeta_tsets: + | addiu CARG2, DISPATCH, DISPATCH_GL(tmptv) + | li TMP0, LJ_TTAB + | sw TAB:RB, LO(CARG2) + | addiu CARG3, DISPATCH, DISPATCH_GL(tmptv2) + | sw TMP0, HI(CARG2) + | li TMP1, LJ_TSTR + | sw STR:RC, LO(CARG3) + | b >1 + |. sw TMP1, HI(CARG3) + | + |->vmeta_tsetb: // TMP0 = index + | addiu CARG3, DISPATCH, DISPATCH_GL(tmptv) + | sw TMP0, LO(CARG3) + | sw TISNUM, HI(CARG3) + | + |->vmeta_tsetv: + |1: + | load_got lj_meta_tset + | sw BASE, L->base + | sw PC, SAVE_PC + | call_intern lj_meta_tset // (lua_State *L, TValue *o, TValue *k) + |. move CARG1, L + | // Returns TValue * (finished) or NULL (metamethod). + | lw SFARG1HI, HI(RA) + | beqz CRET1, >3 + |. lw SFARG1LO, LO(RA) + | // NOBARRIER: lj_meta_tset ensures the table is not black. + | ins_next1 + | sw SFARG1HI, HI(CRET1) + | sw SFARG1LO, LO(CRET1) + | ins_next2 + | + |3: // Call __newindex metamethod. + | // BASE = base, L->top = new base, stack = cont/func/t/k/(v) + | addiu TMP1, BASE, -FRAME_CONT + | lw BASE, L->top + | sw PC, -16+HI(BASE) // [cont|PC] + | subu PC, BASE, TMP1 + | lw LFUNC:RB, FRAME_FUNC(BASE) // Guaranteed to be a function here. + | sw SFARG1HI, 16+HI(BASE) // Copy value to third argument. + | sw SFARG1LO, 16+LO(BASE) + | b ->vm_call_dispatch_f + |. li NARGS8:RC, 24 // 3 args for func(t, k, v) + | + |->vmeta_tsetr: + | load_got lj_tab_setinth + | sw BASE, L->base + | sw PC, SAVE_PC + | call_intern lj_tab_setinth // (lua_State *L, GCtab *t, int32_t key) + |. move CARG1, L + | // Returns TValue *. + | b ->BC_TSETR_Z + |. nop + | + |//-- Comparison metamethods --------------------------------------------- + | + |->vmeta_comp: + | // RA/RD point to o1/o2. + | move CARG2, RA + | move CARG3, RD + | load_got lj_meta_comp + | addiu PC, PC, -4 + | sw BASE, L->base + | sw PC, SAVE_PC + | decode_OP1 CARG4, INS + | call_intern lj_meta_comp // (lua_State *L, TValue *o1, *o2, int op) + |. move CARG1, L + | // Returns 0/1 or TValue * (metamethod). + |3: + | sltiu AT, CRET1, 2 + | beqz AT, ->vmeta_binop + | negu TMP2, CRET1 + |4: + | lhu RD, OFS_RD(PC) + | addiu PC, PC, 4 + | lui TMP1, (-(BCBIAS_J*4 >> 16) & 65535) + | sll RD, RD, 2 + | addu RD, RD, TMP1 + | and RD, RD, TMP2 + | addu PC, PC, RD + |->cont_nop: + | ins_next + | + |->cont_ra: // RA = resultptr + | lbu TMP1, -4+OFS_RA(PC) + | lw SFRETHI, HI(RA) + | lw SFRETLO, LO(RA) + | sll TMP1, TMP1, 3 + | addu TMP1, BASE, TMP1 + | sw SFRETHI, HI(TMP1) + | b ->cont_nop + |. sw SFRETLO, LO(TMP1) + | + |->cont_condt: // RA = resultptr + | lw TMP0, HI(RA) + | sltiu AT, TMP0, LJ_TISTRUECOND + | b <4 + |. negu TMP2, AT // Branch if result is true. + | + |->cont_condf: // RA = resultptr + | lw TMP0, HI(RA) + | sltiu AT, TMP0, LJ_TISTRUECOND + | b <4 + |. addiu TMP2, AT, -1 // Branch if result is false. + | + |->vmeta_equal: + | // SFARG1LO/SFARG2LO point to o1/o2. TMP0 is set to 0/1. + | load_got lj_meta_equal + | move CARG2, SFARG1LO + | move CARG3, SFARG2LO + | move CARG4, TMP0 + | addiu PC, PC, -4 + | sw BASE, L->base + | sw PC, SAVE_PC + | call_intern lj_meta_equal // (lua_State *L, GCobj *o1, *o2, int ne) + |. move CARG1, L + | // Returns 0/1 or TValue * (metamethod). + | b <3 + |. nop + | + |->vmeta_equal_cd: + |.if FFI + | load_got lj_meta_equal_cd + | move CARG2, INS + | addiu PC, PC, -4 + | sw BASE, L->base + | sw PC, SAVE_PC + | call_intern lj_meta_equal_cd // (lua_State *L, BCIns op) + |. move CARG1, L + | // Returns 0/1 or TValue * (metamethod). + | b <3 + |. nop + |.endif + | + |->vmeta_istype: + | load_got lj_meta_istype + | addiu PC, PC, -4 + | sw BASE, L->base + | srl CARG2, RA, 3 + | srl CARG3, RD, 3 + | sw PC, SAVE_PC + | call_intern lj_meta_istype // (lua_State *L, BCReg ra, BCReg tp) + |. move CARG1, L + | b ->cont_nop + |. nop + | + |//-- Arithmetic metamethods --------------------------------------------- + | + |->vmeta_unm: + | move RC, RB + | + |->vmeta_arith: + | load_got lj_meta_arith + | decode_OP1 TMP0, INS + | sw BASE, L->base + | move CARG2, RA + | sw PC, SAVE_PC + | move CARG3, RB + | move CARG4, RC + | sw TMP0, ARG5 + | call_intern lj_meta_arith // (lua_State *L, TValue *ra,*rb,*rc, BCReg op) + |. move CARG1, L + | // Returns NULL (finished) or TValue * (metamethod). + | beqz CRET1, ->cont_nop + |. nop + | + | // Call metamethod for binary op. + |->vmeta_binop: + | // BASE = old base, CRET1 = new base, stack = cont/func/o1/o2 + | subu TMP1, CRET1, BASE + | sw PC, -16+HI(CRET1) // [cont|PC] + | move TMP2, BASE + | addiu PC, TMP1, FRAME_CONT + | move BASE, CRET1 + | b ->vm_call_dispatch + |. li NARGS8:RC, 16 // 2 args for func(o1, o2). + | + |->vmeta_len: + | // CARG2 already set by BC_LEN. +#if LJ_52 + | move MULTRES, CARG1 +#endif + | load_got lj_meta_len + | sw BASE, L->base + | sw PC, SAVE_PC + | call_intern lj_meta_len // (lua_State *L, TValue *o) + |. move CARG1, L + | // Returns NULL (retry) or TValue * (metamethod base). +#if LJ_52 + | bnez CRET1, ->vmeta_binop // Binop call for compatibility. + |. nop + | b ->BC_LEN_Z + |. move CARG1, MULTRES +#else + | b ->vmeta_binop // Binop call for compatibility. + |. nop +#endif + | + |//-- Call metamethod ---------------------------------------------------- + | + |->vmeta_call: // Resolve and call __call metamethod. + | // TMP2 = old base, BASE = new base, RC = nargs*8 + | load_got lj_meta_call + | sw TMP2, L->base // This is the callers base! + | addiu CARG2, BASE, -8 + | sw PC, SAVE_PC + | addu CARG3, BASE, RC + | move MULTRES, NARGS8:RC + | call_intern lj_meta_call // (lua_State *L, TValue *func, TValue *top) + |. move CARG1, L + | lw LFUNC:RB, FRAME_FUNC(BASE) // Guaranteed to be a function here. + | addiu NARGS8:RC, MULTRES, 8 // Got one more argument now. + | ins_call + | + |->vmeta_callt: // Resolve __call for BC_CALLT. + | // BASE = old base, RA = new base, RC = nargs*8 + | load_got lj_meta_call + | sw BASE, L->base + | addiu CARG2, RA, -8 + | sw PC, SAVE_PC + | addu CARG3, RA, RC + | move MULTRES, NARGS8:RC + | call_intern lj_meta_call // (lua_State *L, TValue *func, TValue *top) + |. move CARG1, L + | lw TMP1, FRAME_PC(BASE) + | lw LFUNC:RB, FRAME_FUNC(RA) // Guaranteed to be a function here. + | b ->BC_CALLT_Z + |. addiu NARGS8:RC, MULTRES, 8 // Got one more argument now. + | + |//-- Argument coercion for 'for' statement ------------------------------ + | + |->vmeta_for: + | load_got lj_meta_for + | sw BASE, L->base + | move CARG2, RA + | sw PC, SAVE_PC + | move MULTRES, INS + | call_intern lj_meta_for // (lua_State *L, TValue *base) + |. move CARG1, L + |.if JIT + | decode_OP1 TMP0, MULTRES + | li AT, BC_JFORI + |.endif + | decode_RA8a RA, MULTRES + | decode_RD8a RD, MULTRES + | decode_RA8b RA + |.if JIT + | beq TMP0, AT, =>BC_JFORI + |. decode_RD8b RD + | b =>BC_FORI + |. nop + |.else + | b =>BC_FORI + |. decode_RD8b RD + |.endif + | + |//----------------------------------------------------------------------- + |//-- Fast functions ----------------------------------------------------- + |//----------------------------------------------------------------------- + | + |.macro .ffunc, name + |->ff_ .. name: + |.endmacro + | + |.macro .ffunc_1, name + |->ff_ .. name: + | lw SFARG1HI, HI(BASE) + | beqz NARGS8:RC, ->fff_fallback + |. lw SFARG1LO, LO(BASE) + |.endmacro + | + |.macro .ffunc_2, name + |->ff_ .. name: + | sltiu AT, NARGS8:RC, 16 + | lw SFARG1HI, HI(BASE) + | bnez AT, ->fff_fallback + |. lw SFARG2HI, 8+HI(BASE) + | lw SFARG1LO, LO(BASE) + | lw SFARG2LO, 8+LO(BASE) + |.endmacro + | + |.macro .ffunc_n, name // Caveat: has delay slot! + |->ff_ .. name: + | lw SFARG1HI, HI(BASE) + |.if FPU + | ldc1 FARG1, 0(BASE) + |.else + | lw SFARG1LO, LO(BASE) + |.endif + | beqz NARGS8:RC, ->fff_fallback + |. sltiu AT, SFARG1HI, LJ_TISNUM + | beqz AT, ->fff_fallback + |.endmacro + | + |.macro .ffunc_nn, name // Caveat: has delay slot! + |->ff_ .. name: + | sltiu AT, NARGS8:RC, 16 + | lw SFARG1HI, HI(BASE) + | bnez AT, ->fff_fallback + |. lw SFARG2HI, 8+HI(BASE) + | sltiu TMP0, SFARG1HI, LJ_TISNUM + |.if FPU + | ldc1 FARG1, 0(BASE) + |.else + | lw SFARG1LO, LO(BASE) + |.endif + | sltiu TMP1, SFARG2HI, LJ_TISNUM + |.if FPU + | ldc1 FARG2, 8(BASE) + |.else + | lw SFARG2LO, 8+LO(BASE) + |.endif + | and TMP0, TMP0, TMP1 + | beqz TMP0, ->fff_fallback + |.endmacro + | + |// Inlined GC threshold check. Caveat: uses TMP0 and TMP1 and has delay slot! + |.macro ffgccheck + | lw TMP0, DISPATCH_GL(gc.total)(DISPATCH) + | lw TMP1, DISPATCH_GL(gc.threshold)(DISPATCH) + | subu AT, TMP0, TMP1 + | bgezal AT, ->fff_gcstep + |.endmacro + | + |//-- Base library: checks ----------------------------------------------- + | + |.ffunc_1 assert + | sltiu AT, SFARG1HI, LJ_TISTRUECOND + | beqz AT, ->fff_fallback + |. addiu RA, BASE, -8 + | lw PC, FRAME_PC(BASE) + | addiu RD, NARGS8:RC, 8 // Compute (nresults+1)*8. + | addu TMP2, RA, NARGS8:RC + | sw SFARG1HI, HI(RA) + | addiu TMP1, BASE, 8 + | beq BASE, TMP2, ->fff_res // Done if exactly 1 argument. + |. sw SFARG1LO, LO(RA) + |1: + | lw SFRETHI, HI(TMP1) + | lw SFRETLO, LO(TMP1) + | sw SFRETHI, -8+HI(TMP1) + | sw SFRETLO, -8+LO(TMP1) + | bne TMP1, TMP2, <1 + |. addiu TMP1, TMP1, 8 + | b ->fff_res + |. nop + | + |.ffunc type + | lw SFARG1HI, HI(BASE) + | beqz NARGS8:RC, ->fff_fallback + |. sltiu TMP0, SFARG1HI, LJ_TISNUM + | movn SFARG1HI, TISNUM, TMP0 + | not TMP1, SFARG1HI + | sll TMP1, TMP1, 3 + | addu TMP1, CFUNC:RB, TMP1 + | lw SFARG1HI, CFUNC:TMP1->upvalue[0].u32.hi + | b ->fff_restv + |. lw SFARG1LO, CFUNC:TMP1->upvalue[0].u32.lo + | + |//-- Base library: getters and setters --------------------------------- + | + |.ffunc_1 getmetatable + | li AT, LJ_TTAB + | bne SFARG1HI, AT, >6 + |. li AT, LJ_TUDATA + |1: // Field metatable must be at same offset for GCtab and GCudata! + | lw TAB:SFARG1LO, TAB:SFARG1LO->metatable + |2: + | lw STR:RC, DISPATCH_GL(gcroot[GCROOT_MMNAME+MM_metatable])(DISPATCH) + | beqz TAB:SFARG1LO, ->fff_restv + |. li SFARG1HI, LJ_TNIL + | lw TMP0, TAB:SFARG1LO->hmask + | li SFARG1HI, LJ_TTAB // Use metatable as default result. + | lw TMP1, STR:RC->hash + | lw NODE:TMP2, TAB:SFARG1LO->node + | and TMP1, TMP1, TMP0 // idx = str->hash & tab->hmask + | sll TMP0, TMP1, 5 + | sll TMP1, TMP1, 3 + | subu TMP1, TMP0, TMP1 + | addu NODE:TMP2, NODE:TMP2, TMP1 // node = tab->node + (idx*32-idx*8) + | li AT, LJ_TSTR + |3: // Rearranged logic, because we expect _not_ to find the key. + | lw CARG4, offsetof(Node, key)+HI(NODE:TMP2) + | lw TMP0, offsetof(Node, key)+LO(NODE:TMP2) + | lw NODE:TMP3, NODE:TMP2->next + | bne CARG4, AT, >4 + |. lw CARG3, offsetof(Node, val)+HI(NODE:TMP2) + | beq TMP0, STR:RC, >5 + |. lw TMP1, offsetof(Node, val)+LO(NODE:TMP2) + |4: + | beqz NODE:TMP3, ->fff_restv // Not found, keep default result. + |. move NODE:TMP2, NODE:TMP3 + | b <3 + |. nop + |5: + | beq CARG3, TISNIL, ->fff_restv // Ditto for nil value. + |. nop + | move SFARG1HI, CARG3 // Return value of mt.__metatable. + | b ->fff_restv + |. move SFARG1LO, TMP1 + | + |6: + | beq SFARG1HI, AT, <1 + |. sltu AT, TISNUM, SFARG1HI + | movz SFARG1HI, TISNUM, AT + | not TMP1, SFARG1HI + | sll TMP1, TMP1, 2 + | addu TMP1, DISPATCH, TMP1 + | b <2 + |. lw TAB:SFARG1LO, DISPATCH_GL(gcroot[GCROOT_BASEMT])(TMP1) + | + |.ffunc_2 setmetatable + | // Fast path: no mt for table yet and not clearing the mt. + | li AT, LJ_TTAB + | bne SFARG1HI, AT, ->fff_fallback + |. addiu SFARG2HI, SFARG2HI, -LJ_TTAB + | lw TAB:TMP1, TAB:SFARG1LO->metatable + | lbu TMP3, TAB:SFARG1LO->marked + | or AT, SFARG2HI, TAB:TMP1 + | bnez AT, ->fff_fallback + |. andi AT, TMP3, LJ_GC_BLACK // isblack(table) + | beqz AT, ->fff_restv + |. sw TAB:SFARG2LO, TAB:SFARG1LO->metatable + | barrierback TAB:SFARG1LO, TMP3, TMP0, ->fff_restv + | + |.ffunc rawget + | lw CARG4, HI(BASE) + | sltiu AT, NARGS8:RC, 16 + | lw TAB:CARG2, LO(BASE) + | load_got lj_tab_get + | addiu CARG4, CARG4, -LJ_TTAB + | or AT, AT, CARG4 + | bnez AT, ->fff_fallback + | addiu CARG3, BASE, 8 + | call_intern lj_tab_get // (lua_State *L, GCtab *t, cTValue *key) + |. move CARG1, L + | // Returns cTValue *. + | lw SFARG1HI, HI(CRET1) + | b ->fff_restv + |. lw SFARG1LO, LO(CRET1) + | + |//-- Base library: conversions ------------------------------------------ + | + |.ffunc tonumber + | // Only handles the number case inline (without a base argument). + | lw CARG1, HI(BASE) + | xori AT, NARGS8:RC, 8 // Exactly one number argument. + | sltu TMP0, TISNUM, CARG1 + | or AT, AT, TMP0 + | bnez AT, ->fff_fallback + |. lw SFARG1HI, HI(BASE) + | b ->fff_restv + |. lw SFARG1LO, LO(BASE) + | + |.ffunc_1 tostring + | // Only handles the string or number case inline. + | li AT, LJ_TSTR + | // A __tostring method in the string base metatable is ignored. + | beq SFARG1HI, AT, ->fff_restv // String key? + | // Handle numbers inline, unless a number base metatable is present. + |. lw TMP1, DISPATCH_GL(gcroot[GCROOT_BASEMT_NUM])(DISPATCH) + | sltu TMP0, TISNUM, SFARG1HI + | or TMP0, TMP0, TMP1 + | bnez TMP0, ->fff_fallback + |. sw BASE, L->base // Add frame since C call can throw. + | ffgccheck + |. sw PC, SAVE_PC // Redundant (but a defined value). + | load_got lj_strfmt_number + | move CARG1, L + | call_intern lj_strfmt_number // (lua_State *L, cTValue *o) + |. move CARG2, BASE + | // Returns GCstr *. + | li SFARG1HI, LJ_TSTR + | b ->fff_restv + |. move SFARG1LO, CRET1 + | + |//-- Base library: iterators ------------------------------------------- + | + |.ffunc next + | lw CARG1, HI(BASE) + | lw TAB:CARG2, LO(BASE) + | beqz NARGS8:RC, ->fff_fallback + |. addu TMP2, BASE, NARGS8:RC + | li AT, LJ_TTAB + | sw TISNIL, HI(TMP2) // Set missing 2nd arg to nil. + | bne CARG1, AT, ->fff_fallback + |. lw PC, FRAME_PC(BASE) + | load_got lj_tab_next + | sw BASE, L->base // Add frame since C call can throw. + | sw BASE, L->top // Dummy frame length is ok. + | addiu CARG3, BASE, 8 + | sw PC, SAVE_PC + | call_intern lj_tab_next // (lua_State *L, GCtab *t, TValue *key) + |. move CARG1, L + | // Returns 0 at end of traversal. + | beqz CRET1, ->fff_restv // End of traversal: return nil. + |. li SFARG1HI, LJ_TNIL + | lw TMP0, 8+HI(BASE) + | lw TMP1, 8+LO(BASE) + | addiu RA, BASE, -8 + | lw TMP2, 16+HI(BASE) + | lw TMP3, 16+LO(BASE) + | sw TMP0, HI(RA) + | sw TMP1, LO(RA) + | sw TMP2, 8+HI(RA) + | sw TMP3, 8+LO(RA) + | b ->fff_res + |. li RD, (2+1)*8 + | + |.ffunc_1 pairs + | li AT, LJ_TTAB + | bne SFARG1HI, AT, ->fff_fallback + |. lw PC, FRAME_PC(BASE) +#if LJ_52 + | lw TAB:TMP2, TAB:SFARG1LO->metatable + | lw TMP0, CFUNC:RB->upvalue[0].u32.hi + | lw TMP1, CFUNC:RB->upvalue[0].u32.lo + | bnez TAB:TMP2, ->fff_fallback +#else + | lw TMP0, CFUNC:RB->upvalue[0].u32.hi + | lw TMP1, CFUNC:RB->upvalue[0].u32.lo +#endif + |. addiu RA, BASE, -8 + | sw TISNIL, 8+HI(BASE) + | sw TMP0, HI(RA) + | sw TMP1, LO(RA) + | b ->fff_res + |. li RD, (3+1)*8 + | + |.ffunc ipairs_aux + | sltiu AT, NARGS8:RC, 16 + | lw CARG3, HI(BASE) + | lw TAB:CARG1, LO(BASE) + | lw CARG4, 8+HI(BASE) + | bnez AT, ->fff_fallback + |. addiu CARG3, CARG3, -LJ_TTAB + | xor CARG4, CARG4, TISNUM + | and AT, CARG3, CARG4 + | bnez AT, ->fff_fallback + |. lw PC, FRAME_PC(BASE) + | lw TMP2, 8+LO(BASE) + | lw TMP0, TAB:CARG1->asize + | lw TMP1, TAB:CARG1->array + | addiu TMP2, TMP2, 1 + | sw TISNUM, -8+HI(BASE) + | sltu AT, TMP2, TMP0 + | sw TMP2, -8+LO(BASE) + | beqz AT, >2 // Not in array part? + |. addiu RA, BASE, -8 + | sll TMP3, TMP2, 3 + | addu TMP3, TMP1, TMP3 + | lw TMP1, HI(TMP3) + | lw TMP2, LO(TMP3) + |1: + | beq TMP1, TISNIL, ->fff_res // End of iteration, return 0 results. + |. li RD, (0+1)*8 + | sw TMP1, 8+HI(RA) + | sw TMP2, 8+LO(RA) + | b ->fff_res + |. li RD, (2+1)*8 + | + |2: // Check for empty hash part first. Otherwise call C function. + | lw TMP0, TAB:CARG1->hmask + | load_got lj_tab_getinth + | beqz TMP0, ->fff_res + |. li RD, (0+1)*8 + | call_intern lj_tab_getinth // (GCtab *t, int32_t key) + |. move CARG2, TMP2 + | // Returns cTValue * or NULL. + | beqz CRET1, ->fff_res + |. li RD, (0+1)*8 + | lw TMP1, HI(CRET1) + | b <1 + |. lw TMP2, LO(CRET1) + | + |.ffunc_1 ipairs + | li AT, LJ_TTAB + | bne SFARG1HI, AT, ->fff_fallback + |. lw PC, FRAME_PC(BASE) +#if LJ_52 + | lw TAB:TMP2, TAB:SFARG1LO->metatable + | lw TMP0, CFUNC:RB->upvalue[0].u32.hi + | lw TMP1, CFUNC:RB->upvalue[0].u32.lo + | bnez TAB:TMP2, ->fff_fallback +#else + | lw TMP0, CFUNC:RB->upvalue[0].u32.hi + | lw TMP1, CFUNC:RB->upvalue[0].u32.lo +#endif + |. addiu RA, BASE, -8 + | sw TISNUM, 8+HI(BASE) + | sw r0, 8+LO(BASE) + | sw TMP0, HI(RA) + | sw TMP1, LO(RA) + | b ->fff_res + |. li RD, (3+1)*8 + | + |//-- Base library: catch errors ---------------------------------------- + | + |.ffunc pcall + | lbu TMP3, DISPATCH_GL(hookmask)(DISPATCH) + | beqz NARGS8:RC, ->fff_fallback + | move TMP2, BASE + | addiu BASE, BASE, 8 + | // Remember active hook before pcall. + | srl TMP3, TMP3, HOOK_ACTIVE_SHIFT + | andi TMP3, TMP3, 1 + | addiu PC, TMP3, 8+FRAME_PCALL + | b ->vm_call_dispatch + |. addiu NARGS8:RC, NARGS8:RC, -8 + | + |.ffunc xpcall + | sltiu AT, NARGS8:RC, 16 + | lw CARG4, 8+HI(BASE) + | bnez AT, ->fff_fallback + |. lw CARG3, 8+LO(BASE) + | lw CARG1, LO(BASE) + | lw CARG2, HI(BASE) + | lbu TMP1, DISPATCH_GL(hookmask)(DISPATCH) + | li AT, LJ_TFUNC + | move TMP2, BASE + | bne CARG4, AT, ->fff_fallback // Traceback must be a function. + | addiu BASE, BASE, 16 + | // Remember active hook before pcall. + | srl TMP3, TMP3, HOOK_ACTIVE_SHIFT + | sw CARG3, LO(TMP2) // Swap function and traceback. + | sw CARG4, HI(TMP2) + | andi TMP3, TMP3, 1 + | sw CARG1, 8+LO(TMP2) + | sw CARG2, 8+HI(TMP2) + | addiu PC, TMP3, 16+FRAME_PCALL + | b ->vm_call_dispatch + |. addiu NARGS8:RC, NARGS8:RC, -16 + | + |//-- Coroutine library -------------------------------------------------- + | + |.macro coroutine_resume_wrap, resume + |.if resume + |.ffunc coroutine_resume + | lw CARG3, HI(BASE) + | beqz NARGS8:RC, ->fff_fallback + |. lw CARG1, LO(BASE) + | li AT, LJ_TTHREAD + | bne CARG3, AT, ->fff_fallback + |.else + |.ffunc coroutine_wrap_aux + | lw L:CARG1, CFUNC:RB->upvalue[0].gcr + |.endif + | lbu TMP0, L:CARG1->status + | lw TMP1, L:CARG1->cframe + | lw CARG2, L:CARG1->top + | lw TMP2, L:CARG1->base + | addiu TMP3, TMP0, -LUA_YIELD + | bgtz TMP3, ->fff_fallback // st > LUA_YIELD? + |. xor TMP2, TMP2, CARG2 + | bnez TMP1, ->fff_fallback // cframe != 0? + |. or AT, TMP2, TMP0 + | lw TMP0, L:CARG1->maxstack + | beqz AT, ->fff_fallback // base == top && st == 0? + |. lw PC, FRAME_PC(BASE) + | addu TMP2, CARG2, NARGS8:RC + | sltu AT, TMP0, TMP2 + | bnez AT, ->fff_fallback // Stack overflow? + |. sw PC, SAVE_PC + | sw BASE, L->base + |1: + |.if resume + | addiu BASE, BASE, 8 // Keep resumed thread in stack for GC. + | addiu NARGS8:RC, NARGS8:RC, -8 + | addiu TMP2, TMP2, -8 + |.endif + | sw TMP2, L:CARG1->top + | addu TMP1, BASE, NARGS8:RC + | move CARG3, CARG2 + | sw BASE, L->top + |2: // Move args to coroutine. + | lw SFRETHI, HI(BASE) + | lw SFRETLO, LO(BASE) + | sltu AT, BASE, TMP1 + | beqz AT, >3 + |. addiu BASE, BASE, 8 + | sw SFRETHI, HI(CARG3) + | sw SFRETLO, LO(CARG3) + | b <2 + |. addiu CARG3, CARG3, 8 + |3: + | bal ->vm_resume // (lua_State *L, TValue *base, 0, 0) + |. move L:RA, L:CARG1 + | // Returns thread status. + |4: + | lw TMP2, L:RA->base + | sltiu AT, CRET1, LUA_YIELD+1 + | lw TMP3, L:RA->top + | li_vmstate INTERP + | lw BASE, L->base + | sw L, DISPATCH_GL(cur_L)(DISPATCH) + | st_vmstate + | beqz AT, >8 + |. subu RD, TMP3, TMP2 + | lw TMP0, L->maxstack + | beqz RD, >6 // No results? + |. addu TMP1, BASE, RD + | sltu AT, TMP0, TMP1 + | bnez AT, >9 // Need to grow stack? + |. addu TMP3, TMP2, RD + | sw TMP2, L:RA->top // Clear coroutine stack. + | move TMP1, BASE + |5: // Move results from coroutine. + | lw SFRETHI, HI(TMP2) + | lw SFRETLO, LO(TMP2) + | addiu TMP2, TMP2, 8 + | sltu AT, TMP2, TMP3 + | sw SFRETHI, HI(TMP1) + | sw SFRETLO, LO(TMP1) + | bnez AT, <5 + |. addiu TMP1, TMP1, 8 + |6: + | andi TMP0, PC, FRAME_TYPE + |.if resume + | li TMP1, LJ_TTRUE + | addiu RA, BASE, -8 + | sw TMP1, -8+HI(BASE) // Prepend true to results. + | addiu RD, RD, 16 + |.else + | move RA, BASE + | addiu RD, RD, 8 + |.endif + |7: + | sw PC, SAVE_PC + | beqz TMP0, ->BC_RET_Z + |. move MULTRES, RD + | b ->vm_return + |. nop + | + |8: // Coroutine returned with error (at co->top-1). + |.if resume + | addiu TMP3, TMP3, -8 + | li TMP1, LJ_TFALSE + | lw SFRETHI, HI(TMP3) + | lw SFRETLO, LO(TMP3) + | sw TMP3, L:RA->top // Remove error from coroutine stack. + | li RD, (2+1)*8 + | sw TMP1, -8+HI(BASE) // Prepend false to results. + | addiu RA, BASE, -8 + | sw SFRETHI, HI(BASE) // Copy error message. + | sw SFRETLO, LO(BASE) + | b <7 + |. andi TMP0, PC, FRAME_TYPE + |.else + | load_got lj_ffh_coroutine_wrap_err + | move CARG2, L:RA + | call_intern lj_ffh_coroutine_wrap_err // (lua_State *L, lua_State *co) + |. move CARG1, L + |.endif + | + |9: // Handle stack expansion on return from yield. + | load_got lj_state_growstack + | srl CARG2, RD, 3 + | call_intern lj_state_growstack // (lua_State *L, int n) + |. move CARG1, L + | b <4 + |. li CRET1, 0 + |.endmacro + | + | coroutine_resume_wrap 1 // coroutine.resume + | coroutine_resume_wrap 0 // coroutine.wrap + | + |.ffunc coroutine_yield + | lw TMP0, L->cframe + | addu TMP1, BASE, NARGS8:RC + | sw BASE, L->base + | andi TMP0, TMP0, CFRAME_RESUME + | sw TMP1, L->top + | beqz TMP0, ->fff_fallback + |. li CRET1, LUA_YIELD + | sw r0, L->cframe + | b ->vm_leave_unw + |. sb CRET1, L->status + | + |//-- Math library ------------------------------------------------------- + | + |.ffunc_1 math_abs + | bne SFARG1HI, TISNUM, >1 + |. sra TMP0, SFARG1LO, 31 + | xor TMP1, SFARG1LO, TMP0 + | subu SFARG1LO, TMP1, TMP0 + | bgez SFARG1LO, ->fff_restv + |. nop + | lui SFARG1HI, 0x41e0 // 2^31 as a double. + | b ->fff_restv + |. li SFARG1LO, 0 + |1: + | sltiu AT, SFARG1HI, LJ_TISNUM + | beqz AT, ->fff_fallback + |. sll SFARG1HI, SFARG1HI, 1 + | srl SFARG1HI, SFARG1HI, 1 + |// fallthrough + | + |->fff_restv: + | // SFARG1LO/SFARG1HI = TValue result. + | lw PC, FRAME_PC(BASE) + | sw SFARG1HI, -8+HI(BASE) + | addiu RA, BASE, -8 + | sw SFARG1LO, -8+LO(BASE) + |->fff_res1: + | // RA = results, PC = return. + | li RD, (1+1)*8 + |->fff_res: + | // RA = results, RD = (nresults+1)*8, PC = return. + | andi TMP0, PC, FRAME_TYPE + | bnez TMP0, ->vm_return + |. move MULTRES, RD + | lw INS, -4(PC) + | decode_RB8a RB, INS + | decode_RB8b RB + |5: + | sltu AT, RD, RB + | bnez AT, >6 // More results expected? + |. decode_RA8a TMP0, INS + | decode_RA8b TMP0 + | ins_next1 + | // Adjust BASE. KBASE is assumed to be set for the calling frame. + | subu BASE, RA, TMP0 + | ins_next2 + | + |6: // Fill up results with nil. + | addu TMP1, RA, RD + | addiu RD, RD, 8 + | b <5 + |. sw TISNIL, -8+HI(TMP1) + | + |.macro math_extern, func + | .ffunc math_ .. func + | lw SFARG1HI, HI(BASE) + | beqz NARGS8:RC, ->fff_fallback + |. load_got func + | sltiu AT, SFARG1HI, LJ_TISNUM + | beqz AT, ->fff_fallback + |.if FPU + |. ldc1 FARG1, 0(BASE) + |.else + |. lw SFARG1LO, LO(BASE) + |.endif + | call_extern + |. nop + | b ->fff_resn + |. nop + |.endmacro + | + |.macro math_extern2, func + | .ffunc_nn math_ .. func + |. load_got func + | call_extern + |. nop + | b ->fff_resn + |. nop + |.endmacro + | + |// TODO: Return integer type if result is integer (own sf implementation). + |.macro math_round, func + |->ff_math_ .. func: + | lw SFARG1HI, HI(BASE) + | beqz NARGS8:RC, ->fff_fallback + |. lw SFARG1LO, LO(BASE) + | beq SFARG1HI, TISNUM, ->fff_restv + |. sltu AT, SFARG1HI, TISNUM + | beqz AT, ->fff_fallback + |.if FPU + |. ldc1 FARG1, 0(BASE) + | bal ->vm_ .. func + |.else + |. load_got func + | call_extern + |.endif + |. nop + | b ->fff_resn + |. nop + |.endmacro + | + | math_round floor + | math_round ceil + | + |.ffunc math_log + | li AT, 8 + | bne NARGS8:RC, AT, ->fff_fallback // Exactly 1 argument. + |. lw SFARG1HI, HI(BASE) + | sltiu AT, SFARG1HI, LJ_TISNUM + | beqz AT, ->fff_fallback + |. load_got log + |.if FPU + | call_extern + |. ldc1 FARG1, 0(BASE) + |.else + | call_extern + |. lw SFARG1LO, LO(BASE) + |.endif + | b ->fff_resn + |. nop + | + | math_extern log10 + | math_extern exp + | math_extern sin + | math_extern cos + | math_extern tan + | math_extern asin + | math_extern acos + | math_extern atan + | math_extern sinh + | math_extern cosh + | math_extern tanh + | math_extern2 pow + | math_extern2 atan2 + | math_extern2 fmod + | + |.if FPU + |.ffunc_n math_sqrt + |. sqrt.d FRET1, FARG1 + |// fallthrough to ->fff_resn + |.else + | math_extern sqrt + |.endif + | + |->fff_resn: + | lw PC, FRAME_PC(BASE) + | addiu RA, BASE, -8 + |.if FPU + | b ->fff_res1 + |. sdc1 FRET1, -8(BASE) + |.else + | sw SFRETHI, -8+HI(BASE) + | b ->fff_res1 + |. sw SFRETLO, -8+LO(BASE) + |.endif + | + | + |.ffunc math_ldexp + | sltiu AT, NARGS8:RC, 16 + | lw SFARG1HI, HI(BASE) + | bnez AT, ->fff_fallback + |. lw CARG4, 8+HI(BASE) + | bne CARG4, TISNUM, ->fff_fallback + | load_got ldexp + |. sltu AT, SFARG1HI, TISNUM + | beqz AT, ->fff_fallback + |.if FPU + |. ldc1 FARG1, 0(BASE) + |.else + |. lw SFARG1LO, LO(BASE) + |.endif + | call_extern + |. lw CARG3, 8+LO(BASE) + | b ->fff_resn + |. nop + | + |.ffunc_n math_frexp + | load_got frexp + | lw PC, FRAME_PC(BASE) + | call_extern + |. addiu CARG3, DISPATCH, DISPATCH_GL(tmptv) + | lw TMP1, DISPATCH_GL(tmptv)(DISPATCH) + | addiu RA, BASE, -8 + |.if FPU + | mtc1 TMP1, FARG2 + | sdc1 FRET1, 0(RA) + | cvt.d.w FARG2, FARG2 + | sdc1 FARG2, 8(RA) + |.else + | sw SFRETLO, LO(RA) + | sw SFRETHI, HI(RA) + | sw TMP1, 8+LO(RA) + | sw TISNUM, 8+HI(RA) + |.endif + | b ->fff_res + |. li RD, (2+1)*8 + | + |.ffunc_n math_modf + | load_got modf + | lw PC, FRAME_PC(BASE) + | call_extern + |. addiu CARG3, BASE, -8 + | addiu RA, BASE, -8 + |.if FPU + | sdc1 FRET1, 0(BASE) + |.else + | sw SFRETLO, LO(BASE) + | sw SFRETHI, HI(BASE) + |.endif + | b ->fff_res + |. li RD, (2+1)*8 + | + |.macro math_minmax, name, intins, fpins + | .ffunc_1 name + | addu TMP3, BASE, NARGS8:RC + | bne SFARG1HI, TISNUM, >5 + |. addiu TMP2, BASE, 8 + |1: // Handle integers. + |. lw SFARG2HI, HI(TMP2) + | beq TMP2, TMP3, ->fff_restv + |. lw SFARG2LO, LO(TMP2) + | bne SFARG2HI, TISNUM, >3 + |. slt AT, SFARG1LO, SFARG2LO + | intins SFARG1LO, SFARG2LO, AT + | b <1 + |. addiu TMP2, TMP2, 8 + | + |3: // Convert intermediate result to number and continue with number loop. + | sltiu AT, SFARG2HI, LJ_TISNUM + | beqz AT, ->fff_fallback + |.if FPU + |. mtc1 SFARG1LO, FRET1 + | cvt.d.w FRET1, FRET1 + | b >7 + |. ldc1 FARG1, 0(TMP2) + |.else + |. nop + | bal ->vm_sfi2d_1 + |. nop + | b >7 + |. nop + |.endif + | + |5: + |. sltiu AT, SFARG1HI, LJ_TISNUM + | beqz AT, ->fff_fallback + |.if FPU + |. ldc1 FRET1, 0(BASE) + |.endif + | + |6: // Handle numbers. + |. lw SFARG2HI, HI(TMP2) + |.if FPU + | beq TMP2, TMP3, ->fff_resn + |.else + | beq TMP2, TMP3, ->fff_restv + |.endif + |. sltiu AT, SFARG2HI, LJ_TISNUM + | beqz AT, >8 + |.if FPU + |. ldc1 FARG1, 0(TMP2) + |.else + |. lw SFARG2LO, LO(TMP2) + |.endif + |7: + |.if FPU + | c.olt.d FRET1, FARG1 + | fpins FRET1, FARG1 + |.else + | bal ->vm_sfcmpolt + |. nop + | intins SFARG1LO, SFARG2LO, CRET1 + | intins SFARG1HI, SFARG2HI, CRET1 + |.endif + | b <6 + |. addiu TMP2, TMP2, 8 + | + |8: // Convert integer to number and continue with number loop. + | bne SFARG2HI, TISNUM, ->fff_fallback + |.if FPU + |. lwc1 FARG1, LO(TMP2) + | b <7 + |. cvt.d.w FARG1, FARG1 + |.else + |. nop + | bal ->vm_sfi2d_2 + |. nop + | b <7 + |. nop + |.endif + | + |.endmacro + | + | math_minmax math_min, movz, movf.d + | math_minmax math_max, movn, movt.d + | + |//-- String library ----------------------------------------------------- + | + |.ffunc string_byte // Only handle the 1-arg case here. + | lw CARG3, HI(BASE) + | lw STR:CARG1, LO(BASE) + | xori AT, NARGS8:RC, 8 + | addiu CARG3, CARG3, -LJ_TSTR + | or AT, AT, CARG3 + | bnez AT, ->fff_fallback // Need exactly 1 string argument. + |. nop + | lw TMP0, STR:CARG1->len + | addiu RA, BASE, -8 + | lw PC, FRAME_PC(BASE) + | sltu RD, r0, TMP0 + | lbu TMP1, STR:CARG1[1] // Access is always ok (NUL at end). + | addiu RD, RD, 1 + | sll RD, RD, 3 // RD = ((str->len != 0)+1)*8 + | sw TISNUM, HI(RA) + | b ->fff_res + |. sw TMP1, LO(RA) + | + |.ffunc string_char // Only handle the 1-arg case here. + | ffgccheck + |. nop + | lw CARG3, HI(BASE) + | lw CARG1, LO(BASE) + | li TMP1, 255 + | xori AT, NARGS8:RC, 8 // Exactly 1 argument. + | xor TMP0, CARG3, TISNUM // Integer. + | sltu TMP1, TMP1, CARG1 // !(255 < n). + | or AT, AT, TMP0 + | or AT, AT, TMP1 + | bnez AT, ->fff_fallback + |. li CARG3, 1 + | addiu CARG2, sp, ARG5_OFS + | sb CARG1, ARG5 + |->fff_newstr: + | load_got lj_str_new + | sw BASE, L->base + | sw PC, SAVE_PC + | call_intern lj_str_new // (lua_State *L, char *str, size_t l) + |. move CARG1, L + | // Returns GCstr *. + | lw BASE, L->base + |->fff_resstr: + | move SFARG1LO, CRET1 + | b ->fff_restv + |. li SFARG1HI, LJ_TSTR + | + |.ffunc string_sub + | ffgccheck + |. nop + | addiu AT, NARGS8:RC, -16 + | lw CARG3, 16+HI(BASE) + | lw TMP0, HI(BASE) + | lw STR:CARG1, LO(BASE) + | bltz AT, ->fff_fallback + |. lw CARG2, 8+HI(BASE) + | beqz AT, >1 + |. li CARG4, -1 + | bne CARG3, TISNUM, ->fff_fallback + |. lw CARG4, 16+LO(BASE) + |1: + | bne CARG2, TISNUM, ->fff_fallback + |. li AT, LJ_TSTR + | bne TMP0, AT, ->fff_fallback + |. lw CARG3, 8+LO(BASE) + | lw CARG2, STR:CARG1->len + | // STR:CARG1 = str, CARG2 = str->len, CARG3 = start, CARG4 = end + | slt AT, CARG4, r0 + | addiu TMP0, CARG2, 1 + | addu TMP1, CARG4, TMP0 + | slt TMP3, CARG3, r0 + | movn CARG4, TMP1, AT // if (end < 0) end += len+1 + | addu TMP1, CARG3, TMP0 + | movn CARG3, TMP1, TMP3 // if (start < 0) start += len+1 + | li TMP2, 1 + | slt AT, CARG4, r0 + | slt TMP3, r0, CARG3 + | movn CARG4, r0, AT // if (end < 0) end = 0 + | movz CARG3, TMP2, TMP3 // if (start < 1) start = 1 + | slt AT, CARG2, CARG4 + | movn CARG4, CARG2, AT // if (end > len) end = len + | addu CARG2, STR:CARG1, CARG3 + | subu CARG3, CARG4, CARG3 // len = end - start + | addiu CARG2, CARG2, sizeof(GCstr)-1 + | bgez CARG3, ->fff_newstr + |. addiu CARG3, CARG3, 1 // len++ + |->fff_emptystr: // Return empty string. + | addiu STR:SFARG1LO, DISPATCH, DISPATCH_GL(strempty) + | b ->fff_restv + |. li SFARG1HI, LJ_TSTR + | + |.macro ffstring_op, name + | .ffunc string_ .. name + | ffgccheck + |. nop + | lw CARG3, HI(BASE) + | lw STR:CARG2, LO(BASE) + | beqz NARGS8:RC, ->fff_fallback + |. li AT, LJ_TSTR + | bne CARG3, AT, ->fff_fallback + |. addiu SBUF:CARG1, DISPATCH, DISPATCH_GL(tmpbuf) + | load_got lj_buf_putstr_ .. name + | lw TMP0, SBUF:CARG1->b + | sw L, SBUF:CARG1->L + | sw BASE, L->base + | sw TMP0, SBUF:CARG1->p + | call_intern extern lj_buf_putstr_ .. name + |. sw PC, SAVE_PC + | load_got lj_buf_tostr + | call_intern lj_buf_tostr + |. move SBUF:CARG1, SBUF:CRET1 + | b ->fff_resstr + |. lw BASE, L->base + |.endmacro + | + |ffstring_op reverse + |ffstring_op lower + |ffstring_op upper + | + |//-- Bit library -------------------------------------------------------- + | + |->vm_tobit_fb: + | beqz TMP1, ->fff_fallback + |.if FPU + |. ldc1 FARG1, 0(BASE) + | add.d FARG1, FARG1, TOBIT + | jr ra + |. mfc1 CRET1, FARG1 + |.else + |// FP number to bit conversion for soft-float. + |->vm_tobit: + | sll TMP0, SFARG1HI, 1 + | lui AT, 0x0020 + | addu TMP0, TMP0, AT + | slt AT, TMP0, r0 + | movz SFARG1LO, r0, AT + | beqz AT, >2 + |. li TMP1, 0x3e0 + | not TMP1, TMP1 + | sra TMP0, TMP0, 21 + | subu TMP0, TMP1, TMP0 + | slt AT, TMP0, r0 + | bnez AT, >1 + |. sll TMP1, SFARG1HI, 11 + | lui AT, 0x8000 + | or TMP1, TMP1, AT + | srl AT, SFARG1LO, 21 + | or TMP1, TMP1, AT + | slt AT, SFARG1HI, r0 + | beqz AT, >2 + |. srlv SFARG1LO, TMP1, TMP0 + | subu SFARG1LO, r0, SFARG1LO + |2: + | jr ra + |. move CRET1, SFARG1LO + |1: + | addiu TMP0, TMP0, 21 + | srlv TMP1, SFARG1LO, TMP0 + | li AT, 20 + | subu TMP0, AT, TMP0 + | sll SFARG1LO, SFARG1HI, 12 + | sllv AT, SFARG1LO, TMP0 + | or SFARG1LO, TMP1, AT + | slt AT, SFARG1HI, r0 + | beqz AT, <2 + |. nop + | jr ra + |. subu CRET1, r0, SFARG1LO + |.endif + | + |.macro .ffunc_bit, name + | .ffunc_1 bit_..name + | beq SFARG1HI, TISNUM, >6 + |. move CRET1, SFARG1LO + | bal ->vm_tobit_fb + |. sltu TMP1, SFARG1HI, TISNUM + |6: + |.endmacro + | + |.macro .ffunc_bit_op, name, ins + | .ffunc_bit name + | addiu TMP2, BASE, 8 + | addu TMP3, BASE, NARGS8:RC + |1: + | lw SFARG1HI, HI(TMP2) + | beq TMP2, TMP3, ->fff_resi + |. lw SFARG1LO, LO(TMP2) + |.if FPU + | bne SFARG1HI, TISNUM, >2 + |. addiu TMP2, TMP2, 8 + | b <1 + |. ins CRET1, CRET1, SFARG1LO + |2: + | ldc1 FARG1, -8(TMP2) + | sltu TMP1, SFARG1HI, TISNUM + | beqz TMP1, ->fff_fallback + |. add.d FARG1, FARG1, TOBIT + | mfc1 SFARG1LO, FARG1 + | b <1 + |. ins CRET1, CRET1, SFARG1LO + |.else + | beq SFARG1HI, TISNUM, >2 + |. move CRET2, CRET1 + | bal ->vm_tobit_fb + |. sltu TMP1, SFARG1HI, TISNUM + | move SFARG1LO, CRET2 + |2: + | ins CRET1, CRET1, SFARG1LO + | b <1 + |. addiu TMP2, TMP2, 8 + |.endif + |.endmacro + | + |.ffunc_bit_op band, and + |.ffunc_bit_op bor, or + |.ffunc_bit_op bxor, xor + | + |.ffunc_bit bswap + | srl TMP0, CRET1, 24 + | srl TMP2, CRET1, 8 + | sll TMP1, CRET1, 24 + | andi TMP2, TMP2, 0xff00 + | or TMP0, TMP0, TMP1 + | andi CRET1, CRET1, 0xff00 + | or TMP0, TMP0, TMP2 + | sll CRET1, CRET1, 8 + | b ->fff_resi + |. or CRET1, TMP0, CRET1 + | + |.ffunc_bit bnot + | b ->fff_resi + |. not CRET1, CRET1 + | + |.macro .ffunc_bit_sh, name, ins, shmod + | .ffunc_2 bit_..name + | beq SFARG1HI, TISNUM, >1 + |. nop + | bal ->vm_tobit_fb + |. sltu TMP1, SFARG1HI, TISNUM + | move SFARG1LO, CRET1 + |1: + | bne SFARG2HI, TISNUM, ->fff_fallback + |. nop + |.if shmod == 1 + | li AT, 32 + | subu TMP0, AT, SFARG2LO + | sllv SFARG2LO, SFARG1LO, SFARG2LO + | srlv SFARG1LO, SFARG1LO, TMP0 + |.elif shmod == 2 + | li AT, 32 + | subu TMP0, AT, SFARG2LO + | srlv SFARG2LO, SFARG1LO, SFARG2LO + | sllv SFARG1LO, SFARG1LO, TMP0 + |.endif + | b ->fff_resi + |. ins CRET1, SFARG1LO, SFARG2LO + |.endmacro + | + |.ffunc_bit_sh lshift, sllv, 0 + |.ffunc_bit_sh rshift, srlv, 0 + |.ffunc_bit_sh arshift, srav, 0 + |// Can't use rotrv, since it's only in MIPS32R2. + |.ffunc_bit_sh rol, or, 1 + |.ffunc_bit_sh ror, or, 2 + | + |.ffunc_bit tobit + |->fff_resi: + | lw PC, FRAME_PC(BASE) + | addiu RA, BASE, -8 + | sw TISNUM, -8+HI(BASE) + | b ->fff_res1 + |. sw CRET1, -8+LO(BASE) + | + |//----------------------------------------------------------------------- + | + |->fff_fallback: // Call fast function fallback handler. + | // BASE = new base, RB = CFUNC, RC = nargs*8 + | lw TMP3, CFUNC:RB->f + | addu TMP1, BASE, NARGS8:RC + | lw PC, FRAME_PC(BASE) // Fallback may overwrite PC. + | addiu TMP0, TMP1, 8*LUA_MINSTACK + | lw TMP2, L->maxstack + | sw PC, SAVE_PC // Redundant (but a defined value). + | sltu AT, TMP2, TMP0 + | sw BASE, L->base + | sw TMP1, L->top + | bnez AT, >5 // Need to grow stack. + |. move CFUNCADDR, TMP3 + | jalr TMP3 // (lua_State *L) + |. move CARG1, L + | // Either throws an error, or recovers and returns -1, 0 or nresults+1. + | lw BASE, L->base + | sll RD, CRET1, 3 + | bgtz CRET1, ->fff_res // Returned nresults+1? + |. addiu RA, BASE, -8 + |1: // Returned 0 or -1: retry fast path. + | lw TMP0, L->top + | lw LFUNC:RB, FRAME_FUNC(BASE) + | bnez CRET1, ->vm_call_tail // Returned -1? + |. subu NARGS8:RC, TMP0, BASE + | ins_callt // Returned 0: retry fast path. + | + |// Reconstruct previous base for vmeta_call during tailcall. + |->vm_call_tail: + | andi TMP0, PC, FRAME_TYPE + | li AT, -4 + | bnez TMP0, >3 + |. and TMP1, PC, AT + | lbu TMP1, OFS_RA(PC) + | sll TMP1, TMP1, 3 + | addiu TMP1, TMP1, 8 + |3: + | b ->vm_call_dispatch // Resolve again for tailcall. + |. subu TMP2, BASE, TMP1 + | + |5: // Grow stack for fallback handler. + | load_got lj_state_growstack + | li CARG2, LUA_MINSTACK + | call_intern lj_state_growstack // (lua_State *L, int n) + |. move CARG1, L + | lw BASE, L->base + | b <1 + |. li CRET1, 0 // Force retry. + | + |->fff_gcstep: // Call GC step function. + | // BASE = new base, RC = nargs*8 + | move MULTRES, ra + | load_got lj_gc_step + | sw BASE, L->base + | addu TMP0, BASE, NARGS8:RC + | sw PC, SAVE_PC // Redundant (but a defined value). + | sw TMP0, L->top + | call_intern lj_gc_step // (lua_State *L) + |. move CARG1, L + | lw BASE, L->base + | move ra, MULTRES + | lw TMP0, L->top + | lw CFUNC:RB, FRAME_FUNC(BASE) + | jr ra + |. subu NARGS8:RC, TMP0, BASE + | + |//----------------------------------------------------------------------- + |//-- Special dispatch targets ------------------------------------------- + |//----------------------------------------------------------------------- + | + |->vm_record: // Dispatch target for recording phase. + |.if JIT + | lbu TMP3, DISPATCH_GL(hookmask)(DISPATCH) + | andi AT, TMP3, HOOK_VMEVENT // No recording while in vmevent. + | bnez AT, >5 + | // Decrement the hookcount for consistency, but always do the call. + |. lw TMP2, DISPATCH_GL(hookcount)(DISPATCH) + | andi AT, TMP3, HOOK_ACTIVE + | bnez AT, >1 + |. addiu TMP2, TMP2, -1 + | andi AT, TMP3, LUA_MASKLINE|LUA_MASKCOUNT + | beqz AT, >1 + |. nop + | b >1 + |. sw TMP2, DISPATCH_GL(hookcount)(DISPATCH) + |.endif + | + |->vm_rethook: // Dispatch target for return hooks. + | lbu TMP3, DISPATCH_GL(hookmask)(DISPATCH) + | andi AT, TMP3, HOOK_ACTIVE // Hook already active? + | beqz AT, >1 + |5: // Re-dispatch to static ins. + |. lw AT, GG_DISP2STATIC(TMP0) // Assumes TMP0 holds DISPATCH+OP*4. + | jr AT + |. nop + | + |->vm_inshook: // Dispatch target for instr/line hooks. + | lbu TMP3, DISPATCH_GL(hookmask)(DISPATCH) + | lw TMP2, DISPATCH_GL(hookcount)(DISPATCH) + | andi AT, TMP3, HOOK_ACTIVE // Hook already active? + | bnez AT, <5 + |. andi AT, TMP3, LUA_MASKLINE|LUA_MASKCOUNT + | beqz AT, <5 + |. addiu TMP2, TMP2, -1 + | beqz TMP2, >1 + |. sw TMP2, DISPATCH_GL(hookcount)(DISPATCH) + | andi AT, TMP3, LUA_MASKLINE + | beqz AT, <5 + |1: + |. load_got lj_dispatch_ins + | sw MULTRES, SAVE_MULTRES + | move CARG2, PC + | sw BASE, L->base + | // SAVE_PC must hold the _previous_ PC. The callee updates it with PC. + | call_intern lj_dispatch_ins // (lua_State *L, const BCIns *pc) + |. move CARG1, L + |3: + | lw BASE, L->base + |4: // Re-dispatch to static ins. + | lw INS, -4(PC) + | decode_OP4a TMP1, INS + | decode_OP4b TMP1 + | addu TMP0, DISPATCH, TMP1 + | decode_RD8a RD, INS + | lw AT, GG_DISP2STATIC(TMP0) + | decode_RA8a RA, INS + | decode_RD8b RD + | jr AT + | decode_RA8b RA + | + |->cont_hook: // Continue from hook yield. + | addiu PC, PC, 4 + | b <4 + |. lw MULTRES, -24+LO(RB) // Restore MULTRES for *M ins. + | + |->vm_hotloop: // Hot loop counter underflow. + |.if JIT + | lw LFUNC:TMP1, FRAME_FUNC(BASE) + | addiu CARG1, DISPATCH, GG_DISP2J + | sw PC, SAVE_PC + | lw TMP1, LFUNC:TMP1->pc + | move CARG2, PC + | sw L, DISPATCH_J(L)(DISPATCH) + | lbu TMP1, PC2PROTO(framesize)(TMP1) + | load_got lj_trace_hot + | sw BASE, L->base + | sll TMP1, TMP1, 3 + | addu TMP1, BASE, TMP1 + | call_intern lj_trace_hot // (jit_State *J, const BCIns *pc) + |. sw TMP1, L->top + | b <3 + |. nop + |.endif + | + |->vm_callhook: // Dispatch target for call hooks. + |.if JIT + | b >1 + |.endif + |. move CARG2, PC + | + |->vm_hotcall: // Hot call counter underflow. + |.if JIT + | ori CARG2, PC, 1 + |1: + |.endif + | load_got lj_dispatch_call + | addu TMP0, BASE, RC + | sw PC, SAVE_PC + | sw BASE, L->base + | subu RA, RA, BASE + | sw TMP0, L->top + | call_intern lj_dispatch_call // (lua_State *L, const BCIns *pc) + |. move CARG1, L + | // Returns ASMFunction. + | lw BASE, L->base + | lw TMP0, L->top + | sw r0, SAVE_PC // Invalidate for subsequent line hook. + | subu NARGS8:RC, TMP0, BASE + | addu RA, BASE, RA + | lw LFUNC:RB, FRAME_FUNC(BASE) + | jr CRET1 + |. lw INS, -4(PC) + | + |->cont_stitch: // Trace stitching. + |.if JIT + | // RA = resultptr, RB = meta base + | lw INS, -4(PC) + | lw TMP2, -24+LO(RB) // Save previous trace. + | decode_RA8a RC, INS + | addiu AT, MULTRES, -8 + | decode_RA8b RC + | beqz AT, >2 + |. addu RC, BASE, RC // Call base. + |1: // Move results down. + | lw SFRETHI, HI(RA) + | lw SFRETLO, LO(RA) + | addiu AT, AT, -8 + | addiu RA, RA, 8 + | sw SFRETHI, HI(RC) + | sw SFRETLO, LO(RC) + | bnez AT, <1 + |. addiu RC, RC, 8 + |2: + | decode_RA8a RA, INS + | decode_RB8a RB, INS + | decode_RA8b RA + | decode_RB8b RB + | addu RA, RA, RB + | addu RA, BASE, RA + |3: + | sltu AT, RC, RA + | bnez AT, >9 // More results wanted? + |. nop + | + | lhu TMP3, TRACE:TMP2->traceno + | lhu RD, TRACE:TMP2->link + | beq RD, TMP3, ->cont_nop // Blacklisted. + |. load_got lj_dispatch_stitch + | bnez RD, =>BC_JLOOP // Jump to stitched trace. + |. sll RD, RD, 3 + | + | // Stitch a new trace to the previous trace. + | sw TMP3, DISPATCH_J(exitno)(DISPATCH) + | sw L, DISPATCH_J(L)(DISPATCH) + | sw BASE, L->base + | addiu CARG1, DISPATCH, GG_DISP2J + | call_intern lj_dispatch_stitch // (jit_State *J, const BCIns *pc) + |. move CARG2, PC + | b ->cont_nop + |. lw BASE, L->base + | + |9: + | sw TISNIL, HI(RC) + | b <3 + |. addiu RC, RC, 8 + |.endif + | + |->vm_profhook: // Dispatch target for profiler hook. +#if LJ_HASPROFILE + | load_got lj_dispatch_profile + | sw MULTRES, SAVE_MULTRES + | move CARG2, PC + | sw BASE, L->base + | call_intern lj_dispatch_profile // (lua_State *L, const BCIns *pc) + |. move CARG1, L + | // HOOK_PROFILE is off again, so re-dispatch to dynamic instruction. + | addiu PC, PC, -4 + | b ->cont_nop + |. lw BASE, L->base +#endif + | + |//----------------------------------------------------------------------- + |//-- Trace exit handler ------------------------------------------------- + |//----------------------------------------------------------------------- + | + |.macro savex_, a, b + |.if FPU + | sdc1 f..a, 16+a*8(sp) + | sw r..a, 16+32*8+a*4(sp) + | sw r..b, 16+32*8+b*4(sp) + |.else + | sw r..a, 16+a*4(sp) + | sw r..b, 16+b*4(sp) + |.endif + |.endmacro + | + |->vm_exit_handler: + |.if JIT + |.if FPU + | addiu sp, sp, -(16+32*8+32*4) + |.else + | addiu sp, sp, -(16+32*4) + |.endif + | savex_ 0, 1 + | savex_ 2, 3 + | savex_ 4, 5 + | savex_ 6, 7 + | savex_ 8, 9 + | savex_ 10, 11 + | savex_ 12, 13 + | savex_ 14, 15 + | savex_ 16, 17 + | savex_ 18, 19 + | savex_ 20, 21 + | savex_ 22, 23 + | savex_ 24, 25 + | savex_ 26, 27 + |.if FPU + | sdc1 f28, 16+28*8(sp) + | sdc1 f30, 16+30*8(sp) + | sw r28, 16+32*8+28*4(sp) + | sw r30, 16+32*8+30*4(sp) + | sw r0, 16+32*8+31*4(sp) // Clear RID_TMP. + | addiu TMP2, sp, 16+32*8+32*4 // Recompute original value of sp. + | sw TMP2, 16+32*8+29*4(sp) // Store sp in RID_SP + |.else + | sw r28, 16+28*4(sp) + | sw r30, 16+30*4(sp) + | sw r0, 16+31*4(sp) // Clear RID_TMP. + | addiu TMP2, sp, 16+32*4 // Recompute original value of sp. + | sw TMP2, 16+29*4(sp) // Store sp in RID_SP + |.endif + | li_vmstate EXIT + | addiu DISPATCH, JGL, -GG_DISP2G-32768 + | lw TMP1, 0(TMP2) // Load exit number. + | st_vmstate + | lw L, DISPATCH_GL(cur_L)(DISPATCH) + | lw BASE, DISPATCH_GL(jit_base)(DISPATCH) + | load_got lj_trace_exit + | sw L, DISPATCH_J(L)(DISPATCH) + | sw ra, DISPATCH_J(parent)(DISPATCH) // Store trace number. + | sw BASE, L->base + | sw TMP1, DISPATCH_J(exitno)(DISPATCH) // Store exit number. + | addiu CARG1, DISPATCH, GG_DISP2J + | sw r0, DISPATCH_GL(jit_base)(DISPATCH) + | call_intern lj_trace_exit // (jit_State *J, ExitState *ex) + |. addiu CARG2, sp, 16 + | // Returns MULTRES (unscaled) or negated error code. + | lw TMP1, L->cframe + | li AT, -4 + | lw BASE, L->base + | and sp, TMP1, AT + | lw PC, SAVE_PC // Get SAVE_PC. + | b >1 + |. sw L, SAVE_L // Set SAVE_L (on-trace resume/yield). + |.endif + |->vm_exit_interp: + |.if JIT + | // CRET1 = MULTRES or negated error code, BASE, PC and JGL set. + | lw L, SAVE_L + | addiu DISPATCH, JGL, -GG_DISP2G-32768 + | sw BASE, L->base + |1: + | bltz CRET1, >9 // Check for error from exit. + |. lw LFUNC:RB, FRAME_FUNC(BASE) + | .FPU lui TMP3, 0x59c0 // TOBIT = 2^52 + 2^51 (float). + | sll MULTRES, CRET1, 3 + | li TISNIL, LJ_TNIL + | li TISNUM, LJ_TISNUM // Setup type comparison constants. + | sw MULTRES, SAVE_MULTRES + | .FPU mtc1 TMP3, TOBIT + | lw TMP1, LFUNC:RB->pc + | sw r0, DISPATCH_GL(jit_base)(DISPATCH) + | lw KBASE, PC2PROTO(k)(TMP1) + | .FPU cvt.d.s TOBIT, TOBIT + | // Modified copy of ins_next which handles function header dispatch, too. + | lw INS, 0(PC) + | addiu PC, PC, 4 + | // Assumes TISNIL == ~LJ_VMST_INTERP == -1 + | sw TISNIL, DISPATCH_GL(vmstate)(DISPATCH) + | decode_OP4a TMP1, INS + | decode_OP4b TMP1 + | sltiu TMP2, TMP1, BC_FUNCF*4 + | addu TMP0, DISPATCH, TMP1 + | decode_RD8a RD, INS + | lw AT, 0(TMP0) + | decode_RA8a RA, INS + | beqz TMP2, >2 + |. decode_RA8b RA + | jr AT + |. decode_RD8b RD + |2: + | sltiu TMP2, TMP1, (BC_FUNCC+2)*4 // Fast function? + | bnez TMP2, >3 + |. lw TMP1, FRAME_PC(BASE) + | // Check frame below fast function. + | andi TMP0, TMP1, FRAME_TYPE + | bnez TMP0, >3 // Trace stitching continuation? + |. nop + | // Otherwise set KBASE for Lua function below fast function. + | lw TMP2, -4(TMP1) + | decode_RA8a TMP0, TMP2 + | decode_RA8b TMP0 + | subu TMP1, BASE, TMP0 + | lw LFUNC:TMP2, -8+FRAME_FUNC(TMP1) + | lw TMP1, LFUNC:TMP2->pc + | lw KBASE, PC2PROTO(k)(TMP1) + |3: + | addiu RC, MULTRES, -8 + | jr AT + |. addu RA, RA, BASE + | + |9: // Rethrow error from the right C frame. + | load_got lj_err_throw + | negu CARG2, CRET1 + | call_intern lj_err_throw // (lua_State *L, int errcode) + |. move CARG1, L + |.endif + | + |//----------------------------------------------------------------------- + |//-- Math helper functions ---------------------------------------------- + |//----------------------------------------------------------------------- + | + |// Hard-float round to integer. + |// Modifies AT, TMP0, FRET1, FRET2, f4. Keeps all others incl. FARG1. + |.macro vm_round_hf, func + | lui TMP0, 0x4330 // Hiword of 2^52 (double). + | mtc1 r0, f4 + | mtc1 TMP0, f5 + | abs.d FRET2, FARG1 // |x| + | mfc1 AT, f13 + | c.olt.d 0, FRET2, f4 + | add.d FRET1, FRET2, f4 // (|x| + 2^52) - 2^52 + | bc1f 0, >1 // Truncate only if |x| < 2^52. + |. sub.d FRET1, FRET1, f4 + | slt AT, AT, r0 + |.if "func" == "ceil" + | lui TMP0, 0xbff0 // Hiword of -1 (double). Preserves -0. + |.else + | lui TMP0, 0x3ff0 // Hiword of +1 (double). + |.endif + |.if "func" == "trunc" + | mtc1 TMP0, f5 + | c.olt.d 0, FRET2, FRET1 // |x| < result? + | sub.d FRET2, FRET1, f4 + | movt.d FRET1, FRET2, 0 // If yes, subtract +1. + | neg.d FRET2, FRET1 + | jr ra + |. movn.d FRET1, FRET2, AT // Merge sign bit back in. + |.else + | neg.d FRET2, FRET1 + | mtc1 TMP0, f5 + | movn.d FRET1, FRET2, AT // Merge sign bit back in. + |.if "func" == "ceil" + | c.olt.d 0, FRET1, FARG1 // x > result? + |.else + | c.olt.d 0, FARG1, FRET1 // x < result? + |.endif + | sub.d FRET2, FRET1, f4 // If yes, subtract +-1. + | jr ra + |. movt.d FRET1, FRET2, 0 + |.endif + |1: + | jr ra + |. mov.d FRET1, FARG1 + |.endmacro + | + |.macro vm_round, func + |.if FPU + | vm_round_hf, func + |.endif + |.endmacro + | + |->vm_floor: + | vm_round floor + |->vm_ceil: + | vm_round ceil + |->vm_trunc: + |.if JIT + | vm_round trunc + |.endif + | + |// Soft-float integer to number conversion. + |.macro sfi2d, AHI, ALO + |.if not FPU + | beqz ALO, >9 // Handle zero first. + |. sra TMP0, ALO, 31 + | xor TMP1, ALO, TMP0 + | subu TMP1, TMP1, TMP0 // Absolute value in TMP1. + | clz AHI, TMP1 + | andi TMP0, TMP0, 0x800 // Mask sign bit. + | li AT, 0x3ff+31-1 + | sllv TMP1, TMP1, AHI // Align mantissa left with leading 1. + | subu AHI, AT, AHI // Exponent - 1 in AHI. + | sll ALO, TMP1, 21 + | or AHI, AHI, TMP0 // Sign | Exponent. + | srl TMP1, TMP1, 11 + | sll AHI, AHI, 20 // Align left. + | jr ra + |. addu AHI, AHI, TMP1 // Add mantissa, increment exponent. + |9: + | jr ra + |. li AHI, 0 + |.endif + |.endmacro + | + |// Input SFARG1LO. Output: SFARG1*. Temporaries: AT, TMP0, TMP1. + |->vm_sfi2d_1: + | sfi2d SFARG1HI, SFARG1LO + | + |// Input SFARG2LO. Output: SFARG2*. Temporaries: AT, TMP0, TMP1. + |->vm_sfi2d_2: + | sfi2d SFARG2HI, SFARG2LO + | + |// Soft-float comparison. Equivalent to c.eq.d. + |// Input: SFARG*. Output: CRET1. Temporaries: AT, TMP0, TMP1. + |->vm_sfcmpeq: + |.if not FPU + | sll AT, SFARG1HI, 1 + | sll TMP0, SFARG2HI, 1 + | or CRET1, SFARG1LO, SFARG2LO + | or TMP1, AT, TMP0 + | or TMP1, TMP1, CRET1 + | beqz TMP1, >8 // Both args +-0: return 1. + |. sltu CRET1, r0, SFARG1LO + | lui TMP1, 0xffe0 + | addu AT, AT, CRET1 + | sltu CRET1, r0, SFARG2LO + | sltu AT, TMP1, AT + | addu TMP0, TMP0, CRET1 + | sltu TMP0, TMP1, TMP0 + | or TMP1, AT, TMP0 + | bnez TMP1, >9 // Either arg is NaN: return 0; + |. xor TMP0, SFARG1HI, SFARG2HI + | xor TMP1, SFARG1LO, SFARG2LO + | or AT, TMP0, TMP1 + | jr ra + |. sltiu CRET1, AT, 1 // Same values: return 1. + |8: + | jr ra + |. li CRET1, 1 + |9: + | jr ra + |. li CRET1, 0 + |.endif + | + |// Soft-float comparison. Equivalent to c.ult.d and c.olt.d. + |// Input: SFARG*. Output: CRET1. Temporaries: AT, TMP0, TMP1, CRET2. + |->vm_sfcmpult: + |.if not FPU + | b >1 + |. li CRET2, 1 + |.endif + | + |->vm_sfcmpolt: + |.if not FPU + | li CRET2, 0 + |1: + | sll AT, SFARG1HI, 1 + | sll TMP0, SFARG2HI, 1 + | or CRET1, SFARG1LO, SFARG2LO + | or TMP1, AT, TMP0 + | or TMP1, TMP1, CRET1 + | beqz TMP1, >8 // Both args +-0: return 0. + |. sltu CRET1, r0, SFARG1LO + | lui TMP1, 0xffe0 + | addu AT, AT, CRET1 + | sltu CRET1, r0, SFARG2LO + | sltu AT, TMP1, AT + | addu TMP0, TMP0, CRET1 + | sltu TMP0, TMP1, TMP0 + | or TMP1, AT, TMP0 + | bnez TMP1, >9 // Either arg is NaN: return 0 or 1; + |. and AT, SFARG1HI, SFARG2HI + | bltz AT, >5 // Both args negative? + |. nop + | beq SFARG1HI, SFARG2HI, >8 + |. sltu CRET1, SFARG1LO, SFARG2LO + | jr ra + |. slt CRET1, SFARG1HI, SFARG2HI + |5: // Swap conditions if both operands are negative. + | beq SFARG1HI, SFARG2HI, >8 + |. sltu CRET1, SFARG2LO, SFARG1LO + | jr ra + |. slt CRET1, SFARG2HI, SFARG1HI + |8: + | jr ra + |. nop + |9: + | jr ra + |. move CRET1, CRET2 + |.endif + | + |// Soft-float comparison. Equivalent to c.ole.d a, b or c.ole.d b, a. + |// Input: SFARG*, TMP3. Output: CRET1. Temporaries: AT, TMP0, TMP1. + |->vm_sfcmpolex: + |.if not FPU + | sll AT, SFARG1HI, 1 + | sll TMP0, SFARG2HI, 1 + | or CRET1, SFARG1LO, SFARG2LO + | or TMP1, AT, TMP0 + | or TMP1, TMP1, CRET1 + | beqz TMP1, >8 // Both args +-0: return 1. + |. sltu CRET1, r0, SFARG1LO + | lui TMP1, 0xffe0 + | addu AT, AT, CRET1 + | sltu CRET1, r0, SFARG2LO + | sltu AT, TMP1, AT + | addu TMP0, TMP0, CRET1 + | sltu TMP0, TMP1, TMP0 + | or TMP1, AT, TMP0 + | bnez TMP1, >9 // Either arg is NaN: return 0; + |. and AT, SFARG1HI, SFARG2HI + | xor AT, AT, TMP3 + | bltz AT, >5 // Both args negative? + |. nop + | beq SFARG1HI, SFARG2HI, >6 + |. sltu CRET1, SFARG2LO, SFARG1LO + | jr ra + |. slt CRET1, SFARG2HI, SFARG1HI + |5: // Swap conditions if both operands are negative. + | beq SFARG1HI, SFARG2HI, >6 + |. sltu CRET1, SFARG1LO, SFARG2LO + | slt CRET1, SFARG1HI, SFARG2HI + |6: + | jr ra + |. nop + |8: + | jr ra + |. li CRET1, 1 + |9: + | jr ra + |. li CRET1, 0 + |.endif + | + |.macro sfmin_max, name, intins + |->vm_sf .. name: + |.if JIT and not FPU + | move TMP2, ra + | bal ->vm_sfcmpolt + |. nop + | move TMP0, CRET1 + | move SFRETHI, SFARG1HI + | move SFRETLO, SFARG1LO + | move ra, TMP2 + | intins SFRETHI, SFARG2HI, TMP0 + | jr ra + |. intins SFRETLO, SFARG2LO, TMP0 + |.endif + |.endmacro + | + | sfmin_max min, movz + | sfmin_max max, movn + | + |//----------------------------------------------------------------------- + |//-- Miscellaneous functions -------------------------------------------- + |//----------------------------------------------------------------------- + | + |//----------------------------------------------------------------------- + |//-- FFI helper functions ----------------------------------------------- + |//----------------------------------------------------------------------- + | + |// Handler for callback functions. Callback slot number in r1, g in r2. + |->vm_ffi_callback: + |.if FFI + |.type CTSTATE, CTState, PC + | saveregs + | lw CTSTATE, GL:r2->ctype_state + | addiu DISPATCH, r2, GG_G2DISP + | load_got lj_ccallback_enter + | sw r1, CTSTATE->cb.slot + | sw CARG1, CTSTATE->cb.gpr[0] + | sw CARG2, CTSTATE->cb.gpr[1] + | .FPU sdc1 FARG1, CTSTATE->cb.fpr[0] + | sw CARG3, CTSTATE->cb.gpr[2] + | sw CARG4, CTSTATE->cb.gpr[3] + | .FPU sdc1 FARG2, CTSTATE->cb.fpr[1] + | addiu TMP0, sp, CFRAME_SPACE+16 + | sw TMP0, CTSTATE->cb.stack + | sw r0, SAVE_PC // Any value outside of bytecode is ok. + | move CARG2, sp + | call_intern lj_ccallback_enter // (CTState *cts, void *cf) + |. move CARG1, CTSTATE + | // Returns lua_State *. + | lw BASE, L:CRET1->base + | lw RC, L:CRET1->top + | li TISNUM, LJ_TISNUM // Setup type comparison constants. + | move L, CRET1 + | .FPU lui TMP3, 0x59c0 // TOBIT = 2^52 + 2^51 (float). + | lw LFUNC:RB, FRAME_FUNC(BASE) + | .FPU mtc1 TMP3, TOBIT + | li_vmstate INTERP + | li TISNIL, LJ_TNIL + | subu RC, RC, BASE + | st_vmstate + | .FPU cvt.d.s TOBIT, TOBIT + | ins_callt + |.endif + | + |->cont_ffi_callback: // Return from FFI callback. + |.if FFI + | load_got lj_ccallback_leave + | lw CTSTATE, DISPATCH_GL(ctype_state)(DISPATCH) + | sw BASE, L->base + | sw RB, L->top + | sw L, CTSTATE->L + | move CARG2, RA + | call_intern lj_ccallback_leave // (CTState *cts, TValue *o) + |. move CARG1, CTSTATE + | .FPU ldc1 FRET1, CTSTATE->cb.fpr[0] + | lw CRET1, CTSTATE->cb.gpr[0] + | .FPU ldc1 FRET2, CTSTATE->cb.fpr[1] + | b ->vm_leave_unw + |. lw CRET2, CTSTATE->cb.gpr[1] + |.endif + | + |->vm_ffi_call: // Call C function via FFI. + | // Caveat: needs special frame unwinding, see below. + |.if FFI + | .type CCSTATE, CCallState, CARG1 + | lw TMP1, CCSTATE->spadj + | lbu CARG2, CCSTATE->nsp + | move TMP2, sp + | subu sp, sp, TMP1 + | sw ra, -4(TMP2) + | sll CARG2, CARG2, 2 + | sw r16, -8(TMP2) + | sw CCSTATE, -12(TMP2) + | move r16, TMP2 + | addiu TMP1, CCSTATE, offsetof(CCallState, stack) + | addiu TMP2, sp, 16 + | beqz CARG2, >2 + |. addu TMP3, TMP1, CARG2 + |1: + | lw TMP0, 0(TMP1) + | addiu TMP1, TMP1, 4 + | sltu AT, TMP1, TMP3 + | sw TMP0, 0(TMP2) + | bnez AT, <1 + |. addiu TMP2, TMP2, 4 + |2: + | lw CFUNCADDR, CCSTATE->func + | lw CARG2, CCSTATE->gpr[1] + | lw CARG3, CCSTATE->gpr[2] + | lw CARG4, CCSTATE->gpr[3] + | .FPU ldc1 FARG1, CCSTATE->fpr[0] + | .FPU ldc1 FARG2, CCSTATE->fpr[1] + | jalr CFUNCADDR + |. lw CARG1, CCSTATE->gpr[0] // Do this last, since CCSTATE is CARG1. + | lw CCSTATE:TMP1, -12(r16) + | lw TMP2, -8(r16) + | lw ra, -4(r16) + | sw CRET1, CCSTATE:TMP1->gpr[0] + | sw CRET2, CCSTATE:TMP1->gpr[1] + |.if FPU + | sdc1 FRET1, CCSTATE:TMP1->fpr[0] + | sdc1 FRET2, CCSTATE:TMP1->fpr[1] + |.else + | sw CARG1, CCSTATE:TMP1->gpr[2] // Soft-float: complex double .im part. + | sw CARG2, CCSTATE:TMP1->gpr[3] + |.endif + | move sp, r16 + | jr ra + |. move r16, TMP2 + |.endif + |// Note: vm_ffi_call must be the last function in this object file! + | + |//----------------------------------------------------------------------- +} + +/* Generate the code for a single instruction. */ +static void build_ins(BuildCtx *ctx, BCOp op, int defop) +{ + int vk = 0; + |=>defop: + + switch (op) { + + /* -- Comparison ops ---------------------------------------------------- */ + + /* Remember: all ops branch for a true comparison, fall through otherwise. */ + + case BC_ISLT: case BC_ISGE: case BC_ISLE: case BC_ISGT: + | // RA = src1*8, RD = src2*8, JMP with RD = target + |.macro bc_comp, FRA, FRD, RAHI, RALO, RDHI, RDLO, movop, fmovop, fcomp, sfcomp + | addu RA, BASE, RA + | addu RD, BASE, RD + | lw RAHI, HI(RA) + | lw RDHI, HI(RD) + | lhu TMP2, OFS_RD(PC) + | addiu PC, PC, 4 + | bne RAHI, TISNUM, >2 + |. lw RALO, LO(RA) + | lui TMP3, (-(BCBIAS_J*4 >> 16) & 65535) + | lw RDLO, LO(RD) + | bne RDHI, TISNUM, >5 + |. decode_RD4b TMP2 + | slt AT, SFARG1LO, SFARG2LO + | addu TMP2, TMP2, TMP3 + | movop TMP2, r0, AT + |1: + | addu PC, PC, TMP2 + | ins_next + | + |2: // RA is not an integer. + | sltiu AT, RAHI, LJ_TISNUM + | beqz AT, ->vmeta_comp + |. lui TMP3, (-(BCBIAS_J*4 >> 16) & 65535) + | sltiu AT, RDHI, LJ_TISNUM + |.if FPU + | ldc1 FRA, 0(RA) + | ldc1 FRD, 0(RD) + |.else + | lw RDLO, LO(RD) + |.endif + | beqz AT, >4 + |. decode_RD4b TMP2 + |3: // RA and RD are both numbers. + |.if FPU + | fcomp f20, f22 + | addu TMP2, TMP2, TMP3 + | b <1 + |. fmovop TMP2, r0 + |.else + | bal sfcomp + |. addu TMP2, TMP2, TMP3 + | b <1 + |. movop TMP2, r0, CRET1 + |.endif + | + |4: // RA is a number, RD is not a number. + | bne RDHI, TISNUM, ->vmeta_comp + | // RA is a number, RD is an integer. Convert RD to a number. + |.if FPU + |. lwc1 FRD, LO(RD) + | b <3 + |. cvt.d.w FRD, FRD + |.else + |. nop + |.if "RDHI" == "SFARG1HI" + | bal ->vm_sfi2d_1 + |.else + | bal ->vm_sfi2d_2 + |.endif + |. nop + | b <3 + |. nop + |.endif + | + |5: // RA is an integer, RD is not an integer + | sltiu AT, RDHI, LJ_TISNUM + | beqz AT, ->vmeta_comp + | // RA is an integer, RD is a number. Convert RA to a number. + |.if FPU + |. mtc1 RALO, FRA + | ldc1 FRD, 0(RD) + | b <3 + | cvt.d.w FRA, FRA + |.else + |. nop + |.if "RAHI" == "SFARG1HI" + | bal ->vm_sfi2d_1 + |.else + | bal ->vm_sfi2d_2 + |.endif + |. nop + | b <3 + |. nop + |.endif + |.endmacro + | + if (op == BC_ISLT) { + | bc_comp f20, f22, SFARG1HI, SFARG1LO, SFARG2HI, SFARG2LO, movz, movf, c.olt.d, ->vm_sfcmpolt + } else if (op == BC_ISGE) { + | bc_comp f20, f22, SFARG1HI, SFARG1LO, SFARG2HI, SFARG2LO, movn, movt, c.olt.d, ->vm_sfcmpolt + } else if (op == BC_ISLE) { + | bc_comp f22, f20, SFARG2HI, SFARG2LO, SFARG1HI, SFARG1LO, movn, movt, c.ult.d, ->vm_sfcmpult + } else { + | bc_comp f22, f20, SFARG2HI, SFARG2LO, SFARG1HI, SFARG1LO, movz, movf, c.ult.d, ->vm_sfcmpult + } + break; + + case BC_ISEQV: case BC_ISNEV: + vk = op == BC_ISEQV; + | // RA = src1*8, RD = src2*8, JMP with RD = target + | addu RA, BASE, RA + | addiu PC, PC, 4 + | addu RD, BASE, RD + | lw SFARG1HI, HI(RA) + | lhu TMP2, -4+OFS_RD(PC) + | lw SFARG2HI, HI(RD) + | lui TMP3, (-(BCBIAS_J*4 >> 16) & 65535) + | sltu AT, TISNUM, SFARG1HI + | sltu TMP0, TISNUM, SFARG2HI + | or AT, AT, TMP0 + if (vk) { + | beqz AT, ->BC_ISEQN_Z + } else { + | beqz AT, ->BC_ISNEN_Z + } + |. decode_RD4b TMP2 + | // Either or both types are not numbers. + | lw SFARG1LO, LO(RA) + | lw SFARG2LO, LO(RD) + | addu TMP2, TMP2, TMP3 + |.if FFI + | li TMP3, LJ_TCDATA + | beq SFARG1HI, TMP3, ->vmeta_equal_cd + |.endif + |. sltiu AT, SFARG1HI, LJ_TISPRI // Not a primitive? + |.if FFI + | beq SFARG2HI, TMP3, ->vmeta_equal_cd + |.endif + |. xor TMP3, SFARG1LO, SFARG2LO // Same tv? + | xor SFARG2HI, SFARG2HI, SFARG1HI // Same type? + | sltiu TMP0, SFARG1HI, LJ_TISTABUD+1 // Table or userdata? + | movz TMP3, r0, AT // Ignore tv if primitive. + | movn TMP0, r0, SFARG2HI // Tab/ud and same type? + | or AT, SFARG2HI, TMP3 // Same type && (pri||same tv). + | movz TMP0, r0, AT + | beqz TMP0, >1 // Done if not tab/ud or not same type or same tv. + if (vk) { + |. movn TMP2, r0, AT + } else { + |. movz TMP2, r0, AT + } + | // Different tables or userdatas. Need to check __eq metamethod. + | // Field metatable must be at same offset for GCtab and GCudata! + | lw TAB:TMP1, TAB:SFARG1LO->metatable + | beqz TAB:TMP1, >1 // No metatable? + |. nop + | lbu TMP1, TAB:TMP1->nomm + | andi TMP1, TMP1, 1<1 // Or 'no __eq' flag set? + |. nop + | b ->vmeta_equal // Handle __eq metamethod. + |. li TMP0, 1-vk // ne = 0 or 1. + |1: + | addu PC, PC, TMP2 + | ins_next + break; + + case BC_ISEQS: case BC_ISNES: + vk = op == BC_ISEQS; + | // RA = src*8, RD = str_const*8 (~), JMP with RD = target + | addu RA, BASE, RA + | addiu PC, PC, 4 + | lw TMP0, HI(RA) + | srl RD, RD, 1 + | lw STR:TMP3, LO(RA) + | subu RD, KBASE, RD + | lhu TMP2, -4+OFS_RD(PC) + |.if FFI + | li AT, LJ_TCDATA + | beq TMP0, AT, ->vmeta_equal_cd + |.endif + |. lw STR:TMP1, -4(RD) // KBASE-4-str_const*4 + | addiu TMP0, TMP0, -LJ_TSTR + | decode_RD4b TMP2 + | xor TMP1, STR:TMP1, STR:TMP3 + | or TMP0, TMP0, TMP1 + | lui TMP3, (-(BCBIAS_J*4 >> 16) & 65535) + | addu TMP2, TMP2, TMP3 + if (vk) { + | movn TMP2, r0, TMP0 + } else { + | movz TMP2, r0, TMP0 + } + | addu PC, PC, TMP2 + | ins_next + break; + + case BC_ISEQN: case BC_ISNEN: + vk = op == BC_ISEQN; + | // RA = src*8, RD = num_const*8, JMP with RD = target + | addu RA, BASE, RA + | addu RD, KBASE, RD + | lw SFARG1HI, HI(RA) + | lw SFARG2HI, HI(RD) + | lhu TMP2, OFS_RD(PC) + | addiu PC, PC, 4 + | lui TMP3, (-(BCBIAS_J*4 >> 16) & 65535) + | decode_RD4b TMP2 + if (vk) { + |->BC_ISEQN_Z: + } else { + |->BC_ISNEN_Z: + } + | bne SFARG1HI, TISNUM, >3 + |. lw SFARG1LO, LO(RA) + | lw SFARG2LO, LO(RD) + | addu TMP2, TMP2, TMP3 + | bne SFARG2HI, TISNUM, >6 + |. xor AT, SFARG1LO, SFARG2LO + if (vk) { + | movn TMP2, r0, AT + |1: + | addu PC, PC, TMP2 + |2: + } else { + | movz TMP2, r0, AT + |1: + |2: + | addu PC, PC, TMP2 + } + | ins_next + | + |3: // RA is not an integer. + | sltiu AT, SFARG1HI, LJ_TISNUM + |.if FFI + | beqz AT, >8 + |.else + | beqz AT, <2 + |.endif + |. addu TMP2, TMP2, TMP3 + | sltiu AT, SFARG2HI, LJ_TISNUM + |.if FPU + | ldc1 f20, 0(RA) + | ldc1 f22, 0(RD) + |.endif + | beqz AT, >5 + |. lw SFARG2LO, LO(RD) + |4: // RA and RD are both numbers. + |.if FPU + | c.eq.d f20, f22 + | b <1 + if (vk) { + |. movf TMP2, r0 + } else { + |. movt TMP2, r0 + } + |.else + | bal ->vm_sfcmpeq + |. nop + | b <1 + if (vk) { + |. movz TMP2, r0, CRET1 + } else { + |. movn TMP2, r0, CRET1 + } + |.endif + | + |5: // RA is a number, RD is not a number. + |.if FFI + | bne SFARG2HI, TISNUM, >9 + |.else + | bne SFARG2HI, TISNUM, <2 + |.endif + | // RA is a number, RD is an integer. Convert RD to a number. + |.if FPU + |. lwc1 f22, LO(RD) + | b <4 + |. cvt.d.w f22, f22 + |.else + |. nop + | bal ->vm_sfi2d_2 + |. nop + | b <4 + |. nop + |.endif + | + |6: // RA is an integer, RD is not an integer + | sltiu AT, SFARG2HI, LJ_TISNUM + |.if FFI + | beqz AT, >9 + |.else + | beqz AT, <2 + |.endif + | // RA is an integer, RD is a number. Convert RA to a number. + |.if FPU + |. mtc1 SFARG1LO, f20 + | ldc1 f22, 0(RD) + | b <4 + | cvt.d.w f20, f20 + |.else + |. nop + | bal ->vm_sfi2d_1 + |. nop + | b <4 + |. nop + |.endif + | + |.if FFI + |8: + | li AT, LJ_TCDATA + | bne SFARG1HI, AT, <2 + |. nop + | b ->vmeta_equal_cd + |. nop + |9: + | li AT, LJ_TCDATA + | bne SFARG2HI, AT, <2 + |. nop + | b ->vmeta_equal_cd + |. nop + |.endif + break; + + case BC_ISEQP: case BC_ISNEP: + vk = op == BC_ISEQP; + | // RA = src*8, RD = primitive_type*8 (~), JMP with RD = target + | addu RA, BASE, RA + | srl TMP1, RD, 3 + | lw TMP0, HI(RA) + | lhu TMP2, OFS_RD(PC) + | not TMP1, TMP1 + | addiu PC, PC, 4 + |.if FFI + | li AT, LJ_TCDATA + | beq TMP0, AT, ->vmeta_equal_cd + |.endif + |. xor TMP0, TMP0, TMP1 + | decode_RD4b TMP2 + | lui TMP3, (-(BCBIAS_J*4 >> 16) & 65535) + | addu TMP2, TMP2, TMP3 + if (vk) { + | movn TMP2, r0, TMP0 + } else { + | movz TMP2, r0, TMP0 + } + | addu PC, PC, TMP2 + | ins_next + break; + + /* -- Unary test and copy ops ------------------------------------------- */ + + case BC_ISTC: case BC_ISFC: case BC_IST: case BC_ISF: + | // RA = dst*8 or unused, RD = src*8, JMP with RD = target + | addu RD, BASE, RD + | lhu TMP2, OFS_RD(PC) + | lw TMP0, HI(RD) + | addiu PC, PC, 4 + if (op == BC_IST || op == BC_ISF) { + | sltiu TMP0, TMP0, LJ_TISTRUECOND + | decode_RD4b TMP2 + | lui TMP3, (-(BCBIAS_J*4 >> 16) & 65535) + | addu TMP2, TMP2, TMP3 + if (op == BC_IST) { + | movz TMP2, r0, TMP0 + } else { + | movn TMP2, r0, TMP0 + } + | addu PC, PC, TMP2 + } else { + | sltiu TMP0, TMP0, LJ_TISTRUECOND + | lw SFRETHI, HI(RD) + | lw SFRETLO, LO(RD) + if (op == BC_ISTC) { + | beqz TMP0, >1 + } else { + | bnez TMP0, >1 + } + |. addu RA, BASE, RA + | decode_RD4b TMP2 + | lui TMP3, (-(BCBIAS_J*4 >> 16) & 65535) + | addu TMP2, TMP2, TMP3 + | sw SFRETHI, HI(RA) + | sw SFRETLO, LO(RA) + | addu PC, PC, TMP2 + |1: + } + | ins_next + break; + + case BC_ISTYPE: + | // RA = src*8, RD = -type*8 + | addu TMP2, BASE, RA + | srl TMP1, RD, 3 + | lw TMP0, HI(TMP2) + | ins_next1 + | addu AT, TMP0, TMP1 + | bnez AT, ->vmeta_istype + |. ins_next2 + break; + case BC_ISNUM: + | // RA = src*8, RD = -(TISNUM-1)*8 + | addu TMP2, BASE, RA + | lw TMP0, HI(TMP2) + | ins_next1 + | sltiu AT, TMP0, LJ_TISNUM + | beqz AT, ->vmeta_istype + |. ins_next2 + break; + + /* -- Unary ops --------------------------------------------------------- */ + + case BC_MOV: + | // RA = dst*8, RD = src*8 + | addu RD, BASE, RD + | addu RA, BASE, RA + | lw SFRETHI, HI(RD) + | lw SFRETLO, LO(RD) + | ins_next1 + | sw SFRETHI, HI(RA) + | sw SFRETLO, LO(RA) + | ins_next2 + break; + case BC_NOT: + | // RA = dst*8, RD = src*8 + | addu RD, BASE, RD + | addu RA, BASE, RA + | lw TMP0, HI(RD) + | li TMP1, LJ_TFALSE + | sltiu TMP0, TMP0, LJ_TISTRUECOND + | addiu TMP1, TMP0, LJ_TTRUE + | ins_next1 + | sw TMP1, HI(RA) + | ins_next2 + break; + case BC_UNM: + | // RA = dst*8, RD = src*8 + | addu RB, BASE, RD + | lw SFARG1HI, HI(RB) + | addu RA, BASE, RA + | bne SFARG1HI, TISNUM, >2 + |. lw SFARG1LO, LO(RB) + | lui TMP1, 0x8000 + | beq SFARG1LO, TMP1, ->vmeta_unm // Meta handler deals with -2^31. + |. negu SFARG1LO, SFARG1LO + |1: + | ins_next1 + | sw SFARG1HI, HI(RA) + | sw SFARG1LO, LO(RA) + | ins_next2 + |2: + | sltiu AT, SFARG1HI, LJ_TISNUM + | beqz AT, ->vmeta_unm + |. lui TMP1, 0x8000 + | b <1 + |. xor SFARG1HI, SFARG1HI, TMP1 + break; + case BC_LEN: + | // RA = dst*8, RD = src*8 + | addu CARG2, BASE, RD + | addu RA, BASE, RA + | lw TMP0, HI(CARG2) + | lw CARG1, LO(CARG2) + | li AT, LJ_TSTR + | bne TMP0, AT, >2 + |. li AT, LJ_TTAB + | lw CRET1, STR:CARG1->len + |1: + | ins_next1 + | sw TISNUM, HI(RA) + | sw CRET1, LO(RA) + | ins_next2 + |2: + | bne TMP0, AT, ->vmeta_len + |. nop +#if LJ_52 + | lw TAB:TMP2, TAB:CARG1->metatable + | bnez TAB:TMP2, >9 + |. nop + |3: +#endif + |->BC_LEN_Z: + | load_got lj_tab_len + | call_intern lj_tab_len // (GCtab *t) + |. nop + | // Returns uint32_t (but less than 2^31). + | b <1 + |. nop +#if LJ_52 + |9: + | lbu TMP0, TAB:TMP2->nomm + | andi TMP0, TMP0, 1<vmeta_len + |. nop +#endif + break; + + /* -- Binary ops -------------------------------------------------------- */ + + |.macro fpmod, a, b, c + | bal ->vm_floor // floor(b/c) + |. div.d FARG1, b, c + | mul.d a, FRET1, c + | sub.d a, b, a // b - floor(b/c)*c + |.endmacro + + |.macro sfpmod + | addiu sp, sp, -16 + | + | load_got __divdf3 + | sw SFARG1HI, HI(sp) + | sw SFARG1LO, LO(sp) + | sw SFARG2HI, 8+HI(sp) + | call_extern + |. sw SFARG2LO, 8+LO(sp) + | + | load_got floor + | move SFARG1HI, SFRETHI + | call_extern + |. move SFARG1LO, SFRETLO + | + | load_got __muldf3 + | move SFARG1HI, SFRETHI + | move SFARG1LO, SFRETLO + | lw SFARG2HI, 8+HI(sp) + | call_extern + |. lw SFARG2LO, 8+LO(sp) + | + | load_got __subdf3 + | lw SFARG1HI, HI(sp) + | lw SFARG1LO, LO(sp) + | move SFARG2HI, SFRETHI + | call_extern + |. move SFARG2LO, SFRETLO + | + | addiu sp, sp, 16 + |.endmacro + + |.macro ins_arithpre, label + ||vk = ((int)op - BC_ADDVN) / (BC_ADDNV-BC_ADDVN); + | // RA = dst*8, RB = src1*8, RC = src2*8 | num_const*8 + ||switch (vk) { + ||case 0: + | decode_RB8a RB, INS + | decode_RB8b RB + | decode_RDtoRC8 RC, RD + | // RA = dst*8, RB = src1*8, RC = num_const*8 + | addu RB, BASE, RB + |.if "label" ~= "none" + | b label + |.endif + |. addu RC, KBASE, RC + || break; + ||case 1: + | decode_RB8a RC, INS + | decode_RB8b RC + | decode_RDtoRC8 RB, RD + | // RA = dst*8, RB = num_const*8, RC = src1*8 + | addu RC, BASE, RC + |.if "label" ~= "none" + | b label + |.endif + |. addu RB, KBASE, RB + || break; + ||default: + | decode_RB8a RB, INS + | decode_RB8b RB + | decode_RDtoRC8 RC, RD + | // RA = dst*8, RB = src1*8, RC = src2*8 + | addu RB, BASE, RB + |.if "label" ~= "none" + | b label + |.endif + |. addu RC, BASE, RC + || break; + ||} + |.endmacro + | + |.macro ins_arith, intins, fpins, fpcall, label + | ins_arithpre none + | + |.if "label" ~= "none" + |label: + |.endif + | + | lw SFARG1HI, HI(RB) + | lw SFARG2HI, HI(RC) + | + |.if "intins" ~= "div" + | + | // Check for two integers. + | lw SFARG1LO, LO(RB) + | bne SFARG1HI, TISNUM, >5 + |. lw SFARG2LO, LO(RC) + | bne SFARG2HI, TISNUM, >5 + | + |.if "intins" == "addu" + |. intins CRET1, SFARG1LO, SFARG2LO + | xor TMP1, CRET1, SFARG1LO // ((y^a) & (y^b)) < 0: overflow. + | xor TMP2, CRET1, SFARG2LO + | and TMP1, TMP1, TMP2 + | bltz TMP1, ->vmeta_arith + |. addu RA, BASE, RA + |.elif "intins" == "subu" + |. intins CRET1, SFARG1LO, SFARG2LO + | xor TMP1, CRET1, SFARG1LO // ((y^a) & (a^b)) < 0: overflow. + | xor TMP2, SFARG1LO, SFARG2LO + | and TMP1, TMP1, TMP2 + | bltz TMP1, ->vmeta_arith + |. addu RA, BASE, RA + |.elif "intins" == "mult" + |. intins SFARG1LO, SFARG2LO + | mflo CRET1 + | mfhi TMP2 + | sra TMP1, CRET1, 31 + | bne TMP1, TMP2, ->vmeta_arith + |. addu RA, BASE, RA + |.else + |. load_got lj_vm_modi + | beqz SFARG2LO, ->vmeta_arith + |. addu RA, BASE, RA + |.if ENDIAN_BE + | move CARG1, SFARG1LO + |.endif + | call_extern + |. move CARG2, SFARG2LO + |.endif + | + | ins_next1 + | sw TISNUM, HI(RA) + | sw CRET1, LO(RA) + |3: + | ins_next2 + | + |.elif not FPU + | + | lw SFARG1LO, LO(RB) + | lw SFARG2LO, LO(RC) + | + |.endif + | + |5: // Check for two numbers. + | .FPU ldc1 f20, 0(RB) + | sltiu AT, SFARG1HI, LJ_TISNUM + | sltiu TMP0, SFARG2HI, LJ_TISNUM + | .FPU ldc1 f22, 0(RC) + | and AT, AT, TMP0 + | beqz AT, ->vmeta_arith + |. addu RA, BASE, RA + | + |.if FPU + | fpins FRET1, f20, f22 + |.elif "fpcall" == "sfpmod" + | sfpmod + |.else + | load_got fpcall + | call_extern + |. nop + |.endif + | + | ins_next1 + |.if not FPU + | sw SFRETHI, HI(RA) + |.endif + |.if "intins" ~= "div" + | b <3 + |.endif + |.if FPU + |. sdc1 FRET1, 0(RA) + |.else + |. sw SFRETLO, LO(RA) + |.endif + |.if "intins" == "div" + | ins_next2 + |.endif + | + |.endmacro + + case BC_ADDVN: case BC_ADDNV: case BC_ADDVV: + | ins_arith addu, add.d, __adddf3, none + break; + case BC_SUBVN: case BC_SUBNV: case BC_SUBVV: + | ins_arith subu, sub.d, __subdf3, none + break; + case BC_MULVN: case BC_MULNV: case BC_MULVV: + | ins_arith mult, mul.d, __muldf3, none + break; + case BC_DIVVN: + | ins_arith div, div.d, __divdf3, ->BC_DIVVN_Z + break; + case BC_DIVNV: case BC_DIVVV: + | ins_arithpre ->BC_DIVVN_Z + break; + case BC_MODVN: + | ins_arith modi, fpmod, sfpmod, ->BC_MODVN_Z + break; + case BC_MODNV: case BC_MODVV: + | ins_arithpre ->BC_MODVN_Z + break; + case BC_POW: + | ins_arithpre none + | lw SFARG1HI, HI(RB) + | lw SFARG2HI, HI(RC) + | sltiu AT, SFARG1HI, LJ_TISNUM + | sltiu TMP0, SFARG2HI, LJ_TISNUM + | and AT, AT, TMP0 + | load_got pow + | beqz AT, ->vmeta_arith + |. addu RA, BASE, RA + |.if FPU + | ldc1 FARG1, 0(RB) + | ldc1 FARG2, 0(RC) + |.else + | lw SFARG1LO, LO(RB) + | lw SFARG2LO, LO(RC) + |.endif + | call_extern + |. nop + | ins_next1 + |.if FPU + | sdc1 FRET1, 0(RA) + |.else + | sw SFRETHI, HI(RA) + | sw SFRETLO, LO(RA) + |.endif + | ins_next2 + break; + + case BC_CAT: + | // RA = dst*8, RB = src_start*8, RC = src_end*8 + | decode_RB8a RB, INS + | decode_RB8b RB + | decode_RDtoRC8 RC, RD + | subu CARG3, RC, RB + | sw BASE, L->base + | addu CARG2, BASE, RC + | move MULTRES, RB + |->BC_CAT_Z: + | load_got lj_meta_cat + | srl CARG3, CARG3, 3 + | sw PC, SAVE_PC + | call_intern lj_meta_cat // (lua_State *L, TValue *top, int left) + |. move CARG1, L + | // Returns NULL (finished) or TValue * (metamethod). + | bnez CRET1, ->vmeta_binop + |. lw BASE, L->base + | addu RB, BASE, MULTRES + | lw SFRETHI, HI(RB) + | lw SFRETLO, LO(RB) + | addu RA, BASE, RA + | ins_next1 + | sw SFRETHI, HI(RA) + | sw SFRETLO, LO(RA) + | ins_next2 + break; + + /* -- Constant ops ------------------------------------------------------ */ + + case BC_KSTR: + | // RA = dst*8, RD = str_const*8 (~) + | srl TMP1, RD, 1 + | subu TMP1, KBASE, TMP1 + | ins_next1 + | lw TMP0, -4(TMP1) // KBASE-4-str_const*4 + | addu RA, BASE, RA + | li TMP2, LJ_TSTR + | sw TMP0, LO(RA) + | sw TMP2, HI(RA) + | ins_next2 + break; + case BC_KCDATA: + |.if FFI + | // RA = dst*8, RD = cdata_const*8 (~) + | srl TMP1, RD, 1 + | subu TMP1, KBASE, TMP1 + | ins_next1 + | lw TMP0, -4(TMP1) // KBASE-4-cdata_const*4 + | addu RA, BASE, RA + | li TMP2, LJ_TCDATA + | sw TMP0, LO(RA) + | sw TMP2, HI(RA) + | ins_next2 + |.endif + break; + case BC_KSHORT: + | // RA = dst*8, RD = int16_literal*8 + | sra RD, INS, 16 + | addu RA, BASE, RA + | ins_next1 + | sw TISNUM, HI(RA) + | sw RD, LO(RA) + | ins_next2 + break; + case BC_KNUM: + | // RA = dst*8, RD = num_const*8 + | addu RD, KBASE, RD + | addu RA, BASE, RA + | lw SFRETHI, HI(RD) + | lw SFRETLO, LO(RD) + | ins_next1 + | sw SFRETHI, HI(RA) + | sw SFRETLO, LO(RA) + | ins_next2 + break; + case BC_KPRI: + | // RA = dst*8, RD = primitive_type*8 (~) + | srl TMP1, RD, 3 + | addu RA, BASE, RA + | not TMP0, TMP1 + | ins_next1 + | sw TMP0, HI(RA) + | ins_next2 + break; + case BC_KNIL: + | // RA = base*8, RD = end*8 + | addu RA, BASE, RA + | sw TISNIL, HI(RA) + | addiu RA, RA, 8 + | addu RD, BASE, RD + |1: + | sw TISNIL, HI(RA) + | slt AT, RA, RD + | bnez AT, <1 + |. addiu RA, RA, 8 + | ins_next_ + break; + + /* -- Upvalue and function ops ------------------------------------------ */ + + case BC_UGET: + | // RA = dst*8, RD = uvnum*8 + | lw LFUNC:RB, FRAME_FUNC(BASE) + | srl RD, RD, 1 + | addu RD, RD, LFUNC:RB + | lw UPVAL:RB, LFUNC:RD->uvptr + | ins_next1 + | lw TMP1, UPVAL:RB->v + | lw SFRETHI, HI(TMP1) + | lw SFRETLO, LO(TMP1) + | addu RA, BASE, RA + | sw SFRETHI, HI(RA) + | sw SFRETLO, LO(RA) + | ins_next2 + break; + case BC_USETV: + | // RA = uvnum*8, RD = src*8 + | lw LFUNC:RB, FRAME_FUNC(BASE) + | srl RA, RA, 1 + | addu RD, BASE, RD + | addu RA, RA, LFUNC:RB + | lw UPVAL:RB, LFUNC:RA->uvptr + | lw SFRETHI, HI(RD) + | lw SFRETLO, LO(RD) + | lbu TMP3, UPVAL:RB->marked + | lw CARG2, UPVAL:RB->v + | andi TMP3, TMP3, LJ_GC_BLACK // isblack(uv) + | lbu TMP0, UPVAL:RB->closed + | sw SFRETHI, HI(CARG2) + | sw SFRETLO, LO(CARG2) + | li AT, LJ_GC_BLACK|1 + | or TMP3, TMP3, TMP0 + | beq TMP3, AT, >2 // Upvalue is closed and black? + |. addiu TMP2, SFRETHI, -(LJ_TNUMX+1) + |1: + | ins_next + | + |2: // Check if new value is collectable. + | sltiu AT, TMP2, LJ_TISGCV - (LJ_TNUMX+1) + | beqz AT, <1 // tvisgcv(v) + |. nop + | lbu TMP3, GCOBJ:SFRETLO->gch.marked + | andi TMP3, TMP3, LJ_GC_WHITES // iswhite(v) + | beqz TMP3, <1 + |. load_got lj_gc_barrieruv + | // Crossed a write barrier. Move the barrier forward. + | call_intern lj_gc_barrieruv // (global_State *g, TValue *tv) + |. addiu CARG1, DISPATCH, GG_DISP2G + | b <1 + |. nop + break; + case BC_USETS: + | // RA = uvnum*8, RD = str_const*8 (~) + | lw LFUNC:RB, FRAME_FUNC(BASE) + | srl RA, RA, 1 + | srl TMP1, RD, 1 + | addu RA, RA, LFUNC:RB + | subu TMP1, KBASE, TMP1 + | lw UPVAL:RB, LFUNC:RA->uvptr + | lw STR:TMP1, -4(TMP1) // KBASE-4-str_const*4 + | lbu TMP2, UPVAL:RB->marked + | lw CARG2, UPVAL:RB->v + | lbu TMP3, STR:TMP1->marked + | andi AT, TMP2, LJ_GC_BLACK // isblack(uv) + | lbu TMP2, UPVAL:RB->closed + | li TMP0, LJ_TSTR + | sw STR:TMP1, LO(CARG2) + | bnez AT, >2 + |. sw TMP0, HI(CARG2) + |1: + | ins_next + | + |2: // Check if string is white and ensure upvalue is closed. + | beqz TMP2, <1 + |. andi AT, TMP3, LJ_GC_WHITES // iswhite(str) + | beqz AT, <1 + |. load_got lj_gc_barrieruv + | // Crossed a write barrier. Move the barrier forward. + | call_intern lj_gc_barrieruv // (global_State *g, TValue *tv) + |. addiu CARG1, DISPATCH, GG_DISP2G + | b <1 + |. nop + break; + case BC_USETN: + | // RA = uvnum*8, RD = num_const*8 + | lw LFUNC:RB, FRAME_FUNC(BASE) + | srl RA, RA, 1 + | addu RD, KBASE, RD + | addu RA, RA, LFUNC:RB + | lw UPVAL:RB, LFUNC:RA->uvptr + | lw SFRETHI, HI(RD) + | lw SFRETLO, LO(RD) + | lw TMP1, UPVAL:RB->v + | ins_next1 + | sw SFRETHI, HI(TMP1) + | sw SFRETLO, LO(TMP1) + | ins_next2 + break; + case BC_USETP: + | // RA = uvnum*8, RD = primitive_type*8 (~) + | lw LFUNC:RB, FRAME_FUNC(BASE) + | srl RA, RA, 1 + | srl TMP0, RD, 3 + | addu RA, RA, LFUNC:RB + | not TMP0, TMP0 + | lw UPVAL:RB, LFUNC:RA->uvptr + | ins_next1 + | lw TMP1, UPVAL:RB->v + | sw TMP0, HI(TMP1) + | ins_next2 + break; + + case BC_UCLO: + | // RA = level*8, RD = target + | lw TMP2, L->openupval + | branch_RD // Do this first since RD is not saved. + | load_got lj_func_closeuv + | sw BASE, L->base + | beqz TMP2, >1 + |. move CARG1, L + | call_intern lj_func_closeuv // (lua_State *L, TValue *level) + |. addu CARG2, BASE, RA + | lw BASE, L->base + |1: + | ins_next + break; + + case BC_FNEW: + | // RA = dst*8, RD = proto_const*8 (~) (holding function prototype) + | srl TMP1, RD, 1 + | load_got lj_func_newL_gc + | subu TMP1, KBASE, TMP1 + | lw CARG3, FRAME_FUNC(BASE) + | lw CARG2, -4(TMP1) // KBASE-4-tab_const*4 + | sw BASE, L->base + | sw PC, SAVE_PC + | // (lua_State *L, GCproto *pt, GCfuncL *parent) + | call_intern lj_func_newL_gc + |. move CARG1, L + | // Returns GCfuncL *. + | lw BASE, L->base + | li TMP0, LJ_TFUNC + | ins_next1 + | addu RA, BASE, RA + | sw LFUNC:CRET1, LO(RA) + | sw TMP0, HI(RA) + | ins_next2 + break; + + /* -- Table ops --------------------------------------------------------- */ + + case BC_TNEW: + case BC_TDUP: + | // RA = dst*8, RD = (hbits|asize)*8 | tab_const*8 (~) + | lw TMP0, DISPATCH_GL(gc.total)(DISPATCH) + | lw TMP1, DISPATCH_GL(gc.threshold)(DISPATCH) + | sw BASE, L->base + | sw PC, SAVE_PC + | sltu AT, TMP0, TMP1 + | beqz AT, >5 + |1: + if (op == BC_TNEW) { + | load_got lj_tab_new + | srl CARG2, RD, 3 + | andi CARG2, CARG2, 0x7ff + | li TMP0, 0x801 + | addiu AT, CARG2, -0x7ff + | srl CARG3, RD, 14 + | movz CARG2, TMP0, AT + | // (lua_State *L, int32_t asize, uint32_t hbits) + | call_intern lj_tab_new + |. move CARG1, L + | // Returns Table *. + } else { + | load_got lj_tab_dup + | srl TMP1, RD, 1 + | subu TMP1, KBASE, TMP1 + | move CARG1, L + | call_intern lj_tab_dup // (lua_State *L, Table *kt) + |. lw CARG2, -4(TMP1) // KBASE-4-str_const*4 + | // Returns Table *. + } + | lw BASE, L->base + | ins_next1 + | addu RA, BASE, RA + | li TMP0, LJ_TTAB + | sw TAB:CRET1, LO(RA) + | sw TMP0, HI(RA) + | ins_next2 + |5: + | load_got lj_gc_step_fixtop + | move MULTRES, RD + | call_intern lj_gc_step_fixtop // (lua_State *L) + |. move CARG1, L + | b <1 + |. move RD, MULTRES + break; + + case BC_GGET: + | // RA = dst*8, RD = str_const*8 (~) + case BC_GSET: + | // RA = src*8, RD = str_const*8 (~) + | lw LFUNC:TMP2, FRAME_FUNC(BASE) + | srl TMP1, RD, 1 + | subu TMP1, KBASE, TMP1 + | lw TAB:RB, LFUNC:TMP2->env + | lw STR:RC, -4(TMP1) // KBASE-4-str_const*4 + if (op == BC_GGET) { + | b ->BC_TGETS_Z + } else { + | b ->BC_TSETS_Z + } + |. addu RA, BASE, RA + break; + + case BC_TGETV: + | // RA = dst*8, RB = table*8, RC = key*8 + | decode_RB8a RB, INS + | decode_RB8b RB + | decode_RDtoRC8 RC, RD + | addu CARG2, BASE, RB + | addu CARG3, BASE, RC + | lw TMP1, HI(CARG2) + | lw TMP2, HI(CARG3) + | lw TAB:RB, LO(CARG2) + | li AT, LJ_TTAB + | bne TMP1, AT, ->vmeta_tgetv + |. addu RA, BASE, RA + | bne TMP2, TISNUM, >5 + |. lw RC, LO(CARG3) + | lw TMP0, TAB:RB->asize + | lw TMP1, TAB:RB->array + | sltu AT, RC, TMP0 + | sll TMP2, RC, 3 + | beqz AT, ->vmeta_tgetv // Integer key and in array part? + |. addu TMP2, TMP1, TMP2 + | lw SFRETHI, HI(TMP2) + | beq SFRETHI, TISNIL, >2 + |. lw SFRETLO, LO(TMP2) + |1: + | ins_next1 + | sw SFRETHI, HI(RA) + | sw SFRETLO, LO(RA) + | ins_next2 + | + |2: // Check for __index if table value is nil. + | lw TAB:TMP2, TAB:RB->metatable + | beqz TAB:TMP2, <1 // No metatable: done. + |. nop + | lbu TMP0, TAB:TMP2->nomm + | andi TMP0, TMP0, 1<vmeta_tgetv + |. nop + | + |5: + | li AT, LJ_TSTR + | bne TMP2, AT, ->vmeta_tgetv + |. nop + | b ->BC_TGETS_Z // String key? + |. nop + break; + case BC_TGETS: + | // RA = dst*8, RB = table*8, RC = str_const*4 (~) + | decode_RB8a RB, INS + | decode_RB8b RB + | addu CARG2, BASE, RB + | decode_RC4a RC, INS + | lw TMP0, HI(CARG2) + | decode_RC4b RC + | li AT, LJ_TTAB + | lw TAB:RB, LO(CARG2) + | subu CARG3, KBASE, RC + | lw STR:RC, -4(CARG3) // KBASE-4-str_const*4 + | bne TMP0, AT, ->vmeta_tgets1 + |. addu RA, BASE, RA + |->BC_TGETS_Z: + | // TAB:RB = GCtab *, STR:RC = GCstr *, RA = dst*8 + | lw TMP0, TAB:RB->hmask + | lw TMP1, STR:RC->hash + | lw NODE:TMP2, TAB:RB->node + | and TMP1, TMP1, TMP0 // idx = str->hash & tab->hmask + | sll TMP0, TMP1, 5 + | sll TMP1, TMP1, 3 + | subu TMP1, TMP0, TMP1 + | addu NODE:TMP2, NODE:TMP2, TMP1 // node = tab->node + (idx*32-idx*8) + |1: + | lw CARG1, offsetof(Node, key)+HI(NODE:TMP2) + | lw TMP0, offsetof(Node, key)+LO(NODE:TMP2) + | lw NODE:TMP1, NODE:TMP2->next + | lw SFRETHI, offsetof(Node, val)+HI(NODE:TMP2) + | addiu CARG1, CARG1, -LJ_TSTR + | xor TMP0, TMP0, STR:RC + | or AT, CARG1, TMP0 + | bnez AT, >4 + |. lw TAB:TMP3, TAB:RB->metatable + | beq SFRETHI, TISNIL, >5 // Key found, but nil value? + |. lw SFRETLO, offsetof(Node, val)+LO(NODE:TMP2) + |3: + | ins_next1 + | sw SFRETHI, HI(RA) + | sw SFRETLO, LO(RA) + | ins_next2 + | + |4: // Follow hash chain. + | bnez NODE:TMP1, <1 + |. move NODE:TMP2, NODE:TMP1 + | // End of hash chain: key not found, nil result. + | + |5: // Check for __index if table value is nil. + | beqz TAB:TMP3, <3 // No metatable: done. + |. li SFRETHI, LJ_TNIL + | lbu TMP0, TAB:TMP3->nomm + | andi TMP0, TMP0, 1<vmeta_tgets + |. nop + break; + case BC_TGETB: + | // RA = dst*8, RB = table*8, RC = index*8 + | decode_RB8a RB, INS + | decode_RB8b RB + | addu CARG2, BASE, RB + | decode_RDtoRC8 RC, RD + | lw CARG1, HI(CARG2) + | li AT, LJ_TTAB + | lw TAB:RB, LO(CARG2) + | addu RA, BASE, RA + | bne CARG1, AT, ->vmeta_tgetb + |. srl TMP0, RC, 3 + | lw TMP1, TAB:RB->asize + | lw TMP2, TAB:RB->array + | sltu AT, TMP0, TMP1 + | beqz AT, ->vmeta_tgetb + |. addu RC, TMP2, RC + | lw SFRETHI, HI(RC) + | beq SFRETHI, TISNIL, >5 + |. lw SFRETLO, LO(RC) + |1: + | ins_next1 + | sw SFRETHI, HI(RA) + | sw SFRETLO, LO(RA) + | ins_next2 + | + |5: // Check for __index if table value is nil. + | lw TAB:TMP2, TAB:RB->metatable + | beqz TAB:TMP2, <1 // No metatable: done. + |. nop + | lbu TMP1, TAB:TMP2->nomm + | andi TMP1, TMP1, 1<vmeta_tgetb // Caveat: preserve TMP0 and CARG2! + |. nop + break; + case BC_TGETR: + | // RA = dst*8, RB = table*8, RC = key*8 + | decode_RB8a RB, INS + | decode_RB8b RB + | decode_RDtoRC8 RC, RD + | addu RB, BASE, RB + | addu RC, BASE, RC + | lw TAB:CARG1, LO(RB) + | lw CARG2, LO(RC) + | addu RA, BASE, RA + | lw TMP0, TAB:CARG1->asize + | lw TMP1, TAB:CARG1->array + | sltu AT, CARG2, TMP0 + | sll TMP2, CARG2, 3 + | beqz AT, ->vmeta_tgetr // In array part? + |. addu CRET1, TMP1, TMP2 + | lw SFARG2HI, HI(CRET1) + | lw SFARG2LO, LO(CRET1) + |->BC_TGETR_Z: + | ins_next1 + | sw SFARG2HI, HI(RA) + | sw SFARG2LO, LO(RA) + | ins_next2 + break; + + case BC_TSETV: + | // RA = src*8, RB = table*8, RC = key*8 + | decode_RB8a RB, INS + | decode_RB8b RB + | decode_RDtoRC8 RC, RD + | addu CARG2, BASE, RB + | addu CARG3, BASE, RC + | lw TMP1, HI(CARG2) + | lw TMP2, HI(CARG3) + | lw TAB:RB, LO(CARG2) + | li AT, LJ_TTAB + | bne TMP1, AT, ->vmeta_tsetv + |. addu RA, BASE, RA + | bne TMP2, TISNUM, >5 + |. lw RC, LO(CARG3) + | lw TMP0, TAB:RB->asize + | lw TMP1, TAB:RB->array + | sltu AT, RC, TMP0 + | sll TMP2, RC, 3 + | beqz AT, ->vmeta_tsetv // Integer key and in array part? + |. addu TMP1, TMP1, TMP2 + | lw TMP0, HI(TMP1) + | lbu TMP3, TAB:RB->marked + | lw SFRETHI, HI(RA) + | beq TMP0, TISNIL, >3 + |. lw SFRETLO, LO(RA) + |1: + | andi AT, TMP3, LJ_GC_BLACK // isblack(table) + | sw SFRETHI, HI(TMP1) + | bnez AT, >7 + |. sw SFRETLO, LO(TMP1) + |2: + | ins_next + | + |3: // Check for __newindex if previous value is nil. + | lw TAB:TMP2, TAB:RB->metatable + | beqz TAB:TMP2, <1 // No metatable: done. + |. nop + | lbu TMP2, TAB:TMP2->nomm + | andi TMP2, TMP2, 1<vmeta_tsetv + |. nop + | + |5: + | li AT, LJ_TSTR + | bne TMP2, AT, ->vmeta_tsetv + |. nop + | b ->BC_TSETS_Z // String key? + |. nop + | + |7: // Possible table write barrier for the value. Skip valiswhite check. + | barrierback TAB:RB, TMP3, TMP0, <2 + break; + case BC_TSETS: + | // RA = src*8, RB = table*8, RC = str_const*8 (~) + | decode_RB8a RB, INS + | decode_RB8b RB + | addu CARG2, BASE, RB + | decode_RC4a RC, INS + | lw TMP0, HI(CARG2) + | decode_RC4b RC + | li AT, LJ_TTAB + | subu CARG3, KBASE, RC + | lw TAB:RB, LO(CARG2) + | lw STR:RC, -4(CARG3) // KBASE-4-str_const*4 + | bne TMP0, AT, ->vmeta_tsets1 + |. addu RA, BASE, RA + |->BC_TSETS_Z: + | // TAB:RB = GCtab *, STR:RC = GCstr *, RA = BASE+src*8 + | lw TMP0, TAB:RB->hmask + | lw TMP1, STR:RC->hash + | lw NODE:TMP2, TAB:RB->node + | sb r0, TAB:RB->nomm // Clear metamethod cache. + | and TMP1, TMP1, TMP0 // idx = str->hash & tab->hmask + | sll TMP0, TMP1, 5 + | sll TMP1, TMP1, 3 + | subu TMP1, TMP0, TMP1 + | addu NODE:TMP2, NODE:TMP2, TMP1 // node = tab->node + (idx*32-idx*8) + |.if FPU + | ldc1 f20, 0(RA) + |.else + | lw SFRETHI, HI(RA) + | lw SFRETLO, LO(RA) + |.endif + |1: + | lw CARG1, offsetof(Node, key)+HI(NODE:TMP2) + | lw TMP0, offsetof(Node, key)+LO(NODE:TMP2) + | li AT, LJ_TSTR + | lw NODE:TMP1, NODE:TMP2->next + | bne CARG1, AT, >5 + |. lw CARG2, offsetof(Node, val)+HI(NODE:TMP2) + | bne TMP0, STR:RC, >5 + |. lbu TMP3, TAB:RB->marked + | beq CARG2, TISNIL, >4 // Key found, but nil value? + |. lw TAB:TMP0, TAB:RB->metatable + |2: + | andi AT, TMP3, LJ_GC_BLACK // isblack(table) + |.if FPU + | bnez AT, >7 + |. sdc1 f20, NODE:TMP2->val + |.else + | sw SFRETHI, NODE:TMP2->val.u32.hi + | bnez AT, >7 + |. sw SFRETLO, NODE:TMP2->val.u32.lo + |.endif + |3: + | ins_next + | + |4: // Check for __newindex if previous value is nil. + | beqz TAB:TMP0, <2 // No metatable: done. + |. nop + | lbu TMP0, TAB:TMP0->nomm + | andi TMP0, TMP0, 1<vmeta_tsets + |. nop + | + |5: // Follow hash chain. + | bnez NODE:TMP1, <1 + |. move NODE:TMP2, NODE:TMP1 + | // End of hash chain: key not found, add a new one + | + | // But check for __newindex first. + | lw TAB:TMP2, TAB:RB->metatable + | beqz TAB:TMP2, >6 // No metatable: continue. + |. addiu CARG3, DISPATCH, DISPATCH_GL(tmptv) + | lbu TMP0, TAB:TMP2->nomm + | andi TMP0, TMP0, 1<vmeta_tsets // 'no __newindex' flag NOT set: check. + |. li AT, LJ_TSTR + |6: + | load_got lj_tab_newkey + | sw STR:RC, LO(CARG3) + | sw AT, HI(CARG3) + | sw BASE, L->base + | move CARG2, TAB:RB + | sw PC, SAVE_PC + | call_intern lj_tab_newkey // (lua_State *L, GCtab *t, TValue *k + |. move CARG1, L + | // Returns TValue *. + | lw BASE, L->base + |.if FPU + | b <3 // No 2nd write barrier needed. + |. sdc1 f20, 0(CRET1) + |.else + | lw SFARG1HI, HI(RA) + | lw SFARG1LO, LO(RA) + | sw SFARG1HI, HI(CRET1) + | b <3 // No 2nd write barrier needed. + |. sw SFARG1LO, LO(CRET1) + |.endif + | + |7: // Possible table write barrier for the value. Skip valiswhite check. + | barrierback TAB:RB, TMP3, TMP0, <3 + break; + case BC_TSETB: + | // RA = src*8, RB = table*8, RC = index*8 + | decode_RB8a RB, INS + | decode_RB8b RB + | addu CARG2, BASE, RB + | decode_RDtoRC8 RC, RD + | lw CARG1, HI(CARG2) + | li AT, LJ_TTAB + | lw TAB:RB, LO(CARG2) + | addu RA, BASE, RA + | bne CARG1, AT, ->vmeta_tsetb + |. srl TMP0, RC, 3 + | lw TMP1, TAB:RB->asize + | lw TMP2, TAB:RB->array + | sltu AT, TMP0, TMP1 + | beqz AT, ->vmeta_tsetb + |. addu RC, TMP2, RC + | lw TMP1, HI(RC) + | lbu TMP3, TAB:RB->marked + | beq TMP1, TISNIL, >5 + |1: + |. lw SFRETHI, HI(RA) + | lw SFRETLO, LO(RA) + | andi AT, TMP3, LJ_GC_BLACK // isblack(table) + | sw SFRETHI, HI(RC) + | bnez AT, >7 + |. sw SFRETLO, LO(RC) + |2: + | ins_next + | + |5: // Check for __newindex if previous value is nil. + | lw TAB:TMP2, TAB:RB->metatable + | beqz TAB:TMP2, <1 // No metatable: done. + |. nop + | lbu TMP1, TAB:TMP2->nomm + | andi TMP1, TMP1, 1<vmeta_tsetb // Caveat: preserve TMP0 and CARG2! + |. nop + | + |7: // Possible table write barrier for the value. Skip valiswhite check. + | barrierback TAB:RB, TMP3, TMP0, <2 + break; + case BC_TSETR: + | // RA = dst*8, RB = table*8, RC = key*8 + | decode_RB8a RB, INS + | decode_RB8b RB + | decode_RDtoRC8 RC, RD + | addu CARG1, BASE, RB + | addu CARG3, BASE, RC + | lw TAB:CARG2, LO(CARG1) + | lw CARG3, LO(CARG3) + | lbu TMP3, TAB:CARG2->marked + | lw TMP0, TAB:CARG2->asize + | lw TMP1, TAB:CARG2->array + | andi AT, TMP3, LJ_GC_BLACK // isblack(table) + | bnez AT, >7 + |. addu RA, BASE, RA + |2: + | sltu AT, CARG3, TMP0 + | sll TMP2, CARG3, 3 + | beqz AT, ->vmeta_tsetr // In array part? + |. addu CRET1, TMP1, TMP2 + |->BC_TSETR_Z: + | lw SFARG1HI, HI(RA) + | lw SFARG1LO, LO(RA) + | ins_next1 + | sw SFARG1HI, HI(CRET1) + | sw SFARG1LO, LO(CRET1) + | ins_next2 + | + |7: // Possible table write barrier for the value. Skip valiswhite check. + | barrierback TAB:CARG2, TMP3, CRET1, <2 + break; + + case BC_TSETM: + | // RA = base*8 (table at base-1), RD = num_const*8 (start index) + | addu RA, BASE, RA + |1: + | addu TMP3, KBASE, RD + | lw TAB:CARG2, -8+LO(RA) // Guaranteed to be a table. + | addiu TMP0, MULTRES, -8 + | lw TMP3, LO(TMP3) // Integer constant is in lo-word. + | beqz TMP0, >4 // Nothing to copy? + |. srl CARG3, TMP0, 3 + | addu CARG3, CARG3, TMP3 + | lw TMP2, TAB:CARG2->asize + | sll TMP1, TMP3, 3 + | lbu TMP3, TAB:CARG2->marked + | lw CARG1, TAB:CARG2->array + | sltu AT, TMP2, CARG3 + | bnez AT, >5 + |. addu TMP2, RA, TMP0 + | addu TMP1, TMP1, CARG1 + | andi TMP0, TMP3, LJ_GC_BLACK // isblack(table) + |3: // Copy result slots to table. + | lw SFRETHI, HI(RA) + | lw SFRETLO, LO(RA) + | addiu RA, RA, 8 + | sltu AT, RA, TMP2 + | sw SFRETHI, HI(TMP1) + | sw SFRETLO, LO(TMP1) + | bnez AT, <3 + |. addiu TMP1, TMP1, 8 + | bnez TMP0, >7 + |. nop + |4: + | ins_next + | + |5: // Need to resize array part. + | load_got lj_tab_reasize + | sw BASE, L->base + | sw PC, SAVE_PC + | move BASE, RD + | call_intern lj_tab_reasize // (lua_State *L, GCtab *t, int nasize) + |. move CARG1, L + | // Must not reallocate the stack. + | move RD, BASE + | b <1 + |. lw BASE, L->base // Reload BASE for lack of a saved register. + | + |7: // Possible table write barrier for any value. Skip valiswhite check. + | barrierback TAB:CARG2, TMP3, TMP0, <4 + break; + + /* -- Calls and vararg handling ----------------------------------------- */ + + case BC_CALLM: + | // RA = base*8, (RB = (nresults+1)*8,) RC = extra_nargs*8 + | decode_RDtoRC8 NARGS8:RC, RD + | b ->BC_CALL_Z + |. addu NARGS8:RC, NARGS8:RC, MULTRES + break; + case BC_CALL: + | // RA = base*8, (RB = (nresults+1)*8,) RC = (nargs+1)*8 + | decode_RDtoRC8 NARGS8:RC, RD + |->BC_CALL_Z: + | move TMP2, BASE + | addu BASE, BASE, RA + | li AT, LJ_TFUNC + | lw TMP0, HI(BASE) + | lw LFUNC:RB, LO(BASE) + | addiu BASE, BASE, 8 + | bne TMP0, AT, ->vmeta_call + |. addiu NARGS8:RC, NARGS8:RC, -8 + | ins_call + break; + + case BC_CALLMT: + | // RA = base*8, (RB = 0,) RC = extra_nargs*8 + | addu NARGS8:RD, NARGS8:RD, MULTRES // BC_CALLT gets RC from RD. + | // Fall through. Assumes BC_CALLT follows. + break; + case BC_CALLT: + | // RA = base*8, (RB = 0,) RC = (nargs+1)*8 + | addu RA, BASE, RA + | li AT, LJ_TFUNC + | lw TMP0, HI(RA) + | lw LFUNC:RB, LO(RA) + | move NARGS8:RC, RD + | lw TMP1, FRAME_PC(BASE) + | addiu RA, RA, 8 + | bne TMP0, AT, ->vmeta_callt + |. addiu NARGS8:RC, NARGS8:RC, -8 + |->BC_CALLT_Z: + | andi TMP0, TMP1, FRAME_TYPE // Caveat: preserve TMP0 until the 'or'. + | lbu TMP3, LFUNC:RB->ffid + | bnez TMP0, >7 + |. xori TMP2, TMP1, FRAME_VARG + |1: + | sw LFUNC:RB, FRAME_FUNC(BASE) // Copy function down, but keep PC. + | sltiu AT, TMP3, 2 // (> FF_C) Calling a fast function? + | move TMP2, BASE + | beqz NARGS8:RC, >3 + |. move TMP3, NARGS8:RC + |2: + | lw SFRETHI, HI(RA) + | lw SFRETLO, LO(RA) + | addiu RA, RA, 8 + | addiu TMP3, TMP3, -8 + | sw SFRETHI, HI(TMP2) + | sw SFRETLO, LO(TMP2) + | bnez TMP3, <2 + |. addiu TMP2, TMP2, 8 + |3: + | or TMP0, TMP0, AT + | beqz TMP0, >5 + |. nop + |4: + | ins_callt + | + |5: // Tailcall to a fast function with a Lua frame below. + | lw INS, -4(TMP1) + | decode_RA8a RA, INS + | decode_RA8b RA + | subu TMP1, BASE, RA + | lw LFUNC:TMP1, -8+FRAME_FUNC(TMP1) + | lw TMP1, LFUNC:TMP1->pc + | b <4 + |. lw KBASE, PC2PROTO(k)(TMP1) // Need to prepare KBASE. + | + |7: // Tailcall from a vararg function. + | andi AT, TMP2, FRAME_TYPEP + | bnez AT, <1 // Vararg frame below? + |. subu TMP2, BASE, TMP2 // Relocate BASE down. + | move BASE, TMP2 + | lw TMP1, FRAME_PC(TMP2) + | b <1 + |. andi TMP0, TMP1, FRAME_TYPE + break; + + case BC_ITERC: + | // RA = base*8, (RB = (nresults+1)*8, RC = (nargs+1)*8 ((2+1)*8)) + | move TMP2, BASE + | addu BASE, BASE, RA + | li AT, LJ_TFUNC + | lw TMP1, -24+HI(BASE) + | lw LFUNC:RB, -24+LO(BASE) + | lw SFARG1HI, -16+HI(BASE) + | lw SFARG1LO, -16+LO(BASE) + | lw SFARG2HI, -8+HI(BASE) + | lw SFARG2LO, -8+LO(BASE) + | sw TMP1, HI(BASE) // Copy callable. + | sw LFUNC:RB, LO(BASE) + | sw SFARG1HI, 8+HI(BASE) // Copy state. + | sw SFARG1LO, 8+LO(BASE) + | sw SFARG2HI, 16+HI(BASE) // Copy control var. + | sw SFARG2LO, 16+LO(BASE) + | addiu BASE, BASE, 8 + | bne TMP1, AT, ->vmeta_call + |. li NARGS8:RC, 16 // Iterators get 2 arguments. + | ins_call + break; + + case BC_ITERN: + | // RA = base*8, (RB = (nresults+1)*8, RC = (nargs+1)*8 (2+1)*8) + |.if JIT + | // NYI: add hotloop, record BC_ITERN. + |.endif + | addu RA, BASE, RA + | lw TAB:RB, -16+LO(RA) + | lw RC, -8+LO(RA) // Get index from control var. + | lw TMP0, TAB:RB->asize + | lw TMP1, TAB:RB->array + | addiu PC, PC, 4 + |1: // Traverse array part. + | sltu AT, RC, TMP0 + | beqz AT, >5 // Index points after array part? + |. sll TMP3, RC, 3 + | addu TMP3, TMP1, TMP3 + | lw SFARG1HI, HI(TMP3) + | lw SFARG1LO, LO(TMP3) + | lhu RD, -4+OFS_RD(PC) + | sw TISNUM, HI(RA) + | sw RC, LO(RA) + | beq SFARG1HI, TISNIL, <1 // Skip holes in array part. + |. addiu RC, RC, 1 + | sw SFARG1HI, 8+HI(RA) + | sw SFARG1LO, 8+LO(RA) + | lui TMP3, (-(BCBIAS_J*4 >> 16) & 65535) + | decode_RD4b RD + | addu RD, RD, TMP3 + | sw RC, -8+LO(RA) // Update control var. + | addu PC, PC, RD + |3: + | ins_next + | + |5: // Traverse hash part. + | lw TMP1, TAB:RB->hmask + | subu RC, RC, TMP0 + | lw TMP2, TAB:RB->node + |6: + | sltu AT, TMP1, RC // End of iteration? Branch to ITERL+1. + | bnez AT, <3 + |. sll TMP3, RC, 5 + | sll RB, RC, 3 + | subu TMP3, TMP3, RB + | addu NODE:TMP3, TMP3, TMP2 + | lw SFARG1HI, NODE:TMP3->val.u32.hi + | lw SFARG1LO, NODE:TMP3->val.u32.lo + | lhu RD, -4+OFS_RD(PC) + | beq SFARG1HI, TISNIL, <6 // Skip holes in hash part. + |. addiu RC, RC, 1 + | lw SFARG2HI, NODE:TMP3->key.u32.hi + | lw SFARG2LO, NODE:TMP3->key.u32.lo + | lui TMP3, (-(BCBIAS_J*4 >> 16) & 65535) + | sw SFARG1HI, 8+HI(RA) + | sw SFARG1LO, 8+LO(RA) + | addu RC, RC, TMP0 + | decode_RD4b RD + | addu RD, RD, TMP3 + | sw SFARG2HI, HI(RA) + | sw SFARG2LO, LO(RA) + | addu PC, PC, RD + | b <3 + |. sw RC, -8+LO(RA) // Update control var. + break; + + case BC_ISNEXT: + | // RA = base*8, RD = target (points to ITERN) + | addu RA, BASE, RA + | srl TMP0, RD, 1 + | lw CARG1, -24+HI(RA) + | lw CFUNC:CARG2, -24+LO(RA) + | addu TMP0, PC, TMP0 + | lw CARG3, -16+HI(RA) + | lw CARG4, -8+HI(RA) + | li AT, LJ_TFUNC + | bne CARG1, AT, >5 + |. lui TMP2, (-(BCBIAS_J*4 >> 16) & 65535) + | lbu CARG2, CFUNC:CARG2->ffid + | addiu CARG3, CARG3, -LJ_TTAB + | addiu CARG4, CARG4, -LJ_TNIL + | or CARG3, CARG3, CARG4 + | addiu CARG2, CARG2, -FF_next_N + | or CARG2, CARG2, CARG3 + | bnez CARG2, >5 + |. lui TMP1, 0xfffe + | addu PC, TMP0, TMP2 + | ori TMP1, TMP1, 0x7fff + | sw r0, -8+LO(RA) // Initialize control var. + | sw TMP1, -8+HI(RA) + |1: + | ins_next + |5: // Despecialize bytecode if any of the checks fail. + | li TMP3, BC_JMP + | li TMP1, BC_ITERC + | sb TMP3, -4+OFS_OP(PC) + | addu PC, TMP0, TMP2 + | b <1 + |. sb TMP1, OFS_OP(PC) + break; + + case BC_VARG: + | // RA = base*8, RB = (nresults+1)*8, RC = numparams*8 + | lw TMP0, FRAME_PC(BASE) + | decode_RDtoRC8 RC, RD + | decode_RB8a RB, INS + | addu RC, BASE, RC + | decode_RB8b RB + | addu RA, BASE, RA + | addiu RC, RC, FRAME_VARG + | addu TMP2, RA, RB + | addiu TMP3, BASE, -8 // TMP3 = vtop + | subu RC, RC, TMP0 // RC = vbase + | // Note: RC may now be even _above_ BASE if nargs was < numparams. + | beqz RB, >5 // Copy all varargs? + |. subu TMP1, TMP3, RC + | addiu TMP2, TMP2, -16 + |1: // Copy vararg slots to destination slots. + | lw CARG1, HI(RC) + | sltu AT, RC, TMP3 + | lw CARG2, LO(RC) + | addiu RC, RC, 8 + | movz CARG1, TISNIL, AT + | sw CARG1, HI(RA) + | sw CARG2, LO(RA) + | sltu AT, RA, TMP2 + | bnez AT, <1 + |. addiu RA, RA, 8 + |3: + | ins_next + | + |5: // Copy all varargs. + | lw TMP0, L->maxstack + | blez TMP1, <3 // No vararg slots? + |. li MULTRES, 8 // MULTRES = (0+1)*8 + | addu TMP2, RA, TMP1 + | sltu AT, TMP0, TMP2 + | bnez AT, >7 + |. addiu MULTRES, TMP1, 8 + |6: + | lw SFRETHI, HI(RC) + | lw SFRETLO, LO(RC) + | addiu RC, RC, 8 + | sw SFRETHI, HI(RA) + | sw SFRETLO, LO(RA) + | sltu AT, RC, TMP3 + | bnez AT, <6 // More vararg slots? + |. addiu RA, RA, 8 + | b <3 + |. nop + | + |7: // Grow stack for varargs. + | load_got lj_state_growstack + | sw RA, L->top + | subu RA, RA, BASE + | sw BASE, L->base + | subu BASE, RC, BASE // Need delta, because BASE may change. + | sw PC, SAVE_PC + | srl CARG2, TMP1, 3 + | call_intern lj_state_growstack // (lua_State *L, int n) + |. move CARG1, L + | move RC, BASE + | lw BASE, L->base + | addu RA, BASE, RA + | addu RC, BASE, RC + | b <6 + |. addiu TMP3, BASE, -8 + break; + + /* -- Returns ----------------------------------------------------------- */ + + case BC_RETM: + | // RA = results*8, RD = extra_nresults*8 + | addu RD, RD, MULTRES // MULTRES >= 8, so RD >= 8. + | // Fall through. Assumes BC_RET follows. + break; + + case BC_RET: + | // RA = results*8, RD = (nresults+1)*8 + | lw PC, FRAME_PC(BASE) + | addu RA, BASE, RA + | move MULTRES, RD + |1: + | andi TMP0, PC, FRAME_TYPE + | bnez TMP0, ->BC_RETV_Z + |. xori TMP1, PC, FRAME_VARG + | + |->BC_RET_Z: + | // BASE = base, RA = resultptr, RD = (nresults+1)*8, PC = return + | lw INS, -4(PC) + | addiu TMP2, BASE, -8 + | addiu RC, RD, -8 + | decode_RA8a TMP0, INS + | decode_RB8a RB, INS + | decode_RA8b TMP0 + | decode_RB8b RB + | addu TMP3, TMP2, RB + | beqz RC, >3 + |. subu BASE, TMP2, TMP0 + |2: + | lw SFRETHI, HI(RA) + | lw SFRETLO, LO(RA) + | addiu RA, RA, 8 + | addiu RC, RC, -8 + | sw SFRETHI, HI(TMP2) + | sw SFRETLO, LO(TMP2) + | bnez RC, <2 + |. addiu TMP2, TMP2, 8 + |3: + | addiu TMP3, TMP3, -8 + |5: + | sltu AT, TMP2, TMP3 + | bnez AT, >6 + |. lw LFUNC:TMP1, FRAME_FUNC(BASE) + | ins_next1 + | lw TMP1, LFUNC:TMP1->pc + | lw KBASE, PC2PROTO(k)(TMP1) + | ins_next2 + | + |6: // Fill up results with nil. + | sw TISNIL, HI(TMP2) + | b <5 + |. addiu TMP2, TMP2, 8 + | + |->BC_RETV_Z: // Non-standard return case. + | andi TMP2, TMP1, FRAME_TYPEP + | bnez TMP2, ->vm_return + |. nop + | // Return from vararg function: relocate BASE down. + | subu BASE, BASE, TMP1 + | b <1 + |. lw PC, FRAME_PC(BASE) + break; + + case BC_RET0: case BC_RET1: + | // RA = results*8, RD = (nresults+1)*8 + | lw PC, FRAME_PC(BASE) + | addu RA, BASE, RA + | move MULTRES, RD + | andi TMP0, PC, FRAME_TYPE + | bnez TMP0, ->BC_RETV_Z + |. xori TMP1, PC, FRAME_VARG + | + | lw INS, -4(PC) + | addiu TMP2, BASE, -8 + if (op == BC_RET1) { + | lw SFRETHI, HI(RA) + | lw SFRETLO, LO(RA) + } + | decode_RB8a RB, INS + | decode_RA8a RA, INS + | decode_RB8b RB + | decode_RA8b RA + if (op == BC_RET1) { + | sw SFRETHI, HI(TMP2) + | sw SFRETLO, LO(TMP2) + } + | subu BASE, TMP2, RA + |5: + | sltu AT, RD, RB + | bnez AT, >6 + |. lw LFUNC:TMP1, FRAME_FUNC(BASE) + | ins_next1 + | lw TMP1, LFUNC:TMP1->pc + | lw KBASE, PC2PROTO(k)(TMP1) + | ins_next2 + | + |6: // Fill up results with nil. + | addiu TMP2, TMP2, 8 + | addiu RD, RD, 8 + | b <5 + if (op == BC_RET1) { + |. sw TISNIL, HI(TMP2) + } else { + |. sw TISNIL, -8+HI(TMP2) + } + break; + + /* -- Loops and branches ------------------------------------------------ */ + + case BC_FORL: + |.if JIT + | hotloop + |.endif + | // Fall through. Assumes BC_IFORL follows. + break; + + case BC_JFORI: + case BC_JFORL: +#if !LJ_HASJIT + break; +#endif + case BC_FORI: + case BC_IFORL: + | // RA = base*8, RD = target (after end of loop or start of loop) + vk = (op == BC_IFORL || op == BC_JFORL); + | addu RA, BASE, RA + | lw SFARG1HI, FORL_IDX*8+HI(RA) + | lw SFARG1LO, FORL_IDX*8+LO(RA) + if (op != BC_JFORL) { + | srl RD, RD, 1 + | lui TMP2, (-(BCBIAS_J*4 >> 16) & 65535) + | addu TMP2, RD, TMP2 + } + if (!vk) { + | lw SFARG2HI, FORL_STOP*8+HI(RA) + | lw SFARG2LO, FORL_STOP*8+LO(RA) + | bne SFARG1HI, TISNUM, >5 + |. lw SFRETHI, FORL_STEP*8+HI(RA) + | xor AT, SFARG2HI, TISNUM + | lw SFRETLO, FORL_STEP*8+LO(RA) + | xor TMP0, SFRETHI, TISNUM + | or AT, AT, TMP0 + | bnez AT, ->vmeta_for + |. slt AT, SFRETLO, r0 + | slt CRET1, SFARG2LO, SFARG1LO + | slt TMP1, SFARG1LO, SFARG2LO + | movn CRET1, TMP1, AT + } else { + | bne SFARG1HI, TISNUM, >5 + |. lw SFARG2LO, FORL_STEP*8+LO(RA) + | lw SFRETLO, FORL_STOP*8+LO(RA) + | move TMP3, SFARG1LO + | addu SFARG1LO, SFARG1LO, SFARG2LO + | xor TMP0, SFARG1LO, TMP3 + | xor TMP1, SFARG1LO, SFARG2LO + | and TMP0, TMP0, TMP1 + | slt TMP1, SFARG1LO, SFRETLO + | slt CRET1, SFRETLO, SFARG1LO + | slt AT, SFARG2LO, r0 + | slt TMP0, TMP0, r0 // ((y^a) & (y^b)) < 0: overflow. + | movn CRET1, TMP1, AT + | or CRET1, CRET1, TMP0 + } + |1: + if (op == BC_FORI) { + | movz TMP2, r0, CRET1 + | addu PC, PC, TMP2 + } else if (op == BC_JFORI) { + | addu PC, PC, TMP2 + | lhu RD, -4+OFS_RD(PC) + } else if (op == BC_IFORL) { + | movn TMP2, r0, CRET1 + | addu PC, PC, TMP2 + } + if (vk) { + | sw SFARG1HI, FORL_IDX*8+HI(RA) + | sw SFARG1LO, FORL_IDX*8+LO(RA) + } + | ins_next1 + | sw SFARG1HI, FORL_EXT*8+HI(RA) + | sw SFARG1LO, FORL_EXT*8+LO(RA) + |2: + if (op == BC_JFORI) { + | beqz CRET1, =>BC_JLOOP + |. decode_RD8b RD + } else if (op == BC_JFORL) { + | beqz CRET1, =>BC_JLOOP + } + | ins_next2 + | + |5: // FP loop. + |.if FPU + if (!vk) { + | ldc1 f0, FORL_IDX*8(RA) + | ldc1 f2, FORL_STOP*8(RA) + | sltiu TMP0, SFARG1HI, LJ_TISNUM + | sltiu TMP1, SFARG2HI, LJ_TISNUM + | sltiu AT, SFRETHI, LJ_TISNUM + | and TMP0, TMP0, TMP1 + | and AT, AT, TMP0 + | beqz AT, ->vmeta_for + |. slt TMP3, SFRETHI, r0 + | c.ole.d 0, f0, f2 + | c.ole.d 1, f2, f0 + | li CRET1, 1 + | movt CRET1, r0, 0 + | movt AT, r0, 1 + | b <1 + |. movn CRET1, AT, TMP3 + } else { + | ldc1 f0, FORL_IDX*8(RA) + | ldc1 f4, FORL_STEP*8(RA) + | ldc1 f2, FORL_STOP*8(RA) + | lw SFARG2HI, FORL_STEP*8+HI(RA) + | add.d f0, f0, f4 + | c.ole.d 0, f0, f2 + | c.ole.d 1, f2, f0 + | slt TMP3, SFARG2HI, r0 + | li CRET1, 1 + | li AT, 1 + | movt CRET1, r0, 0 + | movt AT, r0, 1 + | movn CRET1, AT, TMP3 + if (op == BC_IFORL) { + | movn TMP2, r0, CRET1 + | addu PC, PC, TMP2 + } + | sdc1 f0, FORL_IDX*8(RA) + | ins_next1 + | b <2 + |. sdc1 f0, FORL_EXT*8(RA) + } + |.else + if (!vk) { + | sltiu TMP0, SFARG1HI, LJ_TISNUM + | sltiu TMP1, SFARG2HI, LJ_TISNUM + | sltiu AT, SFRETHI, LJ_TISNUM + | and TMP0, TMP0, TMP1 + | and AT, AT, TMP0 + | beqz AT, ->vmeta_for + |. nop + | bal ->vm_sfcmpolex + |. move TMP3, SFRETHI + | b <1 + |. nop + } else { + | lw SFARG2HI, FORL_STEP*8+HI(RA) + | load_got __adddf3 + | call_extern + |. sw TMP2, ARG5 + | lw SFARG2HI, FORL_STOP*8+HI(RA) + | lw SFARG2LO, FORL_STOP*8+LO(RA) + | move SFARG1HI, SFRETHI + | move SFARG1LO, SFRETLO + | bal ->vm_sfcmpolex + |. lw TMP3, FORL_STEP*8+HI(RA) + if ( op == BC_JFORL ) { + | lhu RD, -4+OFS_RD(PC) + | lw TMP2, ARG5 + | b <1 + |. decode_RD8b RD + } else { + | b <1 + |. lw TMP2, ARG5 + } + } + |.endif + break; + + case BC_ITERL: + |.if JIT + | hotloop + |.endif + | // Fall through. Assumes BC_IITERL follows. + break; + + case BC_JITERL: +#if !LJ_HASJIT + break; +#endif + case BC_IITERL: + | // RA = base*8, RD = target + | addu RA, BASE, RA + | lw TMP1, HI(RA) + | beq TMP1, TISNIL, >1 // Stop if iterator returned nil. + |. lw TMP2, LO(RA) + if (op == BC_JITERL) { + | sw TMP1, -8+HI(RA) + | b =>BC_JLOOP + |. sw TMP2, -8+LO(RA) + } else { + | branch_RD // Otherwise save control var + branch. + | sw TMP1, -8+HI(RA) + | sw TMP2, -8+LO(RA) + } + |1: + | ins_next + break; + + case BC_LOOP: + | // RA = base*8, RD = target (loop extent) + | // Note: RA/RD is only used by trace recorder to determine scope/extent + | // This opcode does NOT jump, it's only purpose is to detect a hot loop. + |.if JIT + | hotloop + |.endif + | // Fall through. Assumes BC_ILOOP follows. + break; + + case BC_ILOOP: + | // RA = base*8, RD = target (loop extent) + | ins_next + break; + + case BC_JLOOP: + |.if JIT + | // RA = base*8 (ignored), RD = traceno*8 + | lw TMP1, DISPATCH_J(trace)(DISPATCH) + | srl RD, RD, 1 + | li AT, 0 + | addu TMP1, TMP1, RD + | // Traces on MIPS don't store the trace number, so use 0. + | sw AT, DISPATCH_GL(vmstate)(DISPATCH) + | lw TRACE:TMP2, 0(TMP1) + | sw BASE, DISPATCH_GL(jit_base)(DISPATCH) + | lw TMP2, TRACE:TMP2->mcode + | sw L, DISPATCH_GL(tmpbuf.L)(DISPATCH) + | jr TMP2 + |. addiu JGL, DISPATCH, GG_DISP2G+32768 + |.endif + break; + + case BC_JMP: + | // RA = base*8 (only used by trace recorder), RD = target + | branch_RD + | ins_next + break; + + /* -- Function headers -------------------------------------------------- */ + + case BC_FUNCF: + |.if JIT + | hotcall + |.endif + case BC_FUNCV: /* NYI: compiled vararg functions. */ + | // Fall through. Assumes BC_IFUNCF/BC_IFUNCV follow. + break; + + case BC_JFUNCF: +#if !LJ_HASJIT + break; +#endif + case BC_IFUNCF: + | // BASE = new base, RA = BASE+framesize*8, RB = LFUNC, RC = nargs*8 + | lw TMP2, L->maxstack + | lbu TMP1, -4+PC2PROTO(numparams)(PC) + | lw KBASE, -4+PC2PROTO(k)(PC) + | sltu AT, TMP2, RA + | bnez AT, ->vm_growstack_l + |. sll TMP1, TMP1, 3 + if (op != BC_JFUNCF) { + | ins_next1 + } + |2: + | sltu AT, NARGS8:RC, TMP1 // Check for missing parameters. + | bnez AT, >3 + |. addu AT, BASE, NARGS8:RC + if (op == BC_JFUNCF) { + | decode_RD8a RD, INS + | b =>BC_JLOOP + |. decode_RD8b RD + } else { + | ins_next2 + } + | + |3: // Clear missing parameters. + | sw TISNIL, HI(AT) + | b <2 + |. addiu NARGS8:RC, NARGS8:RC, 8 + break; + + case BC_JFUNCV: +#if !LJ_HASJIT + break; +#endif + | NYI // NYI: compiled vararg functions + break; /* NYI: compiled vararg functions. */ + + case BC_IFUNCV: + | // BASE = new base, RA = BASE+framesize*8, RB = LFUNC, RC = nargs*8 + | addu TMP1, BASE, RC + | lw TMP2, L->maxstack + | addu TMP0, RA, RC + | sw LFUNC:RB, LO(TMP1) // Store copy of LFUNC. + | addiu TMP3, RC, 8+FRAME_VARG + | sltu AT, TMP0, TMP2 + | lw KBASE, -4+PC2PROTO(k)(PC) + | beqz AT, ->vm_growstack_l + |. sw TMP3, HI(TMP1) // Store delta + FRAME_VARG. + | lbu TMP2, -4+PC2PROTO(numparams)(PC) + | move RA, BASE + | move RC, TMP1 + | ins_next1 + | beqz TMP2, >3 + |. addiu BASE, TMP1, 8 + |1: + | lw TMP0, HI(RA) + | lw TMP3, LO(RA) + | sltu AT, RA, RC // Less args than parameters? + | move CARG1, TMP0 + | movz TMP0, TISNIL, AT // Clear missing parameters. + | movn CARG1, TISNIL, AT // Clear old fixarg slot (help the GC). + | sw TMP3, 8+LO(TMP1) + | addiu TMP2, TMP2, -1 + | sw TMP0, 8+HI(TMP1) + | addiu TMP1, TMP1, 8 + | sw CARG1, HI(RA) + | bnez TMP2, <1 + |. addiu RA, RA, 8 + |3: + | ins_next2 + break; + + case BC_FUNCC: + case BC_FUNCCW: + | // BASE = new base, RA = BASE+framesize*8, RB = CFUNC, RC = nargs*8 + if (op == BC_FUNCC) { + | lw CFUNCADDR, CFUNC:RB->f + } else { + | lw CFUNCADDR, DISPATCH_GL(wrapf)(DISPATCH) + } + | addu TMP1, RA, NARGS8:RC + | lw TMP2, L->maxstack + | addu RC, BASE, NARGS8:RC + | sw BASE, L->base + | sltu AT, TMP2, TMP1 + | sw RC, L->top + | li_vmstate C + if (op == BC_FUNCCW) { + | lw CARG2, CFUNC:RB->f + } + | bnez AT, ->vm_growstack_c // Need to grow stack. + |. move CARG1, L + | jalr CFUNCADDR // (lua_State *L [, lua_CFunction f]) + |. st_vmstate + | // Returns nresults. + | lw BASE, L->base + | sll RD, CRET1, 3 + | lw TMP1, L->top + | li_vmstate INTERP + | lw PC, FRAME_PC(BASE) // Fetch PC of caller. + | subu RA, TMP1, RD // RA = L->top - nresults*8 + | sw L, DISPATCH_GL(cur_L)(DISPATCH) + | b ->vm_returnc + |. st_vmstate + break; + + /* ---------------------------------------------------------------------- */ + + default: + fprintf(stderr, "Error: undefined opcode BC_%s\n", bc_names[op]); + exit(2); + break; + } +} + +static int build_backend(BuildCtx *ctx) +{ + int op; + + dasm_growpc(Dst, BC__MAX); + + build_subroutines(ctx); + + |.code_op + for (op = 0; op < BC__MAX; op++) + build_ins(ctx, (BCOp)op, op); + + return BC__MAX; +} + +/* Emit pseudo frame-info for all assembler functions. */ +static void emit_asm_debug(BuildCtx *ctx) +{ + int fcofs = (int)((uint8_t *)ctx->glob[GLOB_vm_ffi_call] - ctx->code); + int i; + switch (ctx->mode) { + case BUILD_elfasm: + fprintf(ctx->fp, "\t.section .debug_frame,\"\",@progbits\n"); + fprintf(ctx->fp, + ".Lframe0:\n" + "\t.4byte .LECIE0-.LSCIE0\n" + ".LSCIE0:\n" + "\t.4byte 0xffffffff\n" + "\t.byte 0x1\n" + "\t.string \"\"\n" + "\t.uleb128 0x1\n" + "\t.sleb128 -4\n" + "\t.byte 31\n" + "\t.byte 0xc\n\t.uleb128 29\n\t.uleb128 0\n" + "\t.align 2\n" + ".LECIE0:\n\n"); + fprintf(ctx->fp, + ".LSFDE0:\n" + "\t.4byte .LEFDE0-.LASFDE0\n" + ".LASFDE0:\n" + "\t.4byte .Lframe0\n" + "\t.4byte .Lbegin\n" + "\t.4byte %d\n" + "\t.byte 0xe\n\t.uleb128 %d\n" + "\t.byte 0x9f\n\t.sleb128 1\n" + "\t.byte 0x9e\n\t.sleb128 2\n", + fcofs, CFRAME_SIZE); + for (i = 23; i >= 16; i--) + fprintf(ctx->fp, "\t.byte %d\n\t.uleb128 %d\n", 0x80+i, 26-i); +#if !LJ_SOFTFP + for (i = 30; i >= 20; i -= 2) + fprintf(ctx->fp, "\t.byte %d\n\t.uleb128 %d\n", 0x80+32+i, 42-i); +#endif + fprintf(ctx->fp, + "\t.align 2\n" + ".LEFDE0:\n\n"); +#if LJ_HASFFI + fprintf(ctx->fp, + ".LSFDE1:\n" + "\t.4byte .LEFDE1-.LASFDE1\n" + ".LASFDE1:\n" + "\t.4byte .Lframe0\n" + "\t.4byte lj_vm_ffi_call\n" + "\t.4byte %d\n" + "\t.byte 0x9f\n\t.uleb128 1\n" + "\t.byte 0x90\n\t.uleb128 2\n" + "\t.byte 0xd\n\t.uleb128 0x10\n" + "\t.align 2\n" + ".LEFDE1:\n\n", (int)ctx->codesz - fcofs); +#endif +#if !LJ_NO_UNWIND + fprintf(ctx->fp, "\t.section .eh_frame,\"aw\",@progbits\n"); + fprintf(ctx->fp, + "\t.globl lj_err_unwind_dwarf\n" + ".Lframe1:\n" + "\t.4byte .LECIE1-.LSCIE1\n" + ".LSCIE1:\n" + "\t.4byte 0\n" + "\t.byte 0x1\n" + "\t.string \"zPR\"\n" + "\t.uleb128 0x1\n" + "\t.sleb128 -4\n" + "\t.byte 31\n" + "\t.uleb128 6\n" /* augmentation length */ + "\t.byte 0\n" + "\t.4byte lj_err_unwind_dwarf\n" + "\t.byte 0\n" + "\t.byte 0xc\n\t.uleb128 29\n\t.uleb128 0\n" + "\t.align 2\n" + ".LECIE1:\n\n"); + fprintf(ctx->fp, + ".LSFDE2:\n" + "\t.4byte .LEFDE2-.LASFDE2\n" + ".LASFDE2:\n" + "\t.4byte .LASFDE2-.Lframe1\n" + "\t.4byte .Lbegin\n" + "\t.4byte %d\n" + "\t.uleb128 0\n" /* augmentation length */ + "\t.byte 0xe\n\t.uleb128 %d\n" + "\t.byte 0x9f\n\t.sleb128 1\n" + "\t.byte 0x9e\n\t.sleb128 2\n", + fcofs, CFRAME_SIZE); + for (i = 23; i >= 16; i--) + fprintf(ctx->fp, "\t.byte %d\n\t.uleb128 %d\n", 0x80+i, 26-i); +#if !LJ_SOFTFP + for (i = 30; i >= 20; i -= 2) + fprintf(ctx->fp, "\t.byte %d\n\t.uleb128 %d\n", 0x80+32+i, 42-i); +#endif + fprintf(ctx->fp, + "\t.align 2\n" + ".LEFDE2:\n\n"); +#if LJ_HASFFI + fprintf(ctx->fp, + ".Lframe2:\n" + "\t.4byte .LECIE2-.LSCIE2\n" + ".LSCIE2:\n" + "\t.4byte 0\n" + "\t.byte 0x1\n" + "\t.string \"zR\"\n" + "\t.uleb128 0x1\n" + "\t.sleb128 -4\n" + "\t.byte 31\n" + "\t.uleb128 1\n" /* augmentation length */ + "\t.byte 0\n" + "\t.byte 0xc\n\t.uleb128 29\n\t.uleb128 0\n" + "\t.align 2\n" + ".LECIE2:\n\n"); + fprintf(ctx->fp, + ".LSFDE3:\n" + "\t.4byte .LEFDE3-.LASFDE3\n" + ".LASFDE3:\n" + "\t.4byte .LASFDE3-.Lframe2\n" + "\t.4byte lj_vm_ffi_call\n" + "\t.4byte %d\n" + "\t.uleb128 0\n" /* augmentation length */ + "\t.byte 0x9f\n\t.uleb128 1\n" + "\t.byte 0x90\n\t.uleb128 2\n" + "\t.byte 0xd\n\t.uleb128 0x10\n" + "\t.align 2\n" + ".LEFDE3:\n\n", (int)ctx->codesz - fcofs); +#endif +#endif + break; + default: + break; + } +} + diff --git a/lib/LuaJIT/vm_mips64.dasc b/lib/LuaJIT/vm_mips64.dasc new file mode 100644 index 0000000..1682c81 --- /dev/null +++ b/lib/LuaJIT/vm_mips64.dasc @@ -0,0 +1,5112 @@ +|// Low-level VM code for MIPS64 CPUs. +|// Bytecode interpreter, fast functions and helper functions. +|// Copyright (C) 2005-2017 Mike Pall. See Copyright Notice in luajit.h +|// +|// Contributed by Djordje Kovacevic and Stefan Pejic from RT-RK.com. +|// Sponsored by Cisco Systems, Inc. +| +|.arch mips64 +|.section code_op, code_sub +| +|.actionlist build_actionlist +|.globals GLOB_ +|.globalnames globnames +|.externnames extnames +| +|// Note: The ragged indentation of the instructions is intentional. +|// The starting columns indicate data dependencies. +| +|//----------------------------------------------------------------------- +| +|// Fixed register assignments for the interpreter. +|// Don't use: r0 = 0, r26/r27 = reserved, r28 = gp, r29 = sp, r31 = ra +| +|.macro .FPU, a, b +|.if FPU +| a, b +|.endif +|.endmacro +| +|// The following must be C callee-save (but BASE is often refetched). +|.define BASE, r16 // Base of current Lua stack frame. +|.define KBASE, r17 // Constants of current Lua function. +|.define PC, r18 // Next PC. +|.define DISPATCH, r19 // Opcode dispatch table. +|.define LREG, r20 // Register holding lua_State (also in SAVE_L). +|.define MULTRES, r21 // Size of multi-result: (nresults+1)*8. +| +|.define JGL, r30 // On-trace: global_State + 32768. +| +|// Constants for type-comparisons, stores and conversions. C callee-save. +|.define TISNIL, r30 +|.define TISNUM, r22 +|.if FPU +|.define TOBIT, f30 // 2^52 + 2^51. +|.endif +| +|// The following temporaries are not saved across C calls, except for RA. +|.define RA, r23 // Callee-save. +|.define RB, r8 +|.define RC, r9 +|.define RD, r10 +|.define INS, r11 +| +|.define AT, r1 // Assembler temporary. +|.define TMP0, r12 +|.define TMP1, r13 +|.define TMP2, r14 +|.define TMP3, r15 +| +|// MIPS n64 calling convention. +|.define CFUNCADDR, r25 +|.define CARG1, r4 +|.define CARG2, r5 +|.define CARG3, r6 +|.define CARG4, r7 +|.define CARG5, r8 +|.define CARG6, r9 +|.define CARG7, r10 +|.define CARG8, r11 +| +|.define CRET1, r2 +|.define CRET2, r3 +| +|.if FPU +|.define FARG1, f12 +|.define FARG2, f13 +|.define FARG3, f14 +|.define FARG4, f15 +|.define FARG5, f16 +|.define FARG6, f17 +|.define FARG7, f18 +|.define FARG8, f19 +| +|.define FRET1, f0 +|.define FRET2, f2 +|.endif +| +|// Stack layout while in interpreter. Must match with lj_frame.h. +|.if FPU // MIPS64 hard-float. +| +|.define CFRAME_SPACE, 192 // Delta for sp. +| +|//----- 16 byte aligned, <-- sp entering interpreter +|.define SAVE_ERRF, 188(sp) // 32 bit values. +|.define SAVE_NRES, 184(sp) +|.define SAVE_CFRAME, 176(sp) // 64 bit values. +|.define SAVE_L, 168(sp) +|.define SAVE_PC, 160(sp) +|//----- 16 byte aligned +|.define SAVE_GPR_, 80 // .. 80+10*8: 64 bit GPR saves. +|.define SAVE_FPR_, 16 // .. 16+8*8: 64 bit FPR saves. +| +|.else // MIPS64 soft-float +| +|.define CFRAME_SPACE, 128 // Delta for sp. +| +|//----- 16 byte aligned, <-- sp entering interpreter +|.define SAVE_ERRF, 124(sp) // 32 bit values. +|.define SAVE_NRES, 120(sp) +|.define SAVE_CFRAME, 112(sp) // 64 bit values. +|.define SAVE_L, 104(sp) +|.define SAVE_PC, 96(sp) +|//----- 16 byte aligned +|.define SAVE_GPR_, 16 // .. 16+10*8: 64 bit GPR saves. +| +|.endif +| +|.define TMPX, 8(sp) // Unused by interpreter, temp for JIT code. +|.define TMPD, 0(sp) +|//----- 16 byte aligned +| +|.define TMPD_OFS, 0 +| +|.define SAVE_MULTRES, TMPD +| +|//----------------------------------------------------------------------- +| +|.macro saveregs +| daddiu sp, sp, -CFRAME_SPACE +| sd ra, SAVE_GPR_+9*8(sp) +| sd r30, SAVE_GPR_+8*8(sp) +| .FPU sdc1 f31, SAVE_FPR_+7*8(sp) +| sd r23, SAVE_GPR_+7*8(sp) +| .FPU sdc1 f30, SAVE_FPR_+6*8(sp) +| sd r22, SAVE_GPR_+6*8(sp) +| .FPU sdc1 f29, SAVE_FPR_+5*8(sp) +| sd r21, SAVE_GPR_+5*8(sp) +| .FPU sdc1 f28, SAVE_FPR_+4*8(sp) +| sd r20, SAVE_GPR_+4*8(sp) +| .FPU sdc1 f27, SAVE_FPR_+3*8(sp) +| sd r19, SAVE_GPR_+3*8(sp) +| .FPU sdc1 f26, SAVE_FPR_+2*8(sp) +| sd r18, SAVE_GPR_+2*8(sp) +| .FPU sdc1 f25, SAVE_FPR_+1*8(sp) +| sd r17, SAVE_GPR_+1*8(sp) +| .FPU sdc1 f24, SAVE_FPR_+0*8(sp) +| sd r16, SAVE_GPR_+0*8(sp) +|.endmacro +| +|.macro restoreregs_ret +| ld ra, SAVE_GPR_+9*8(sp) +| ld r30, SAVE_GPR_+8*8(sp) +| ld r23, SAVE_GPR_+7*8(sp) +| .FPU ldc1 f31, SAVE_FPR_+7*8(sp) +| ld r22, SAVE_GPR_+6*8(sp) +| .FPU ldc1 f30, SAVE_FPR_+6*8(sp) +| ld r21, SAVE_GPR_+5*8(sp) +| .FPU ldc1 f29, SAVE_FPR_+5*8(sp) +| ld r20, SAVE_GPR_+4*8(sp) +| .FPU ldc1 f28, SAVE_FPR_+4*8(sp) +| ld r19, SAVE_GPR_+3*8(sp) +| .FPU ldc1 f27, SAVE_FPR_+3*8(sp) +| ld r18, SAVE_GPR_+2*8(sp) +| .FPU ldc1 f26, SAVE_FPR_+2*8(sp) +| ld r17, SAVE_GPR_+1*8(sp) +| .FPU ldc1 f25, SAVE_FPR_+1*8(sp) +| ld r16, SAVE_GPR_+0*8(sp) +| .FPU ldc1 f24, SAVE_FPR_+0*8(sp) +| jr ra +| daddiu sp, sp, CFRAME_SPACE +|.endmacro +| +|// Type definitions. Some of these are only used for documentation. +|.type L, lua_State, LREG +|.type GL, global_State +|.type TVALUE, TValue +|.type GCOBJ, GCobj +|.type STR, GCstr +|.type TAB, GCtab +|.type LFUNC, GCfuncL +|.type CFUNC, GCfuncC +|.type PROTO, GCproto +|.type UPVAL, GCupval +|.type NODE, Node +|.type NARGS8, int +|.type TRACE, GCtrace +|.type SBUF, SBuf +| +|//----------------------------------------------------------------------- +| +|// Trap for not-yet-implemented parts. +|.macro NYI; .long 0xf0f0f0f0; .endmacro +| +|// Macros to mark delay slots. +|.macro ., a; a; .endmacro +|.macro ., a,b; a,b; .endmacro +|.macro ., a,b,c; a,b,c; .endmacro +|.macro ., a,b,c,d; a,b,c,d; .endmacro +| +|.define FRAME_PC, -8 +|.define FRAME_FUNC, -16 +| +|//----------------------------------------------------------------------- +| +|// Endian-specific defines. +|.if ENDIAN_LE +|.define HI, 4 +|.define LO, 0 +|.define OFS_RD, 2 +|.define OFS_RA, 1 +|.define OFS_OP, 0 +|.else +|.define HI, 0 +|.define LO, 4 +|.define OFS_RD, 0 +|.define OFS_RA, 2 +|.define OFS_OP, 3 +|.endif +| +|// Instruction decode. +|.macro decode_OP1, dst, ins; andi dst, ins, 0xff; .endmacro +|.macro decode_OP8a, dst, ins; andi dst, ins, 0xff; .endmacro +|.macro decode_OP8b, dst; sll dst, dst, 3; .endmacro +|.macro decode_RC8a, dst, ins; srl dst, ins, 13; .endmacro +|.macro decode_RC8b, dst; andi dst, dst, 0x7f8; .endmacro +|.macro decode_RD4b, dst; sll dst, dst, 2; .endmacro +|.macro decode_RA8a, dst, ins; srl dst, ins, 5; .endmacro +|.macro decode_RA8b, dst; andi dst, dst, 0x7f8; .endmacro +|.macro decode_RB8a, dst, ins; srl dst, ins, 21; .endmacro +|.macro decode_RB8b, dst; andi dst, dst, 0x7f8; .endmacro +|.macro decode_RD8a, dst, ins; srl dst, ins, 16; .endmacro +|.macro decode_RD8b, dst; sll dst, dst, 3; .endmacro +|.macro decode_RDtoRC8, dst, src; andi dst, src, 0x7f8; .endmacro +| +|// Instruction fetch. +|.macro ins_NEXT1 +| lw INS, 0(PC) +| daddiu PC, PC, 4 +|.endmacro +|// Instruction decode+dispatch. +|.macro ins_NEXT2 +| decode_OP8a TMP1, INS +| decode_OP8b TMP1 +| daddu TMP0, DISPATCH, TMP1 +| decode_RD8a RD, INS +| ld AT, 0(TMP0) +| decode_RA8a RA, INS +| decode_RD8b RD +| jr AT +| decode_RA8b RA +|.endmacro +|.macro ins_NEXT +| ins_NEXT1 +| ins_NEXT2 +|.endmacro +| +|// Instruction footer. +|.if 1 +| // Replicated dispatch. Less unpredictable branches, but higher I-Cache use. +| .define ins_next, ins_NEXT +| .define ins_next_, ins_NEXT +| .define ins_next1, ins_NEXT1 +| .define ins_next2, ins_NEXT2 +|.else +| // Common dispatch. Lower I-Cache use, only one (very) unpredictable branch. +| // Affects only certain kinds of benchmarks (and only with -j off). +| .macro ins_next +| b ->ins_next +| .endmacro +| .macro ins_next1 +| .endmacro +| .macro ins_next2 +| b ->ins_next +| .endmacro +| .macro ins_next_ +| ->ins_next: +| ins_NEXT +| .endmacro +|.endif +| +|// Call decode and dispatch. +|.macro ins_callt +| // BASE = new base, RB = LFUNC/CFUNC, RC = nargs*8, FRAME_PC(BASE) = PC +| ld PC, LFUNC:RB->pc +| lw INS, 0(PC) +| daddiu PC, PC, 4 +| decode_OP8a TMP1, INS +| decode_RA8a RA, INS +| decode_OP8b TMP1 +| decode_RA8b RA +| daddu TMP0, DISPATCH, TMP1 +| ld TMP0, 0(TMP0) +| jr TMP0 +| daddu RA, RA, BASE +|.endmacro +| +|.macro ins_call +| // BASE = new base, RB = LFUNC/CFUNC, RC = nargs*8, PC = caller PC +| sd PC, FRAME_PC(BASE) +| ins_callt +|.endmacro +| +|//----------------------------------------------------------------------- +| +|.macro branch_RD +| srl TMP0, RD, 1 +| lui AT, (-(BCBIAS_J*4 >> 16) & 65535) +| addu TMP0, TMP0, AT +| daddu PC, PC, TMP0 +|.endmacro +| +|// Assumes DISPATCH is relative to GL. +#define DISPATCH_GL(field) (GG_DISP2G + (int)offsetof(global_State, field)) +#define DISPATCH_J(field) (GG_DISP2J + (int)offsetof(jit_State, field)) +#define GG_DISP2GOT (GG_OFS(got) - GG_OFS(dispatch)) +#define DISPATCH_GOT(name) (GG_DISP2GOT + sizeof(void*)*LJ_GOT_##name) +| +#define PC2PROTO(field) ((int)offsetof(GCproto, field)-(int)sizeof(GCproto)) +| +|.macro load_got, func +| ld CFUNCADDR, DISPATCH_GOT(func)(DISPATCH) +|.endmacro +|// Much faster. Sadly, there's no easy way to force the required code layout. +|// .macro call_intern, func; bal extern func; .endmacro +|.macro call_intern, func; jalr CFUNCADDR; .endmacro +|.macro call_extern; jalr CFUNCADDR; .endmacro +|.macro jmp_extern; jr CFUNCADDR; .endmacro +| +|.macro hotcheck, delta, target +| dsrl TMP1, PC, 1 +| andi TMP1, TMP1, 126 +| daddu TMP1, TMP1, DISPATCH +| lhu TMP2, GG_DISP2HOT(TMP1) +| addiu TMP2, TMP2, -delta +| bltz TMP2, target +|. sh TMP2, GG_DISP2HOT(TMP1) +|.endmacro +| +|.macro hotloop +| hotcheck HOTCOUNT_LOOP, ->vm_hotloop +|.endmacro +| +|.macro hotcall +| hotcheck HOTCOUNT_CALL, ->vm_hotcall +|.endmacro +| +|// Set current VM state. Uses TMP0. +|.macro li_vmstate, st; li TMP0, ~LJ_VMST_..st; .endmacro +|.macro st_vmstate; sw TMP0, DISPATCH_GL(vmstate)(DISPATCH); .endmacro +| +|// Move table write barrier back. Overwrites mark and tmp. +|.macro barrierback, tab, mark, tmp, target +| ld tmp, DISPATCH_GL(gc.grayagain)(DISPATCH) +| andi mark, mark, ~LJ_GC_BLACK & 255 // black2gray(tab) +| sd tab, DISPATCH_GL(gc.grayagain)(DISPATCH) +| sb mark, tab->marked +| b target +|. sd tmp, tab->gclist +|.endmacro +| +|// Clear type tag. Isolate lowest 14+32+1=47 bits of reg. +|.macro cleartp, reg; dextm reg, reg, 0, 14; .endmacro +|.macro cleartp, dst, reg; dextm dst, reg, 0, 14; .endmacro +| +|// Set type tag: Merge 17 type bits into bits [15+32=47, 31+32+1=64) of dst. +|.macro settp, dst, tp; dinsu dst, tp, 15, 31; .endmacro +| +|// Extract (negative) type tag. +|.macro gettp, dst, src; dsra dst, src, 47; .endmacro +| +|// Macros to check the TValue type and extract the GCobj. Branch on failure. +|.macro checktp, reg, tp, target +| gettp AT, reg +| daddiu AT, AT, tp +| bnez AT, target +|. cleartp reg +|.endmacro +|.macro checktp, dst, reg, tp, target +| gettp AT, reg +| daddiu AT, AT, tp +| bnez AT, target +|. cleartp dst, reg +|.endmacro +|.macro checkstr, reg, target; checktp reg, -LJ_TSTR, target; .endmacro +|.macro checktab, reg, target; checktp reg, -LJ_TTAB, target; .endmacro +|.macro checkfunc, reg, target; checktp reg, -LJ_TFUNC, target; .endmacro +|.macro checkint, reg, target // Caveat: has delay slot! +| gettp AT, reg +| bne AT, TISNUM, target +|.endmacro +|.macro checknum, reg, target // Caveat: has delay slot! +| gettp AT, reg +| sltiu AT, AT, LJ_TISNUM +| beqz AT, target +|.endmacro +| +|.macro mov_false, reg +| lu reg, 0x8000 +| dsll reg, reg, 32 +| not reg, reg +|.endmacro +|.macro mov_true, reg +| li reg, 0x0001 +| dsll reg, reg, 48 +| not reg, reg +|.endmacro +| +|//----------------------------------------------------------------------- + +/* Generate subroutines used by opcodes and other parts of the VM. */ +/* The .code_sub section should be last to help static branch prediction. */ +static void build_subroutines(BuildCtx *ctx) +{ + |.code_sub + | + |//----------------------------------------------------------------------- + |//-- Return handling ---------------------------------------------------- + |//----------------------------------------------------------------------- + | + |->vm_returnp: + | // See vm_return. Also: TMP2 = previous base. + | andi AT, PC, FRAME_P + | beqz AT, ->cont_dispatch + | + | // Return from pcall or xpcall fast func. + |. mov_true TMP1 + | ld PC, FRAME_PC(TMP2) // Fetch PC of previous frame. + | move BASE, TMP2 // Restore caller base. + | // Prepending may overwrite the pcall frame, so do it at the end. + | sd TMP1, -8(RA) // Prepend true to results. + | daddiu RA, RA, -8 + | + |->vm_returnc: + | addiu RD, RD, 8 // RD = (nresults+1)*8. + | andi TMP0, PC, FRAME_TYPE + | beqz RD, ->vm_unwind_c_eh + |. li CRET1, LUA_YIELD + | beqz TMP0, ->BC_RET_Z // Handle regular return to Lua. + |. move MULTRES, RD + | + |->vm_return: + | // BASE = base, RA = resultptr, RD/MULTRES = (nresults+1)*8, PC = return + | // TMP0 = PC & FRAME_TYPE + | li TMP2, -8 + | xori AT, TMP0, FRAME_C + | and TMP2, PC, TMP2 + | bnez AT, ->vm_returnp + | dsubu TMP2, BASE, TMP2 // TMP2 = previous base. + | + | addiu TMP1, RD, -8 + | sd TMP2, L->base + | li_vmstate C + | lw TMP2, SAVE_NRES + | daddiu BASE, BASE, -16 + | st_vmstate + | beqz TMP1, >2 + |. sll TMP2, TMP2, 3 + |1: + | addiu TMP1, TMP1, -8 + | ld CRET1, 0(RA) + | daddiu RA, RA, 8 + | sd CRET1, 0(BASE) + | bnez TMP1, <1 + |. daddiu BASE, BASE, 8 + | + |2: + | bne TMP2, RD, >6 + |3: + |. sd BASE, L->top // Store new top. + | + |->vm_leave_cp: + | ld TMP0, SAVE_CFRAME // Restore previous C frame. + | move CRET1, r0 // Ok return status for vm_pcall. + | sd TMP0, L->cframe + | + |->vm_leave_unw: + | restoreregs_ret + | + |6: + | ld TMP1, L->maxstack + | slt AT, TMP2, RD + | bnez AT, >7 // Less results wanted? + | // More results wanted. Check stack size and fill up results with nil. + |. slt AT, BASE, TMP1 + | beqz AT, >8 + |. nop + | sd TISNIL, 0(BASE) + | addiu RD, RD, 8 + | b <2 + |. daddiu BASE, BASE, 8 + | + |7: // Less results wanted. + | subu TMP0, RD, TMP2 + | dsubu TMP0, BASE, TMP0 // Either keep top or shrink it. + | b <3 + |. movn BASE, TMP0, TMP2 // LUA_MULTRET+1 case? + | + |8: // Corner case: need to grow stack for filling up results. + | // This can happen if: + | // - A C function grows the stack (a lot). + | // - The GC shrinks the stack in between. + | // - A return back from a lua_call() with (high) nresults adjustment. + | load_got lj_state_growstack + | move MULTRES, RD + | srl CARG2, TMP2, 3 + | call_intern lj_state_growstack // (lua_State *L, int n) + |. move CARG1, L + | lw TMP2, SAVE_NRES + | ld BASE, L->top // Need the (realloced) L->top in BASE. + | move RD, MULTRES + | b <2 + |. sll TMP2, TMP2, 3 + | + |->vm_unwind_c: // Unwind C stack, return from vm_pcall. + | // (void *cframe, int errcode) + | move sp, CARG1 + | move CRET1, CARG2 + |->vm_unwind_c_eh: // Landing pad for external unwinder. + | ld L, SAVE_L + | li TMP0, ~LJ_VMST_C + | ld GL:TMP1, L->glref + | b ->vm_leave_unw + |. sw TMP0, GL:TMP1->vmstate + | + |->vm_unwind_ff: // Unwind C stack, return from ff pcall. + | // (void *cframe) + | li AT, -4 + | and sp, CARG1, AT + |->vm_unwind_ff_eh: // Landing pad for external unwinder. + | ld L, SAVE_L + | .FPU lui TMP3, 0x59c0 // TOBIT = 2^52 + 2^51 (float). + | li TISNIL, LJ_TNIL + | li TISNUM, LJ_TISNUM + | ld BASE, L->base + | ld DISPATCH, L->glref // Setup pointer to dispatch table. + | .FPU mtc1 TMP3, TOBIT + | mov_false TMP1 + | li_vmstate INTERP + | ld PC, FRAME_PC(BASE) // Fetch PC of previous frame. + | .FPU cvt.d.s TOBIT, TOBIT + | daddiu RA, BASE, -8 // Results start at BASE-8. + | daddiu DISPATCH, DISPATCH, GG_G2DISP + | sd TMP1, 0(RA) // Prepend false to error message. + | st_vmstate + | b ->vm_returnc + |. li RD, 16 // 2 results: false + error message. + | + |//----------------------------------------------------------------------- + |//-- Grow stack for calls ----------------------------------------------- + |//----------------------------------------------------------------------- + | + |->vm_growstack_c: // Grow stack for C function. + | b >2 + |. li CARG2, LUA_MINSTACK + | + |->vm_growstack_l: // Grow stack for Lua function. + | // BASE = new base, RA = BASE+framesize*8, RC = nargs*8, PC = first PC + | daddu RC, BASE, RC + | dsubu RA, RA, BASE + | sd BASE, L->base + | daddiu PC, PC, 4 // Must point after first instruction. + | sd RC, L->top + | srl CARG2, RA, 3 + |2: + | // L->base = new base, L->top = top + | load_got lj_state_growstack + | sd PC, SAVE_PC + | call_intern lj_state_growstack // (lua_State *L, int n) + |. move CARG1, L + | ld BASE, L->base + | ld RC, L->top + | ld LFUNC:RB, FRAME_FUNC(BASE) + | dsubu RC, RC, BASE + | cleartp LFUNC:RB + | // BASE = new base, RB = LFUNC/CFUNC, RC = nargs*8, FRAME_PC(BASE) = PC + | ins_callt // Just retry the call. + | + |//----------------------------------------------------------------------- + |//-- Entry points into the assembler VM --------------------------------- + |//----------------------------------------------------------------------- + | + |->vm_resume: // Setup C frame and resume thread. + | // (lua_State *L, TValue *base, int nres1 = 0, ptrdiff_t ef = 0) + | saveregs + | move L, CARG1 + | ld DISPATCH, L->glref // Setup pointer to dispatch table. + | move BASE, CARG2 + | lbu TMP1, L->status + | sd L, SAVE_L + | li PC, FRAME_CP + | daddiu TMP0, sp, CFRAME_RESUME + | daddiu DISPATCH, DISPATCH, GG_G2DISP + | sw r0, SAVE_NRES + | sw r0, SAVE_ERRF + | sd CARG1, SAVE_PC // Any value outside of bytecode is ok. + | sd r0, SAVE_CFRAME + | beqz TMP1, >3 + |. sd TMP0, L->cframe + | + | // Resume after yield (like a return). + | sd L, DISPATCH_GL(cur_L)(DISPATCH) + | move RA, BASE + | ld BASE, L->base + | ld TMP1, L->top + | ld PC, FRAME_PC(BASE) + | .FPU lui TMP3, 0x59c0 // TOBIT = 2^52 + 2^51 (float). + | dsubu RD, TMP1, BASE + | .FPU mtc1 TMP3, TOBIT + | sb r0, L->status + | .FPU cvt.d.s TOBIT, TOBIT + | li_vmstate INTERP + | daddiu RD, RD, 8 + | st_vmstate + | move MULTRES, RD + | andi TMP0, PC, FRAME_TYPE + | li TISNIL, LJ_TNIL + | beqz TMP0, ->BC_RET_Z + |. li TISNUM, LJ_TISNUM + | b ->vm_return + |. nop + | + |->vm_pcall: // Setup protected C frame and enter VM. + | // (lua_State *L, TValue *base, int nres1, ptrdiff_t ef) + | saveregs + | sw CARG4, SAVE_ERRF + | b >1 + |. li PC, FRAME_CP + | + |->vm_call: // Setup C frame and enter VM. + | // (lua_State *L, TValue *base, int nres1) + | saveregs + | li PC, FRAME_C + | + |1: // Entry point for vm_pcall above (PC = ftype). + | ld TMP1, L:CARG1->cframe + | move L, CARG1 + | sw CARG3, SAVE_NRES + | ld DISPATCH, L->glref // Setup pointer to dispatch table. + | sd CARG1, SAVE_L + | move BASE, CARG2 + | daddiu DISPATCH, DISPATCH, GG_G2DISP + | sd CARG1, SAVE_PC // Any value outside of bytecode is ok. + | sd TMP1, SAVE_CFRAME + | sd sp, L->cframe // Add our C frame to cframe chain. + | + |3: // Entry point for vm_cpcall/vm_resume (BASE = base, PC = ftype). + | sd L, DISPATCH_GL(cur_L)(DISPATCH) + | ld TMP2, L->base // TMP2 = old base (used in vmeta_call). + | .FPU lui TMP3, 0x59c0 // TOBIT = 2^52 + 2^51 (float). + | ld TMP1, L->top + | .FPU mtc1 TMP3, TOBIT + | daddu PC, PC, BASE + | dsubu NARGS8:RC, TMP1, BASE + | li TISNUM, LJ_TISNUM + | dsubu PC, PC, TMP2 // PC = frame delta + frame type + | .FPU cvt.d.s TOBIT, TOBIT + | li_vmstate INTERP + | li TISNIL, LJ_TNIL + | st_vmstate + | + |->vm_call_dispatch: + | // TMP2 = old base, BASE = new base, RC = nargs*8, PC = caller PC + | ld LFUNC:RB, FRAME_FUNC(BASE) + | checkfunc LFUNC:RB, ->vmeta_call + | + |->vm_call_dispatch_f: + | ins_call + | // BASE = new base, RB = func, RC = nargs*8, PC = caller PC + | + |->vm_cpcall: // Setup protected C frame, call C. + | // (lua_State *L, lua_CFunction func, void *ud, lua_CPFunction cp) + | saveregs + | move L, CARG1 + | ld TMP0, L:CARG1->stack + | sd CARG1, SAVE_L + | ld TMP1, L->top + | ld DISPATCH, L->glref // Setup pointer to dispatch table. + | sd CARG1, SAVE_PC // Any value outside of bytecode is ok. + | dsubu TMP0, TMP0, TMP1 // Compute -savestack(L, L->top). + | ld TMP1, L->cframe + | daddiu DISPATCH, DISPATCH, GG_G2DISP + | sw TMP0, SAVE_NRES // Neg. delta means cframe w/o frame. + | sw r0, SAVE_ERRF // No error function. + | sd TMP1, SAVE_CFRAME + | sd sp, L->cframe // Add our C frame to cframe chain. + | sd L, DISPATCH_GL(cur_L)(DISPATCH) + | jalr CARG4 // (lua_State *L, lua_CFunction func, void *ud) + |. move CFUNCADDR, CARG4 + | move BASE, CRET1 + | bnez CRET1, <3 // Else continue with the call. + |. li PC, FRAME_CP + | b ->vm_leave_cp // No base? Just remove C frame. + |. nop + | + |//----------------------------------------------------------------------- + |//-- Metamethod handling ------------------------------------------------ + |//----------------------------------------------------------------------- + | + |// The lj_meta_* functions (except for lj_meta_cat) don't reallocate the + |// stack, so BASE doesn't need to be reloaded across these calls. + | + |//-- Continuation dispatch ---------------------------------------------- + | + |->cont_dispatch: + | // BASE = meta base, RA = resultptr, RD = (nresults+1)*8 + | ld TMP0, -32(BASE) // Continuation. + | move RB, BASE + | move BASE, TMP2 // Restore caller BASE. + | ld LFUNC:TMP1, FRAME_FUNC(TMP2) + |.if FFI + | sltiu AT, TMP0, 2 + |.endif + | ld PC, -24(RB) // Restore PC from [cont|PC]. + | cleartp LFUNC:TMP1 + | daddu TMP2, RA, RD + | ld TMP1, LFUNC:TMP1->pc + |.if FFI + | bnez AT, >1 + |.endif + |. sd TISNIL, -8(TMP2) // Ensure one valid arg. + | // BASE = base, RA = resultptr, RB = meta base + | jr TMP0 // Jump to continuation. + |. ld KBASE, PC2PROTO(k)(TMP1) + | + |.if FFI + |1: + | bnez TMP0, ->cont_ffi_callback // cont = 1: return from FFI callback. + | // cont = 0: tailcall from C function. + |. daddiu TMP1, RB, -32 + | b ->vm_call_tail + |. dsubu RC, TMP1, BASE + |.endif + | + |->cont_cat: // RA = resultptr, RB = meta base + | lw INS, -4(PC) + | daddiu CARG2, RB, -32 + | ld CRET1, 0(RA) + | decode_RB8a MULTRES, INS + | decode_RA8a RA, INS + | decode_RB8b MULTRES + | decode_RA8b RA + | daddu TMP1, BASE, MULTRES + | sd BASE, L->base + | dsubu CARG3, CARG2, TMP1 + | bne TMP1, CARG2, ->BC_CAT_Z + |. sd CRET1, 0(CARG2) + | daddu RA, BASE, RA + | b ->cont_nop + |. sd CRET1, 0(RA) + | + |//-- Table indexing metamethods ----------------------------------------- + | + |->vmeta_tgets1: + | daddiu CARG3, DISPATCH, DISPATCH_GL(tmptv) + | li TMP0, LJ_TSTR + | settp STR:RC, TMP0 + | b >1 + |. sd STR:RC, 0(CARG3) + | + |->vmeta_tgets: + | daddiu CARG2, DISPATCH, DISPATCH_GL(tmptv) + | li TMP0, LJ_TTAB + | li TMP1, LJ_TSTR + | settp TAB:RB, TMP0 + | daddiu CARG3, DISPATCH, DISPATCH_GL(tmptv2) + | sd TAB:RB, 0(CARG2) + | settp STR:RC, TMP1 + | b >1 + |. sd STR:RC, 0(CARG3) + | + |->vmeta_tgetb: // TMP0 = index + | daddiu CARG3, DISPATCH, DISPATCH_GL(tmptv) + | settp TMP0, TISNUM + | sd TMP0, 0(CARG3) + | + |->vmeta_tgetv: + |1: + | load_got lj_meta_tget + | sd BASE, L->base + | sd PC, SAVE_PC + | call_intern lj_meta_tget // (lua_State *L, TValue *o, TValue *k) + |. move CARG1, L + | // Returns TValue * (finished) or NULL (metamethod). + | beqz CRET1, >3 + |. daddiu TMP1, BASE, -FRAME_CONT + | ld CARG1, 0(CRET1) + | ins_next1 + | sd CARG1, 0(RA) + | ins_next2 + | + |3: // Call __index metamethod. + | // BASE = base, L->top = new base, stack = cont/func/t/k + | ld BASE, L->top + | sd PC, -24(BASE) // [cont|PC] + | dsubu PC, BASE, TMP1 + | ld LFUNC:RB, FRAME_FUNC(BASE) // Guaranteed to be a function here. + | cleartp LFUNC:RB + | b ->vm_call_dispatch_f + |. li NARGS8:RC, 16 // 2 args for func(t, k). + | + |->vmeta_tgetr: + | load_got lj_tab_getinth + | call_intern lj_tab_getinth // (GCtab *t, int32_t key) + |. nop + | // Returns cTValue * or NULL. + | beqz CRET1, ->BC_TGETR_Z + |. move CARG2, TISNIL + | b ->BC_TGETR_Z + |. ld CARG2, 0(CRET1) + | + |//----------------------------------------------------------------------- + | + |->vmeta_tsets1: + | daddiu CARG3, DISPATCH, DISPATCH_GL(tmptv) + | li TMP0, LJ_TSTR + | settp STR:RC, TMP0 + | b >1 + |. sd STR:RC, 0(CARG3) + | + |->vmeta_tsets: + | daddiu CARG2, DISPATCH, DISPATCH_GL(tmptv) + | li TMP0, LJ_TTAB + | li TMP1, LJ_TSTR + | settp TAB:RB, TMP0 + | daddiu CARG3, DISPATCH, DISPATCH_GL(tmptv2) + | sd TAB:RB, 0(CARG2) + | settp STR:RC, TMP1 + | b >1 + |. sd STR:RC, 0(CARG3) + | + |->vmeta_tsetb: // TMP0 = index + | daddiu CARG3, DISPATCH, DISPATCH_GL(tmptv) + | settp TMP0, TISNUM + | sd TMP0, 0(CARG3) + | + |->vmeta_tsetv: + |1: + | load_got lj_meta_tset + | sd BASE, L->base + | sd PC, SAVE_PC + | call_intern lj_meta_tset // (lua_State *L, TValue *o, TValue *k) + |. move CARG1, L + | // Returns TValue * (finished) or NULL (metamethod). + | beqz CRET1, >3 + |. ld CARG1, 0(RA) + | // NOBARRIER: lj_meta_tset ensures the table is not black. + | ins_next1 + | sd CARG1, 0(CRET1) + | ins_next2 + | + |3: // Call __newindex metamethod. + | // BASE = base, L->top = new base, stack = cont/func/t/k/(v) + | daddiu TMP1, BASE, -FRAME_CONT + | ld BASE, L->top + | sd PC, -24(BASE) // [cont|PC] + | dsubu PC, BASE, TMP1 + | ld LFUNC:RB, FRAME_FUNC(BASE) // Guaranteed to be a function here. + | cleartp LFUNC:RB + | sd CARG1, 16(BASE) // Copy value to third argument. + | b ->vm_call_dispatch_f + |. li NARGS8:RC, 24 // 3 args for func(t, k, v) + | + |->vmeta_tsetr: + | load_got lj_tab_setinth + | sd BASE, L->base + | sd PC, SAVE_PC + | call_intern lj_tab_setinth // (lua_State *L, GCtab *t, int32_t key) + |. move CARG1, L + | // Returns TValue *. + | b ->BC_TSETR_Z + |. nop + | + |//-- Comparison metamethods --------------------------------------------- + | + |->vmeta_comp: + | // RA/RD point to o1/o2. + | move CARG2, RA + | move CARG3, RD + | load_got lj_meta_comp + | daddiu PC, PC, -4 + | sd BASE, L->base + | sd PC, SAVE_PC + | decode_OP1 CARG4, INS + | call_intern lj_meta_comp // (lua_State *L, TValue *o1, *o2, int op) + |. move CARG1, L + | // Returns 0/1 or TValue * (metamethod). + |3: + | sltiu AT, CRET1, 2 + | beqz AT, ->vmeta_binop + | negu TMP2, CRET1 + |4: + | lhu RD, OFS_RD(PC) + | daddiu PC, PC, 4 + | lui TMP1, (-(BCBIAS_J*4 >> 16) & 65535) + | sll RD, RD, 2 + | addu RD, RD, TMP1 + | and RD, RD, TMP2 + | daddu PC, PC, RD + |->cont_nop: + | ins_next + | + |->cont_ra: // RA = resultptr + | lbu TMP1, -4+OFS_RA(PC) + | ld CRET1, 0(RA) + | sll TMP1, TMP1, 3 + | daddu TMP1, BASE, TMP1 + | b ->cont_nop + |. sd CRET1, 0(TMP1) + | + |->cont_condt: // RA = resultptr + | ld TMP0, 0(RA) + | gettp TMP0, TMP0 + | sltiu AT, TMP0, LJ_TISTRUECOND + | b <4 + |. negu TMP2, AT // Branch if result is true. + | + |->cont_condf: // RA = resultptr + | ld TMP0, 0(RA) + | gettp TMP0, TMP0 + | sltiu AT, TMP0, LJ_TISTRUECOND + | b <4 + |. addiu TMP2, AT, -1 // Branch if result is false. + | + |->vmeta_equal: + | // CARG1/CARG2 point to o1/o2. TMP0 is set to 0/1. + | load_got lj_meta_equal + | cleartp LFUNC:CARG3, CARG2 + | cleartp LFUNC:CARG2, CARG1 + | move CARG4, TMP0 + | daddiu PC, PC, -4 + | sd BASE, L->base + | sd PC, SAVE_PC + | call_intern lj_meta_equal // (lua_State *L, GCobj *o1, *o2, int ne) + |. move CARG1, L + | // Returns 0/1 or TValue * (metamethod). + | b <3 + |. nop + | + |->vmeta_equal_cd: + |.if FFI + | load_got lj_meta_equal_cd + | move CARG2, INS + | daddiu PC, PC, -4 + | sd BASE, L->base + | sd PC, SAVE_PC + | call_intern lj_meta_equal_cd // (lua_State *L, BCIns op) + |. move CARG1, L + | // Returns 0/1 or TValue * (metamethod). + | b <3 + |. nop + |.endif + | + |->vmeta_istype: + | load_got lj_meta_istype + | daddiu PC, PC, -4 + | sd BASE, L->base + | srl CARG2, RA, 3 + | srl CARG3, RD, 3 + | sd PC, SAVE_PC + | call_intern lj_meta_istype // (lua_State *L, BCReg ra, BCReg tp) + |. move CARG1, L + | b ->cont_nop + |. nop + | + |//-- Arithmetic metamethods --------------------------------------------- + | + |->vmeta_unm: + | move RC, RB + | + |->vmeta_arith: + | load_got lj_meta_arith + | sd BASE, L->base + | move CARG2, RA + | sd PC, SAVE_PC + | move CARG3, RB + | move CARG4, RC + | decode_OP1 CARG5, INS // CARG5 == RB. + | call_intern lj_meta_arith // (lua_State *L, TValue *ra,*rb,*rc, BCReg op) + |. move CARG1, L + | // Returns NULL (finished) or TValue * (metamethod). + | beqz CRET1, ->cont_nop + |. nop + | + | // Call metamethod for binary op. + |->vmeta_binop: + | // BASE = old base, CRET1 = new base, stack = cont/func/o1/o2 + | dsubu TMP1, CRET1, BASE + | sd PC, -24(CRET1) // [cont|PC] + | move TMP2, BASE + | daddiu PC, TMP1, FRAME_CONT + | move BASE, CRET1 + | b ->vm_call_dispatch + |. li NARGS8:RC, 16 // 2 args for func(o1, o2). + | + |->vmeta_len: + | // CARG2 already set by BC_LEN. +#if LJ_52 + | move MULTRES, CARG1 +#endif + | load_got lj_meta_len + | sd BASE, L->base + | sd PC, SAVE_PC + | call_intern lj_meta_len // (lua_State *L, TValue *o) + |. move CARG1, L + | // Returns NULL (retry) or TValue * (metamethod base). +#if LJ_52 + | bnez CRET1, ->vmeta_binop // Binop call for compatibility. + |. nop + | b ->BC_LEN_Z + |. move CARG1, MULTRES +#else + | b ->vmeta_binop // Binop call for compatibility. + |. nop +#endif + | + |//-- Call metamethod ---------------------------------------------------- + | + |->vmeta_call: // Resolve and call __call metamethod. + | // TMP2 = old base, BASE = new base, RC = nargs*8 + | load_got lj_meta_call + | sd TMP2, L->base // This is the callers base! + | daddiu CARG2, BASE, -16 + | sd PC, SAVE_PC + | daddu CARG3, BASE, RC + | move MULTRES, NARGS8:RC + | call_intern lj_meta_call // (lua_State *L, TValue *func, TValue *top) + |. move CARG1, L + | ld LFUNC:RB, FRAME_FUNC(BASE) // Guaranteed to be a function here. + | daddiu NARGS8:RC, MULTRES, 8 // Got one more argument now. + | cleartp LFUNC:RB + | ins_call + | + |->vmeta_callt: // Resolve __call for BC_CALLT. + | // BASE = old base, RA = new base, RC = nargs*8 + | load_got lj_meta_call + | sd BASE, L->base + | daddiu CARG2, RA, -16 + | sd PC, SAVE_PC + | daddu CARG3, RA, RC + | move MULTRES, NARGS8:RC + | call_intern lj_meta_call // (lua_State *L, TValue *func, TValue *top) + |. move CARG1, L + | ld RB, FRAME_FUNC(RA) // Guaranteed to be a function here. + | ld TMP1, FRAME_PC(BASE) + | daddiu NARGS8:RC, MULTRES, 8 // Got one more argument now. + | b ->BC_CALLT_Z + |. cleartp LFUNC:CARG3, RB + | + |//-- Argument coercion for 'for' statement ------------------------------ + | + |->vmeta_for: + | load_got lj_meta_for + | sd BASE, L->base + | move CARG2, RA + | sd PC, SAVE_PC + | move MULTRES, INS + | call_intern lj_meta_for // (lua_State *L, TValue *base) + |. move CARG1, L + |.if JIT + | decode_OP1 TMP0, MULTRES + | li AT, BC_JFORI + |.endif + | decode_RA8a RA, MULTRES + | decode_RD8a RD, MULTRES + | decode_RA8b RA + |.if JIT + | beq TMP0, AT, =>BC_JFORI + |. decode_RD8b RD + | b =>BC_FORI + |. nop + |.else + | b =>BC_FORI + |. decode_RD8b RD + |.endif + | + |//----------------------------------------------------------------------- + |//-- Fast functions ----------------------------------------------------- + |//----------------------------------------------------------------------- + | + |.macro .ffunc, name + |->ff_ .. name: + |.endmacro + | + |.macro .ffunc_1, name + |->ff_ .. name: + | beqz NARGS8:RC, ->fff_fallback + |. ld CARG1, 0(BASE) + |.endmacro + | + |.macro .ffunc_2, name + |->ff_ .. name: + | sltiu AT, NARGS8:RC, 16 + | ld CARG1, 0(BASE) + | bnez AT, ->fff_fallback + |. ld CARG2, 8(BASE) + |.endmacro + | + |.macro .ffunc_n, name // Caveat: has delay slot! + |->ff_ .. name: + | ld CARG1, 0(BASE) + | beqz NARGS8:RC, ->fff_fallback + | // Either ldc1 or the 1st instruction of checknum is in the delay slot. + | .FPU ldc1 FARG1, 0(BASE) + | checknum CARG1, ->fff_fallback + |.endmacro + | + |.macro .ffunc_nn, name // Caveat: has delay slot! + |->ff_ .. name: + | ld CARG1, 0(BASE) + | sltiu AT, NARGS8:RC, 16 + | ld CARG2, 8(BASE) + | bnez AT, ->fff_fallback + |. gettp TMP0, CARG1 + | gettp TMP1, CARG2 + | sltiu TMP0, TMP0, LJ_TISNUM + | sltiu TMP1, TMP1, LJ_TISNUM + | .FPU ldc1 FARG1, 0(BASE) + | and TMP0, TMP0, TMP1 + | .FPU ldc1 FARG2, 8(BASE) + | beqz TMP0, ->fff_fallback + |.endmacro + | + |// Inlined GC threshold check. Caveat: uses TMP0 and TMP1 and has delay slot! + |.macro ffgccheck + | ld TMP0, DISPATCH_GL(gc.total)(DISPATCH) + | ld TMP1, DISPATCH_GL(gc.threshold)(DISPATCH) + | dsubu AT, TMP0, TMP1 + | bgezal AT, ->fff_gcstep + |.endmacro + | + |//-- Base library: checks ----------------------------------------------- + |.ffunc_1 assert + | gettp AT, CARG1 + | sltiu AT, AT, LJ_TISTRUECOND + | beqz AT, ->fff_fallback + |. daddiu RA, BASE, -16 + | ld PC, FRAME_PC(BASE) + | addiu RD, NARGS8:RC, 8 // Compute (nresults+1)*8. + | daddu TMP2, RA, RD + | daddiu TMP1, BASE, 8 + | beq BASE, TMP2, ->fff_res // Done if exactly 1 argument. + |. sd CARG1, 0(RA) + |1: + | ld CRET1, 0(TMP1) + | sd CRET1, -16(TMP1) + | bne TMP1, TMP2, <1 + |. daddiu TMP1, TMP1, 8 + | b ->fff_res + |. nop + | + |.ffunc_1 type + | gettp TMP0, CARG1 + | sltu TMP1, TISNUM, TMP0 + | not TMP2, TMP0 + | li TMP3, ~LJ_TISNUM + | movz TMP2, TMP3, TMP1 + | dsll TMP2, TMP2, 3 + | daddu TMP2, CFUNC:RB, TMP2 + | b ->fff_restv + |. ld CARG1, CFUNC:TMP2->upvalue + | + |//-- Base library: getters and setters --------------------------------- + | + |.ffunc_1 getmetatable + | gettp TMP2, CARG1 + | daddiu TMP0, TMP2, -LJ_TTAB + | daddiu TMP1, TMP2, -LJ_TUDATA + | movn TMP0, TMP1, TMP0 + | bnez TMP0, >6 + |. cleartp TAB:CARG1 + |1: // Field metatable must be at same offset for GCtab and GCudata! + | ld TAB:RB, TAB:CARG1->metatable + |2: + | ld STR:RC, DISPATCH_GL(gcroot[GCROOT_MMNAME+MM_metatable])(DISPATCH) + | beqz TAB:RB, ->fff_restv + |. li CARG1, LJ_TNIL + | lw TMP0, TAB:RB->hmask + | lw TMP1, STR:RC->hash + | ld NODE:TMP2, TAB:RB->node + | and TMP1, TMP1, TMP0 // idx = str->hash & tab->hmask + | dsll TMP0, TMP1, 5 + | dsll TMP1, TMP1, 3 + | dsubu TMP1, TMP0, TMP1 + | daddu NODE:TMP2, NODE:TMP2, TMP1 // node = tab->node + (idx*32-idx*8) + | li CARG4, LJ_TSTR + | settp STR:RC, CARG4 // Tagged key to look for. + |3: // Rearranged logic, because we expect _not_ to find the key. + | ld TMP0, NODE:TMP2->key + | ld CARG1, NODE:TMP2->val + | ld NODE:TMP2, NODE:TMP2->next + | beq RC, TMP0, >5 + |. li AT, LJ_TTAB + | bnez NODE:TMP2, <3 + |. nop + |4: + | move CARG1, RB + | b ->fff_restv // Not found, keep default result. + |. settp CARG1, AT + |5: + | bne CARG1, TISNIL, ->fff_restv + |. nop + | b <4 // Ditto for nil value. + |. nop + | + |6: + | sltiu AT, TMP2, LJ_TISNUM + | movn TMP2, TISNUM, AT + | dsll TMP2, TMP2, 3 + | dsubu TMP0, DISPATCH, TMP2 + | b <2 + |. ld TAB:RB, DISPATCH_GL(gcroot[GCROOT_BASEMT])-8(TMP0) + | + |.ffunc_2 setmetatable + | // Fast path: no mt for table yet and not clearing the mt. + | checktp TMP1, CARG1, -LJ_TTAB, ->fff_fallback + | gettp TMP3, CARG2 + | ld TAB:TMP0, TAB:TMP1->metatable + | lbu TMP2, TAB:TMP1->marked + | daddiu AT, TMP3, -LJ_TTAB + | cleartp TAB:CARG2 + | or AT, AT, TAB:TMP0 + | bnez AT, ->fff_fallback + |. andi AT, TMP2, LJ_GC_BLACK // isblack(table) + | beqz AT, ->fff_restv + |. sd TAB:CARG2, TAB:TMP1->metatable + | barrierback TAB:TMP1, TMP2, TMP0, ->fff_restv + | + |.ffunc rawget + | ld CARG2, 0(BASE) + | sltiu AT, NARGS8:RC, 16 + | load_got lj_tab_get + | gettp TMP0, CARG2 + | cleartp CARG2 + | daddiu TMP0, TMP0, -LJ_TTAB + | or AT, AT, TMP0 + | bnez AT, ->fff_fallback + |. daddiu CARG3, BASE, 8 + | call_intern lj_tab_get // (lua_State *L, GCtab *t, cTValue *key) + |. move CARG1, L + | b ->fff_restv + |. ld CARG1, 0(CRET1) + | + |//-- Base library: conversions ------------------------------------------ + | + |.ffunc tonumber + | // Only handles the number case inline (without a base argument). + | ld CARG1, 0(BASE) + | xori AT, NARGS8:RC, 8 // Exactly one number argument. + | gettp TMP1, CARG1 + | sltu TMP0, TISNUM, TMP1 + | or AT, AT, TMP0 + | bnez AT, ->fff_fallback + |. nop + | b ->fff_restv + |. nop + | + |.ffunc_1 tostring + | // Only handles the string or number case inline. + | gettp TMP0, CARG1 + | daddiu AT, TMP0, -LJ_TSTR + | // A __tostring method in the string base metatable is ignored. + | beqz AT, ->fff_restv // String key? + | // Handle numbers inline, unless a number base metatable is present. + |. ld TMP1, DISPATCH_GL(gcroot[GCROOT_BASEMT_NUM])(DISPATCH) + | sltu TMP0, TISNUM, TMP0 + | or TMP0, TMP0, TMP1 + | bnez TMP0, ->fff_fallback + |. sd BASE, L->base // Add frame since C call can throw. + | ffgccheck + |. sd PC, SAVE_PC // Redundant (but a defined value). + | load_got lj_strfmt_number + | move CARG1, L + | call_intern lj_strfmt_number // (lua_State *L, cTValue *o) + |. move CARG2, BASE + | // Returns GCstr *. + | li AT, LJ_TSTR + | settp CRET1, AT + | b ->fff_restv + |. move CARG1, CRET1 + | + |//-- Base library: iterators ------------------------------------------- + | + |.ffunc_1 next + | checktp CARG2, CARG1, -LJ_TTAB, ->fff_fallback + | daddu TMP2, BASE, NARGS8:RC + | sd TISNIL, 0(TMP2) // Set missing 2nd arg to nil. + | ld PC, FRAME_PC(BASE) + | load_got lj_tab_next + | sd BASE, L->base // Add frame since C call can throw. + | sd BASE, L->top // Dummy frame length is ok. + | daddiu CARG3, BASE, 8 + | sd PC, SAVE_PC + | call_intern lj_tab_next // (lua_State *L, GCtab *t, TValue *key) + |. move CARG1, L + | // Returns 0 at end of traversal. + | beqz CRET1, ->fff_restv // End of traversal: return nil. + |. move CARG1, TISNIL + | ld TMP0, 8(BASE) + | daddiu RA, BASE, -16 + | ld TMP2, 16(BASE) + | sd TMP0, 0(RA) + | sd TMP2, 8(RA) + | b ->fff_res + |. li RD, (2+1)*8 + | + |.ffunc_1 pairs + | checktp TAB:TMP1, CARG1, -LJ_TTAB, ->fff_fallback + | ld PC, FRAME_PC(BASE) +#if LJ_52 + | ld TAB:TMP2, TAB:TMP1->metatable + | ld TMP0, CFUNC:RB->upvalue[0] + | bnez TAB:TMP2, ->fff_fallback +#else + | ld TMP0, CFUNC:RB->upvalue[0] +#endif + |. daddiu RA, BASE, -16 + | sd TISNIL, 0(BASE) + | sd CARG1, -8(BASE) + | sd TMP0, 0(RA) + | b ->fff_res + |. li RD, (3+1)*8 + | + |.ffunc_2 ipairs_aux + | checktab CARG1, ->fff_fallback + | checkint CARG2, ->fff_fallback + |. lw TMP0, TAB:CARG1->asize + | ld TMP1, TAB:CARG1->array + | ld PC, FRAME_PC(BASE) + | sextw TMP2, CARG2 + | addiu TMP2, TMP2, 1 + | sltu AT, TMP2, TMP0 + | daddiu RA, BASE, -16 + | zextw TMP0, TMP2 + | settp TMP0, TISNUM + | beqz AT, >2 // Not in array part? + |. sd TMP0, 0(RA) + | dsll TMP3, TMP2, 3 + | daddu TMP3, TMP1, TMP3 + | ld TMP1, 0(TMP3) + |1: + | beq TMP1, TISNIL, ->fff_res // End of iteration, return 0 results. + |. li RD, (0+1)*8 + | sd TMP1, -8(BASE) + | b ->fff_res + |. li RD, (2+1)*8 + |2: // Check for empty hash part first. Otherwise call C function. + | lw TMP0, TAB:CARG1->hmask + | load_got lj_tab_getinth + | beqz TMP0, ->fff_res + |. li RD, (0+1)*8 + | call_intern lj_tab_getinth // (GCtab *t, int32_t key) + |. move CARG2, TMP2 + | // Returns cTValue * or NULL. + | beqz CRET1, ->fff_res + |. li RD, (0+1)*8 + | b <1 + |. ld TMP1, 0(CRET1) + | + |.ffunc_1 ipairs + | checktp TAB:TMP1, CARG1, -LJ_TTAB, ->fff_fallback + | ld PC, FRAME_PC(BASE) +#if LJ_52 + | ld TAB:TMP2, TAB:TMP1->metatable + | ld CFUNC:TMP0, CFUNC:RB->upvalue[0] + | bnez TAB:TMP2, ->fff_fallback +#else + | ld TMP0, CFUNC:RB->upvalue[0] +#endif + | daddiu RA, BASE, -16 + | dsll AT, TISNUM, 47 + | sd CARG1, -8(BASE) + | sd AT, 0(BASE) + | sd CFUNC:TMP0, 0(RA) + | b ->fff_res + |. li RD, (3+1)*8 + | + |//-- Base library: catch errors ---------------------------------------- + | + |.ffunc pcall + | daddiu NARGS8:RC, NARGS8:RC, -8 + | lbu TMP3, DISPATCH_GL(hookmask)(DISPATCH) + | bltz NARGS8:RC, ->fff_fallback + |. move TMP2, BASE + | daddiu BASE, BASE, 16 + | // Remember active hook before pcall. + | srl TMP3, TMP3, HOOK_ACTIVE_SHIFT + | andi TMP3, TMP3, 1 + | daddiu PC, TMP3, 16+FRAME_PCALL + | beqz NARGS8:RC, ->vm_call_dispatch + |1: + |. daddu TMP0, BASE, NARGS8:RC + |2: + | ld TMP1, -16(TMP0) + | sd TMP1, -8(TMP0) + | daddiu TMP0, TMP0, -8 + | bne TMP0, BASE, <2 + |. nop + | b ->vm_call_dispatch + |. nop + | + |.ffunc xpcall + | daddiu NARGS8:TMP0, NARGS8:RC, -16 + | ld CARG1, 0(BASE) + | ld CARG2, 8(BASE) + | bltz NARGS8:TMP0, ->fff_fallback + |. lbu TMP1, DISPATCH_GL(hookmask)(DISPATCH) + | gettp AT, CARG2 + | daddiu AT, AT, -LJ_TFUNC + | bnez AT, ->fff_fallback // Traceback must be a function. + |. move TMP2, BASE + | move NARGS8:RC, NARGS8:TMP0 + | daddiu BASE, BASE, 24 + | // Remember active hook before pcall. + | srl TMP3, TMP3, HOOK_ACTIVE_SHIFT + | sd CARG2, 0(TMP2) // Swap function and traceback. + | andi TMP3, TMP3, 1 + | sd CARG1, 8(TMP2) + | beqz NARGS8:RC, ->vm_call_dispatch + |. daddiu PC, TMP3, 24+FRAME_PCALL + | b <1 + |. nop + | + |//-- Coroutine library -------------------------------------------------- + | + |.macro coroutine_resume_wrap, resume + |.if resume + |.ffunc_1 coroutine_resume + | checktp CARG1, CARG1, -LJ_TTHREAD, ->fff_fallback + |.else + |.ffunc coroutine_wrap_aux + | ld L:CARG1, CFUNC:RB->upvalue[0].gcr + | cleartp L:CARG1 + |.endif + | lbu TMP0, L:CARG1->status + | ld TMP1, L:CARG1->cframe + | ld CARG2, L:CARG1->top + | ld TMP2, L:CARG1->base + | addiu AT, TMP0, -LUA_YIELD + | daddu CARG3, CARG2, TMP0 + | daddiu TMP3, CARG2, 8 + | bgtz AT, ->fff_fallback // st > LUA_YIELD? + |. movn CARG2, TMP3, AT + | xor TMP2, TMP2, CARG3 + | bnez TMP1, ->fff_fallback // cframe != 0? + |. or AT, TMP2, TMP0 + | ld TMP0, L:CARG1->maxstack + | beqz AT, ->fff_fallback // base == top && st == 0? + |. ld PC, FRAME_PC(BASE) + | daddu TMP2, CARG2, NARGS8:RC + | sltu AT, TMP0, TMP2 + | bnez AT, ->fff_fallback // Stack overflow? + |. sd PC, SAVE_PC + | sd BASE, L->base + |1: + |.if resume + | daddiu BASE, BASE, 8 // Keep resumed thread in stack for GC. + | daddiu NARGS8:RC, NARGS8:RC, -8 + | daddiu TMP2, TMP2, -8 + |.endif + | sd TMP2, L:CARG1->top + | daddu TMP1, BASE, NARGS8:RC + | move CARG3, CARG2 + | sd BASE, L->top + |2: // Move args to coroutine. + | ld CRET1, 0(BASE) + | sltu AT, BASE, TMP1 + | beqz AT, >3 + |. daddiu BASE, BASE, 8 + | sd CRET1, 0(CARG3) + | b <2 + |. daddiu CARG3, CARG3, 8 + |3: + | bal ->vm_resume // (lua_State *L, TValue *base, 0, 0) + |. move L:RA, L:CARG1 + | // Returns thread status. + |4: + | ld TMP2, L:RA->base + | sltiu AT, CRET1, LUA_YIELD+1 + | ld TMP3, L:RA->top + | li_vmstate INTERP + | ld BASE, L->base + | sd L, DISPATCH_GL(cur_L)(DISPATCH) + | st_vmstate + | beqz AT, >8 + |. dsubu RD, TMP3, TMP2 + | ld TMP0, L->maxstack + | beqz RD, >6 // No results? + |. daddu TMP1, BASE, RD + | sltu AT, TMP0, TMP1 + | bnez AT, >9 // Need to grow stack? + |. daddu TMP3, TMP2, RD + | sd TMP2, L:RA->top // Clear coroutine stack. + | move TMP1, BASE + |5: // Move results from coroutine. + | ld CRET1, 0(TMP2) + | daddiu TMP2, TMP2, 8 + | sltu AT, TMP2, TMP3 + | sd CRET1, 0(TMP1) + | bnez AT, <5 + |. daddiu TMP1, TMP1, 8 + |6: + | andi TMP0, PC, FRAME_TYPE + |.if resume + | mov_true TMP1 + | daddiu RA, BASE, -8 + | sd TMP1, -8(BASE) // Prepend true to results. + | daddiu RD, RD, 16 + |.else + | move RA, BASE + | daddiu RD, RD, 8 + |.endif + |7: + | sd PC, SAVE_PC + | beqz TMP0, ->BC_RET_Z + |. move MULTRES, RD + | b ->vm_return + |. nop + | + |8: // Coroutine returned with error (at co->top-1). + |.if resume + | daddiu TMP3, TMP3, -8 + | mov_false TMP1 + | ld CRET1, 0(TMP3) + | sd TMP3, L:RA->top // Remove error from coroutine stack. + | li RD, (2+1)*8 + | sd TMP1, -8(BASE) // Prepend false to results. + | daddiu RA, BASE, -8 + | sd CRET1, 0(BASE) // Copy error message. + | b <7 + |. andi TMP0, PC, FRAME_TYPE + |.else + | load_got lj_ffh_coroutine_wrap_err + | move CARG2, L:RA + | call_intern lj_ffh_coroutine_wrap_err // (lua_State *L, lua_State *co) + |. move CARG1, L + |.endif + | + |9: // Handle stack expansion on return from yield. + | load_got lj_state_growstack + | srl CARG2, RD, 3 + | call_intern lj_state_growstack // (lua_State *L, int n) + |. move CARG1, L + | b <4 + |. li CRET1, 0 + |.endmacro + | + | coroutine_resume_wrap 1 // coroutine.resume + | coroutine_resume_wrap 0 // coroutine.wrap + | + |.ffunc coroutine_yield + | ld TMP0, L->cframe + | daddu TMP1, BASE, NARGS8:RC + | sd BASE, L->base + | andi TMP0, TMP0, CFRAME_RESUME + | sd TMP1, L->top + | beqz TMP0, ->fff_fallback + |. li CRET1, LUA_YIELD + | sd r0, L->cframe + | b ->vm_leave_unw + |. sb CRET1, L->status + | + |//-- Math library ------------------------------------------------------- + | + |.ffunc_1 math_abs + | gettp CARG2, CARG1 + | daddiu AT, CARG2, -LJ_TISNUM + | bnez AT, >1 + |. sextw TMP1, CARG1 + | sra TMP0, TMP1, 31 // Extract sign. + | xor TMP1, TMP1, TMP0 + | dsubu CARG1, TMP1, TMP0 + | dsll TMP3, CARG1, 32 + | bgez TMP3, ->fff_restv + |. settp CARG1, TISNUM + | li CARG1, 0x41e0 // 2^31 as a double. + | b ->fff_restv + |. dsll CARG1, CARG1, 48 + |1: + | sltiu AT, CARG2, LJ_TISNUM + | beqz AT, ->fff_fallback + |. dextm CARG1, CARG1, 0, 30 + |// fallthrough + | + |->fff_restv: + | // CARG1 = TValue result. + | ld PC, FRAME_PC(BASE) + | daddiu RA, BASE, -16 + | sd CARG1, -16(BASE) + |->fff_res1: + | // RA = results, PC = return. + | li RD, (1+1)*8 + |->fff_res: + | // RA = results, RD = (nresults+1)*8, PC = return. + | andi TMP0, PC, FRAME_TYPE + | bnez TMP0, ->vm_return + |. move MULTRES, RD + | lw INS, -4(PC) + | decode_RB8a RB, INS + | decode_RB8b RB + |5: + | sltu AT, RD, RB + | bnez AT, >6 // More results expected? + |. decode_RA8a TMP0, INS + | decode_RA8b TMP0 + | ins_next1 + | // Adjust BASE. KBASE is assumed to be set for the calling frame. + | dsubu BASE, RA, TMP0 + | ins_next2 + | + |6: // Fill up results with nil. + | daddu TMP1, RA, RD + | daddiu RD, RD, 8 + | b <5 + |. sd TISNIL, -8(TMP1) + | + |.macro math_extern, func + | .ffunc_n math_ .. func + | load_got func + | call_extern + |. nop + | b ->fff_resn + |. nop + |.endmacro + | + |.macro math_extern2, func + | .ffunc_nn math_ .. func + |. load_got func + | call_extern + |. nop + | b ->fff_resn + |. nop + |.endmacro + | + |// TODO: Return integer type if result is integer (own sf implementation). + |.macro math_round, func + |->ff_math_ .. func: + | ld CARG1, 0(BASE) + | beqz NARGS8:RC, ->fff_fallback + |. gettp TMP0, CARG1 + | beq TMP0, TISNUM, ->fff_restv + |. sltu AT, TMP0, TISNUM + | beqz AT, ->fff_fallback + |.if FPU + |. ldc1 FARG1, 0(BASE) + | bal ->vm_ .. func + |. nop + |.else + |. load_got func + | call_extern + |. nop + |.endif + | b ->fff_resn + |. nop + |.endmacro + | + | math_round floor + | math_round ceil + | + |.ffunc math_log + | li AT, 8 + | bne NARGS8:RC, AT, ->fff_fallback // Exactly 1 argument. + |. ld CARG1, 0(BASE) + | checknum CARG1, ->fff_fallback + |. load_got log + |.if FPU + | call_extern + |. ldc1 FARG1, 0(BASE) + |.else + | call_extern + |. nop + |.endif + | b ->fff_resn + |. nop + | + | math_extern log10 + | math_extern exp + | math_extern sin + | math_extern cos + | math_extern tan + | math_extern asin + | math_extern acos + | math_extern atan + | math_extern sinh + | math_extern cosh + | math_extern tanh + | math_extern2 pow + | math_extern2 atan2 + | math_extern2 fmod + | + |.if FPU + |.ffunc_n math_sqrt + |. sqrt.d FRET1, FARG1 + |// fallthrough to ->fff_resn + |.else + | math_extern sqrt + |.endif + | + |->fff_resn: + | ld PC, FRAME_PC(BASE) + | daddiu RA, BASE, -16 + | b ->fff_res1 + |.if FPU + |. sdc1 FRET1, 0(RA) + |.else + |. sd CRET1, 0(RA) + |.endif + | + | + |.ffunc_2 math_ldexp + | checknum CARG1, ->fff_fallback + | checkint CARG2, ->fff_fallback + |. load_got ldexp + | .FPU ldc1 FARG1, 0(BASE) + | call_extern + |. lw CARG2, 8+LO(BASE) + | b ->fff_resn + |. nop + | + |.ffunc_n math_frexp + | load_got frexp + | ld PC, FRAME_PC(BASE) + | call_extern + |. daddiu CARG2, DISPATCH, DISPATCH_GL(tmptv) + | lw TMP1, DISPATCH_GL(tmptv)(DISPATCH) + | daddiu RA, BASE, -16 + |.if FPU + | mtc1 TMP1, FARG2 + | sdc1 FRET1, 0(RA) + | cvt.d.w FARG2, FARG2 + | sdc1 FARG2, 8(RA) + |.else + | sd CRET1, 0(RA) + | zextw TMP1, TMP1 + | settp TMP1, TISNUM + | sd TMP1, 8(RA) + |.endif + | b ->fff_res + |. li RD, (2+1)*8 + | + |.ffunc_n math_modf + | load_got modf + | ld PC, FRAME_PC(BASE) + | call_extern + |. daddiu CARG2, BASE, -16 + | daddiu RA, BASE, -16 + |.if FPU + | sdc1 FRET1, -8(BASE) + |.else + | sd CRET1, -8(BASE) + |.endif + | b ->fff_res + |. li RD, (2+1)*8 + | + |.macro math_minmax, name, intins, fpins + | .ffunc_1 name + | daddu TMP3, BASE, NARGS8:RC + | checkint CARG1, >5 + |. daddiu TMP2, BASE, 8 + |1: // Handle integers. + | beq TMP2, TMP3, ->fff_restv + |. ld CARG2, 0(TMP2) + | checkint CARG2, >3 + |. sextw CARG1, CARG1 + | lw CARG2, LO(TMP2) + |. slt AT, CARG1, CARG2 + | intins CARG1, CARG2, AT + | daddiu TMP2, TMP2, 8 + | zextw CARG1, CARG1 + | b <1 + |. settp CARG1, TISNUM + | + |3: // Convert intermediate result to number and continue with number loop. + | checknum CARG2, ->fff_fallback + |.if FPU + |. mtc1 CARG1, FRET1 + | cvt.d.w FRET1, FRET1 + | b >7 + |. ldc1 FARG1, 0(TMP2) + |.else + |. nop + | bal ->vm_sfi2d_1 + |. nop + | b >7 + |. nop + |.endif + | + |5: + | .FPU ldc1 FRET1, 0(BASE) + | checknum CARG1, ->fff_fallback + |6: // Handle numbers. + |. ld CARG2, 0(TMP2) + | beq TMP2, TMP3, ->fff_resn + |.if FPU + | ldc1 FARG1, 0(TMP2) + |.else + | move CRET1, CARG1 + |.endif + | checknum CARG2, >8 + |. nop + |7: + |.if FPU + | c.olt.d FRET1, FARG1 + | fpins FRET1, FARG1 + |.else + | bal ->vm_sfcmpolt + |. nop + | intins CARG1, CARG2, CRET1 + |.endif + | b <6 + |. daddiu TMP2, TMP2, 8 + | + |8: // Convert integer to number and continue with number loop. + | checkint CARG2, ->fff_fallback + |.if FPU + |. lwc1 FARG1, LO(TMP2) + | b <7 + |. cvt.d.w FARG1, FARG1 + |.else + |. lw CARG2, LO(TMP2) + | bal ->vm_sfi2d_2 + |. nop + | b <7 + |. nop + |.endif + | + |.endmacro + | + | math_minmax math_min, movz, movf.d + | math_minmax math_max, movn, movt.d + | + |//-- String library ----------------------------------------------------- + | + |.ffunc string_byte // Only handle the 1-arg case here. + | ld CARG1, 0(BASE) + | gettp TMP0, CARG1 + | xori AT, NARGS8:RC, 8 + | daddiu TMP0, TMP0, -LJ_TSTR + | or AT, AT, TMP0 + | bnez AT, ->fff_fallback // Need exactly 1 string argument. + |. cleartp STR:CARG1 + | lw TMP0, STR:CARG1->len + | daddiu RA, BASE, -16 + | ld PC, FRAME_PC(BASE) + | sltu RD, r0, TMP0 + | lbu TMP1, STR:CARG1[1] // Access is always ok (NUL at end). + | addiu RD, RD, 1 + | sll RD, RD, 3 // RD = ((str->len != 0)+1)*8 + | settp TMP1, TISNUM + | b ->fff_res + |. sd TMP1, 0(RA) + | + |.ffunc string_char // Only handle the 1-arg case here. + | ffgccheck + |. nop + | ld CARG1, 0(BASE) + | gettp TMP0, CARG1 + | xori AT, NARGS8:RC, 8 // Exactly 1 argument. + | daddiu TMP0, TMP0, -LJ_TISNUM // Integer. + | li TMP1, 255 + | sextw CARG1, CARG1 + | or AT, AT, TMP0 + | sltu TMP1, TMP1, CARG1 // !(255 < n). + | or AT, AT, TMP1 + | bnez AT, ->fff_fallback + |. li CARG3, 1 + | daddiu CARG2, sp, TMPD_OFS + | sb CARG1, TMPD + |->fff_newstr: + | load_got lj_str_new + | sd BASE, L->base + | sd PC, SAVE_PC + | call_intern lj_str_new // (lua_State *L, char *str, size_t l) + |. move CARG1, L + | // Returns GCstr *. + | ld BASE, L->base + |->fff_resstr: + | li AT, LJ_TSTR + | settp CRET1, AT + | b ->fff_restv + |. move CARG1, CRET1 + | + |.ffunc string_sub + | ffgccheck + |. nop + | addiu AT, NARGS8:RC, -16 + | ld TMP0, 0(BASE) + | bltz AT, ->fff_fallback + |. gettp TMP3, TMP0 + | cleartp STR:CARG1, TMP0 + | ld CARG2, 8(BASE) + | beqz AT, >1 + |. li CARG4, -1 + | ld CARG3, 16(BASE) + | checkint CARG3, ->fff_fallback + |. sextw CARG4, CARG3 + |1: + | checkint CARG2, ->fff_fallback + |. li AT, LJ_TSTR + | bne TMP3, AT, ->fff_fallback + |. sextw CARG3, CARG2 + | lw CARG2, STR:CARG1->len + | // STR:CARG1 = str, CARG2 = str->len, CARG3 = start, CARG4 = end + | slt AT, CARG4, r0 + | addiu TMP0, CARG2, 1 + | addu TMP1, CARG4, TMP0 + | slt TMP3, CARG3, r0 + | movn CARG4, TMP1, AT // if (end < 0) end += len+1 + | addu TMP1, CARG3, TMP0 + | movn CARG3, TMP1, TMP3 // if (start < 0) start += len+1 + | li TMP2, 1 + | slt AT, CARG4, r0 + | slt TMP3, r0, CARG3 + | movn CARG4, r0, AT // if (end < 0) end = 0 + | movz CARG3, TMP2, TMP3 // if (start < 1) start = 1 + | slt AT, CARG2, CARG4 + | movn CARG4, CARG2, AT // if (end > len) end = len + | daddu CARG2, STR:CARG1, CARG3 + | subu CARG3, CARG4, CARG3 // len = end - start + | daddiu CARG2, CARG2, sizeof(GCstr)-1 + | bgez CARG3, ->fff_newstr + |. addiu CARG3, CARG3, 1 // len++ + |->fff_emptystr: // Return empty string. + | li AT, LJ_TSTR + | daddiu STR:CARG1, DISPATCH, DISPATCH_GL(strempty) + | b ->fff_restv + |. settp CARG1, AT + | + |.macro ffstring_op, name + | .ffunc string_ .. name + | ffgccheck + |. nop + | beqz NARGS8:RC, ->fff_fallback + |. ld CARG2, 0(BASE) + | checkstr STR:CARG2, ->fff_fallback + | daddiu SBUF:CARG1, DISPATCH, DISPATCH_GL(tmpbuf) + | load_got lj_buf_putstr_ .. name + | ld TMP0, SBUF:CARG1->b + | sd L, SBUF:CARG1->L + | sd BASE, L->base + | sd TMP0, SBUF:CARG1->p + | call_intern extern lj_buf_putstr_ .. name + |. sd PC, SAVE_PC + | load_got lj_buf_tostr + | call_intern lj_buf_tostr + |. move SBUF:CARG1, SBUF:CRET1 + | b ->fff_resstr + |. ld BASE, L->base + |.endmacro + | + |ffstring_op reverse + |ffstring_op lower + |ffstring_op upper + | + |//-- Bit library -------------------------------------------------------- + | + |->vm_tobit_fb: + | beqz TMP1, ->fff_fallback + |.if FPU + |. ldc1 FARG1, 0(BASE) + | add.d FARG1, FARG1, TOBIT + | mfc1 CRET1, FARG1 + | jr ra + |. zextw CRET1, CRET1 + |.else + |// FP number to bit conversion for soft-float. + |->vm_tobit: + | dsll TMP0, CARG1, 1 + | li CARG3, 1076 + | dsrl AT, TMP0, 53 + | dsubu CARG3, CARG3, AT + | sltiu AT, CARG3, 54 + | beqz AT, >1 + |. dextm TMP0, TMP0, 0, 20 + | dinsu TMP0, AT, 21, 21 + | slt AT, CARG1, r0 + | dsrlv CRET1, TMP0, CARG3 + | dsubu TMP0, r0, CRET1 + | movn CRET1, TMP0, AT + | jr ra + |. zextw CRET1, CRET1 + |1: + | jr ra + |. move CRET1, r0 + | + |// FP number to int conversion with a check for soft-float. + |// Modifies CARG1, CRET1, CRET2, TMP0, AT. + |->vm_tointg: + |.if JIT + | dsll CRET2, CARG1, 1 + | beqz CRET2, >2 + |. li TMP0, 1076 + | dsrl AT, CRET2, 53 + | dsubu TMP0, TMP0, AT + | sltiu AT, TMP0, 54 + | beqz AT, >1 + |. dextm CRET2, CRET2, 0, 20 + | dinsu CRET2, AT, 21, 21 + | slt AT, CARG1, r0 + | dsrlv CRET1, CRET2, TMP0 + | dsubu CARG1, r0, CRET1 + | movn CRET1, CARG1, AT + | li CARG1, 64 + | subu TMP0, CARG1, TMP0 + | dsllv CRET2, CRET2, TMP0 // Integer check. + | sextw AT, CRET1 + | xor AT, CRET1, AT // Range check. + | jr ra + |. movz CRET2, AT, CRET2 + |1: + | jr ra + |. li CRET2, 1 + |2: + | jr ra + |. move CRET1, r0 + |.endif + |.endif + | + |.macro .ffunc_bit, name + | .ffunc_1 bit_..name + | gettp TMP0, CARG1 + | beq TMP0, TISNUM, >6 + |. zextw CRET1, CARG1 + | bal ->vm_tobit_fb + |. sltiu TMP1, TMP0, LJ_TISNUM + |6: + |.endmacro + | + |.macro .ffunc_bit_op, name, bins + | .ffunc_bit name + | daddiu TMP2, BASE, 8 + | daddu TMP3, BASE, NARGS8:RC + |1: + | beq TMP2, TMP3, ->fff_resi + |. ld CARG1, 0(TMP2) + | gettp TMP0, CARG1 + |.if FPU + | bne TMP0, TISNUM, >2 + |. daddiu TMP2, TMP2, 8 + | zextw CARG1, CARG1 + | b <1 + |. bins CRET1, CRET1, CARG1 + |2: + | ldc1 FARG1, -8(TMP2) + | sltiu AT, TMP0, LJ_TISNUM + | beqz AT, ->fff_fallback + |. add.d FARG1, FARG1, TOBIT + | mfc1 CARG1, FARG1 + | zextw CARG1, CARG1 + | b <1 + |. bins CRET1, CRET1, CARG1 + |.else + | beq TMP0, TISNUM, >2 + |. move CRET2, CRET1 + | bal ->vm_tobit_fb + |. sltiu TMP1, TMP0, LJ_TISNUM + | move CARG1, CRET2 + |2: + | zextw CARG1, CARG1 + | bins CRET1, CRET1, CARG1 + | b <1 + |. daddiu TMP2, TMP2, 8 + |.endif + |.endmacro + | + |.ffunc_bit_op band, and + |.ffunc_bit_op bor, or + |.ffunc_bit_op bxor, xor + | + |.ffunc_bit bswap + | dsrl TMP0, CRET1, 8 + | dsrl TMP1, CRET1, 24 + | andi TMP2, TMP0, 0xff00 + | dins TMP1, CRET1, 24, 31 + | dins TMP2, TMP0, 16, 23 + | b ->fff_resi + |. or CRET1, TMP1, TMP2 + | + |.ffunc_bit bnot + | not CRET1, CRET1 + | b ->fff_resi + |. zextw CRET1, CRET1 + | + |.macro .ffunc_bit_sh, name, shins, shmod + | .ffunc_2 bit_..name + | gettp TMP0, CARG1 + | beq TMP0, TISNUM, >1 + |. nop + | bal ->vm_tobit_fb + |. sltiu TMP1, TMP0, LJ_TISNUM + | move CARG1, CRET1 + |1: + | gettp TMP0, CARG2 + | bne TMP0, TISNUM, ->fff_fallback + |. zextw CARG2, CARG2 + | sextw CARG1, CARG1 + |.if shmod == 1 + | negu CARG2, CARG2 + |.endif + | shins CRET1, CARG1, CARG2 + | b ->fff_resi + |. zextw CRET1, CRET1 + |.endmacro + | + |.ffunc_bit_sh lshift, sllv, 0 + |.ffunc_bit_sh rshift, srlv, 0 + |.ffunc_bit_sh arshift, srav, 0 + |.ffunc_bit_sh rol, rotrv, 1 + |.ffunc_bit_sh ror, rotrv, 0 + | + |.ffunc_bit tobit + |->fff_resi: + | ld PC, FRAME_PC(BASE) + | daddiu RA, BASE, -16 + | settp CRET1, TISNUM + | b ->fff_res1 + |. sd CRET1, -16(BASE) + | + |//----------------------------------------------------------------------- + |->fff_fallback: // Call fast function fallback handler. + | // BASE = new base, RB = CFUNC, RC = nargs*8 + | ld TMP3, CFUNC:RB->f + | daddu TMP1, BASE, NARGS8:RC + | ld PC, FRAME_PC(BASE) // Fallback may overwrite PC. + | daddiu TMP0, TMP1, 8*LUA_MINSTACK + | ld TMP2, L->maxstack + | sd PC, SAVE_PC // Redundant (but a defined value). + | sltu AT, TMP2, TMP0 + | sd BASE, L->base + | sd TMP1, L->top + | bnez AT, >5 // Need to grow stack. + |. move CFUNCADDR, TMP3 + | jalr TMP3 // (lua_State *L) + |. move CARG1, L + | // Either throws an error, or recovers and returns -1, 0 or nresults+1. + | ld BASE, L->base + | sll RD, CRET1, 3 + | bgtz CRET1, ->fff_res // Returned nresults+1? + |. daddiu RA, BASE, -16 + |1: // Returned 0 or -1: retry fast path. + | ld LFUNC:RB, FRAME_FUNC(BASE) + | ld TMP0, L->top + | cleartp LFUNC:RB + | bnez CRET1, ->vm_call_tail // Returned -1? + |. dsubu NARGS8:RC, TMP0, BASE + | ins_callt // Returned 0: retry fast path. + | + |// Reconstruct previous base for vmeta_call during tailcall. + |->vm_call_tail: + | andi TMP0, PC, FRAME_TYPE + | li AT, -4 + | bnez TMP0, >3 + |. and TMP1, PC, AT + | lbu TMP1, OFS_RA(PC) + | sll TMP1, TMP1, 3 + | addiu TMP1, TMP1, 16 + |3: + | b ->vm_call_dispatch // Resolve again for tailcall. + |. dsubu TMP2, BASE, TMP1 + | + |5: // Grow stack for fallback handler. + | load_got lj_state_growstack + | li CARG2, LUA_MINSTACK + | call_intern lj_state_growstack // (lua_State *L, int n) + |. move CARG1, L + | ld BASE, L->base + | b <1 + |. li CRET1, 0 // Force retry. + | + |->fff_gcstep: // Call GC step function. + | // BASE = new base, RC = nargs*8 + | move MULTRES, ra + | load_got lj_gc_step + | sd BASE, L->base + | daddu TMP0, BASE, NARGS8:RC + | sd PC, SAVE_PC // Redundant (but a defined value). + | sd TMP0, L->top + | call_intern lj_gc_step // (lua_State *L) + |. move CARG1, L + | ld BASE, L->base + | move ra, MULTRES + | ld TMP0, L->top + | ld CFUNC:RB, FRAME_FUNC(BASE) + | cleartp CFUNC:RB + | jr ra + |. dsubu NARGS8:RC, TMP0, BASE + | + |//----------------------------------------------------------------------- + |//-- Special dispatch targets ------------------------------------------- + |//----------------------------------------------------------------------- + | + |->vm_record: // Dispatch target for recording phase. + |.if JIT + | lbu TMP3, DISPATCH_GL(hookmask)(DISPATCH) + | andi AT, TMP3, HOOK_VMEVENT // No recording while in vmevent. + | bnez AT, >5 + | // Decrement the hookcount for consistency, but always do the call. + |. lw TMP2, DISPATCH_GL(hookcount)(DISPATCH) + | andi AT, TMP3, HOOK_ACTIVE + | bnez AT, >1 + |. addiu TMP2, TMP2, -1 + | andi AT, TMP3, LUA_MASKLINE|LUA_MASKCOUNT + | beqz AT, >1 + |. nop + | b >1 + |. sw TMP2, DISPATCH_GL(hookcount)(DISPATCH) + |.endif + | + |->vm_rethook: // Dispatch target for return hooks. + | lbu TMP3, DISPATCH_GL(hookmask)(DISPATCH) + | andi AT, TMP3, HOOK_ACTIVE // Hook already active? + | beqz AT, >1 + |5: // Re-dispatch to static ins. + |. ld AT, GG_DISP2STATIC(TMP0) // Assumes TMP0 holds DISPATCH+OP*4. + | jr AT + |. nop + | + |->vm_inshook: // Dispatch target for instr/line hooks. + | lbu TMP3, DISPATCH_GL(hookmask)(DISPATCH) + | lw TMP2, DISPATCH_GL(hookcount)(DISPATCH) + | andi AT, TMP3, HOOK_ACTIVE // Hook already active? + | bnez AT, <5 + |. andi AT, TMP3, LUA_MASKLINE|LUA_MASKCOUNT + | beqz AT, <5 + |. addiu TMP2, TMP2, -1 + | beqz TMP2, >1 + |. sw TMP2, DISPATCH_GL(hookcount)(DISPATCH) + | andi AT, TMP3, LUA_MASKLINE + | beqz AT, <5 + |1: + |. load_got lj_dispatch_ins + | sw MULTRES, SAVE_MULTRES + | move CARG2, PC + | sd BASE, L->base + | // SAVE_PC must hold the _previous_ PC. The callee updates it with PC. + | call_intern lj_dispatch_ins // (lua_State *L, const BCIns *pc) + |. move CARG1, L + |3: + | ld BASE, L->base + |4: // Re-dispatch to static ins. + | lw INS, -4(PC) + | decode_OP8a TMP1, INS + | decode_OP8b TMP1 + | daddu TMP0, DISPATCH, TMP1 + | decode_RD8a RD, INS + | ld AT, GG_DISP2STATIC(TMP0) + | decode_RA8a RA, INS + | decode_RD8b RD + | jr AT + | decode_RA8b RA + | + |->cont_hook: // Continue from hook yield. + | daddiu PC, PC, 4 + | b <4 + |. lw MULTRES, -24+LO(RB) // Restore MULTRES for *M ins. + | + |->vm_hotloop: // Hot loop counter underflow. + |.if JIT + | ld LFUNC:TMP1, FRAME_FUNC(BASE) + | daddiu CARG1, DISPATCH, GG_DISP2J + | cleartp LFUNC:TMP1 + | sd PC, SAVE_PC + | ld TMP1, LFUNC:TMP1->pc + | move CARG2, PC + | sd L, DISPATCH_J(L)(DISPATCH) + | lbu TMP1, PC2PROTO(framesize)(TMP1) + | load_got lj_trace_hot + | sd BASE, L->base + | dsll TMP1, TMP1, 3 + | daddu TMP1, BASE, TMP1 + | call_intern lj_trace_hot // (jit_State *J, const BCIns *pc) + |. sd TMP1, L->top + | b <3 + |. nop + |.endif + | + | + |->vm_callhook: // Dispatch target for call hooks. + |.if JIT + | b >1 + |.endif + |. move CARG2, PC + | + |->vm_hotcall: // Hot call counter underflow. + |.if JIT + | ori CARG2, PC, 1 + |1: + |.endif + | load_got lj_dispatch_call + | daddu TMP0, BASE, RC + | sd PC, SAVE_PC + | sd BASE, L->base + | dsubu RA, RA, BASE + | sd TMP0, L->top + | call_intern lj_dispatch_call // (lua_State *L, const BCIns *pc) + |. move CARG1, L + | // Returns ASMFunction. + | ld BASE, L->base + | ld TMP0, L->top + | sd r0, SAVE_PC // Invalidate for subsequent line hook. + | dsubu NARGS8:RC, TMP0, BASE + | daddu RA, BASE, RA + | ld LFUNC:RB, FRAME_FUNC(BASE) + | cleartp LFUNC:RB + | jr CRET1 + |. lw INS, -4(PC) + | + |->cont_stitch: // Trace stitching. + |.if JIT + | // RA = resultptr, RB = meta base + | lw INS, -4(PC) + | ld TRACE:TMP2, -40(RB) // Save previous trace. + | decode_RA8a RC, INS + | daddiu AT, MULTRES, -8 + | cleartp TRACE:TMP2 + | decode_RA8b RC + | beqz AT, >2 + |. daddu RC, BASE, RC // Call base. + |1: // Move results down. + | ld CARG1, 0(RA) + | daddiu AT, AT, -8 + | daddiu RA, RA, 8 + | sd CARG1, 0(RC) + | bnez AT, <1 + |. daddiu RC, RC, 8 + |2: + | decode_RA8a RA, INS + | decode_RB8a RB, INS + | decode_RA8b RA + | decode_RB8b RB + | daddu RA, RA, RB + | daddu RA, BASE, RA + |3: + | sltu AT, RC, RA + | bnez AT, >9 // More results wanted? + |. nop + | + | lhu TMP3, TRACE:TMP2->traceno + | lhu RD, TRACE:TMP2->link + | beq RD, TMP3, ->cont_nop // Blacklisted. + |. load_got lj_dispatch_stitch + | bnez RD, =>BC_JLOOP // Jump to stitched trace. + |. sll RD, RD, 3 + | + | // Stitch a new trace to the previous trace. + | sw TMP3, DISPATCH_J(exitno)(DISPATCH) + | sd L, DISPATCH_J(L)(DISPATCH) + | sd BASE, L->base + | daddiu CARG1, DISPATCH, GG_DISP2J + | call_intern lj_dispatch_stitch // (jit_State *J, const BCIns *pc) + |. move CARG2, PC + | b ->cont_nop + |. ld BASE, L->base + | + |9: + | sd TISNIL, 0(RC) + | b <3 + |. daddiu RC, RC, 8 + |.endif + | + |->vm_profhook: // Dispatch target for profiler hook. +#if LJ_HASPROFILE + | load_got lj_dispatch_profile + | sw MULTRES, SAVE_MULTRES + | move CARG2, PC + | sd BASE, L->base + | call_intern lj_dispatch_profile // (lua_State *L, const BCIns *pc) + |. move CARG1, L + | // HOOK_PROFILE is off again, so re-dispatch to dynamic instruction. + | daddiu PC, PC, -4 + | b ->cont_nop + |. ld BASE, L->base +#endif + | + |//----------------------------------------------------------------------- + |//-- Trace exit handler ------------------------------------------------- + |//----------------------------------------------------------------------- + | + |.macro savex_, a, b + |.if FPU + | sdc1 f..a, a*8(sp) + | sdc1 f..b, b*8(sp) + | sd r..a, 32*8+a*8(sp) + | sd r..b, 32*8+b*8(sp) + |.else + | sd r..a, a*8(sp) + | sd r..b, b*8(sp) + |.endif + |.endmacro + | + |->vm_exit_handler: + |.if JIT + |.if FPU + | daddiu sp, sp, -(32*8+32*8) + |.else + | daddiu sp, sp, -(32*8) + |.endif + | savex_ 0, 1 + | savex_ 2, 3 + | savex_ 4, 5 + | savex_ 6, 7 + | savex_ 8, 9 + | savex_ 10, 11 + | savex_ 12, 13 + | savex_ 14, 15 + | savex_ 16, 17 + | savex_ 18, 19 + | savex_ 20, 21 + | savex_ 22, 23 + | savex_ 24, 25 + | savex_ 26, 27 + | savex_ 28, 30 + |.if FPU + | sdc1 f29, 29*8(sp) + | sdc1 f31, 31*8(sp) + | sd r0, 32*8+31*8(sp) // Clear RID_TMP. + | daddiu TMP2, sp, 32*8+32*8 // Recompute original value of sp. + | sd TMP2, 32*8+29*8(sp) // Store sp in RID_SP + |.else + | sd r0, 31*8(sp) // Clear RID_TMP. + | daddiu TMP2, sp, 32*8 // Recompute original value of sp. + | sd TMP2, 29*8(sp) // Store sp in RID_SP + |.endif + | li_vmstate EXIT + | daddiu DISPATCH, JGL, -GG_DISP2G-32768 + | lw TMP1, 0(TMP2) // Load exit number. + | st_vmstate + | ld L, DISPATCH_GL(cur_L)(DISPATCH) + | ld BASE, DISPATCH_GL(jit_base)(DISPATCH) + | load_got lj_trace_exit + | sd L, DISPATCH_J(L)(DISPATCH) + | sw ra, DISPATCH_J(parent)(DISPATCH) // Store trace number. + | sd BASE, L->base + | sw TMP1, DISPATCH_J(exitno)(DISPATCH) // Store exit number. + | daddiu CARG1, DISPATCH, GG_DISP2J + | sd r0, DISPATCH_GL(jit_base)(DISPATCH) + | call_intern lj_trace_exit // (jit_State *J, ExitState *ex) + |. move CARG2, sp + | // Returns MULTRES (unscaled) or negated error code. + | ld TMP1, L->cframe + | li AT, -4 + | ld BASE, L->base + | and sp, TMP1, AT + | ld PC, SAVE_PC // Get SAVE_PC. + | b >1 + |. sd L, SAVE_L // Set SAVE_L (on-trace resume/yield). + |.endif + |->vm_exit_interp: + |.if JIT + | // CRET1 = MULTRES or negated error code, BASE, PC and JGL set. + | ld L, SAVE_L + | daddiu DISPATCH, JGL, -GG_DISP2G-32768 + | sd BASE, L->base + |1: + | bltz CRET1, >9 // Check for error from exit. + |. ld LFUNC:RB, FRAME_FUNC(BASE) + | .FPU lui TMP3, 0x59c0 // TOBIT = 2^52 + 2^51 (float). + | dsll MULTRES, CRET1, 3 + | cleartp LFUNC:RB + | sw MULTRES, SAVE_MULTRES + | li TISNIL, LJ_TNIL + | li TISNUM, LJ_TISNUM // Setup type comparison constants. + | .FPU mtc1 TMP3, TOBIT + | ld TMP1, LFUNC:RB->pc + | sd r0, DISPATCH_GL(jit_base)(DISPATCH) + | ld KBASE, PC2PROTO(k)(TMP1) + | .FPU cvt.d.s TOBIT, TOBIT + | // Modified copy of ins_next which handles function header dispatch, too. + | lw INS, 0(PC) + | daddiu PC, PC, 4 + | // Assumes TISNIL == ~LJ_VMST_INTERP == -1 + | sw TISNIL, DISPATCH_GL(vmstate)(DISPATCH) + | decode_OP8a TMP1, INS + | decode_OP8b TMP1 + | sltiu TMP2, TMP1, BC_FUNCF*8 + | daddu TMP0, DISPATCH, TMP1 + | decode_RD8a RD, INS + | ld AT, 0(TMP0) + | decode_RA8a RA, INS + | beqz TMP2, >2 + |. decode_RA8b RA + | jr AT + |. decode_RD8b RD + |2: + | sltiu TMP2, TMP1, (BC_FUNCC+2)*8 // Fast function? + | bnez TMP2, >3 + |. ld TMP1, FRAME_PC(BASE) + | // Check frame below fast function. + | andi TMP0, TMP1, FRAME_TYPE + | bnez TMP0, >3 // Trace stitching continuation? + |. nop + | // Otherwise set KBASE for Lua function below fast function. + | lw TMP2, -4(TMP1) + | decode_RA8a TMP0, TMP2 + | decode_RA8b TMP0 + | dsubu TMP1, BASE, TMP0 + | ld LFUNC:TMP2, -32(TMP1) + | cleartp LFUNC:TMP2 + | ld TMP1, LFUNC:TMP2->pc + | ld KBASE, PC2PROTO(k)(TMP1) + |3: + | daddiu RC, MULTRES, -8 + | jr AT + |. daddu RA, RA, BASE + | + |9: // Rethrow error from the right C frame. + | load_got lj_err_throw + | negu CARG2, CRET1 + | call_intern lj_err_throw // (lua_State *L, int errcode) + |. move CARG1, L + |.endif + | + |//----------------------------------------------------------------------- + |//-- Math helper functions ---------------------------------------------- + |//----------------------------------------------------------------------- + | + |// Hard-float round to integer. + |// Modifies AT, TMP0, FRET1, FRET2, f4. Keeps all others incl. FARG1. + |.macro vm_round_hf, func + | lui TMP0, 0x4330 // Hiword of 2^52 (double). + | dsll TMP0, TMP0, 32 + | dmtc1 TMP0, f4 + | abs.d FRET2, FARG1 // |x| + | dmfc1 AT, FARG1 + | c.olt.d 0, FRET2, f4 + | add.d FRET1, FRET2, f4 // (|x| + 2^52) - 2^52 + | bc1f 0, >1 // Truncate only if |x| < 2^52. + |. sub.d FRET1, FRET1, f4 + | slt AT, AT, r0 + |.if "func" == "ceil" + | lui TMP0, 0xbff0 // Hiword of -1 (double). Preserves -0. + |.else + | lui TMP0, 0x3ff0 // Hiword of +1 (double). + |.endif + |.if "func" == "trunc" + | dsll TMP0, TMP0, 32 + | dmtc1 TMP0, f4 + | c.olt.d 0, FRET2, FRET1 // |x| < result? + | sub.d FRET2, FRET1, f4 + | movt.d FRET1, FRET2, 0 // If yes, subtract +1. + | neg.d FRET2, FRET1 + | jr ra + |. movn.d FRET1, FRET2, AT // Merge sign bit back in. + |.else + | neg.d FRET2, FRET1 + | dsll TMP0, TMP0, 32 + | dmtc1 TMP0, f4 + | movn.d FRET1, FRET2, AT // Merge sign bit back in. + |.if "func" == "ceil" + | c.olt.d 0, FRET1, FARG1 // x > result? + |.else + | c.olt.d 0, FARG1, FRET1 // x < result? + |.endif + | sub.d FRET2, FRET1, f4 // If yes, subtract +-1. + | jr ra + |. movt.d FRET1, FRET2, 0 + |.endif + |1: + | jr ra + |. mov.d FRET1, FARG1 + |.endmacro + | + |.macro vm_round, func + |.if FPU + | vm_round_hf, func + |.endif + |.endmacro + | + |->vm_floor: + | vm_round floor + |->vm_ceil: + | vm_round ceil + |->vm_trunc: + |.if JIT + | vm_round trunc + |.endif + | + |// Soft-float integer to number conversion. + |.macro sfi2d, ARG + |.if not FPU + | beqz ARG, >9 // Handle zero first. + |. sra TMP0, ARG, 31 + | xor TMP1, ARG, TMP0 + | dsubu TMP1, TMP1, TMP0 // Absolute value in TMP1. + | dclz ARG, TMP1 + | addiu ARG, ARG, -11 + | li AT, 0x3ff+63-11-1 + | dsllv TMP1, TMP1, ARG // Align mantissa left with leading 1. + | subu ARG, AT, ARG // Exponent - 1. + | ins ARG, TMP0, 11, 11 // Sign | Exponent. + | dsll ARG, ARG, 52 // Align left. + | jr ra + |. daddu ARG, ARG, TMP1 // Add mantissa, increment exponent. + |9: + | jr ra + |. nop + |.endif + |.endmacro + | + |// Input CARG1. Output: CARG1. Temporaries: AT, TMP0, TMP1. + |->vm_sfi2d_1: + | sfi2d CARG1 + | + |// Input CARG2. Output: CARG2. Temporaries: AT, TMP0, TMP1. + |->vm_sfi2d_2: + | sfi2d CARG2 + | + |// Soft-float comparison. Equivalent to c.eq.d. + |// Input: CARG*. Output: CRET1. Temporaries: AT, TMP0, TMP1. + |->vm_sfcmpeq: + |.if not FPU + | dsll AT, CARG1, 1 + | dsll TMP0, CARG2, 1 + | or TMP1, AT, TMP0 + | beqz TMP1, >8 // Both args +-0: return 1. + |. lui TMP1, 0xffe0 + | dsll TMP1, TMP1, 32 + | sltu AT, TMP1, AT + | sltu TMP0, TMP1, TMP0 + | or TMP1, AT, TMP0 + | bnez TMP1, >9 // Either arg is NaN: return 0; + |. xor AT, CARG1, CARG2 + | jr ra + |. sltiu CRET1, AT, 1 // Same values: return 1. + |8: + | jr ra + |. li CRET1, 1 + |9: + | jr ra + |. li CRET1, 0 + |.endif + | + |// Soft-float comparison. Equivalent to c.ult.d and c.olt.d. + |// Input: CARG1, CARG2. Output: CRET1. Temporaries: AT, TMP0, TMP1, CRET2. + |->vm_sfcmpult: + |.if not FPU + | b >1 + |. li CRET2, 1 + |.endif + | + |->vm_sfcmpolt: + |.if not FPU + | li CRET2, 0 + |1: + | dsll AT, CARG1, 1 + | dsll TMP0, CARG2, 1 + | or TMP1, AT, TMP0 + | beqz TMP1, >8 // Both args +-0: return 0. + |. lui TMP1, 0xffe0 + | dsll TMP1, TMP1, 32 + | sltu AT, TMP1, AT + | sltu TMP0, TMP1, TMP0 + | or TMP1, AT, TMP0 + | bnez TMP1, >9 // Either arg is NaN: return 0 or 1; + |. and AT, CARG1, CARG2 + | bltz AT, >5 // Both args negative? + |. nop + | jr ra + |. slt CRET1, CARG1, CARG2 + |5: // Swap conditions if both operands are negative. + | jr ra + |. slt CRET1, CARG2, CARG1 + |8: + | jr ra + |. li CRET1, 0 + |9: + | jr ra + |. move CRET1, CRET2 + |.endif + | + |// Soft-float comparison. Equivalent to c.ole.d a, b or c.ole.d b, a. + |// Input: CARG1, CARG2, TMP3. Output: CRET1. Temporaries: AT, TMP0, TMP1. + |->vm_sfcmpolex: + |.if not FPU + | dsll AT, CARG1, 1 + | dsll TMP0, CARG2, 1 + | or TMP1, AT, TMP0 + | beqz TMP1, >8 // Both args +-0: return 1. + |. lui TMP1, 0xffe0 + | dsll TMP1, TMP1, 32 + | sltu AT, TMP1, AT + | sltu TMP0, TMP1, TMP0 + | or TMP1, AT, TMP0 + | bnez TMP1, >9 // Either arg is NaN: return 0; + |. and AT, CARG1, CARG2 + | xor AT, AT, TMP3 + | bltz AT, >5 // Both args negative? + |. nop + | jr ra + |. slt CRET1, CARG2, CARG1 + |5: // Swap conditions if both operands are negative. + | jr ra + |. slt CRET1, CARG1, CARG2 + |8: + | jr ra + |. li CRET1, 1 + |9: + | jr ra + |. li CRET1, 0 + |.endif + | + |.macro sfmin_max, name, intins + |->vm_sf .. name: + |.if JIT and not FPU + | move TMP2, ra + | bal ->vm_sfcmpolt + |. nop + | move ra, TMP2 + | move TMP0, CRET1 + | move CRET1, CARG1 + | jr ra + |. intins CRET1, CARG2, TMP0 + |.endif + |.endmacro + | + | sfmin_max min, movz + | sfmin_max max, movn + | + |//----------------------------------------------------------------------- + |//-- Miscellaneous functions -------------------------------------------- + |//----------------------------------------------------------------------- + | + |//----------------------------------------------------------------------- + |//-- FFI helper functions ----------------------------------------------- + |//----------------------------------------------------------------------- + | + |// Handler for callback functions. Callback slot number in r1, g in r2. + |->vm_ffi_callback: + |.if FFI + |.type CTSTATE, CTState, PC + | saveregs + | ld CTSTATE, GL:r2->ctype_state + | daddiu DISPATCH, r2, GG_G2DISP + | load_got lj_ccallback_enter + | sw r1, CTSTATE->cb.slot + | sd CARG1, CTSTATE->cb.gpr[0] + | .FPU sdc1 FARG1, CTSTATE->cb.fpr[0] + | sd CARG2, CTSTATE->cb.gpr[1] + | .FPU sdc1 FARG2, CTSTATE->cb.fpr[1] + | sd CARG3, CTSTATE->cb.gpr[2] + | .FPU sdc1 FARG3, CTSTATE->cb.fpr[2] + | sd CARG4, CTSTATE->cb.gpr[3] + | .FPU sdc1 FARG4, CTSTATE->cb.fpr[3] + | sd CARG5, CTSTATE->cb.gpr[4] + | .FPU sdc1 FARG5, CTSTATE->cb.fpr[4] + | sd CARG6, CTSTATE->cb.gpr[5] + | .FPU sdc1 FARG6, CTSTATE->cb.fpr[5] + | sd CARG7, CTSTATE->cb.gpr[6] + | .FPU sdc1 FARG7, CTSTATE->cb.fpr[6] + | sd CARG8, CTSTATE->cb.gpr[7] + | .FPU sdc1 FARG8, CTSTATE->cb.fpr[7] + | daddiu TMP0, sp, CFRAME_SPACE + | sd TMP0, CTSTATE->cb.stack + | sd r0, SAVE_PC // Any value outside of bytecode is ok. + | move CARG2, sp + | call_intern lj_ccallback_enter // (CTState *cts, void *cf) + |. move CARG1, CTSTATE + | // Returns lua_State *. + | ld BASE, L:CRET1->base + | ld RC, L:CRET1->top + | move L, CRET1 + | .FPU lui TMP3, 0x59c0 // TOBIT = 2^52 + 2^51 (float). + | ld LFUNC:RB, FRAME_FUNC(BASE) + | .FPU mtc1 TMP3, TOBIT + | li TISNIL, LJ_TNIL + | li TISNUM, LJ_TISNUM + | li_vmstate INTERP + | subu RC, RC, BASE + | cleartp LFUNC:RB + | st_vmstate + | .FPU cvt.d.s TOBIT, TOBIT + | ins_callt + |.endif + | + |->cont_ffi_callback: // Return from FFI callback. + |.if FFI + | load_got lj_ccallback_leave + | ld CTSTATE, DISPATCH_GL(ctype_state)(DISPATCH) + | sd BASE, L->base + | sd RB, L->top + | sd L, CTSTATE->L + | move CARG2, RA + | call_intern lj_ccallback_leave // (CTState *cts, TValue *o) + |. move CARG1, CTSTATE + | .FPU ldc1 FRET1, CTSTATE->cb.fpr[0] + | ld CRET1, CTSTATE->cb.gpr[0] + | .FPU ldc1 FRET2, CTSTATE->cb.fpr[1] + | b ->vm_leave_unw + |. ld CRET2, CTSTATE->cb.gpr[1] + |.endif + | + |->vm_ffi_call: // Call C function via FFI. + | // Caveat: needs special frame unwinding, see below. + |.if FFI + | .type CCSTATE, CCallState, CARG1 + | lw TMP1, CCSTATE->spadj + | lbu CARG2, CCSTATE->nsp + | move TMP2, sp + | dsubu sp, sp, TMP1 + | sd ra, -8(TMP2) + | sll CARG2, CARG2, 3 + | sd r16, -16(TMP2) + | sd CCSTATE, -24(TMP2) + | move r16, TMP2 + | daddiu TMP1, CCSTATE, offsetof(CCallState, stack) + | move TMP2, sp + | beqz CARG2, >2 + |. daddu TMP3, TMP1, CARG2 + |1: + | ld TMP0, 0(TMP1) + | daddiu TMP1, TMP1, 8 + | sltu AT, TMP1, TMP3 + | sd TMP0, 0(TMP2) + | bnez AT, <1 + |. daddiu TMP2, TMP2, 8 + |2: + | ld CFUNCADDR, CCSTATE->func + | .FPU ldc1 FARG1, CCSTATE->gpr[0] + | ld CARG2, CCSTATE->gpr[1] + | .FPU ldc1 FARG2, CCSTATE->gpr[1] + | ld CARG3, CCSTATE->gpr[2] + | .FPU ldc1 FARG3, CCSTATE->gpr[2] + | ld CARG4, CCSTATE->gpr[3] + | .FPU ldc1 FARG4, CCSTATE->gpr[3] + | ld CARG5, CCSTATE->gpr[4] + | .FPU ldc1 FARG5, CCSTATE->gpr[4] + | ld CARG6, CCSTATE->gpr[5] + | .FPU ldc1 FARG6, CCSTATE->gpr[5] + | ld CARG7, CCSTATE->gpr[6] + | .FPU ldc1 FARG7, CCSTATE->gpr[6] + | ld CARG8, CCSTATE->gpr[7] + | .FPU ldc1 FARG8, CCSTATE->gpr[7] + | jalr CFUNCADDR + |. ld CARG1, CCSTATE->gpr[0] // Do this last, since CCSTATE is CARG1. + | ld CCSTATE:TMP1, -24(r16) + | ld TMP2, -16(r16) + | ld ra, -8(r16) + | sd CRET1, CCSTATE:TMP1->gpr[0] + | sd CRET2, CCSTATE:TMP1->gpr[1] + |.if FPU + | sdc1 FRET1, CCSTATE:TMP1->fpr[0] + | sdc1 FRET2, CCSTATE:TMP1->fpr[1] + |.else + | sd CARG1, CCSTATE:TMP1->gpr[2] // 2nd FP struct field for soft-float. + |.endif + | move sp, r16 + | jr ra + |. move r16, TMP2 + |.endif + |// Note: vm_ffi_call must be the last function in this object file! + | + |//----------------------------------------------------------------------- +} + +/* Generate the code for a single instruction. */ +static void build_ins(BuildCtx *ctx, BCOp op, int defop) +{ + int vk = 0; + |=>defop: + + switch (op) { + + /* -- Comparison ops ---------------------------------------------------- */ + + /* Remember: all ops branch for a true comparison, fall through otherwise. */ + + case BC_ISLT: case BC_ISGE: case BC_ISLE: case BC_ISGT: + | // RA = src1*8, RD = src2*8, JMP with RD = target + |.macro bc_comp, FRA, FRD, ARGRA, ARGRD, movop, fmovop, fcomp, sfcomp + | daddu RA, BASE, RA + | daddu RD, BASE, RD + | ld ARGRA, 0(RA) + | ld ARGRD, 0(RD) + | lhu TMP2, OFS_RD(PC) + | gettp CARG3, ARGRA + | gettp CARG4, ARGRD + | bne CARG3, TISNUM, >2 + |. daddiu PC, PC, 4 + | bne CARG4, TISNUM, >5 + |. decode_RD4b TMP2 + | sextw ARGRA, ARGRA + | sextw ARGRD, ARGRD + | lui TMP3, (-(BCBIAS_J*4 >> 16) & 65535) + | slt AT, CARG1, CARG2 + | addu TMP2, TMP2, TMP3 + | movop TMP2, r0, AT + |1: + | daddu PC, PC, TMP2 + | ins_next + | + |2: // RA is not an integer. + | sltiu AT, CARG3, LJ_TISNUM + | beqz AT, ->vmeta_comp + |. lui TMP3, (-(BCBIAS_J*4 >> 16) & 65535) + | sltiu AT, CARG4, LJ_TISNUM + | beqz AT, >4 + |. decode_RD4b TMP2 + |.if FPU + | ldc1 FRA, 0(RA) + | ldc1 FRD, 0(RD) + |.endif + |3: // RA and RD are both numbers. + |.if FPU + | fcomp f20, f22 + | addu TMP2, TMP2, TMP3 + | b <1 + |. fmovop TMP2, r0 + |.else + | bal sfcomp + |. addu TMP2, TMP2, TMP3 + | b <1 + |. movop TMP2, r0, CRET1 + |.endif + | + |4: // RA is a number, RD is not a number. + | bne CARG4, TISNUM, ->vmeta_comp + | // RA is a number, RD is an integer. Convert RD to a number. + |.if FPU + |. lwc1 FRD, LO(RD) + | ldc1 FRA, 0(RA) + | b <3 + |. cvt.d.w FRD, FRD + |.else + |.if "ARGRD" == "CARG1" + |. sextw CARG1, CARG1 + | bal ->vm_sfi2d_1 + |. nop + |.else + |. sextw CARG2, CARG2 + | bal ->vm_sfi2d_2 + |. nop + |.endif + | b <3 + |. nop + |.endif + | + |5: // RA is an integer, RD is not an integer + | sltiu AT, CARG4, LJ_TISNUM + | beqz AT, ->vmeta_comp + |. lui TMP3, (-(BCBIAS_J*4 >> 16) & 65535) + | // RA is an integer, RD is a number. Convert RA to a number. + |.if FPU + | lwc1 FRA, LO(RA) + | ldc1 FRD, 0(RD) + | b <3 + | cvt.d.w FRA, FRA + |.else + |.if "ARGRA" == "CARG1" + | bal ->vm_sfi2d_1 + |. sextw CARG1, CARG1 + |.else + | bal ->vm_sfi2d_2 + |. sextw CARG2, CARG2 + |.endif + | b <3 + |. nop + |.endif + |.endmacro + | + if (op == BC_ISLT) { + | bc_comp f20, f22, CARG1, CARG2, movz, movf, c.olt.d, ->vm_sfcmpolt + } else if (op == BC_ISGE) { + | bc_comp f20, f22, CARG1, CARG2, movn, movt, c.olt.d, ->vm_sfcmpolt + } else if (op == BC_ISLE) { + | bc_comp f22, f20, CARG2, CARG1, movn, movt, c.ult.d, ->vm_sfcmpult + } else { + | bc_comp f22, f20, CARG2, CARG1, movz, movf, c.ult.d, ->vm_sfcmpult + } + break; + + case BC_ISEQV: case BC_ISNEV: + vk = op == BC_ISEQV; + | // RA = src1*8, RD = src2*8, JMP with RD = target + | daddu RA, BASE, RA + | daddiu PC, PC, 4 + | daddu RD, BASE, RD + | ld CARG1, 0(RA) + | lhu TMP2, -4+OFS_RD(PC) + | ld CARG2, 0(RD) + | gettp CARG3, CARG1 + | gettp CARG4, CARG2 + | sltu AT, TISNUM, CARG3 + | sltu TMP1, TISNUM, CARG4 + | or AT, AT, TMP1 + if (vk) { + | beqz AT, ->BC_ISEQN_Z + } else { + | beqz AT, ->BC_ISNEN_Z + } + | // Either or both types are not numbers. + | lui TMP3, (-(BCBIAS_J*4 >> 16) & 65535) + |.if FFI + |. li AT, LJ_TCDATA + | beq CARG3, AT, ->vmeta_equal_cd + |.endif + | decode_RD4b TMP2 + |.if FFI + | beq CARG4, AT, ->vmeta_equal_cd + |. nop + |.endif + | bne CARG1, CARG2, >2 + |. addu TMP2, TMP2, TMP3 + | // Tag and value are equal. + if (vk) { + |->BC_ISEQV_Z: + | daddu PC, PC, TMP2 + } + |1: + | ins_next + | + |2: // Check if the tags are the same and it's a table or userdata. + | xor AT, CARG3, CARG4 // Same type? + | sltiu TMP0, CARG3, LJ_TISTABUD+1 // Table or userdata? + | movn TMP0, r0, AT + if (vk) { + | beqz TMP0, <1 + } else { + | beqz TMP0, ->BC_ISEQV_Z // Reuse code from opposite instruction. + } + | // Different tables or userdatas. Need to check __eq metamethod. + | // Field metatable must be at same offset for GCtab and GCudata! + |. cleartp TAB:TMP1, CARG1 + | ld TAB:TMP3, TAB:TMP1->metatable + if (vk) { + | beqz TAB:TMP3, <1 // No metatable? + |. nop + | lbu TMP3, TAB:TMP3->nomm + | andi TMP3, TMP3, 1<1 // Or 'no __eq' flag set? + } else { + | beqz TAB:TMP3,->BC_ISEQV_Z // No metatable? + |. nop + | lbu TMP3, TAB:TMP3->nomm + | andi TMP3, TMP3, 1<BC_ISEQV_Z // Or 'no __eq' flag set? + } + |. nop + | b ->vmeta_equal // Handle __eq metamethod. + |. li TMP0, 1-vk // ne = 0 or 1. + break; + + case BC_ISEQS: case BC_ISNES: + vk = op == BC_ISEQS; + | // RA = src*8, RD = str_const*8 (~), JMP with RD = target + | daddu RA, BASE, RA + | daddiu PC, PC, 4 + | ld CARG1, 0(RA) + | dsubu RD, KBASE, RD + | lhu TMP2, -4+OFS_RD(PC) + | ld CARG2, -8(RD) // KBASE-8-str_const*8 + |.if FFI + | gettp TMP0, CARG1 + | li AT, LJ_TCDATA + |.endif + | li TMP1, LJ_TSTR + | decode_RD4b TMP2 + |.if FFI + | beq TMP0, AT, ->vmeta_equal_cd + |.endif + |. settp CARG2, TMP1 + | lui TMP3, (-(BCBIAS_J*4 >> 16) & 65535) + | xor TMP1, CARG1, CARG2 + | addu TMP2, TMP2, TMP3 + if (vk) { + | movn TMP2, r0, TMP1 + } else { + | movz TMP2, r0, TMP1 + } + | daddu PC, PC, TMP2 + | ins_next + break; + + case BC_ISEQN: case BC_ISNEN: + vk = op == BC_ISEQN; + | // RA = src*8, RD = num_const*8, JMP with RD = target + | daddu RA, BASE, RA + | daddu RD, KBASE, RD + | ld CARG1, 0(RA) + | ld CARG2, 0(RD) + | lhu TMP2, OFS_RD(PC) + | gettp CARG3, CARG1 + | gettp CARG4, CARG2 + | daddiu PC, PC, 4 + | lui TMP3, (-(BCBIAS_J*4 >> 16) & 65535) + if (vk) { + |->BC_ISEQN_Z: + } else { + |->BC_ISNEN_Z: + } + | bne CARG3, TISNUM, >3 + |. decode_RD4b TMP2 + | bne CARG4, TISNUM, >6 + |. addu TMP2, TMP2, TMP3 + | xor AT, CARG1, CARG2 + if (vk) { + | movn TMP2, r0, AT + |1: + | daddu PC, PC, TMP2 + |2: + } else { + | movz TMP2, r0, AT + |1: + |2: + | daddu PC, PC, TMP2 + } + | ins_next + | + |3: // RA is not an integer. + | sltu AT, CARG3, TISNUM + |.if FFI + | beqz AT, >8 + |.else + | beqz AT, <2 + |.endif + |. addu TMP2, TMP2, TMP3 + | sltu AT, CARG4, TISNUM + |.if FPU + | ldc1 f20, 0(RA) + | ldc1 f22, 0(RD) + |.endif + | beqz AT, >5 + |. nop + |4: // RA and RD are both numbers. + |.if FPU + | c.eq.d f20, f22 + | b <1 + if (vk) { + |. movf TMP2, r0 + } else { + |. movt TMP2, r0 + } + |.else + | bal ->vm_sfcmpeq + |. nop + | b <1 + if (vk) { + |. movz TMP2, r0, CRET1 + } else { + |. movn TMP2, r0, CRET1 + } + |.endif + | + |5: // RA is a number, RD is not a number. + |.if FFI + | bne CARG4, TISNUM, >9 + |.else + | bne CARG4, TISNUM, <2 + |.endif + | // RA is a number, RD is an integer. Convert RD to a number. + |.if FPU + |. lwc1 f22, LO(RD) + | b <4 + |. cvt.d.w f22, f22 + |.else + |. sextw CARG2, CARG2 + | bal ->vm_sfi2d_2 + |. nop + | b <4 + |. nop + |.endif + | + |6: // RA is an integer, RD is not an integer + | sltu AT, CARG4, TISNUM + |.if FFI + | beqz AT, >9 + |.else + | beqz AT, <2 + |.endif + | // RA is an integer, RD is a number. Convert RA to a number. + |.if FPU + |. lwc1 f20, LO(RA) + | ldc1 f22, 0(RD) + | b <4 + | cvt.d.w f20, f20 + |.else + |. sextw CARG1, CARG1 + | bal ->vm_sfi2d_1 + |. nop + | b <4 + |. nop + |.endif + | + |.if FFI + |8: + | li AT, LJ_TCDATA + | bne CARG3, AT, <2 + |. nop + | b ->vmeta_equal_cd + |. nop + |9: + | li AT, LJ_TCDATA + | bne CARG4, AT, <2 + |. nop + | b ->vmeta_equal_cd + |. nop + |.endif + break; + + case BC_ISEQP: case BC_ISNEP: + vk = op == BC_ISEQP; + | // RA = src*8, RD = primitive_type*8 (~), JMP with RD = target + | daddu RA, BASE, RA + | srl TMP1, RD, 3 + | ld TMP0, 0(RA) + | lhu TMP2, OFS_RD(PC) + | not TMP1, TMP1 + | gettp TMP0, TMP0 + | daddiu PC, PC, 4 + |.if FFI + | li AT, LJ_TCDATA + | beq TMP0, AT, ->vmeta_equal_cd + |.endif + |. xor TMP0, TMP0, TMP1 + | decode_RD4b TMP2 + | lui TMP3, (-(BCBIAS_J*4 >> 16) & 65535) + | addu TMP2, TMP2, TMP3 + if (vk) { + | movn TMP2, r0, TMP0 + } else { + | movz TMP2, r0, TMP0 + } + | daddu PC, PC, TMP2 + | ins_next + break; + + /* -- Unary test and copy ops ------------------------------------------- */ + + case BC_ISTC: case BC_ISFC: case BC_IST: case BC_ISF: + | // RA = dst*8 or unused, RD = src*8, JMP with RD = target + | daddu RD, BASE, RD + | lhu TMP2, OFS_RD(PC) + | ld TMP0, 0(RD) + | daddiu PC, PC, 4 + | gettp TMP0, TMP0 + | sltiu TMP0, TMP0, LJ_TISTRUECOND + if (op == BC_IST || op == BC_ISF) { + | decode_RD4b TMP2 + | lui TMP3, (-(BCBIAS_J*4 >> 16) & 65535) + | addu TMP2, TMP2, TMP3 + if (op == BC_IST) { + | movz TMP2, r0, TMP0 + } else { + | movn TMP2, r0, TMP0 + } + | daddu PC, PC, TMP2 + } else { + | ld CRET1, 0(RD) + if (op == BC_ISTC) { + | beqz TMP0, >1 + } else { + | bnez TMP0, >1 + } + |. daddu RA, BASE, RA + | decode_RD4b TMP2 + | lui TMP3, (-(BCBIAS_J*4 >> 16) & 65535) + | addu TMP2, TMP2, TMP3 + | sd CRET1, 0(RA) + | daddu PC, PC, TMP2 + |1: + } + | ins_next + break; + + case BC_ISTYPE: + | // RA = src*8, RD = -type*8 + | daddu TMP2, BASE, RA + | srl TMP1, RD, 3 + | ld TMP0, 0(TMP2) + | ins_next1 + | gettp TMP0, TMP0 + | daddu AT, TMP0, TMP1 + | bnez AT, ->vmeta_istype + |. ins_next2 + break; + case BC_ISNUM: + | // RA = src*8, RD = -(TISNUM-1)*8 + | daddu TMP2, BASE, RA + | ld TMP0, 0(TMP2) + | ins_next1 + | checknum TMP0, ->vmeta_istype + |. ins_next2 + break; + + /* -- Unary ops --------------------------------------------------------- */ + + case BC_MOV: + | // RA = dst*8, RD = src*8 + | daddu RD, BASE, RD + | daddu RA, BASE, RA + | ld CRET1, 0(RD) + | ins_next1 + | sd CRET1, 0(RA) + | ins_next2 + break; + case BC_NOT: + | // RA = dst*8, RD = src*8 + | daddu RD, BASE, RD + | daddu RA, BASE, RA + | ld TMP0, 0(RD) + | li AT, LJ_TTRUE + | gettp TMP0, TMP0 + | sltu TMP0, AT, TMP0 + | addiu TMP0, TMP0, 1 + | dsll TMP0, TMP0, 47 + | not TMP0, TMP0 + | ins_next1 + | sd TMP0, 0(RA) + | ins_next2 + break; + case BC_UNM: + | // RA = dst*8, RD = src*8 + | daddu RB, BASE, RD + | ld CARG1, 0(RB) + | daddu RA, BASE, RA + | gettp CARG3, CARG1 + | bne CARG3, TISNUM, >2 + |. lui TMP1, 0x8000 + | sextw CARG1, CARG1 + | beq CARG1, TMP1, ->vmeta_unm // Meta handler deals with -2^31. + |. negu CARG1, CARG1 + | zextw CARG1, CARG1 + | settp CARG1, TISNUM + |1: + | ins_next1 + | sd CARG1, 0(RA) + | ins_next2 + |2: + | sltiu AT, CARG3, LJ_TISNUM + | beqz AT, ->vmeta_unm + |. dsll TMP1, TMP1, 32 + | b <1 + |. xor CARG1, CARG1, TMP1 + break; + case BC_LEN: + | // RA = dst*8, RD = src*8 + | daddu CARG2, BASE, RD + | daddu RA, BASE, RA + | ld TMP0, 0(CARG2) + | gettp TMP1, TMP0 + | daddiu AT, TMP1, -LJ_TSTR + | bnez AT, >2 + |. cleartp STR:CARG1, TMP0 + | lw CRET1, STR:CARG1->len + |1: + | settp CRET1, TISNUM + | ins_next1 + | sd CRET1, 0(RA) + | ins_next2 + |2: + | daddiu AT, TMP1, -LJ_TTAB + | bnez AT, ->vmeta_len + |. nop +#if LJ_52 + | ld TAB:TMP2, TAB:CARG1->metatable + | bnez TAB:TMP2, >9 + |. nop + |3: +#endif + |->BC_LEN_Z: + | load_got lj_tab_len + | call_intern lj_tab_len // (GCtab *t) + |. nop + | // Returns uint32_t (but less than 2^31). + | b <1 + |. nop +#if LJ_52 + |9: + | lbu TMP0, TAB:TMP2->nomm + | andi TMP0, TMP0, 1<vmeta_len + |. nop +#endif + break; + + /* -- Binary ops -------------------------------------------------------- */ + + |.macro fpmod, a, b, c + | bal ->vm_floor // floor(b/c) + |. div.d FARG1, b, c + | mul.d a, FRET1, c + | sub.d a, b, a // b - floor(b/c)*c + |.endmacro + + |.macro sfpmod + | daddiu sp, sp, -16 + | + | load_got __divdf3 + | sd CARG1, 0(sp) + | call_extern + |. sd CARG2, 8(sp) + | + | load_got floor + | call_extern + |. move CARG1, CRET1 + | + | load_got __muldf3 + | move CARG1, CRET1 + | call_extern + |. ld CARG2, 8(sp) + | + | load_got __subdf3 + | ld CARG1, 0(sp) + | call_extern + |. move CARG2, CRET1 + | + | daddiu sp, sp, 16 + |.endmacro + + |.macro ins_arithpre, label + ||vk = ((int)op - BC_ADDVN) / (BC_ADDNV-BC_ADDVN); + | // RA = dst*8, RB = src1*8, RC = src2*8 | num_const*8 + ||switch (vk) { + ||case 0: + | decode_RB8a RB, INS + | decode_RB8b RB + | decode_RDtoRC8 RC, RD + | // RA = dst*8, RB = src1*8, RC = num_const*8 + | daddu RB, BASE, RB + |.if "label" ~= "none" + | b label + |.endif + |. daddu RC, KBASE, RC + || break; + ||case 1: + | decode_RB8a RC, INS + | decode_RB8b RC + | decode_RDtoRC8 RB, RD + | // RA = dst*8, RB = num_const*8, RC = src1*8 + | daddu RC, BASE, RC + |.if "label" ~= "none" + | b label + |.endif + |. daddu RB, KBASE, RB + || break; + ||default: + | decode_RB8a RB, INS + | decode_RB8b RB + | decode_RDtoRC8 RC, RD + | // RA = dst*8, RB = src1*8, RC = src2*8 + | daddu RB, BASE, RB + |.if "label" ~= "none" + | b label + |.endif + |. daddu RC, BASE, RC + || break; + ||} + |.endmacro + | + |.macro ins_arith, intins, fpins, fpcall, label + | ins_arithpre none + | + |.if "label" ~= "none" + |label: + |.endif + | + |// Used in 5. + | ld CARG1, 0(RB) + | ld CARG2, 0(RC) + | gettp TMP0, CARG1 + | gettp TMP1, CARG2 + | + |.if "intins" ~= "div" + | + | // Check for two integers. + | sextw CARG3, CARG1 + | bne TMP0, TISNUM, >5 + |. sextw CARG4, CARG2 + | bne TMP1, TISNUM, >5 + | + |.if "intins" == "addu" + |. intins CRET1, CARG3, CARG4 + | xor TMP1, CRET1, CARG3 // ((y^a) & (y^b)) < 0: overflow. + | xor TMP2, CRET1, CARG4 + | and TMP1, TMP1, TMP2 + | bltz TMP1, ->vmeta_arith + |. daddu RA, BASE, RA + |.elif "intins" == "subu" + |. intins CRET1, CARG3, CARG4 + | xor TMP1, CRET1, CARG3 // ((y^a) & (a^b)) < 0: overflow. + | xor TMP2, CARG3, CARG4 + | and TMP1, TMP1, TMP2 + | bltz TMP1, ->vmeta_arith + |. daddu RA, BASE, RA + |.elif "intins" == "mult" + |. intins CARG3, CARG4 + | mflo CRET1 + | mfhi TMP2 + | sra TMP1, CRET1, 31 + | bne TMP1, TMP2, ->vmeta_arith + |. daddu RA, BASE, RA + |.else + |. load_got lj_vm_modi + | beqz CARG4, ->vmeta_arith + |. daddu RA, BASE, RA + | move CARG1, CARG3 + | call_extern + |. move CARG2, CARG4 + |.endif + | + | zextw CRET1, CRET1 + | settp CRET1, TISNUM + | ins_next1 + | sd CRET1, 0(RA) + |3: + | ins_next2 + | + |.endif + | + |5: // Check for two numbers. + | .FPU ldc1 f20, 0(RB) + | sltu AT, TMP0, TISNUM + | sltu TMP0, TMP1, TISNUM + | .FPU ldc1 f22, 0(RC) + | and AT, AT, TMP0 + | beqz AT, ->vmeta_arith + |. daddu RA, BASE, RA + | + |.if FPU + | fpins FRET1, f20, f22 + |.elif "fpcall" == "sfpmod" + | sfpmod + |.else + | load_got fpcall + | call_extern + |. nop + |.endif + | + | ins_next1 + |.if "intins" ~= "div" + | b <3 + |.endif + |.if FPU + |. sdc1 FRET1, 0(RA) + |.else + |. sd CRET1, 0(RA) + |.endif + |.if "intins" == "div" + | ins_next2 + |.endif + | + |.endmacro + + case BC_ADDVN: case BC_ADDNV: case BC_ADDVV: + | ins_arith addu, add.d, __adddf3, none + break; + case BC_SUBVN: case BC_SUBNV: case BC_SUBVV: + | ins_arith subu, sub.d, __subdf3, none + break; + case BC_MULVN: case BC_MULNV: case BC_MULVV: + | ins_arith mult, mul.d, __muldf3, none + break; + case BC_DIVVN: + | ins_arith div, div.d, __divdf3, ->BC_DIVVN_Z + break; + case BC_DIVNV: case BC_DIVVV: + | ins_arithpre ->BC_DIVVN_Z + break; + case BC_MODVN: + | ins_arith modi, fpmod, sfpmod, ->BC_MODVN_Z + break; + case BC_MODNV: case BC_MODVV: + | ins_arithpre ->BC_MODVN_Z + break; + case BC_POW: + | ins_arithpre none + | ld CARG1, 0(RB) + | ld CARG2, 0(RC) + | gettp TMP0, CARG1 + | gettp TMP1, CARG2 + | sltiu TMP0, TMP0, LJ_TISNUM + | sltiu TMP1, TMP1, LJ_TISNUM + | and AT, TMP0, TMP1 + | load_got pow + | beqz AT, ->vmeta_arith + |. daddu RA, BASE, RA + |.if FPU + | ldc1 FARG1, 0(RB) + | ldc1 FARG2, 0(RC) + |.endif + | call_extern + |. nop + | ins_next1 + |.if FPU + | sdc1 FRET1, 0(RA) + |.else + | sd CRET1, 0(RA) + |.endif + | ins_next2 + break; + + case BC_CAT: + | // RA = dst*8, RB = src_start*8, RC = src_end*8 + | decode_RB8a RB, INS + | decode_RB8b RB + | decode_RDtoRC8 RC, RD + | dsubu CARG3, RC, RB + | sd BASE, L->base + | daddu CARG2, BASE, RC + | move MULTRES, RB + |->BC_CAT_Z: + | load_got lj_meta_cat + | srl CARG3, CARG3, 3 + | sd PC, SAVE_PC + | call_intern lj_meta_cat // (lua_State *L, TValue *top, int left) + |. move CARG1, L + | // Returns NULL (finished) or TValue * (metamethod). + | bnez CRET1, ->vmeta_binop + |. ld BASE, L->base + | daddu RB, BASE, MULTRES + | ld CRET1, 0(RB) + | daddu RA, BASE, RA + | ins_next1 + | sd CRET1, 0(RA) + | ins_next2 + break; + + /* -- Constant ops ------------------------------------------------------ */ + + case BC_KSTR: + | // RA = dst*8, RD = str_const*8 (~) + | dsubu TMP1, KBASE, RD + | ins_next1 + | li TMP2, LJ_TSTR + | ld TMP0, -8(TMP1) // KBASE-8-str_const*8 + | daddu RA, BASE, RA + | settp TMP0, TMP2 + | sd TMP0, 0(RA) + | ins_next2 + break; + case BC_KCDATA: + |.if FFI + | // RA = dst*8, RD = cdata_const*8 (~) + | dsubu TMP1, KBASE, RD + | ins_next1 + | ld TMP0, -8(TMP1) // KBASE-8-cdata_const*8 + | li TMP2, LJ_TCDATA + | daddu RA, BASE, RA + | settp TMP0, TMP2 + | sd TMP0, 0(RA) + | ins_next2 + |.endif + break; + case BC_KSHORT: + | // RA = dst*8, RD = int16_literal*8 + | sra RD, INS, 16 + | daddu RA, BASE, RA + | zextw RD, RD + | ins_next1 + | settp RD, TISNUM + | sd RD, 0(RA) + | ins_next2 + break; + case BC_KNUM: + | // RA = dst*8, RD = num_const*8 + | daddu RD, KBASE, RD + | daddu RA, BASE, RA + | ld CRET1, 0(RD) + | ins_next1 + | sd CRET1, 0(RA) + | ins_next2 + break; + case BC_KPRI: + | // RA = dst*8, RD = primitive_type*8 (~) + | daddu RA, BASE, RA + | dsll TMP0, RD, 44 + | not TMP0, TMP0 + | ins_next1 + | sd TMP0, 0(RA) + | ins_next2 + break; + case BC_KNIL: + | // RA = base*8, RD = end*8 + | daddu RA, BASE, RA + | sd TISNIL, 0(RA) + | daddiu RA, RA, 8 + | daddu RD, BASE, RD + |1: + | sd TISNIL, 0(RA) + | slt AT, RA, RD + | bnez AT, <1 + |. daddiu RA, RA, 8 + | ins_next_ + break; + + /* -- Upvalue and function ops ------------------------------------------ */ + + case BC_UGET: + | // RA = dst*8, RD = uvnum*8 + | ld LFUNC:RB, FRAME_FUNC(BASE) + | daddu RA, BASE, RA + | cleartp LFUNC:RB + | daddu RD, RD, LFUNC:RB + | ld UPVAL:RB, LFUNC:RD->uvptr + | ins_next1 + | ld TMP1, UPVAL:RB->v + | ld CRET1, 0(TMP1) + | sd CRET1, 0(RA) + | ins_next2 + break; + case BC_USETV: + | // RA = uvnum*8, RD = src*8 + | ld LFUNC:RB, FRAME_FUNC(BASE) + | daddu RD, BASE, RD + | cleartp LFUNC:RB + | daddu RA, RA, LFUNC:RB + | ld UPVAL:RB, LFUNC:RA->uvptr + | ld CRET1, 0(RD) + | lbu TMP3, UPVAL:RB->marked + | ld CARG2, UPVAL:RB->v + | andi TMP3, TMP3, LJ_GC_BLACK // isblack(uv) + | lbu TMP0, UPVAL:RB->closed + | gettp TMP2, CRET1 + | sd CRET1, 0(CARG2) + | li AT, LJ_GC_BLACK|1 + | or TMP3, TMP3, TMP0 + | beq TMP3, AT, >2 // Upvalue is closed and black? + |. daddiu TMP2, TMP2, -(LJ_TNUMX+1) + |1: + | ins_next + | + |2: // Check if new value is collectable. + | sltiu AT, TMP2, LJ_TISGCV - (LJ_TNUMX+1) + | beqz AT, <1 // tvisgcv(v) + |. cleartp GCOBJ:CRET1, CRET1 + | lbu TMP3, GCOBJ:CRET1->gch.marked + | andi TMP3, TMP3, LJ_GC_WHITES // iswhite(v) + | beqz TMP3, <1 + |. load_got lj_gc_barrieruv + | // Crossed a write barrier. Move the barrier forward. + | call_intern lj_gc_barrieruv // (global_State *g, TValue *tv) + |. daddiu CARG1, DISPATCH, GG_DISP2G + | b <1 + |. nop + break; + case BC_USETS: + | // RA = uvnum*8, RD = str_const*8 (~) + | ld LFUNC:RB, FRAME_FUNC(BASE) + | dsubu TMP1, KBASE, RD + | cleartp LFUNC:RB + | daddu RA, RA, LFUNC:RB + | ld UPVAL:RB, LFUNC:RA->uvptr + | ld STR:TMP1, -8(TMP1) // KBASE-8-str_const*8 + | lbu TMP2, UPVAL:RB->marked + | ld CARG2, UPVAL:RB->v + | lbu TMP3, STR:TMP1->marked + | andi AT, TMP2, LJ_GC_BLACK // isblack(uv) + | lbu TMP2, UPVAL:RB->closed + | li TMP0, LJ_TSTR + | settp TMP1, TMP0 + | bnez AT, >2 + |. sd TMP1, 0(CARG2) + |1: + | ins_next + | + |2: // Check if string is white and ensure upvalue is closed. + | beqz TMP2, <1 + |. andi AT, TMP3, LJ_GC_WHITES // iswhite(str) + | beqz AT, <1 + |. load_got lj_gc_barrieruv + | // Crossed a write barrier. Move the barrier forward. + | call_intern lj_gc_barrieruv // (global_State *g, TValue *tv) + |. daddiu CARG1, DISPATCH, GG_DISP2G + | b <1 + |. nop + break; + case BC_USETN: + | // RA = uvnum*8, RD = num_const*8 + | ld LFUNC:RB, FRAME_FUNC(BASE) + | daddu RD, KBASE, RD + | cleartp LFUNC:RB + | daddu RA, RA, LFUNC:RB + | ld UPVAL:RB, LFUNC:RA->uvptr + | ld CRET1, 0(RD) + | ld TMP1, UPVAL:RB->v + | ins_next1 + | sd CRET1, 0(TMP1) + | ins_next2 + break; + case BC_USETP: + | // RA = uvnum*8, RD = primitive_type*8 (~) + | ld LFUNC:RB, FRAME_FUNC(BASE) + | dsll TMP0, RD, 44 + | cleartp LFUNC:RB + | daddu RA, RA, LFUNC:RB + | not TMP0, TMP0 + | ld UPVAL:RB, LFUNC:RA->uvptr + | ins_next1 + | ld TMP1, UPVAL:RB->v + | sd TMP0, 0(TMP1) + | ins_next2 + break; + + case BC_UCLO: + | // RA = level*8, RD = target + | ld TMP2, L->openupval + | branch_RD // Do this first since RD is not saved. + | load_got lj_func_closeuv + | sd BASE, L->base + | beqz TMP2, >1 + |. move CARG1, L + | call_intern lj_func_closeuv // (lua_State *L, TValue *level) + |. daddu CARG2, BASE, RA + | ld BASE, L->base + |1: + | ins_next + break; + + case BC_FNEW: + | // RA = dst*8, RD = proto_const*8 (~) (holding function prototype) + | load_got lj_func_newL_gc + | dsubu TMP1, KBASE, RD + | ld CARG3, FRAME_FUNC(BASE) + | ld CARG2, -8(TMP1) // KBASE-8-tab_const*8 + | sd BASE, L->base + | sd PC, SAVE_PC + | cleartp CARG3 + | // (lua_State *L, GCproto *pt, GCfuncL *parent) + | call_intern lj_func_newL_gc + |. move CARG1, L + | // Returns GCfuncL *. + | li TMP0, LJ_TFUNC + | ld BASE, L->base + | ins_next1 + | settp CRET1, TMP0 + | daddu RA, BASE, RA + | sd CRET1, 0(RA) + | ins_next2 + break; + + /* -- Table ops --------------------------------------------------------- */ + + case BC_TNEW: + case BC_TDUP: + | // RA = dst*8, RD = (hbits|asize)*8 | tab_const*8 (~) + | ld TMP0, DISPATCH_GL(gc.total)(DISPATCH) + | ld TMP1, DISPATCH_GL(gc.threshold)(DISPATCH) + | sd BASE, L->base + | sd PC, SAVE_PC + | sltu AT, TMP0, TMP1 + | beqz AT, >5 + |1: + if (op == BC_TNEW) { + | load_got lj_tab_new + | srl CARG2, RD, 3 + | andi CARG2, CARG2, 0x7ff + | li TMP0, 0x801 + | addiu AT, CARG2, -0x7ff + | srl CARG3, RD, 14 + | movz CARG2, TMP0, AT + | // (lua_State *L, int32_t asize, uint32_t hbits) + | call_intern lj_tab_new + |. move CARG1, L + | // Returns Table *. + } else { + | load_got lj_tab_dup + | dsubu TMP1, KBASE, RD + | move CARG1, L + | call_intern lj_tab_dup // (lua_State *L, Table *kt) + |. ld CARG2, -8(TMP1) // KBASE-8-str_const*8 + | // Returns Table *. + } + | li TMP0, LJ_TTAB + | ld BASE, L->base + | ins_next1 + | daddu RA, BASE, RA + | settp CRET1, TMP0 + | sd CRET1, 0(RA) + | ins_next2 + |5: + | load_got lj_gc_step_fixtop + | move MULTRES, RD + | call_intern lj_gc_step_fixtop // (lua_State *L) + |. move CARG1, L + | b <1 + |. move RD, MULTRES + break; + + case BC_GGET: + | // RA = dst*8, RD = str_const*8 (~) + case BC_GSET: + | // RA = src*8, RD = str_const*8 (~) + | ld LFUNC:TMP2, FRAME_FUNC(BASE) + | dsubu TMP1, KBASE, RD + | ld STR:RC, -8(TMP1) // KBASE-8-str_const*8 + | cleartp LFUNC:TMP2 + | ld TAB:RB, LFUNC:TMP2->env + if (op == BC_GGET) { + | b ->BC_TGETS_Z + } else { + | b ->BC_TSETS_Z + } + |. daddu RA, BASE, RA + break; + + case BC_TGETV: + | // RA = dst*8, RB = table*8, RC = key*8 + | decode_RB8a RB, INS + | decode_RB8b RB + | decode_RDtoRC8 RC, RD + | daddu CARG2, BASE, RB + | daddu CARG3, BASE, RC + | ld TAB:RB, 0(CARG2) + | ld TMP2, 0(CARG3) + | daddu RA, BASE, RA + | checktab TAB:RB, ->vmeta_tgetv + | gettp TMP3, TMP2 + | bne TMP3, TISNUM, >5 // Integer key? + |. lw TMP0, TAB:RB->asize + | sextw TMP2, TMP2 + | ld TMP1, TAB:RB->array + | sltu AT, TMP2, TMP0 + | sll TMP2, TMP2, 3 + | beqz AT, ->vmeta_tgetv // Integer key and in array part? + |. daddu TMP2, TMP1, TMP2 + | ld AT, 0(TMP2) + | beq AT, TISNIL, >2 + |. ld CRET1, 0(TMP2) + |1: + | ins_next1 + | sd CRET1, 0(RA) + | ins_next2 + | + |2: // Check for __index if table value is nil. + | ld TAB:TMP2, TAB:RB->metatable + | beqz TAB:TMP2, <1 // No metatable: done. + |. nop + | lbu TMP0, TAB:TMP2->nomm + | andi TMP0, TMP0, 1<vmeta_tgetv + |. nop + | + |5: + | li AT, LJ_TSTR + | bne TMP3, AT, ->vmeta_tgetv + |. cleartp RC, TMP2 + | b ->BC_TGETS_Z // String key? + |. nop + break; + case BC_TGETS: + | // RA = dst*8, RB = table*8, RC = str_const*8 (~) + | decode_RB8a RB, INS + | decode_RB8b RB + | decode_RC8a RC, INS + | daddu CARG2, BASE, RB + | decode_RC8b RC + | ld TAB:RB, 0(CARG2) + | dsubu CARG3, KBASE, RC + | daddu RA, BASE, RA + | ld STR:RC, -8(CARG3) // KBASE-8-str_const*8 + | checktab TAB:RB, ->vmeta_tgets1 + |->BC_TGETS_Z: + | // TAB:RB = GCtab *, STR:RC = GCstr *, RA = dst*8 + | lw TMP0, TAB:RB->hmask + | lw TMP1, STR:RC->hash + | ld NODE:TMP2, TAB:RB->node + | and TMP1, TMP1, TMP0 // idx = str->hash & tab->hmask + | sll TMP0, TMP1, 5 + | sll TMP1, TMP1, 3 + | subu TMP1, TMP0, TMP1 + | li TMP3, LJ_TSTR + | daddu NODE:TMP2, NODE:TMP2, TMP1 // node = tab->node + (idx*32-idx*8) + | settp STR:RC, TMP3 // Tagged key to look for. + |1: + | ld CARG1, NODE:TMP2->key + | ld CRET1, NODE:TMP2->val + | ld NODE:TMP1, NODE:TMP2->next + | bne CARG1, RC, >4 + |. ld TAB:TMP3, TAB:RB->metatable + | beq CRET1, TISNIL, >5 // Key found, but nil value? + |. nop + |3: + | ins_next1 + | sd CRET1, 0(RA) + | ins_next2 + | + |4: // Follow hash chain. + | bnez NODE:TMP1, <1 + |. move NODE:TMP2, NODE:TMP1 + | // End of hash chain: key not found, nil result. + | + |5: // Check for __index if table value is nil. + | beqz TAB:TMP3, <3 // No metatable: done. + |. move CRET1, TISNIL + | lbu TMP0, TAB:TMP3->nomm + | andi TMP0, TMP0, 1<vmeta_tgets + |. nop + break; + case BC_TGETB: + | // RA = dst*8, RB = table*8, RC = index*8 + | decode_RB8a RB, INS + | decode_RB8b RB + | daddu CARG2, BASE, RB + | decode_RDtoRC8 RC, RD + | ld TAB:RB, 0(CARG2) + | daddu RA, BASE, RA + | srl TMP0, RC, 3 + | checktab TAB:RB, ->vmeta_tgetb + | lw TMP1, TAB:RB->asize + | ld TMP2, TAB:RB->array + | sltu AT, TMP0, TMP1 + | beqz AT, ->vmeta_tgetb + |. daddu RC, TMP2, RC + | ld AT, 0(RC) + | beq AT, TISNIL, >5 + |. ld CRET1, 0(RC) + |1: + | ins_next1 + | sd CRET1, 0(RA) + | ins_next2 + | + |5: // Check for __index if table value is nil. + | ld TAB:TMP2, TAB:RB->metatable + | beqz TAB:TMP2, <1 // No metatable: done. + |. nop + | lbu TMP1, TAB:TMP2->nomm + | andi TMP1, TMP1, 1<vmeta_tgetb // Caveat: preserve TMP0 and CARG2! + |. nop + break; + case BC_TGETR: + | // RA = dst*8, RB = table*8, RC = key*8 + | decode_RB8a RB, INS + | decode_RB8b RB + | decode_RDtoRC8 RC, RD + | daddu RB, BASE, RB + | daddu RC, BASE, RC + | ld TAB:CARG1, 0(RB) + | lw CARG2, LO(RC) + | daddu RA, BASE, RA + | cleartp TAB:CARG1 + | lw TMP0, TAB:CARG1->asize + | ld TMP1, TAB:CARG1->array + | sltu AT, CARG2, TMP0 + | sll TMP2, CARG2, 3 + | beqz AT, ->vmeta_tgetr // In array part? + |. daddu CRET1, TMP1, TMP2 + | ld CARG2, 0(CRET1) + |->BC_TGETR_Z: + | ins_next1 + | sd CARG2, 0(RA) + | ins_next2 + break; + + case BC_TSETV: + | // RA = src*8, RB = table*8, RC = key*8 + | decode_RB8a RB, INS + | decode_RB8b RB + | decode_RDtoRC8 RC, RD + | daddu CARG2, BASE, RB + | daddu CARG3, BASE, RC + | ld RB, 0(CARG2) + | ld TMP2, 0(CARG3) + | daddu RA, BASE, RA + | checktab RB, ->vmeta_tsetv + | checkint TMP2, >5 + |. sextw RC, TMP2 + | lw TMP0, TAB:RB->asize + | ld TMP1, TAB:RB->array + | sltu AT, RC, TMP0 + | sll TMP2, RC, 3 + | beqz AT, ->vmeta_tsetv // Integer key and in array part? + |. daddu TMP1, TMP1, TMP2 + | ld TMP0, 0(TMP1) + | lbu TMP3, TAB:RB->marked + | beq TMP0, TISNIL, >3 + |. ld CRET1, 0(RA) + |1: + | andi AT, TMP3, LJ_GC_BLACK // isblack(table) + | bnez AT, >7 + |. sd CRET1, 0(TMP1) + |2: + | ins_next + | + |3: // Check for __newindex if previous value is nil. + | ld TAB:TMP2, TAB:RB->metatable + | beqz TAB:TMP2, <1 // No metatable: done. + |. nop + | lbu TMP2, TAB:TMP2->nomm + | andi TMP2, TMP2, 1<vmeta_tsetv + |. nop + | + |5: + | gettp AT, TMP2 + | daddiu AT, AT, -LJ_TSTR + | bnez AT, ->vmeta_tsetv + |. nop + | b ->BC_TSETS_Z // String key? + |. cleartp STR:RC, TMP2 + | + |7: // Possible table write barrier for the value. Skip valiswhite check. + | barrierback TAB:RB, TMP3, TMP0, <2 + break; + case BC_TSETS: + | // RA = src*8, RB = table*8, RC = str_const*8 (~) + | decode_RB8a RB, INS + | decode_RB8b RB + | daddu CARG2, BASE, RB + | decode_RC8a RC, INS + | ld TAB:RB, 0(CARG2) + | decode_RC8b RC + | dsubu CARG3, KBASE, RC + | ld RC, -8(CARG3) // KBASE-8-str_const*8 + | daddu RA, BASE, RA + | cleartp STR:RC + | checktab TAB:RB, ->vmeta_tsets1 + |->BC_TSETS_Z: + | // TAB:RB = GCtab *, STR:RC = GCstr *, RA = BASE+src*8 + | lw TMP0, TAB:RB->hmask + | lw TMP1, STR:RC->hash + | ld NODE:TMP2, TAB:RB->node + | sb r0, TAB:RB->nomm // Clear metamethod cache. + | and TMP1, TMP1, TMP0 // idx = str->hash & tab->hmask + | sll TMP0, TMP1, 5 + | sll TMP1, TMP1, 3 + | subu TMP1, TMP0, TMP1 + | li TMP3, LJ_TSTR + | daddu NODE:TMP2, NODE:TMP2, TMP1 // node = tab->node + (idx*32-idx*8) + | settp STR:RC, TMP3 // Tagged key to look for. + |.if FPU + | ldc1 f20, 0(RA) + |.else + | ld CRET1, 0(RA) + |.endif + |1: + | ld TMP0, NODE:TMP2->key + | ld CARG2, NODE:TMP2->val + | ld NODE:TMP1, NODE:TMP2->next + | bne TMP0, RC, >5 + |. lbu TMP3, TAB:RB->marked + | beq CARG2, TISNIL, >4 // Key found, but nil value? + |. ld TAB:TMP0, TAB:RB->metatable + |2: + | andi AT, TMP3, LJ_GC_BLACK // isblack(table) + | bnez AT, >7 + |.if FPU + |. sdc1 f20, NODE:TMP2->val + |.else + |. sd CRET1, NODE:TMP2->val + |.endif + |3: + | ins_next + | + |4: // Check for __newindex if previous value is nil. + | beqz TAB:TMP0, <2 // No metatable: done. + |. nop + | lbu TMP0, TAB:TMP0->nomm + | andi TMP0, TMP0, 1<vmeta_tsets + |. nop + | + |5: // Follow hash chain. + | bnez NODE:TMP1, <1 + |. move NODE:TMP2, NODE:TMP1 + | // End of hash chain: key not found, add a new one + | + | // But check for __newindex first. + | ld TAB:TMP2, TAB:RB->metatable + | beqz TAB:TMP2, >6 // No metatable: continue. + |. daddiu CARG3, DISPATCH, DISPATCH_GL(tmptv) + | lbu TMP0, TAB:TMP2->nomm + | andi TMP0, TMP0, 1<vmeta_tsets // 'no __newindex' flag NOT set: check. + |6: + | load_got lj_tab_newkey + | sd RC, 0(CARG3) + | sd BASE, L->base + | move CARG2, TAB:RB + | sd PC, SAVE_PC + | call_intern lj_tab_newkey // (lua_State *L, GCtab *t, TValue *k + |. move CARG1, L + | // Returns TValue *. + | ld BASE, L->base + |.if FPU + | b <3 // No 2nd write barrier needed. + |. sdc1 f20, 0(CRET1) + |.else + | ld CARG1, 0(RA) + | b <3 // No 2nd write barrier needed. + |. sd CARG1, 0(CRET1) + |.endif + | + |7: // Possible table write barrier for the value. Skip valiswhite check. + | barrierback TAB:RB, TMP3, TMP0, <3 + break; + case BC_TSETB: + | // RA = src*8, RB = table*8, RC = index*8 + | decode_RB8a RB, INS + | decode_RB8b RB + | daddu CARG2, BASE, RB + | decode_RDtoRC8 RC, RD + | ld TAB:RB, 0(CARG2) + | daddu RA, BASE, RA + | srl TMP0, RC, 3 + | checktab RB, ->vmeta_tsetb + | lw TMP1, TAB:RB->asize + | ld TMP2, TAB:RB->array + | sltu AT, TMP0, TMP1 + | beqz AT, ->vmeta_tsetb + |. daddu RC, TMP2, RC + | ld TMP1, 0(RC) + | lbu TMP3, TAB:RB->marked + | beq TMP1, TISNIL, >5 + |1: + |. ld CRET1, 0(RA) + | andi AT, TMP3, LJ_GC_BLACK // isblack(table) + | bnez AT, >7 + |. sd CRET1, 0(RC) + |2: + | ins_next + | + |5: // Check for __newindex if previous value is nil. + | ld TAB:TMP2, TAB:RB->metatable + | beqz TAB:TMP2, <1 // No metatable: done. + |. nop + | lbu TMP1, TAB:TMP2->nomm + | andi TMP1, TMP1, 1<vmeta_tsetb // Caveat: preserve TMP0 and CARG2! + |. nop + | + |7: // Possible table write barrier for the value. Skip valiswhite check. + | barrierback TAB:RB, TMP3, TMP0, <2 + break; + case BC_TSETR: + | // RA = dst*8, RB = table*8, RC = key*8 + | decode_RB8a RB, INS + | decode_RB8b RB + | decode_RDtoRC8 RC, RD + | daddu CARG1, BASE, RB + | daddu CARG3, BASE, RC + | ld TAB:CARG2, 0(CARG1) + | lw CARG3, LO(CARG3) + | cleartp TAB:CARG2 + | lbu TMP3, TAB:CARG2->marked + | lw TMP0, TAB:CARG2->asize + | ld TMP1, TAB:CARG2->array + | andi AT, TMP3, LJ_GC_BLACK // isblack(table) + | bnez AT, >7 + |. daddu RA, BASE, RA + |2: + | sltu AT, CARG3, TMP0 + | sll TMP2, CARG3, 3 + | beqz AT, ->vmeta_tsetr // In array part? + |. daddu CRET1, TMP1, TMP2 + |->BC_TSETR_Z: + | ld CARG1, 0(RA) + | ins_next1 + | sd CARG1, 0(CRET1) + | ins_next2 + | + |7: // Possible table write barrier for the value. Skip valiswhite check. + | barrierback TAB:CARG2, TMP3, CRET1, <2 + break; + + case BC_TSETM: + | // RA = base*8 (table at base-1), RD = num_const*8 (start index) + | daddu RA, BASE, RA + |1: + | daddu TMP3, KBASE, RD + | ld TAB:CARG2, -8(RA) // Guaranteed to be a table. + | addiu TMP0, MULTRES, -8 + | lw TMP3, LO(TMP3) // Integer constant is in lo-word. + | beqz TMP0, >4 // Nothing to copy? + |. srl CARG3, TMP0, 3 + | cleartp CARG2 + | addu CARG3, CARG3, TMP3 + | lw TMP2, TAB:CARG2->asize + | sll TMP1, TMP3, 3 + | lbu TMP3, TAB:CARG2->marked + | ld CARG1, TAB:CARG2->array + | sltu AT, TMP2, CARG3 + | bnez AT, >5 + |. daddu TMP2, RA, TMP0 + | daddu TMP1, TMP1, CARG1 + | andi TMP0, TMP3, LJ_GC_BLACK // isblack(table) + |3: // Copy result slots to table. + | ld CRET1, 0(RA) + | daddiu RA, RA, 8 + | sltu AT, RA, TMP2 + | sd CRET1, 0(TMP1) + | bnez AT, <3 + |. daddiu TMP1, TMP1, 8 + | bnez TMP0, >7 + |. nop + |4: + | ins_next + | + |5: // Need to resize array part. + | load_got lj_tab_reasize + | sd BASE, L->base + | sd PC, SAVE_PC + | move BASE, RD + | call_intern lj_tab_reasize // (lua_State *L, GCtab *t, int nasize) + |. move CARG1, L + | // Must not reallocate the stack. + | move RD, BASE + | b <1 + |. ld BASE, L->base // Reload BASE for lack of a saved register. + | + |7: // Possible table write barrier for any value. Skip valiswhite check. + | barrierback TAB:CARG2, TMP3, TMP0, <4 + break; + + /* -- Calls and vararg handling ----------------------------------------- */ + + case BC_CALLM: + | // RA = base*8, (RB = (nresults+1)*8,) RC = extra_nargs*8 + | decode_RDtoRC8 NARGS8:RC, RD + | b ->BC_CALL_Z + |. addu NARGS8:RC, NARGS8:RC, MULTRES + break; + case BC_CALL: + | // RA = base*8, (RB = (nresults+1)*8,) RC = (nargs+1)*8 + | decode_RDtoRC8 NARGS8:RC, RD + |->BC_CALL_Z: + | move TMP2, BASE + | daddu BASE, BASE, RA + | ld LFUNC:RB, 0(BASE) + | daddiu BASE, BASE, 16 + | addiu NARGS8:RC, NARGS8:RC, -8 + | checkfunc RB, ->vmeta_call + | ins_call + break; + + case BC_CALLMT: + | // RA = base*8, (RB = 0,) RC = extra_nargs*8 + | addu NARGS8:RD, NARGS8:RD, MULTRES // BC_CALLT gets RC from RD. + | // Fall through. Assumes BC_CALLT follows. + break; + case BC_CALLT: + | // RA = base*8, (RB = 0,) RC = (nargs+1)*8 + | daddu RA, BASE, RA + | ld RB, 0(RA) + | move NARGS8:RC, RD + | ld TMP1, FRAME_PC(BASE) + | daddiu RA, RA, 16 + | addiu NARGS8:RC, NARGS8:RC, -8 + | checktp CARG3, RB, -LJ_TFUNC, ->vmeta_callt + |->BC_CALLT_Z: + | andi TMP0, TMP1, FRAME_TYPE // Caveat: preserve TMP0 until the 'or'. + | lbu TMP3, LFUNC:CARG3->ffid + | bnez TMP0, >7 + |. xori TMP2, TMP1, FRAME_VARG + |1: + | sd RB, FRAME_FUNC(BASE) // Copy function down, but keep PC. + | sltiu AT, TMP3, 2 // (> FF_C) Calling a fast function? + | move TMP2, BASE + | move RB, CARG3 + | beqz NARGS8:RC, >3 + |. move TMP3, NARGS8:RC + |2: + | ld CRET1, 0(RA) + | daddiu RA, RA, 8 + | addiu TMP3, TMP3, -8 + | sd CRET1, 0(TMP2) + | bnez TMP3, <2 + |. daddiu TMP2, TMP2, 8 + |3: + | or TMP0, TMP0, AT + | beqz TMP0, >5 + |. nop + |4: + | ins_callt + | + |5: // Tailcall to a fast function with a Lua frame below. + | lw INS, -4(TMP1) + | decode_RA8a RA, INS + | decode_RA8b RA + | dsubu TMP1, BASE, RA + | ld TMP1, -32(TMP1) + | cleartp LFUNC:TMP1 + | ld TMP1, LFUNC:TMP1->pc + | b <4 + |. ld KBASE, PC2PROTO(k)(TMP1) // Need to prepare KBASE. + | + |7: // Tailcall from a vararg function. + | andi AT, TMP2, FRAME_TYPEP + | bnez AT, <1 // Vararg frame below? + |. dsubu TMP2, BASE, TMP2 // Relocate BASE down. + | move BASE, TMP2 + | ld TMP1, FRAME_PC(TMP2) + | b <1 + |. andi TMP0, TMP1, FRAME_TYPE + break; + + case BC_ITERC: + | // RA = base*8, (RB = (nresults+1)*8, RC = (nargs+1)*8 ((2+1)*8)) + | move TMP2, BASE // Save old BASE fir vmeta_call. + | daddu BASE, BASE, RA + | ld RB, -24(BASE) + | ld CARG1, -16(BASE) + | ld CARG2, -8(BASE) + | li NARGS8:RC, 16 // Iterators get 2 arguments. + | sd RB, 0(BASE) // Copy callable. + | sd CARG1, 16(BASE) // Copy state. + | sd CARG2, 24(BASE) // Copy control var. + | daddiu BASE, BASE, 16 + | checkfunc RB, ->vmeta_call + | ins_call + break; + + case BC_ITERN: + | // RA = base*8, (RB = (nresults+1)*8, RC = (nargs+1)*8 (2+1)*8) + |.if JIT + | // NYI: add hotloop, record BC_ITERN. + |.endif + | daddu RA, BASE, RA + | ld TAB:RB, -16(RA) + | lw RC, -8+LO(RA) // Get index from control var. + | cleartp TAB:RB + | daddiu PC, PC, 4 + | lw TMP0, TAB:RB->asize + | ld TMP1, TAB:RB->array + | dsll CARG3, TISNUM, 47 + |1: // Traverse array part. + | sltu AT, RC, TMP0 + | beqz AT, >5 // Index points after array part? + |. sll TMP3, RC, 3 + | daddu TMP3, TMP1, TMP3 + | ld CARG1, 0(TMP3) + | lhu RD, -4+OFS_RD(PC) + | or TMP2, RC, CARG3 + | beq CARG1, TISNIL, <1 // Skip holes in array part. + |. addiu RC, RC, 1 + | sd TMP2, 0(RA) + | sd CARG1, 8(RA) + | or TMP0, RC, CARG3 + | lui TMP3, (-(BCBIAS_J*4 >> 16) & 65535) + | decode_RD4b RD + | daddu RD, RD, TMP3 + | sw TMP0, -8+LO(RA) // Update control var. + | daddu PC, PC, RD + |3: + | ins_next + | + |5: // Traverse hash part. + | lw TMP1, TAB:RB->hmask + | subu RC, RC, TMP0 + | ld TMP2, TAB:RB->node + |6: + | sltu AT, TMP1, RC // End of iteration? Branch to ITERL+1. + | bnez AT, <3 + |. sll TMP3, RC, 5 + | sll RB, RC, 3 + | subu TMP3, TMP3, RB + | daddu NODE:TMP3, TMP3, TMP2 + | ld CARG1, 0(NODE:TMP3) + | lhu RD, -4+OFS_RD(PC) + | beq CARG1, TISNIL, <6 // Skip holes in hash part. + |. addiu RC, RC, 1 + | ld CARG2, NODE:TMP3->key + | lui TMP3, (-(BCBIAS_J*4 >> 16) & 65535) + | sd CARG1, 8(RA) + | addu RC, RC, TMP0 + | decode_RD4b RD + | addu RD, RD, TMP3 + | sd CARG2, 0(RA) + | daddu PC, PC, RD + | b <3 + |. sw RC, -8+LO(RA) // Update control var. + break; + + case BC_ISNEXT: + | // RA = base*8, RD = target (points to ITERN) + | daddu RA, BASE, RA + | srl TMP0, RD, 1 + | ld CFUNC:CARG1, -24(RA) + | daddu TMP0, PC, TMP0 + | ld CARG2, -16(RA) + | ld CARG3, -8(RA) + | lui TMP2, (-(BCBIAS_J*4 >> 16) & 65535) + | checkfunc CFUNC:CARG1, >5 + | gettp CARG2, CARG2 + | daddiu CARG2, CARG2, -LJ_TTAB + | lbu TMP1, CFUNC:CARG1->ffid + | daddiu CARG3, CARG3, -LJ_TNIL + | or AT, CARG2, CARG3 + | daddiu TMP1, TMP1, -FF_next_N + | or AT, AT, TMP1 + | bnez AT, >5 + |. lui TMP1, 0xfffe + | daddu PC, TMP0, TMP2 + | ori TMP1, TMP1, 0x7fff + | dsll TMP1, TMP1, 32 + | sd TMP1, -8(RA) + |1: + | ins_next + |5: // Despecialize bytecode if any of the checks fail. + | li TMP3, BC_JMP + | li TMP1, BC_ITERC + | sb TMP3, -4+OFS_OP(PC) + | daddu PC, TMP0, TMP2 + | b <1 + |. sb TMP1, OFS_OP(PC) + break; + + case BC_VARG: + | // RA = base*8, RB = (nresults+1)*8, RC = numparams*8 + | ld TMP0, FRAME_PC(BASE) + | decode_RDtoRC8 RC, RD + | decode_RB8a RB, INS + | daddu RC, BASE, RC + | decode_RB8b RB + | daddu RA, BASE, RA + | daddiu RC, RC, FRAME_VARG + | daddu TMP2, RA, RB + | daddiu TMP3, BASE, -16 // TMP3 = vtop + | dsubu RC, RC, TMP0 // RC = vbase + | // Note: RC may now be even _above_ BASE if nargs was < numparams. + | beqz RB, >5 // Copy all varargs? + |. dsubu TMP1, TMP3, RC + | daddiu TMP2, TMP2, -16 + |1: // Copy vararg slots to destination slots. + | ld CARG1, 0(RC) + | sltu AT, RC, TMP3 + | daddiu RC, RC, 8 + | movz CARG1, TISNIL, AT + | sd CARG1, 0(RA) + | sltu AT, RA, TMP2 + | bnez AT, <1 + |. daddiu RA, RA, 8 + |3: + | ins_next + | + |5: // Copy all varargs. + | ld TMP0, L->maxstack + | blez TMP1, <3 // No vararg slots? + |. li MULTRES, 8 // MULTRES = (0+1)*8 + | daddu TMP2, RA, TMP1 + | sltu AT, TMP0, TMP2 + | bnez AT, >7 + |. daddiu MULTRES, TMP1, 8 + |6: + | ld CRET1, 0(RC) + | daddiu RC, RC, 8 + | sd CRET1, 0(RA) + | sltu AT, RC, TMP3 + | bnez AT, <6 // More vararg slots? + |. daddiu RA, RA, 8 + | b <3 + |. nop + | + |7: // Grow stack for varargs. + | load_got lj_state_growstack + | sd RA, L->top + | dsubu RA, RA, BASE + | sd BASE, L->base + | dsubu BASE, RC, BASE // Need delta, because BASE may change. + | sd PC, SAVE_PC + | srl CARG2, TMP1, 3 + | call_intern lj_state_growstack // (lua_State *L, int n) + |. move CARG1, L + | move RC, BASE + | ld BASE, L->base + | daddu RA, BASE, RA + | daddu RC, BASE, RC + | b <6 + |. daddiu TMP3, BASE, -16 + break; + + /* -- Returns ----------------------------------------------------------- */ + + case BC_RETM: + | // RA = results*8, RD = extra_nresults*8 + | addu RD, RD, MULTRES // MULTRES >= 8, so RD >= 8. + | // Fall through. Assumes BC_RET follows. + break; + + case BC_RET: + | // RA = results*8, RD = (nresults+1)*8 + | ld PC, FRAME_PC(BASE) + | daddu RA, BASE, RA + | move MULTRES, RD + |1: + | andi TMP0, PC, FRAME_TYPE + | bnez TMP0, ->BC_RETV_Z + |. xori TMP1, PC, FRAME_VARG + | + |->BC_RET_Z: + | // BASE = base, RA = resultptr, RD = (nresults+1)*8, PC = return + | lw INS, -4(PC) + | daddiu TMP2, BASE, -16 + | daddiu RC, RD, -8 + | decode_RA8a TMP0, INS + | decode_RB8a RB, INS + | decode_RA8b TMP0 + | decode_RB8b RB + | daddu TMP3, TMP2, RB + | beqz RC, >3 + |. dsubu BASE, TMP2, TMP0 + |2: + | ld CRET1, 0(RA) + | daddiu RA, RA, 8 + | daddiu RC, RC, -8 + | sd CRET1, 0(TMP2) + | bnez RC, <2 + |. daddiu TMP2, TMP2, 8 + |3: + | daddiu TMP3, TMP3, -8 + |5: + | sltu AT, TMP2, TMP3 + | bnez AT, >6 + |. ld LFUNC:TMP1, FRAME_FUNC(BASE) + | ins_next1 + | cleartp LFUNC:TMP1 + | ld TMP1, LFUNC:TMP1->pc + | ld KBASE, PC2PROTO(k)(TMP1) + | ins_next2 + | + |6: // Fill up results with nil. + | sd TISNIL, 0(TMP2) + | b <5 + |. daddiu TMP2, TMP2, 8 + | + |->BC_RETV_Z: // Non-standard return case. + | andi TMP2, TMP1, FRAME_TYPEP + | bnez TMP2, ->vm_return + |. nop + | // Return from vararg function: relocate BASE down. + | dsubu BASE, BASE, TMP1 + | b <1 + |. ld PC, FRAME_PC(BASE) + break; + + case BC_RET0: case BC_RET1: + | // RA = results*8, RD = (nresults+1)*8 + | ld PC, FRAME_PC(BASE) + | daddu RA, BASE, RA + | move MULTRES, RD + | andi TMP0, PC, FRAME_TYPE + | bnez TMP0, ->BC_RETV_Z + |. xori TMP1, PC, FRAME_VARG + | lw INS, -4(PC) + | daddiu TMP2, BASE, -16 + if (op == BC_RET1) { + | ld CRET1, 0(RA) + } + | decode_RB8a RB, INS + | decode_RA8a RA, INS + | decode_RB8b RB + | decode_RA8b RA + | dsubu BASE, TMP2, RA + if (op == BC_RET1) { + | sd CRET1, 0(TMP2) + } + |5: + | sltu AT, RD, RB + | bnez AT, >6 + |. ld TMP1, FRAME_FUNC(BASE) + | ins_next1 + | cleartp LFUNC:TMP1 + | ld TMP1, LFUNC:TMP1->pc + | ld KBASE, PC2PROTO(k)(TMP1) + | ins_next2 + | + |6: // Fill up results with nil. + | daddiu TMP2, TMP2, 8 + | daddiu RD, RD, 8 + | b <5 + if (op == BC_RET1) { + |. sd TISNIL, 0(TMP2) + } else { + |. sd TISNIL, -8(TMP2) + } + break; + + /* -- Loops and branches ------------------------------------------------ */ + + case BC_FORL: + |.if JIT + | hotloop + |.endif + | // Fall through. Assumes BC_IFORL follows. + break; + + case BC_JFORI: + case BC_JFORL: +#if !LJ_HASJIT + break; +#endif + case BC_FORI: + case BC_IFORL: + | // RA = base*8, RD = target (after end of loop or start of loop) + vk = (op == BC_IFORL || op == BC_JFORL); + | daddu RA, BASE, RA + | ld CARG1, FORL_IDX*8(RA) // IDX CARG1 - CARG3 type + | gettp CARG3, CARG1 + if (op != BC_JFORL) { + | srl RD, RD, 1 + | lui TMP2, (-(BCBIAS_J*4 >> 16) & 65535) + | daddu TMP2, RD, TMP2 + } + if (!vk) { + | ld CARG2, FORL_STOP*8(RA) // STOP CARG2 - CARG4 type + | ld CRET1, FORL_STEP*8(RA) // STEP CRET1 - CRET2 type + | gettp CARG4, CARG2 + | bne CARG3, TISNUM, >5 + |. gettp CRET2, CRET1 + | bne CARG4, TISNUM, ->vmeta_for + |. sextw CARG3, CARG1 + | bne CRET2, TISNUM, ->vmeta_for + |. sextw CARG2, CARG2 + | dext AT, CRET1, 31, 0 + | slt CRET1, CARG2, CARG3 + | slt TMP1, CARG3, CARG2 + | movn CRET1, TMP1, AT + } else { + | bne CARG3, TISNUM, >5 + |. ld CARG2, FORL_STEP*8(RA) // STEP CARG2 - CARG4 type + | ld CRET1, FORL_STOP*8(RA) // STOP CRET1 - CRET2 type + | sextw TMP3, CARG1 + | sextw CARG2, CARG2 + | sextw CRET1, CRET1 + | addu CARG1, TMP3, CARG2 + | xor TMP0, CARG1, TMP3 + | xor TMP1, CARG1, CARG2 + | and TMP0, TMP0, TMP1 + | slt TMP1, CARG1, CRET1 + | slt CRET1, CRET1, CARG1 + | slt AT, CARG2, r0 + | slt TMP0, TMP0, r0 // ((y^a) & (y^b)) < 0: overflow. + | movn CRET1, TMP1, AT + | or CRET1, CRET1, TMP0 + | zextw CARG1, CARG1 + | settp CARG1, TISNUM + } + |1: + if (op == BC_FORI) { + | movz TMP2, r0, CRET1 + | daddu PC, PC, TMP2 + } else if (op == BC_JFORI) { + | daddu PC, PC, TMP2 + | lhu RD, -4+OFS_RD(PC) + } else if (op == BC_IFORL) { + | movn TMP2, r0, CRET1 + | daddu PC, PC, TMP2 + } + if (vk) { + | sd CARG1, FORL_IDX*8(RA) + } + | ins_next1 + | sd CARG1, FORL_EXT*8(RA) + |2: + if (op == BC_JFORI) { + | beqz CRET1, =>BC_JLOOP + |. decode_RD8b RD + } else if (op == BC_JFORL) { + | beqz CRET1, =>BC_JLOOP + } + | ins_next2 + | + |5: // FP loop. + |.if FPU + if (!vk) { + | ldc1 f0, FORL_IDX*8(RA) + | ldc1 f2, FORL_STOP*8(RA) + | sltiu TMP0, CARG3, LJ_TISNUM + | sltiu TMP1, CARG4, LJ_TISNUM + | sltiu AT, CRET2, LJ_TISNUM + | ld TMP3, FORL_STEP*8(RA) + | and TMP0, TMP0, TMP1 + | and AT, AT, TMP0 + | beqz AT, ->vmeta_for + |. slt TMP3, TMP3, r0 + | c.ole.d 0, f0, f2 + | c.ole.d 1, f2, f0 + | li CRET1, 1 + | movt CRET1, r0, 0 + | movt AT, r0, 1 + | b <1 + |. movn CRET1, AT, TMP3 + } else { + | ldc1 f0, FORL_IDX*8(RA) + | ldc1 f4, FORL_STEP*8(RA) + | ldc1 f2, FORL_STOP*8(RA) + | ld TMP3, FORL_STEP*8(RA) + | add.d f0, f0, f4 + | c.ole.d 0, f0, f2 + | c.ole.d 1, f2, f0 + | slt TMP3, TMP3, r0 + | li CRET1, 1 + | li AT, 1 + | movt CRET1, r0, 0 + | movt AT, r0, 1 + | movn CRET1, AT, TMP3 + if (op == BC_IFORL) { + | movn TMP2, r0, CRET1 + | daddu PC, PC, TMP2 + } + | sdc1 f0, FORL_IDX*8(RA) + | ins_next1 + | b <2 + |. sdc1 f0, FORL_EXT*8(RA) + } + |.else + if (!vk) { + | sltiu TMP0, CARG3, LJ_TISNUM + | sltiu TMP1, CARG4, LJ_TISNUM + | sltiu AT, CRET2, LJ_TISNUM + | and TMP0, TMP0, TMP1 + | and AT, AT, TMP0 + | beqz AT, ->vmeta_for + |. nop + | bal ->vm_sfcmpolex + |. lw TMP3, FORL_STEP*8+HI(RA) + | b <1 + |. nop + } else { + | load_got __adddf3 + | call_extern + |. sw TMP2, TMPD + | ld CARG2, FORL_STOP*8(RA) + | move CARG1, CRET1 + if ( op == BC_JFORL ) { + | lhu RD, -4+OFS_RD(PC) + | decode_RD8b RD + } + | bal ->vm_sfcmpolex + |. lw TMP3, FORL_STEP*8+HI(RA) + | b <1 + |. lw TMP2, TMPD + } + |.endif + break; + + case BC_ITERL: + |.if JIT + | hotloop + |.endif + | // Fall through. Assumes BC_IITERL follows. + break; + + case BC_JITERL: +#if !LJ_HASJIT + break; +#endif + case BC_IITERL: + | // RA = base*8, RD = target + | daddu RA, BASE, RA + | ld TMP1, 0(RA) + | beq TMP1, TISNIL, >1 // Stop if iterator returned nil. + |. nop + if (op == BC_JITERL) { + | b =>BC_JLOOP + |. sd TMP1, -8(RA) + } else { + | branch_RD // Otherwise save control var + branch. + | sd TMP1, -8(RA) + } + |1: + | ins_next + break; + + case BC_LOOP: + | // RA = base*8, RD = target (loop extent) + | // Note: RA/RD is only used by trace recorder to determine scope/extent + | // This opcode does NOT jump, it's only purpose is to detect a hot loop. + |.if JIT + | hotloop + |.endif + | // Fall through. Assumes BC_ILOOP follows. + break; + + case BC_ILOOP: + | // RA = base*8, RD = target (loop extent) + | ins_next + break; + + case BC_JLOOP: + |.if JIT + | // RA = base*8 (ignored), RD = traceno*8 + | ld TMP1, DISPATCH_J(trace)(DISPATCH) + | li AT, 0 + | daddu TMP1, TMP1, RD + | // Traces on MIPS don't store the trace number, so use 0. + | sd AT, DISPATCH_GL(vmstate)(DISPATCH) + | ld TRACE:TMP2, 0(TMP1) + | sd BASE, DISPATCH_GL(jit_base)(DISPATCH) + | ld TMP2, TRACE:TMP2->mcode + | sd L, DISPATCH_GL(tmpbuf.L)(DISPATCH) + | jr TMP2 + |. daddiu JGL, DISPATCH, GG_DISP2G+32768 + |.endif + break; + + case BC_JMP: + | // RA = base*8 (only used by trace recorder), RD = target + | branch_RD + | ins_next + break; + + /* -- Function headers -------------------------------------------------- */ + + case BC_FUNCF: + |.if JIT + | hotcall + |.endif + case BC_FUNCV: /* NYI: compiled vararg functions. */ + | // Fall through. Assumes BC_IFUNCF/BC_IFUNCV follow. + break; + + case BC_JFUNCF: +#if !LJ_HASJIT + break; +#endif + case BC_IFUNCF: + | // BASE = new base, RA = BASE+framesize*8, RB = LFUNC, RC = nargs*8 + | ld TMP2, L->maxstack + | lbu TMP1, -4+PC2PROTO(numparams)(PC) + | ld KBASE, -4+PC2PROTO(k)(PC) + | sltu AT, TMP2, RA + | bnez AT, ->vm_growstack_l + |. sll TMP1, TMP1, 3 + if (op != BC_JFUNCF) { + | ins_next1 + } + |2: + | sltu AT, NARGS8:RC, TMP1 // Check for missing parameters. + | bnez AT, >3 + |. daddu AT, BASE, NARGS8:RC + if (op == BC_JFUNCF) { + | decode_RD8a RD, INS + | b =>BC_JLOOP + |. decode_RD8b RD + } else { + | ins_next2 + } + | + |3: // Clear missing parameters. + | sd TISNIL, 0(AT) + | b <2 + |. addiu NARGS8:RC, NARGS8:RC, 8 + break; + + case BC_JFUNCV: +#if !LJ_HASJIT + break; +#endif + | NYI // NYI: compiled vararg functions + break; /* NYI: compiled vararg functions. */ + + case BC_IFUNCV: + | // BASE = new base, RA = BASE+framesize*8, RB = LFUNC, RC = nargs*8 + | li TMP0, LJ_TFUNC + | daddu TMP1, BASE, RC + | ld TMP2, L->maxstack + | settp LFUNC:RB, TMP0 + | daddu TMP0, RA, RC + | sd LFUNC:RB, 0(TMP1) // Store (tagged) copy of LFUNC. + | daddiu TMP3, RC, 16+FRAME_VARG + | sltu AT, TMP0, TMP2 + | ld KBASE, -4+PC2PROTO(k)(PC) + | beqz AT, ->vm_growstack_l + |. sd TMP3, 8(TMP1) // Store delta + FRAME_VARG. + | lbu TMP2, -4+PC2PROTO(numparams)(PC) + | move RA, BASE + | move RC, TMP1 + | ins_next1 + | beqz TMP2, >3 + |. daddiu BASE, TMP1, 16 + |1: + | ld TMP0, 0(RA) + | sltu AT, RA, RC // Less args than parameters? + | move CARG1, TMP0 + | movz TMP0, TISNIL, AT // Clear missing parameters. + | movn CARG1, TISNIL, AT // Clear old fixarg slot (help the GC). + | addiu TMP2, TMP2, -1 + | sd TMP0, 16(TMP1) + | daddiu TMP1, TMP1, 8 + | sd CARG1, 0(RA) + | bnez TMP2, <1 + |. daddiu RA, RA, 8 + |3: + | ins_next2 + break; + + case BC_FUNCC: + case BC_FUNCCW: + | // BASE = new base, RA = BASE+framesize*8, RB = CFUNC, RC = nargs*8 + if (op == BC_FUNCC) { + | ld CFUNCADDR, CFUNC:RB->f + } else { + | ld CFUNCADDR, DISPATCH_GL(wrapf)(DISPATCH) + } + | daddu TMP1, RA, NARGS8:RC + | ld TMP2, L->maxstack + | daddu RC, BASE, NARGS8:RC + | sd BASE, L->base + | sltu AT, TMP2, TMP1 + | sd RC, L->top + | li_vmstate C + if (op == BC_FUNCCW) { + | ld CARG2, CFUNC:RB->f + } + | bnez AT, ->vm_growstack_c // Need to grow stack. + |. move CARG1, L + | jalr CFUNCADDR // (lua_State *L [, lua_CFunction f]) + |. st_vmstate + | // Returns nresults. + | ld BASE, L->base + | sll RD, CRET1, 3 + | ld TMP1, L->top + | li_vmstate INTERP + | ld PC, FRAME_PC(BASE) // Fetch PC of caller. + | dsubu RA, TMP1, RD // RA = L->top - nresults*8 + | sd L, DISPATCH_GL(cur_L)(DISPATCH) + | b ->vm_returnc + |. st_vmstate + break; + + /* ---------------------------------------------------------------------- */ + + default: + fprintf(stderr, "Error: undefined opcode BC_%s\n", bc_names[op]); + exit(2); + break; + } +} + +static int build_backend(BuildCtx *ctx) +{ + int op; + + dasm_growpc(Dst, BC__MAX); + + build_subroutines(ctx); + + |.code_op + for (op = 0; op < BC__MAX; op++) + build_ins(ctx, (BCOp)op, op); + + return BC__MAX; +} + +/* Emit pseudo frame-info for all assembler functions. */ +static void emit_asm_debug(BuildCtx *ctx) +{ + int fcofs = (int)((uint8_t *)ctx->glob[GLOB_vm_ffi_call] - ctx->code); + int i; + switch (ctx->mode) { + case BUILD_elfasm: + fprintf(ctx->fp, "\t.section .debug_frame,\"\",@progbits\n"); + fprintf(ctx->fp, + ".Lframe0:\n" + "\t.4byte .LECIE0-.LSCIE0\n" + ".LSCIE0:\n" + "\t.4byte 0xffffffff\n" + "\t.byte 0x1\n" + "\t.string \"\"\n" + "\t.uleb128 0x1\n" + "\t.sleb128 -4\n" + "\t.byte 31\n" + "\t.byte 0xc\n\t.uleb128 29\n\t.uleb128 0\n" + "\t.align 2\n" + ".LECIE0:\n\n"); + fprintf(ctx->fp, + ".LSFDE0:\n" + "\t.4byte .LEFDE0-.LASFDE0\n" + ".LASFDE0:\n" + "\t.4byte .Lframe0\n" + "\t.8byte .Lbegin\n" + "\t.8byte %d\n" + "\t.byte 0xe\n\t.uleb128 %d\n" + "\t.byte 0x9f\n\t.sleb128 2*5\n" + "\t.byte 0x9e\n\t.sleb128 2*6\n", + fcofs, CFRAME_SIZE); + for (i = 23; i >= 16; i--) + fprintf(ctx->fp, "\t.byte %d\n\t.uleb128 %d\n", 0x80+i, 2*(30-i)); +#if !LJ_SOFTFP + for (i = 31; i >= 24; i--) + fprintf(ctx->fp, "\t.byte %d\n\t.uleb128 %d\n", 0x80+32+i, 2*(46-i)); +#endif + fprintf(ctx->fp, + "\t.align 2\n" + ".LEFDE0:\n\n"); +#if LJ_HASFFI + fprintf(ctx->fp, + ".LSFDE1:\n" + "\t.4byte .LEFDE1-.LASFDE1\n" + ".LASFDE1:\n" + "\t.4byte .Lframe0\n" + "\t.4byte lj_vm_ffi_call\n" + "\t.4byte %d\n" + "\t.byte 0x9f\n\t.uleb128 2*1\n" + "\t.byte 0x90\n\t.uleb128 2*2\n" + "\t.byte 0xd\n\t.uleb128 0x10\n" + "\t.align 2\n" + ".LEFDE1:\n\n", (int)ctx->codesz - fcofs); +#endif +#if !LJ_NO_UNWIND + /* NYI */ +#endif + break; + default: + break; + } +} + diff --git a/lib/LuaJIT/vm_ppc.dasc b/lib/LuaJIT/vm_ppc.dasc new file mode 100644 index 0000000..0839668 --- /dev/null +++ b/lib/LuaJIT/vm_ppc.dasc @@ -0,0 +1,6053 @@ +|// Low-level VM code for PowerPC 32 bit or 32on64 bit mode. +|// Bytecode interpreter, fast functions and helper functions. +|// Copyright (C) 2005-2017 Mike Pall. See Copyright Notice in luajit.h +| +|.arch ppc +|.section code_op, code_sub +| +|.actionlist build_actionlist +|.globals GLOB_ +|.globalnames globnames +|.externnames extnames +| +|// Note: The ragged indentation of the instructions is intentional. +|// The starting columns indicate data dependencies. +| +|//----------------------------------------------------------------------- +| +|// DynASM defines used by the PPC port: +|// +|// P64 64 bit pointers (only for GPR64 testing). +|// Note: see vm_ppc64.dasc for a full PPC64 _LP64 port. +|// GPR64 64 bit registers (but possibly 32 bit pointers, e.g. PS3). +|// Affects reg saves, stack layout, carry/overflow/dot flags etc. +|// FRAME32 Use 32 bit frame layout, even with GPR64 (Xbox 360). +|// TOC Need table of contents (64 bit or 32 bit variant, e.g. PS3). +|// Function pointers are really a struct: code, TOC, env (optional). +|// TOCENV Function pointers have an environment pointer, too (not on PS3). +|// PPE Power Processor Element of Cell (PS3) or Xenon (Xbox 360). +|// Must avoid (slow) micro-coded instructions. +| +|.if P64 +|.define TOC, 1 +|.define TOCENV, 1 +|.macro lpx, a, b, c; ldx a, b, c; .endmacro +|.macro lp, a, b; ld a, b; .endmacro +|.macro stp, a, b; std a, b; .endmacro +|.define decode_OPP, decode_OP8 +|.if FFI +|// Missing: Calling conventions, 64 bit regs, TOC. +|.error lib_ffi not yet implemented for PPC64 +|.endif +|.else +|.macro lpx, a, b, c; lwzx a, b, c; .endmacro +|.macro lp, a, b; lwz a, b; .endmacro +|.macro stp, a, b; stw a, b; .endmacro +|.define decode_OPP, decode_OP4 +|.endif +| +|// Convenience macros for TOC handling. +|.if TOC +|// Linker needs a TOC patch area for every external call relocation. +|.macro blex, target; bl extern target@plt; nop; .endmacro +|.macro .toc, a, b; a, b; .endmacro +|.if P64 +|.define TOC_OFS, 8 +|.define ENV_OFS, 16 +|.else +|.define TOC_OFS, 4 +|.define ENV_OFS, 8 +|.endif +|.else // No TOC. +|.macro blex, target; bl extern target@plt; .endmacro +|.macro .toc, a, b; .endmacro +|.endif +|.macro .tocenv, a, b; .if TOCENV; a, b; .endif; .endmacro +| +|.macro .gpr64, a, b; .if GPR64; a, b; .endif; .endmacro +| +|.macro andix., y, a, i +|.if PPE +| rlwinm y, a, 0, 31-lj_fls(i), 31-lj_ffs(i) +| cmpwi y, 0 +|.else +| andi. y, a, i +|.endif +|.endmacro +| +|.macro clrso, reg +|.if PPE +| li reg, 0 +| mtxer reg +|.else +| mcrxr cr0 +|.endif +|.endmacro +| +|.macro checkov, reg, noov +|.if PPE +| mfxer reg +| add reg, reg, reg +| cmpwi reg, 0 +| li reg, 0 +| mtxer reg +| bgey noov +|.else +| mcrxr cr0 +| bley noov +|.endif +|.endmacro +| +|//----------------------------------------------------------------------- +| +|// Fixed register assignments for the interpreter. +|// Don't use: r1 = sp, r2 and r13 = reserved (TOC, TLS or SDATA) +| +|.macro .FPU, a, b +|.if FPU +| a, b +|.endif +|.endmacro +| +|.macro .FPU, a, b, c +|.if FPU +| a, b, c +|.endif +|.endmacro +| +|// The following must be C callee-save (but BASE is often refetched). +|.define BASE, r14 // Base of current Lua stack frame. +|.define KBASE, r15 // Constants of current Lua function. +|.define PC, r16 // Next PC. +|.define DISPATCH, r17 // Opcode dispatch table. +|.define LREG, r18 // Register holding lua_State (also in SAVE_L). +|.define MULTRES, r19 // Size of multi-result: (nresults+1)*8. +|.define JGL, r31 // On-trace: global_State + 32768. +| +|// Constants for type-comparisons, stores and conversions. C callee-save. +|.define TISNUM, r22 +|.define TISNIL, r23 +|.define ZERO, r24 +|.if FPU +|.define TOBIT, f30 // 2^52 + 2^51. +|.define TONUM, f31 // 2^52 + 2^51 + 2^31. +|.endif +| +|// The following temporaries are not saved across C calls, except for RA. +|.define RA, r20 // Callee-save. +|.define RB, r10 +|.define RC, r11 +|.define RD, r12 +|.define INS, r7 // Overlaps CARG5. +| +|.define TMP0, r0 +|.define TMP1, r8 +|.define TMP2, r9 +|.define TMP3, r6 // Overlaps CARG4. +| +|// Saved temporaries. +|.define SAVE0, r21 +|.define SAVE1, r25 +| +|// Calling conventions. +|.define CARG1, r3 +|.define CARG2, r4 +|.define CARG3, r5 +|.define CARG4, r6 // Overlaps TMP3. +|.define CARG5, r7 // Overlaps INS. +| +|.if FPU +|.define FARG1, f1 +|.define FARG2, f2 +|.endif +| +|.define CRET1, r3 +|.define CRET2, r4 +| +|.define TOCREG, r2 // TOC register (only used by C code). +|.define ENVREG, r11 // Environment pointer (nested C functions). +| +|// Stack layout while in interpreter. Must match with lj_frame.h. +|.if GPR64 +|.if FRAME32 +| +|// 456(sp) // \ 32/64 bit C frame info +|.define TONUM_LO, 452(sp) // | +|.define TONUM_HI, 448(sp) // | +|.define TMPD_LO, 444(sp) // | +|.define TMPD_HI, 440(sp) // | +|.define SAVE_CR, 432(sp) // | 64 bit CR save. +|.define SAVE_ERRF, 424(sp) // > Parameter save area. +|.define SAVE_NRES, 420(sp) // | +|.define SAVE_L, 416(sp) // | +|.define SAVE_PC, 412(sp) // | +|.define SAVE_MULTRES, 408(sp) // | +|.define SAVE_CFRAME, 400(sp) // / 64 bit C frame chain. +|// 392(sp) // Reserved. +|.define CFRAME_SPACE, 384 // Delta for sp. +|// Back chain for sp: 384(sp) <-- sp entering interpreter +|.define SAVE_LR, 376(sp) // 32 bit LR stored in hi-part. +|.define SAVE_GPR_, 232 // .. 232+18*8: 64 bit GPR saves. +|.define SAVE_FPR_, 88 // .. 88+18*8: 64 bit FPR saves. +|// 80(sp) // Needed for 16 byte stack frame alignment. +|// 16(sp) // Callee parameter save area (ABI mandated). +|// 8(sp) // Reserved +|// Back chain for sp: 0(sp) <-- sp while in interpreter +|// 32 bit sp stored in hi-part of 0(sp). +| +|.define TMPD_BLO, 447(sp) +|.define TMPD, TMPD_HI +|.define TONUM_D, TONUM_HI +| +|.else +| +|// 508(sp) // \ 32 bit C frame info. +|.define SAVE_ERRF, 472(sp) // | +|.define SAVE_NRES, 468(sp) // | +|.define SAVE_L, 464(sp) // > Parameter save area. +|.define SAVE_PC, 460(sp) // | +|.define SAVE_MULTRES, 456(sp) // | +|.define SAVE_CFRAME, 448(sp) // / 64 bit C frame chain. +|.define SAVE_LR, 416(sp) +|.define CFRAME_SPACE, 400 // Delta for sp. +|// Back chain for sp: 400(sp) <-- sp entering interpreter +|.define SAVE_FPR_, 256 // .. 256+18*8: 64 bit FPR saves. +|.define SAVE_GPR_, 112 // .. 112+18*8: 64 bit GPR saves. +|// 48(sp) // Callee parameter save area (ABI mandated). +|.define SAVE_TOC, 40(sp) // TOC save area. +|.define TMPD_LO, 36(sp) // \ Link editor temp (ABI mandated). +|.define TMPD_HI, 32(sp) // / +|.define TONUM_LO, 28(sp) // \ Compiler temp (ABI mandated). +|.define TONUM_HI, 24(sp) // / +|// Next frame lr: 16(sp) +|.define SAVE_CR, 8(sp) // 64 bit CR save. +|// Back chain for sp: 0(sp) <-- sp while in interpreter +| +|.define TMPD_BLO, 39(sp) +|.define TMPD, TMPD_HI +|.define TONUM_D, TONUM_HI +| +|.endif +|.else +| +|.if FPU +|.define SAVE_LR, 276(sp) +|.define CFRAME_SPACE, 272 // Delta for sp. +|// Back chain for sp: 272(sp) <-- sp entering interpreter +|.define SAVE_FPR_, 128 // .. 128+18*8: 64 bit FPR saves. +|.else +|.define SAVE_LR, 132(sp) +|.define CFRAME_SPACE, 128 // Delta for sp. +|// Back chain for sp: 128(sp) <-- sp entering interpreter +|.endif +|.define SAVE_GPR_, 56 // .. 56+18*4: 32 bit GPR saves. +|.define SAVE_CR, 52(sp) // 32 bit CR save. +|.define SAVE_ERRF, 48(sp) // 32 bit C frame info. +|.define SAVE_NRES, 44(sp) +|.define SAVE_CFRAME, 40(sp) +|.define SAVE_L, 36(sp) +|.define SAVE_PC, 32(sp) +|.define SAVE_MULTRES, 28(sp) +|.define UNUSED1, 24(sp) +|.if FPU +|.define TMPD_LO, 20(sp) +|.define TMPD_HI, 16(sp) +|.define TONUM_LO, 12(sp) +|.define TONUM_HI, 8(sp) +|.else +|.define SFSAVE_4, 20(sp) +|.define SFSAVE_3, 16(sp) +|.define SFSAVE_2, 12(sp) +|.define SFSAVE_1, 8(sp) +|.endif +|// Next frame lr: 4(sp) +|// Back chain for sp: 0(sp) <-- sp while in interpreter +| +|.if FPU +|.define TMPD_BLO, 23(sp) +|.define TMPD, TMPD_HI +|.define TONUM_D, TONUM_HI +|.endif +| +|.endif +| +|.macro save_, reg +|.if GPR64 +| std r..reg, SAVE_GPR_+(reg-14)*8(sp) +|.else +| stw r..reg, SAVE_GPR_+(reg-14)*4(sp) +|.endif +| .FPU stfd f..reg, SAVE_FPR_+(reg-14)*8(sp) +|.endmacro +|.macro rest_, reg +|.if GPR64 +| ld r..reg, SAVE_GPR_+(reg-14)*8(sp) +|.else +| lwz r..reg, SAVE_GPR_+(reg-14)*4(sp) +|.endif +| .FPU lfd f..reg, SAVE_FPR_+(reg-14)*8(sp) +|.endmacro +| +|.macro saveregs +|.if GPR64 and not FRAME32 +| stdu sp, -CFRAME_SPACE(sp) +|.else +| stwu sp, -CFRAME_SPACE(sp) +|.endif +| save_ 14; save_ 15; save_ 16 +| mflr r0 +| save_ 17; save_ 18; save_ 19; save_ 20; save_ 21; save_ 22 +|.if GPR64 and not FRAME32 +| std r0, SAVE_LR +|.else +| stw r0, SAVE_LR +|.endif +| save_ 23; save_ 24; save_ 25 +| mfcr r0 +| save_ 26; save_ 27; save_ 28; save_ 29; save_ 30; save_ 31 +|.if GPR64 +| std r0, SAVE_CR +|.else +| stw r0, SAVE_CR +|.endif +| .toc std TOCREG, SAVE_TOC +|.endmacro +| +|.macro restoreregs +|.if GPR64 and not FRAME32 +| ld r0, SAVE_LR +|.else +| lwz r0, SAVE_LR +|.endif +|.if GPR64 +| ld r12, SAVE_CR +|.else +| lwz r12, SAVE_CR +|.endif +| rest_ 14; rest_ 15; rest_ 16; rest_ 17; rest_ 18; rest_ 19 +| mtlr r0; +|.if PPE; mtocrf 0x20, r12; .else; mtcrf 0x38, r12; .endif +| rest_ 20; rest_ 21; rest_ 22; rest_ 23; rest_ 24; rest_ 25 +|.if PPE; mtocrf 0x10, r12; .endif +| rest_ 26; rest_ 27; rest_ 28; rest_ 29; rest_ 30; rest_ 31 +|.if PPE; mtocrf 0x08, r12; .endif +| addi sp, sp, CFRAME_SPACE +|.endmacro +| +|// Type definitions. Some of these are only used for documentation. +|.type L, lua_State, LREG +|.type GL, global_State +|.type TVALUE, TValue +|.type GCOBJ, GCobj +|.type STR, GCstr +|.type TAB, GCtab +|.type LFUNC, GCfuncL +|.type CFUNC, GCfuncC +|.type PROTO, GCproto +|.type UPVAL, GCupval +|.type NODE, Node +|.type NARGS8, int +|.type TRACE, GCtrace +|.type SBUF, SBuf +| +|//----------------------------------------------------------------------- +| +|// Trap for not-yet-implemented parts. +|.macro NYI; tw 4, sp, sp; .endmacro +| +|.if FPU +|// int/FP conversions. +|.macro tonum_i, freg, reg +| xoris reg, reg, 0x8000 +| stw reg, TONUM_LO +| lfd freg, TONUM_D +| fsub freg, freg, TONUM +|.endmacro +| +|.macro tonum_u, freg, reg +| stw reg, TONUM_LO +| lfd freg, TONUM_D +| fsub freg, freg, TOBIT +|.endmacro +| +|.macro toint, reg, freg, tmpfreg +| fctiwz tmpfreg, freg +| stfd tmpfreg, TMPD +| lwz reg, TMPD_LO +|.endmacro +| +|.macro toint, reg, freg +| toint reg, freg, freg +|.endmacro +|.endif +| +|//----------------------------------------------------------------------- +| +|// Access to frame relative to BASE. +|.define FRAME_PC, -8 +|.define FRAME_FUNC, -4 +| +|// Instruction decode. +|.macro decode_OP4, dst, ins; rlwinm dst, ins, 2, 22, 29; .endmacro +|.macro decode_OP8, dst, ins; rlwinm dst, ins, 3, 21, 28; .endmacro +|.macro decode_RA8, dst, ins; rlwinm dst, ins, 27, 21, 28; .endmacro +|.macro decode_RB8, dst, ins; rlwinm dst, ins, 11, 21, 28; .endmacro +|.macro decode_RC8, dst, ins; rlwinm dst, ins, 19, 21, 28; .endmacro +|.macro decode_RD8, dst, ins; rlwinm dst, ins, 19, 13, 28; .endmacro +| +|.macro decode_OP1, dst, ins; rlwinm dst, ins, 0, 24, 31; .endmacro +|.macro decode_RD4, dst, ins; rlwinm dst, ins, 18, 14, 29; .endmacro +| +|// Instruction fetch. +|.macro ins_NEXT1 +| lwz INS, 0(PC) +| addi PC, PC, 4 +|.endmacro +|// Instruction decode+dispatch. Note: optimized for e300! +|.macro ins_NEXT2 +| decode_OPP TMP1, INS +| lpx TMP0, DISPATCH, TMP1 +| mtctr TMP0 +| decode_RB8 RB, INS +| decode_RD8 RD, INS +| decode_RA8 RA, INS +| decode_RC8 RC, INS +| bctr +|.endmacro +|.macro ins_NEXT +| ins_NEXT1 +| ins_NEXT2 +|.endmacro +| +|// Instruction footer. +|.if 1 +| // Replicated dispatch. Less unpredictable branches, but higher I-Cache use. +| .define ins_next, ins_NEXT +| .define ins_next_, ins_NEXT +| .define ins_next1, ins_NEXT1 +| .define ins_next2, ins_NEXT2 +|.else +| // Common dispatch. Lower I-Cache use, only one (very) unpredictable branch. +| // Affects only certain kinds of benchmarks (and only with -j off). +| .macro ins_next +| b ->ins_next +| .endmacro +| .macro ins_next1 +| .endmacro +| .macro ins_next2 +| b ->ins_next +| .endmacro +| .macro ins_next_ +| ->ins_next: +| ins_NEXT +| .endmacro +|.endif +| +|// Call decode and dispatch. +|.macro ins_callt +| // BASE = new base, RB = LFUNC/CFUNC, RC = nargs*8, FRAME_PC(BASE) = PC +| lwz PC, LFUNC:RB->pc +| lwz INS, 0(PC) +| addi PC, PC, 4 +| decode_OPP TMP1, INS +| decode_RA8 RA, INS +| lpx TMP0, DISPATCH, TMP1 +| add RA, RA, BASE +| mtctr TMP0 +| bctr +|.endmacro +| +|.macro ins_call +| // BASE = new base, RB = LFUNC/CFUNC, RC = nargs*8, PC = caller PC +| stw PC, FRAME_PC(BASE) +| ins_callt +|.endmacro +| +|//----------------------------------------------------------------------- +| +|// Macros to test operand types. +|.macro checknum, reg; cmplw reg, TISNUM; .endmacro +|.macro checknum, cr, reg; cmplw cr, reg, TISNUM; .endmacro +|.macro checkstr, reg; cmpwi reg, LJ_TSTR; .endmacro +|.macro checktab, reg; cmpwi reg, LJ_TTAB; .endmacro +|.macro checkfunc, reg; cmpwi reg, LJ_TFUNC; .endmacro +|.macro checknil, reg; cmpwi reg, LJ_TNIL; .endmacro +| +|.macro branch_RD +| srwi TMP0, RD, 1 +| addis PC, PC, -(BCBIAS_J*4 >> 16) +| add PC, PC, TMP0 +|.endmacro +| +|// Assumes DISPATCH is relative to GL. +#define DISPATCH_GL(field) (GG_DISP2G + (int)offsetof(global_State, field)) +#define DISPATCH_J(field) (GG_DISP2J + (int)offsetof(jit_State, field)) +| +#define PC2PROTO(field) ((int)offsetof(GCproto, field)-(int)sizeof(GCproto)) +| +|.macro hotcheck, delta, target +| rlwinm TMP1, PC, 31, 25, 30 +| addi TMP1, TMP1, GG_DISP2HOT +| lhzx TMP2, DISPATCH, TMP1 +| addic. TMP2, TMP2, -delta +| sthx TMP2, DISPATCH, TMP1 +| blt target +|.endmacro +| +|.macro hotloop +| hotcheck HOTCOUNT_LOOP, ->vm_hotloop +|.endmacro +| +|.macro hotcall +| hotcheck HOTCOUNT_CALL, ->vm_hotcall +|.endmacro +| +|// Set current VM state. Uses TMP0. +|.macro li_vmstate, st; li TMP0, ~LJ_VMST_..st; .endmacro +|.macro st_vmstate; stw TMP0, DISPATCH_GL(vmstate)(DISPATCH); .endmacro +| +|// Move table write barrier back. Overwrites mark and tmp. +|.macro barrierback, tab, mark, tmp +| lwz tmp, DISPATCH_GL(gc.grayagain)(DISPATCH) +| // Assumes LJ_GC_BLACK is 0x04. +| rlwinm mark, mark, 0, 30, 28 // black2gray(tab) +| stw tab, DISPATCH_GL(gc.grayagain)(DISPATCH) +| stb mark, tab->marked +| stw tmp, tab->gclist +|.endmacro +| +|//----------------------------------------------------------------------- + +/* Generate subroutines used by opcodes and other parts of the VM. */ +/* The .code_sub section should be last to help static branch prediction. */ +static void build_subroutines(BuildCtx *ctx) +{ + |.code_sub + | + |//----------------------------------------------------------------------- + |//-- Return handling ---------------------------------------------------- + |//----------------------------------------------------------------------- + | + |->vm_returnp: + | // See vm_return. Also: TMP2 = previous base. + | andix. TMP0, PC, FRAME_P + | li TMP1, LJ_TTRUE + | beq ->cont_dispatch + | + | // Return from pcall or xpcall fast func. + | lwz PC, FRAME_PC(TMP2) // Fetch PC of previous frame. + | mr BASE, TMP2 // Restore caller base. + | // Prepending may overwrite the pcall frame, so do it at the end. + | stwu TMP1, FRAME_PC(RA) // Prepend true to results. + | + |->vm_returnc: + | addi RD, RD, 8 // RD = (nresults+1)*8. + | andix. TMP0, PC, FRAME_TYPE + | cmpwi cr1, RD, 0 + | li CRET1, LUA_YIELD + | beq cr1, ->vm_unwind_c_eh + | mr MULTRES, RD + | beq ->BC_RET_Z // Handle regular return to Lua. + | + |->vm_return: + | // BASE = base, RA = resultptr, RD/MULTRES = (nresults+1)*8, PC = return + | // TMP0 = PC & FRAME_TYPE + | cmpwi TMP0, FRAME_C + | rlwinm TMP2, PC, 0, 0, 28 + | li_vmstate C + | sub TMP2, BASE, TMP2 // TMP2 = previous base. + | bney ->vm_returnp + | + | addic. TMP1, RD, -8 + | stp TMP2, L->base + | lwz TMP2, SAVE_NRES + | subi BASE, BASE, 8 + | st_vmstate + | slwi TMP2, TMP2, 3 + | beq >2 + |1: + | addic. TMP1, TMP1, -8 + |.if FPU + | lfd f0, 0(RA) + |.else + | lwz CARG1, 0(RA) + | lwz CARG2, 4(RA) + |.endif + | addi RA, RA, 8 + |.if FPU + | stfd f0, 0(BASE) + |.else + | stw CARG1, 0(BASE) + | stw CARG2, 4(BASE) + |.endif + | addi BASE, BASE, 8 + | bney <1 + | + |2: + | cmpw TMP2, RD // More/less results wanted? + | bne >6 + |3: + | stp BASE, L->top // Store new top. + | + |->vm_leave_cp: + | lp TMP0, SAVE_CFRAME // Restore previous C frame. + | li CRET1, 0 // Ok return status for vm_pcall. + | stp TMP0, L->cframe + | + |->vm_leave_unw: + | restoreregs + | blr + | + |6: + | ble >7 // Less results wanted? + | // More results wanted. Check stack size and fill up results with nil. + | lwz TMP1, L->maxstack + | cmplw BASE, TMP1 + | bge >8 + | stw TISNIL, 0(BASE) + | addi RD, RD, 8 + | addi BASE, BASE, 8 + | b <2 + | + |7: // Less results wanted. + | subfic TMP3, TMP2, 0 // LUA_MULTRET+1 case? + | sub TMP0, RD, TMP2 + | subfe TMP1, TMP1, TMP1 // TMP1 = TMP2 == 0 ? 0 : -1 + | and TMP0, TMP0, TMP1 + | sub BASE, BASE, TMP0 // Either keep top or shrink it. + | b <3 + | + |8: // Corner case: need to grow stack for filling up results. + | // This can happen if: + | // - A C function grows the stack (a lot). + | // - The GC shrinks the stack in between. + | // - A return back from a lua_call() with (high) nresults adjustment. + | stp BASE, L->top // Save current top held in BASE (yes). + | mr SAVE0, RD + | srwi CARG2, TMP2, 3 + | mr CARG1, L + | bl extern lj_state_growstack // (lua_State *L, int n) + | lwz TMP2, SAVE_NRES + | mr RD, SAVE0 + | slwi TMP2, TMP2, 3 + | lp BASE, L->top // Need the (realloced) L->top in BASE. + | b <2 + | + |->vm_unwind_c: // Unwind C stack, return from vm_pcall. + | // (void *cframe, int errcode) + | mr sp, CARG1 + | mr CRET1, CARG2 + |->vm_unwind_c_eh: // Landing pad for external unwinder. + | lwz L, SAVE_L + | .toc ld TOCREG, SAVE_TOC + | li TMP0, ~LJ_VMST_C + | lwz GL:TMP1, L->glref + | stw TMP0, GL:TMP1->vmstate + | b ->vm_leave_unw + | + |->vm_unwind_ff: // Unwind C stack, return from ff pcall. + | // (void *cframe) + |.if GPR64 + | rldicr sp, CARG1, 0, 61 + |.else + | rlwinm sp, CARG1, 0, 0, 29 + |.endif + |->vm_unwind_ff_eh: // Landing pad for external unwinder. + | lwz L, SAVE_L + | .toc ld TOCREG, SAVE_TOC + | li TISNUM, LJ_TISNUM // Setup type comparison constants. + | lp BASE, L->base + | .FPU lus TMP3, 0x59c0 // TOBIT = 2^52 + 2^51 (float). + | lwz DISPATCH, L->glref // Setup pointer to dispatch table. + | li ZERO, 0 + | .FPU stw TMP3, TMPD + | li TMP1, LJ_TFALSE + | .FPU ori TMP3, TMP3, 0x0004 // TONUM = 2^52 + 2^51 + 2^31 (float). + | li TISNIL, LJ_TNIL + | li_vmstate INTERP + | .FPU lfs TOBIT, TMPD + | lwz PC, FRAME_PC(BASE) // Fetch PC of previous frame. + | la RA, -8(BASE) // Results start at BASE-8. + | .FPU stw TMP3, TMPD + | addi DISPATCH, DISPATCH, GG_G2DISP + | stw TMP1, 0(RA) // Prepend false to error message. + | li RD, 16 // 2 results: false + error message. + | st_vmstate + | .FPU lfs TONUM, TMPD + | b ->vm_returnc + | + |//----------------------------------------------------------------------- + |//-- Grow stack for calls ----------------------------------------------- + |//----------------------------------------------------------------------- + | + |->vm_growstack_c: // Grow stack for C function. + | li CARG2, LUA_MINSTACK + | b >2 + | + |->vm_growstack_l: // Grow stack for Lua function. + | // BASE = new base, RA = BASE+framesize*8, RC = nargs*8, PC = first PC + | add RC, BASE, RC + | sub RA, RA, BASE + | stp BASE, L->base + | addi PC, PC, 4 // Must point after first instruction. + | stp RC, L->top + | srwi CARG2, RA, 3 + |2: + | // L->base = new base, L->top = top + | stw PC, SAVE_PC + | mr CARG1, L + | bl extern lj_state_growstack // (lua_State *L, int n) + | lp BASE, L->base + | lp RC, L->top + | lwz LFUNC:RB, FRAME_FUNC(BASE) + | sub RC, RC, BASE + | // BASE = new base, RB = LFUNC/CFUNC, RC = nargs*8, FRAME_PC(BASE) = PC + | ins_callt // Just retry the call. + | + |//----------------------------------------------------------------------- + |//-- Entry points into the assembler VM --------------------------------- + |//----------------------------------------------------------------------- + | + |->vm_resume: // Setup C frame and resume thread. + | // (lua_State *L, TValue *base, int nres1 = 0, ptrdiff_t ef = 0) + | saveregs + | mr L, CARG1 + | lwz DISPATCH, L->glref // Setup pointer to dispatch table. + | mr BASE, CARG2 + | lbz TMP1, L->status + | stw L, SAVE_L + | li PC, FRAME_CP + | addi TMP0, sp, CFRAME_RESUME + | addi DISPATCH, DISPATCH, GG_G2DISP + | stw CARG3, SAVE_NRES + | cmplwi TMP1, 0 + | stw CARG3, SAVE_ERRF + | stp CARG3, SAVE_CFRAME + | stw CARG1, SAVE_PC // Any value outside of bytecode is ok. + | stp TMP0, L->cframe + | beq >3 + | + | // Resume after yield (like a return). + | stw L, DISPATCH_GL(cur_L)(DISPATCH) + | mr RA, BASE + | lp BASE, L->base + | li TISNUM, LJ_TISNUM // Setup type comparison constants. + | lp TMP1, L->top + | lwz PC, FRAME_PC(BASE) + | .FPU lus TMP3, 0x59c0 // TOBIT = 2^52 + 2^51 (float). + | stb CARG3, L->status + | .FPU stw TMP3, TMPD + | .FPU ori TMP3, TMP3, 0x0004 // TONUM = 2^52 + 2^51 + 2^31 (float). + | .FPU lfs TOBIT, TMPD + | sub RD, TMP1, BASE + | .FPU stw TMP3, TMPD + | .FPU lus TMP0, 0x4338 // Hiword of 2^52 + 2^51 (double) + | addi RD, RD, 8 + | .FPU stw TMP0, TONUM_HI + | li_vmstate INTERP + | li ZERO, 0 + | st_vmstate + | andix. TMP0, PC, FRAME_TYPE + | mr MULTRES, RD + | .FPU lfs TONUM, TMPD + | li TISNIL, LJ_TNIL + | beq ->BC_RET_Z + | b ->vm_return + | + |->vm_pcall: // Setup protected C frame and enter VM. + | // (lua_State *L, TValue *base, int nres1, ptrdiff_t ef) + | saveregs + | li PC, FRAME_CP + | stw CARG4, SAVE_ERRF + | b >1 + | + |->vm_call: // Setup C frame and enter VM. + | // (lua_State *L, TValue *base, int nres1) + | saveregs + | li PC, FRAME_C + | + |1: // Entry point for vm_pcall above (PC = ftype). + | lp TMP1, L:CARG1->cframe + | mr L, CARG1 + | stw CARG3, SAVE_NRES + | lwz DISPATCH, L->glref // Setup pointer to dispatch table. + | stw CARG1, SAVE_L + | mr BASE, CARG2 + | addi DISPATCH, DISPATCH, GG_G2DISP + | stw CARG1, SAVE_PC // Any value outside of bytecode is ok. + | stp TMP1, SAVE_CFRAME + | stp sp, L->cframe // Add our C frame to cframe chain. + | + |3: // Entry point for vm_cpcall/vm_resume (BASE = base, PC = ftype). + | stw L, DISPATCH_GL(cur_L)(DISPATCH) + | lp TMP2, L->base // TMP2 = old base (used in vmeta_call). + | li TISNUM, LJ_TISNUM // Setup type comparison constants. + | lp TMP1, L->top + | .FPU lus TMP3, 0x59c0 // TOBIT = 2^52 + 2^51 (float). + | add PC, PC, BASE + | .FPU stw TMP3, TMPD + | li ZERO, 0 + | .FPU ori TMP3, TMP3, 0x0004 // TONUM = 2^52 + 2^51 + 2^31 (float). + | .FPU lfs TOBIT, TMPD + | sub PC, PC, TMP2 // PC = frame delta + frame type + | .FPU stw TMP3, TMPD + | .FPU lus TMP0, 0x4338 // Hiword of 2^52 + 2^51 (double) + | sub NARGS8:RC, TMP1, BASE + | .FPU stw TMP0, TONUM_HI + | li_vmstate INTERP + | .FPU lfs TONUM, TMPD + | li TISNIL, LJ_TNIL + | st_vmstate + | + |->vm_call_dispatch: + | // TMP2 = old base, BASE = new base, RC = nargs*8, PC = caller PC + | lwz TMP0, FRAME_PC(BASE) + | lwz LFUNC:RB, FRAME_FUNC(BASE) + | checkfunc TMP0; bne ->vmeta_call + | + |->vm_call_dispatch_f: + | ins_call + | // BASE = new base, RB = func, RC = nargs*8, PC = caller PC + | + |->vm_cpcall: // Setup protected C frame, call C. + | // (lua_State *L, lua_CFunction func, void *ud, lua_CPFunction cp) + | saveregs + | mr L, CARG1 + | lwz TMP0, L:CARG1->stack + | stw CARG1, SAVE_L + | lp TMP1, L->top + | lwz DISPATCH, L->glref // Setup pointer to dispatch table. + | stw CARG1, SAVE_PC // Any value outside of bytecode is ok. + | sub TMP0, TMP0, TMP1 // Compute -savestack(L, L->top). + | lp TMP1, L->cframe + | addi DISPATCH, DISPATCH, GG_G2DISP + | .toc lp CARG4, 0(CARG4) + | li TMP2, 0 + | stw TMP0, SAVE_NRES // Neg. delta means cframe w/o frame. + | stw TMP2, SAVE_ERRF // No error function. + | stp TMP1, SAVE_CFRAME + | stp sp, L->cframe // Add our C frame to cframe chain. + | stw L, DISPATCH_GL(cur_L)(DISPATCH) + | mtctr CARG4 + | bctrl // (lua_State *L, lua_CFunction func, void *ud) + |.if PPE + | mr BASE, CRET1 + | cmpwi CRET1, 0 + |.else + | mr. BASE, CRET1 + |.endif + | li PC, FRAME_CP + | bne <3 // Else continue with the call. + | b ->vm_leave_cp // No base? Just remove C frame. + | + |//----------------------------------------------------------------------- + |//-- Metamethod handling ------------------------------------------------ + |//----------------------------------------------------------------------- + | + |// The lj_meta_* functions (except for lj_meta_cat) don't reallocate the + |// stack, so BASE doesn't need to be reloaded across these calls. + | + |//-- Continuation dispatch ---------------------------------------------- + | + |->cont_dispatch: + | // BASE = meta base, RA = resultptr, RD = (nresults+1)*8 + | lwz TMP0, -12(BASE) // Continuation. + | mr RB, BASE + | mr BASE, TMP2 // Restore caller BASE. + | lwz LFUNC:TMP1, FRAME_FUNC(TMP2) + |.if FFI + | cmplwi TMP0, 1 + |.endif + | lwz PC, -16(RB) // Restore PC from [cont|PC]. + | subi TMP2, RD, 8 + | lwz TMP1, LFUNC:TMP1->pc + | stwx TISNIL, RA, TMP2 // Ensure one valid arg. + |.if FFI + | ble >1 + |.endif + | lwz KBASE, PC2PROTO(k)(TMP1) + | // BASE = base, RA = resultptr, RB = meta base + | mtctr TMP0 + | bctr // Jump to continuation. + | + |.if FFI + |1: + | beq ->cont_ffi_callback // cont = 1: return from FFI callback. + | // cont = 0: tailcall from C function. + | subi TMP1, RB, 16 + | sub RC, TMP1, BASE + | b ->vm_call_tail + |.endif + | + |->cont_cat: // RA = resultptr, RB = meta base + | lwz INS, -4(PC) + | subi CARG2, RB, 16 + | decode_RB8 SAVE0, INS + |.if FPU + | lfd f0, 0(RA) + |.else + | lwz TMP2, 0(RA) + | lwz TMP3, 4(RA) + |.endif + | add TMP1, BASE, SAVE0 + | stp BASE, L->base + | cmplw TMP1, CARG2 + | sub CARG3, CARG2, TMP1 + | decode_RA8 RA, INS + |.if FPU + | stfd f0, 0(CARG2) + |.else + | stw TMP2, 0(CARG2) + | stw TMP3, 4(CARG2) + |.endif + | bney ->BC_CAT_Z + |.if FPU + | stfdx f0, BASE, RA + |.else + | stwux TMP2, RA, BASE + | stw TMP3, 4(RA) + |.endif + | b ->cont_nop + | + |//-- Table indexing metamethods ----------------------------------------- + | + |->vmeta_tgets1: + | la CARG3, DISPATCH_GL(tmptv)(DISPATCH) + | li TMP0, LJ_TSTR + | decode_RB8 RB, INS + | stw STR:RC, 4(CARG3) + | add CARG2, BASE, RB + | stw TMP0, 0(CARG3) + | b >1 + | + |->vmeta_tgets: + | la CARG2, DISPATCH_GL(tmptv)(DISPATCH) + | li TMP0, LJ_TTAB + | stw TAB:RB, 4(CARG2) + | la CARG3, DISPATCH_GL(tmptv2)(DISPATCH) + | stw TMP0, 0(CARG2) + | li TMP1, LJ_TSTR + | stw STR:RC, 4(CARG3) + | stw TMP1, 0(CARG3) + | b >1 + | + |->vmeta_tgetb: // TMP0 = index + |.if not DUALNUM + | tonum_u f0, TMP0 + |.endif + | decode_RB8 RB, INS + | la CARG3, DISPATCH_GL(tmptv)(DISPATCH) + | add CARG2, BASE, RB + |.if DUALNUM + | stw TISNUM, 0(CARG3) + | stw TMP0, 4(CARG3) + |.else + | stfd f0, 0(CARG3) + |.endif + | b >1 + | + |->vmeta_tgetv: + | decode_RB8 RB, INS + | decode_RC8 RC, INS + | add CARG2, BASE, RB + | add CARG3, BASE, RC + |1: + | stp BASE, L->base + | mr CARG1, L + | stw PC, SAVE_PC + | bl extern lj_meta_tget // (lua_State *L, TValue *o, TValue *k) + | // Returns TValue * (finished) or NULL (metamethod). + | cmplwi CRET1, 0 + | beq >3 + |.if FPU + | lfd f0, 0(CRET1) + |.else + | lwz TMP0, 0(CRET1) + | lwz TMP1, 4(CRET1) + |.endif + | ins_next1 + |.if FPU + | stfdx f0, BASE, RA + |.else + | stwux TMP0, RA, BASE + | stw TMP1, 4(RA) + |.endif + | ins_next2 + | + |3: // Call __index metamethod. + | // BASE = base, L->top = new base, stack = cont/func/t/k + | subfic TMP1, BASE, FRAME_CONT + | lp BASE, L->top + | stw PC, -16(BASE) // [cont|PC] + | add PC, TMP1, BASE + | lwz LFUNC:RB, FRAME_FUNC(BASE) // Guaranteed to be a function here. + | li NARGS8:RC, 16 // 2 args for func(t, k). + | b ->vm_call_dispatch_f + | + |->vmeta_tgetr: + | bl extern lj_tab_getinth // (GCtab *t, int32_t key) + | // Returns cTValue * or NULL. + | cmplwi CRET1, 0 + | beq >1 + |.if FPU + | lfd f14, 0(CRET1) + |.else + | lwz SAVE0, 0(CRET1) + | lwz SAVE1, 4(CRET1) + |.endif + | b ->BC_TGETR_Z + |1: + | stwx TISNIL, BASE, RA + | b ->cont_nop + | + |//----------------------------------------------------------------------- + | + |->vmeta_tsets1: + | la CARG3, DISPATCH_GL(tmptv)(DISPATCH) + | li TMP0, LJ_TSTR + | decode_RB8 RB, INS + | stw STR:RC, 4(CARG3) + | add CARG2, BASE, RB + | stw TMP0, 0(CARG3) + | b >1 + | + |->vmeta_tsets: + | la CARG2, DISPATCH_GL(tmptv)(DISPATCH) + | li TMP0, LJ_TTAB + | stw TAB:RB, 4(CARG2) + | la CARG3, DISPATCH_GL(tmptv2)(DISPATCH) + | stw TMP0, 0(CARG2) + | li TMP1, LJ_TSTR + | stw STR:RC, 4(CARG3) + | stw TMP1, 0(CARG3) + | b >1 + | + |->vmeta_tsetb: // TMP0 = index + |.if not DUALNUM + | tonum_u f0, TMP0 + |.endif + | decode_RB8 RB, INS + | la CARG3, DISPATCH_GL(tmptv)(DISPATCH) + | add CARG2, BASE, RB + |.if DUALNUM + | stw TISNUM, 0(CARG3) + | stw TMP0, 4(CARG3) + |.else + | stfd f0, 0(CARG3) + |.endif + | b >1 + | + |->vmeta_tsetv: + | decode_RB8 RB, INS + | decode_RC8 RC, INS + | add CARG2, BASE, RB + | add CARG3, BASE, RC + |1: + | stp BASE, L->base + | mr CARG1, L + | stw PC, SAVE_PC + | bl extern lj_meta_tset // (lua_State *L, TValue *o, TValue *k) + | // Returns TValue * (finished) or NULL (metamethod). + | cmplwi CRET1, 0 + |.if FPU + | lfdx f0, BASE, RA + |.else + | lwzux TMP2, RA, BASE + | lwz TMP3, 4(RA) + |.endif + | beq >3 + | // NOBARRIER: lj_meta_tset ensures the table is not black. + | ins_next1 + |.if FPU + | stfd f0, 0(CRET1) + |.else + | stw TMP2, 0(CRET1) + | stw TMP3, 4(CRET1) + |.endif + | ins_next2 + | + |3: // Call __newindex metamethod. + | // BASE = base, L->top = new base, stack = cont/func/t/k/(v) + | subfic TMP1, BASE, FRAME_CONT + | lp BASE, L->top + | stw PC, -16(BASE) // [cont|PC] + | add PC, TMP1, BASE + | lwz LFUNC:RB, FRAME_FUNC(BASE) // Guaranteed to be a function here. + | li NARGS8:RC, 24 // 3 args for func(t, k, v) + |.if FPU + | stfd f0, 16(BASE) // Copy value to third argument. + |.else + | stw TMP2, 16(BASE) + | stw TMP3, 20(BASE) + |.endif + | b ->vm_call_dispatch_f + | + |->vmeta_tsetr: + | stp BASE, L->base + | stw PC, SAVE_PC + | bl extern lj_tab_setinth // (lua_State *L, GCtab *t, int32_t key) + | // Returns TValue *. + |.if FPU + | stfd f14, 0(CRET1) + |.else + | stw SAVE0, 0(CRET1) + | stw SAVE1, 4(CRET1) + |.endif + | b ->cont_nop + | + |//-- Comparison metamethods --------------------------------------------- + | + |->vmeta_comp: + | mr CARG1, L + | subi PC, PC, 4 + |.if DUALNUM + | mr CARG2, RA + |.else + | add CARG2, BASE, RA + |.endif + | stw PC, SAVE_PC + |.if DUALNUM + | mr CARG3, RD + |.else + | add CARG3, BASE, RD + |.endif + | stp BASE, L->base + | decode_OP1 CARG4, INS + | bl extern lj_meta_comp // (lua_State *L, TValue *o1, *o2, int op) + | // Returns 0/1 or TValue * (metamethod). + |3: + | cmplwi CRET1, 1 + | bgt ->vmeta_binop + | subfic CRET1, CRET1, 0 + |4: + | lwz INS, 0(PC) + | addi PC, PC, 4 + | decode_RD4 TMP2, INS + | addis TMP2, TMP2, -(BCBIAS_J*4 >> 16) + | and TMP2, TMP2, CRET1 + | add PC, PC, TMP2 + |->cont_nop: + | ins_next + | + |->cont_ra: // RA = resultptr + | lwz INS, -4(PC) + |.if FPU + | lfd f0, 0(RA) + |.else + | lwz CARG1, 0(RA) + | lwz CARG2, 4(RA) + |.endif + | decode_RA8 TMP1, INS + |.if FPU + | stfdx f0, BASE, TMP1 + |.else + | stwux CARG1, TMP1, BASE + | stw CARG2, 4(TMP1) + |.endif + | b ->cont_nop + | + |->cont_condt: // RA = resultptr + | lwz TMP0, 0(RA) + | .gpr64 extsw TMP0, TMP0 + | subfic TMP0, TMP0, LJ_TTRUE // Branch if result is true. + | subfe CRET1, CRET1, CRET1 + | not CRET1, CRET1 + | b <4 + | + |->cont_condf: // RA = resultptr + | lwz TMP0, 0(RA) + | .gpr64 extsw TMP0, TMP0 + | subfic TMP0, TMP0, LJ_TTRUE // Branch if result is false. + | subfe CRET1, CRET1, CRET1 + | b <4 + | + |->vmeta_equal: + | // CARG2, CARG3, CARG4 are already set by BC_ISEQV/BC_ISNEV. + | subi PC, PC, 4 + | stp BASE, L->base + | mr CARG1, L + | stw PC, SAVE_PC + | bl extern lj_meta_equal // (lua_State *L, GCobj *o1, *o2, int ne) + | // Returns 0/1 or TValue * (metamethod). + | b <3 + | + |->vmeta_equal_cd: + |.if FFI + | mr CARG2, INS + | subi PC, PC, 4 + | stp BASE, L->base + | mr CARG1, L + | stw PC, SAVE_PC + | bl extern lj_meta_equal_cd // (lua_State *L, BCIns op) + | // Returns 0/1 or TValue * (metamethod). + | b <3 + |.endif + | + |->vmeta_istype: + | subi PC, PC, 4 + | stp BASE, L->base + | srwi CARG2, RA, 3 + | mr CARG1, L + | srwi CARG3, RD, 3 + | stw PC, SAVE_PC + | bl extern lj_meta_istype // (lua_State *L, BCReg ra, BCReg tp) + | b ->cont_nop + | + |//-- Arithmetic metamethods --------------------------------------------- + | + |->vmeta_arith_nv: + | add CARG3, KBASE, RC + | add CARG4, BASE, RB + | b >1 + |->vmeta_arith_nv2: + |.if DUALNUM + | mr CARG3, RC + | mr CARG4, RB + | b >1 + |.endif + | + |->vmeta_unm: + | mr CARG3, RD + | mr CARG4, RD + | b >1 + | + |->vmeta_arith_vn: + | add CARG3, BASE, RB + | add CARG4, KBASE, RC + | b >1 + | + |->vmeta_arith_vv: + | add CARG3, BASE, RB + | add CARG4, BASE, RC + |.if DUALNUM + | b >1 + |.endif + |->vmeta_arith_vn2: + |->vmeta_arith_vv2: + |.if DUALNUM + | mr CARG3, RB + | mr CARG4, RC + |.endif + |1: + | add CARG2, BASE, RA + | stp BASE, L->base + | mr CARG1, L + | stw PC, SAVE_PC + | decode_OP1 CARG5, INS // Caveat: CARG5 overlaps INS. + | bl extern lj_meta_arith // (lua_State *L, TValue *ra,*rb,*rc, BCReg op) + | // Returns NULL (finished) or TValue * (metamethod). + | cmplwi CRET1, 0 + | beq ->cont_nop + | + | // Call metamethod for binary op. + |->vmeta_binop: + | // BASE = old base, CRET1 = new base, stack = cont/func/o1/o2 + | sub TMP1, CRET1, BASE + | stw PC, -16(CRET1) // [cont|PC] + | mr TMP2, BASE + | addi PC, TMP1, FRAME_CONT + | mr BASE, CRET1 + | li NARGS8:RC, 16 // 2 args for func(o1, o2). + | b ->vm_call_dispatch + | + |->vmeta_len: +#if LJ_52 + | mr SAVE0, CARG1 +#endif + | mr CARG2, RD + | stp BASE, L->base + | mr CARG1, L + | stw PC, SAVE_PC + | bl extern lj_meta_len // (lua_State *L, TValue *o) + | // Returns NULL (retry) or TValue * (metamethod base). +#if LJ_52 + | cmplwi CRET1, 0 + | bne ->vmeta_binop // Binop call for compatibility. + | mr CARG1, SAVE0 + | b ->BC_LEN_Z +#else + | b ->vmeta_binop // Binop call for compatibility. +#endif + | + |//-- Call metamethod ---------------------------------------------------- + | + |->vmeta_call: // Resolve and call __call metamethod. + | // TMP2 = old base, BASE = new base, RC = nargs*8 + | mr CARG1, L + | stp TMP2, L->base // This is the callers base! + | subi CARG2, BASE, 8 + | stw PC, SAVE_PC + | add CARG3, BASE, RC + | mr SAVE0, NARGS8:RC + | bl extern lj_meta_call // (lua_State *L, TValue *func, TValue *top) + | lwz LFUNC:RB, FRAME_FUNC(BASE) // Guaranteed to be a function here. + | addi NARGS8:RC, SAVE0, 8 // Got one more argument now. + | ins_call + | + |->vmeta_callt: // Resolve __call for BC_CALLT. + | // BASE = old base, RA = new base, RC = nargs*8 + | mr CARG1, L + | stp BASE, L->base + | subi CARG2, RA, 8 + | stw PC, SAVE_PC + | add CARG3, RA, RC + | mr SAVE0, NARGS8:RC + | bl extern lj_meta_call // (lua_State *L, TValue *func, TValue *top) + | lwz TMP1, FRAME_PC(BASE) + | addi NARGS8:RC, SAVE0, 8 // Got one more argument now. + | lwz LFUNC:RB, FRAME_FUNC(RA) // Guaranteed to be a function here. + | b ->BC_CALLT_Z + | + |//-- Argument coercion for 'for' statement ------------------------------ + | + |->vmeta_for: + | mr CARG1, L + | stp BASE, L->base + | mr CARG2, RA + | stw PC, SAVE_PC + | mr SAVE0, INS + | bl extern lj_meta_for // (lua_State *L, TValue *base) + |.if JIT + | decode_OP1 TMP0, SAVE0 + |.endif + | decode_RA8 RA, SAVE0 + |.if JIT + | cmpwi TMP0, BC_JFORI + |.endif + | decode_RD8 RD, SAVE0 + |.if JIT + | beqy =>BC_JFORI + |.endif + | b =>BC_FORI + | + |//----------------------------------------------------------------------- + |//-- Fast functions ----------------------------------------------------- + |//----------------------------------------------------------------------- + | + |.macro .ffunc, name + |->ff_ .. name: + |.endmacro + | + |.macro .ffunc_1, name + |->ff_ .. name: + | cmplwi NARGS8:RC, 8 + | lwz CARG3, 0(BASE) + | lwz CARG1, 4(BASE) + | blt ->fff_fallback + |.endmacro + | + |.macro .ffunc_2, name + |->ff_ .. name: + | cmplwi NARGS8:RC, 16 + | lwz CARG3, 0(BASE) + | lwz CARG4, 8(BASE) + | lwz CARG1, 4(BASE) + | lwz CARG2, 12(BASE) + | blt ->fff_fallback + |.endmacro + | + |.macro .ffunc_n, name + |->ff_ .. name: + | cmplwi NARGS8:RC, 8 + | lwz CARG1, 0(BASE) + |.if FPU + | lfd FARG1, 0(BASE) + |.else + | lwz CARG2, 4(BASE) + |.endif + | blt ->fff_fallback + | checknum CARG1; bge ->fff_fallback + |.endmacro + | + |.macro .ffunc_nn, name + |->ff_ .. name: + | cmplwi NARGS8:RC, 16 + | lwz CARG1, 0(BASE) + |.if FPU + | lfd FARG1, 0(BASE) + | lwz CARG3, 8(BASE) + | lfd FARG2, 8(BASE) + |.else + | lwz CARG2, 4(BASE) + | lwz CARG3, 8(BASE) + | lwz CARG4, 12(BASE) + |.endif + | blt ->fff_fallback + | checknum CARG1; bge ->fff_fallback + | checknum CARG3; bge ->fff_fallback + |.endmacro + | + |// Inlined GC threshold check. Caveat: uses TMP0 and TMP1. + |.macro ffgccheck + | lwz TMP0, DISPATCH_GL(gc.total)(DISPATCH) + | lwz TMP1, DISPATCH_GL(gc.threshold)(DISPATCH) + | cmplw TMP0, TMP1 + | bgel ->fff_gcstep + |.endmacro + | + |//-- Base library: checks ----------------------------------------------- + | + |.ffunc_1 assert + | li TMP1, LJ_TFALSE + | la RA, -8(BASE) + | cmplw cr1, CARG3, TMP1 + | lwz PC, FRAME_PC(BASE) + | bge cr1, ->fff_fallback + | stw CARG3, 0(RA) + | addi RD, NARGS8:RC, 8 // Compute (nresults+1)*8. + | addi TMP1, BASE, 8 + | add TMP2, RA, NARGS8:RC + | stw CARG1, 4(RA) + | beq ->fff_res // Done if exactly 1 argument. + |1: + | cmplw TMP1, TMP2 + |.if FPU + | lfd f0, 0(TMP1) + | stfd f0, 0(TMP1) + |.else + | lwz CARG1, 0(TMP1) + | lwz CARG2, 4(TMP1) + | stw CARG1, -8(TMP1) + | stw CARG2, -4(TMP1) + |.endif + | addi TMP1, TMP1, 8 + | bney <1 + | b ->fff_res + | + |.ffunc type + | cmplwi NARGS8:RC, 8 + | lwz CARG1, 0(BASE) + | blt ->fff_fallback + | .gpr64 extsw CARG1, CARG1 + | subfc TMP0, TISNUM, CARG1 + | subfe TMP2, CARG1, CARG1 + | orc TMP1, TMP2, TMP0 + | addi TMP1, TMP1, ~LJ_TISNUM+1 + | slwi TMP1, TMP1, 3 + |.if FPU + | la TMP2, CFUNC:RB->upvalue + | lfdx FARG1, TMP2, TMP1 + |.else + | add TMP1, CFUNC:RB, TMP1 + | lwz CARG1, CFUNC:TMP1->upvalue[0].u32.hi + | lwz CARG2, CFUNC:TMP1->upvalue[0].u32.lo + |.endif + | b ->fff_resn + | + |//-- Base library: getters and setters --------------------------------- + | + |.ffunc_1 getmetatable + | checktab CARG3; bne >6 + |1: // Field metatable must be at same offset for GCtab and GCudata! + | lwz TAB:CARG1, TAB:CARG1->metatable + |2: + | li CARG3, LJ_TNIL + | cmplwi TAB:CARG1, 0 + | lwz STR:RC, DISPATCH_GL(gcroot[GCROOT_MMNAME+MM_metatable])(DISPATCH) + | beq ->fff_restv + | lwz TMP0, TAB:CARG1->hmask + | li CARG3, LJ_TTAB // Use metatable as default result. + | lwz TMP1, STR:RC->hash + | lwz NODE:TMP2, TAB:CARG1->node + | and TMP1, TMP1, TMP0 // idx = str->hash & tab->hmask + | slwi TMP0, TMP1, 5 + | slwi TMP1, TMP1, 3 + | sub TMP1, TMP0, TMP1 + | add NODE:TMP2, NODE:TMP2, TMP1 // node = tab->node + (idx*32-idx*8) + |3: // Rearranged logic, because we expect _not_ to find the key. + | lwz CARG4, NODE:TMP2->key + | lwz TMP0, 4+offsetof(Node, key)(NODE:TMP2) + | lwz CARG2, NODE:TMP2->val + | lwz TMP1, 4+offsetof(Node, val)(NODE:TMP2) + | checkstr CARG4; bne >4 + | cmpw TMP0, STR:RC; beq >5 + |4: + | lwz NODE:TMP2, NODE:TMP2->next + | cmplwi NODE:TMP2, 0 + | beq ->fff_restv // Not found, keep default result. + | b <3 + |5: + | checknil CARG2 + | beq ->fff_restv // Ditto for nil value. + | mr CARG3, CARG2 // Return value of mt.__metatable. + | mr CARG1, TMP1 + | b ->fff_restv + | + |6: + | cmpwi CARG3, LJ_TUDATA; beq <1 + | .gpr64 extsw CARG3, CARG3 + | subfc TMP0, TISNUM, CARG3 + | subfe TMP2, CARG3, CARG3 + | orc TMP1, TMP2, TMP0 + | addi TMP1, TMP1, ~LJ_TISNUM+1 + | slwi TMP1, TMP1, 2 + | la TMP2, DISPATCH_GL(gcroot[GCROOT_BASEMT])(DISPATCH) + | lwzx TAB:CARG1, TMP2, TMP1 + | b <2 + | + |.ffunc_2 setmetatable + | // Fast path: no mt for table yet and not clearing the mt. + | checktab CARG3; bne ->fff_fallback + | lwz TAB:TMP1, TAB:CARG1->metatable + | checktab CARG4; bne ->fff_fallback + | cmplwi TAB:TMP1, 0 + | lbz TMP3, TAB:CARG1->marked + | bne ->fff_fallback + | andix. TMP0, TMP3, LJ_GC_BLACK // isblack(table) + | stw TAB:CARG2, TAB:CARG1->metatable + | beq ->fff_restv + | barrierback TAB:CARG1, TMP3, TMP0 + | b ->fff_restv + | + |.ffunc rawget + | cmplwi NARGS8:RC, 16 + | lwz CARG4, 0(BASE) + | lwz TAB:CARG2, 4(BASE) + | blt ->fff_fallback + | checktab CARG4; bne ->fff_fallback + | la CARG3, 8(BASE) + | mr CARG1, L + | bl extern lj_tab_get // (lua_State *L, GCtab *t, cTValue *key) + | // Returns cTValue *. + |.if FPU + | lfd FARG1, 0(CRET1) + |.else + | lwz CARG2, 4(CRET1) + | lwz CARG1, 0(CRET1) // Caveat: CARG1 == CRET1. + |.endif + | b ->fff_resn + | + |//-- Base library: conversions ------------------------------------------ + | + |.ffunc tonumber + | // Only handles the number case inline (without a base argument). + | cmplwi NARGS8:RC, 8 + | lwz CARG1, 0(BASE) + |.if FPU + | lfd FARG1, 0(BASE) + |.else + | lwz CARG2, 4(BASE) + |.endif + | bne ->fff_fallback // Exactly one argument. + | checknum CARG1; bgt ->fff_fallback + | b ->fff_resn + | + |.ffunc_1 tostring + | // Only handles the string or number case inline. + | checkstr CARG3 + | // A __tostring method in the string base metatable is ignored. + | beq ->fff_restv // String key? + | // Handle numbers inline, unless a number base metatable is present. + | lwz TMP0, DISPATCH_GL(gcroot[GCROOT_BASEMT_NUM])(DISPATCH) + | checknum CARG3 + | cmplwi cr1, TMP0, 0 + | stp BASE, L->base // Add frame since C call can throw. + | crorc 4*cr0+eq, 4*cr0+gt, 4*cr1+eq + | stw PC, SAVE_PC // Redundant (but a defined value). + | beq ->fff_fallback + | ffgccheck + | mr CARG1, L + | mr CARG2, BASE + |.if DUALNUM + | bl extern lj_strfmt_number // (lua_State *L, cTValue *o) + |.else + | bl extern lj_strfmt_num // (lua_State *L, lua_Number *np) + |.endif + | // Returns GCstr *. + | li CARG3, LJ_TSTR + | b ->fff_restv + | + |//-- Base library: iterators ------------------------------------------- + | + |.ffunc next + | cmplwi NARGS8:RC, 8 + | lwz CARG1, 0(BASE) + | lwz TAB:CARG2, 4(BASE) + | blt ->fff_fallback + | stwx TISNIL, BASE, NARGS8:RC // Set missing 2nd arg to nil. + | checktab CARG1 + | lwz PC, FRAME_PC(BASE) + | bne ->fff_fallback + | stp BASE, L->base // Add frame since C call can throw. + | mr CARG1, L + | stp BASE, L->top // Dummy frame length is ok. + | la CARG3, 8(BASE) + | stw PC, SAVE_PC + | bl extern lj_tab_next // (lua_State *L, GCtab *t, TValue *key) + | // Returns 0 at end of traversal. + | cmplwi CRET1, 0 + | li CARG3, LJ_TNIL + | beq ->fff_restv // End of traversal: return nil. + | la RA, -8(BASE) + |.if FPU + | lfd f0, 8(BASE) // Copy key and value to results. + | lfd f1, 16(BASE) + | stfd f0, 0(RA) + | stfd f1, 8(RA) + |.else + | lwz CARG1, 8(BASE) + | lwz CARG2, 12(BASE) + | lwz CARG3, 16(BASE) + | lwz CARG4, 20(BASE) + | stw CARG1, 0(RA) + | stw CARG2, 4(RA) + | stw CARG3, 8(RA) + | stw CARG4, 12(RA) + |.endif + | li RD, (2+1)*8 + | b ->fff_res + | + |.ffunc_1 pairs + | checktab CARG3 + | lwz PC, FRAME_PC(BASE) + | bne ->fff_fallback +#if LJ_52 + | lwz TAB:TMP2, TAB:CARG1->metatable + |.if FPU + | lfd f0, CFUNC:RB->upvalue[0] + |.else + | lwz TMP0, CFUNC:RB->upvalue[0].u32.hi + | lwz TMP1, CFUNC:RB->upvalue[0].u32.lo + |.endif + | cmplwi TAB:TMP2, 0 + | la RA, -8(BASE) + | bne ->fff_fallback +#else + |.if FPU + | lfd f0, CFUNC:RB->upvalue[0] + |.else + | lwz TMP0, CFUNC:RB->upvalue[0].u32.hi + | lwz TMP1, CFUNC:RB->upvalue[0].u32.lo + |.endif + | la RA, -8(BASE) +#endif + | stw TISNIL, 8(BASE) + | li RD, (3+1)*8 + |.if FPU + | stfd f0, 0(RA) + |.else + | stw TMP0, 0(RA) + | stw TMP1, 4(RA) + |.endif + | b ->fff_res + | + |.ffunc ipairs_aux + | cmplwi NARGS8:RC, 16 + | lwz CARG3, 0(BASE) + | lwz TAB:CARG1, 4(BASE) + | lwz CARG4, 8(BASE) + |.if DUALNUM + | lwz TMP2, 12(BASE) + |.else + | lfd FARG2, 8(BASE) + |.endif + | blt ->fff_fallback + | checktab CARG3 + | checknum cr1, CARG4 + | lwz PC, FRAME_PC(BASE) + |.if DUALNUM + | bne ->fff_fallback + | bne cr1, ->fff_fallback + |.else + | lus TMP0, 0x3ff0 + | stw ZERO, TMPD_LO + | bne ->fff_fallback + | stw TMP0, TMPD_HI + | bge cr1, ->fff_fallback + | lfd FARG1, TMPD + | toint TMP2, FARG2, f0 + |.endif + | lwz TMP0, TAB:CARG1->asize + | lwz TMP1, TAB:CARG1->array + |.if not DUALNUM + | fadd FARG2, FARG2, FARG1 + |.endif + | addi TMP2, TMP2, 1 + | la RA, -8(BASE) + | cmplw TMP0, TMP2 + |.if DUALNUM + | stw TISNUM, 0(RA) + | slwi TMP3, TMP2, 3 + | stw TMP2, 4(RA) + |.else + | slwi TMP3, TMP2, 3 + | stfd FARG2, 0(RA) + |.endif + | ble >2 // Not in array part? + |.if FPU + | lwzx TMP2, TMP1, TMP3 + | lfdx f0, TMP1, TMP3 + |.else + | lwzux TMP2, TMP1, TMP3 + | lwz TMP3, 4(TMP1) + |.endif + |1: + | checknil TMP2 + | li RD, (0+1)*8 + | beq ->fff_res // End of iteration, return 0 results. + | li RD, (2+1)*8 + |.if FPU + | stfd f0, 8(RA) + |.else + | stw TMP2, 8(RA) + | stw TMP3, 12(RA) + |.endif + | b ->fff_res + |2: // Check for empty hash part first. Otherwise call C function. + | lwz TMP0, TAB:CARG1->hmask + | cmplwi TMP0, 0 + | li RD, (0+1)*8 + | beq ->fff_res + | mr CARG2, TMP2 + | bl extern lj_tab_getinth // (GCtab *t, int32_t key) + | // Returns cTValue * or NULL. + | cmplwi CRET1, 0 + | li RD, (0+1)*8 + | beq ->fff_res + | lwz TMP2, 0(CRET1) + |.if FPU + | lfd f0, 0(CRET1) + |.else + | lwz TMP3, 4(CRET1) + |.endif + | b <1 + | + |.ffunc_1 ipairs + | checktab CARG3 + | lwz PC, FRAME_PC(BASE) + | bne ->fff_fallback +#if LJ_52 + | lwz TAB:TMP2, TAB:CARG1->metatable + |.if FPU + | lfd f0, CFUNC:RB->upvalue[0] + |.else + | lwz TMP0, CFUNC:RB->upvalue[0].u32.hi + | lwz TMP1, CFUNC:RB->upvalue[0].u32.lo + |.endif + | cmplwi TAB:TMP2, 0 + | la RA, -8(BASE) + | bne ->fff_fallback +#else + |.if FPU + | lfd f0, CFUNC:RB->upvalue[0] + |.else + | lwz TMP0, CFUNC:RB->upvalue[0].u32.hi + | lwz TMP1, CFUNC:RB->upvalue[0].u32.lo + |.endif + | la RA, -8(BASE) +#endif + |.if DUALNUM + | stw TISNUM, 8(BASE) + |.else + | stw ZERO, 8(BASE) + |.endif + | stw ZERO, 12(BASE) + | li RD, (3+1)*8 + |.if FPU + | stfd f0, 0(RA) + |.else + | stw TMP0, 0(RA) + | stw TMP1, 4(RA) + |.endif + | b ->fff_res + | + |//-- Base library: catch errors ---------------------------------------- + | + |.ffunc pcall + | cmplwi NARGS8:RC, 8 + | lbz TMP3, DISPATCH_GL(hookmask)(DISPATCH) + | blt ->fff_fallback + | mr TMP2, BASE + | la BASE, 8(BASE) + | // Remember active hook before pcall. + | rlwinm TMP3, TMP3, 32-HOOK_ACTIVE_SHIFT, 31, 31 + | subi NARGS8:RC, NARGS8:RC, 8 + | addi PC, TMP3, 8+FRAME_PCALL + | b ->vm_call_dispatch + | + |.ffunc xpcall + | cmplwi NARGS8:RC, 16 + | lwz CARG3, 8(BASE) + |.if FPU + | lfd FARG2, 8(BASE) + | lfd FARG1, 0(BASE) + |.else + | lwz CARG1, 0(BASE) + | lwz CARG2, 4(BASE) + | lwz CARG4, 12(BASE) + |.endif + | blt ->fff_fallback + | lbz TMP1, DISPATCH_GL(hookmask)(DISPATCH) + | mr TMP2, BASE + | checkfunc CARG3; bne ->fff_fallback // Traceback must be a function. + | la BASE, 16(BASE) + | // Remember active hook before pcall. + | rlwinm TMP1, TMP1, 32-HOOK_ACTIVE_SHIFT, 31, 31 + |.if FPU + | stfd FARG2, 0(TMP2) // Swap function and traceback. + | stfd FARG1, 8(TMP2) + |.else + | stw CARG3, 0(TMP2) + | stw CARG4, 4(TMP2) + | stw CARG1, 8(TMP2) + | stw CARG2, 12(TMP2) + |.endif + | subi NARGS8:RC, NARGS8:RC, 16 + | addi PC, TMP1, 16+FRAME_PCALL + | b ->vm_call_dispatch + | + |//-- Coroutine library -------------------------------------------------- + | + |.macro coroutine_resume_wrap, resume + |.if resume + |.ffunc_1 coroutine_resume + | cmpwi CARG3, LJ_TTHREAD; bne ->fff_fallback + |.else + |.ffunc coroutine_wrap_aux + | lwz L:CARG1, CFUNC:RB->upvalue[0].gcr + |.endif + | lbz TMP0, L:CARG1->status + | lp TMP1, L:CARG1->cframe + | lp CARG2, L:CARG1->top + | cmplwi cr0, TMP0, LUA_YIELD + | lp TMP2, L:CARG1->base + | cmplwi cr1, TMP1, 0 + | lwz TMP0, L:CARG1->maxstack + | cmplw cr7, CARG2, TMP2 + | lwz PC, FRAME_PC(BASE) + | crorc 4*cr6+lt, 4*cr0+gt, 4*cr1+eq // st>LUA_YIELD || cframe!=0 + | add TMP2, CARG2, NARGS8:RC + | crandc 4*cr6+gt, 4*cr7+eq, 4*cr0+eq // base==top && st!=LUA_YIELD + | cmplw cr1, TMP2, TMP0 + | cror 4*cr6+lt, 4*cr6+lt, 4*cr6+gt + | stw PC, SAVE_PC + | cror 4*cr6+lt, 4*cr6+lt, 4*cr1+gt // cond1 || cond2 || stackov + | stp BASE, L->base + | blt cr6, ->fff_fallback + |1: + |.if resume + | addi BASE, BASE, 8 // Keep resumed thread in stack for GC. + | subi NARGS8:RC, NARGS8:RC, 8 + | subi TMP2, TMP2, 8 + |.endif + | stp TMP2, L:CARG1->top + | li TMP1, 0 + | stp BASE, L->top + |2: // Move args to coroutine. + | cmpw TMP1, NARGS8:RC + |.if FPU + | lfdx f0, BASE, TMP1 + |.else + | add CARG3, BASE, TMP1 + | lwz TMP2, 0(CARG3) + | lwz TMP3, 4(CARG3) + |.endif + | beq >3 + |.if FPU + | stfdx f0, CARG2, TMP1 + |.else + | add CARG3, CARG2, TMP1 + | stw TMP2, 0(CARG3) + | stw TMP3, 4(CARG3) + |.endif + | addi TMP1, TMP1, 8 + | b <2 + |3: + | li CARG3, 0 + | mr L:SAVE0, L:CARG1 + | li CARG4, 0 + | bl ->vm_resume // (lua_State *L, TValue *base, 0, 0) + | // Returns thread status. + |4: + | lp TMP2, L:SAVE0->base + | cmplwi CRET1, LUA_YIELD + | lp TMP3, L:SAVE0->top + | li_vmstate INTERP + | lp BASE, L->base + | stw L, DISPATCH_GL(cur_L)(DISPATCH) + | st_vmstate + | bgt >8 + | sub RD, TMP3, TMP2 + | lwz TMP0, L->maxstack + | cmplwi RD, 0 + | add TMP1, BASE, RD + | beq >6 // No results? + | cmplw TMP1, TMP0 + | li TMP1, 0 + | bgt >9 // Need to grow stack? + | + | subi TMP3, RD, 8 + | stp TMP2, L:SAVE0->top // Clear coroutine stack. + |5: // Move results from coroutine. + | cmplw TMP1, TMP3 + |.if FPU + | lfdx f0, TMP2, TMP1 + | stfdx f0, BASE, TMP1 + |.else + | add CARG3, TMP2, TMP1 + | lwz CARG1, 0(CARG3) + | lwz CARG2, 4(CARG3) + | add CARG3, BASE, TMP1 + | stw CARG1, 0(CARG3) + | stw CARG2, 4(CARG3) + |.endif + | addi TMP1, TMP1, 8 + | bne <5 + |6: + | andix. TMP0, PC, FRAME_TYPE + |.if resume + | li TMP1, LJ_TTRUE + | la RA, -8(BASE) + | stw TMP1, -8(BASE) // Prepend true to results. + | addi RD, RD, 16 + |.else + | mr RA, BASE + | addi RD, RD, 8 + |.endif + |7: + | stw PC, SAVE_PC + | mr MULTRES, RD + | beq ->BC_RET_Z + | b ->vm_return + | + |8: // Coroutine returned with error (at co->top-1). + |.if resume + | andix. TMP0, PC, FRAME_TYPE + | la TMP3, -8(TMP3) + | li TMP1, LJ_TFALSE + |.if FPU + | lfd f0, 0(TMP3) + |.else + | lwz CARG1, 0(TMP3) + | lwz CARG2, 4(TMP3) + |.endif + | stp TMP3, L:SAVE0->top // Remove error from coroutine stack. + | li RD, (2+1)*8 + | stw TMP1, -8(BASE) // Prepend false to results. + | la RA, -8(BASE) + |.if FPU + | stfd f0, 0(BASE) // Copy error message. + |.else + | stw CARG1, 0(BASE) // Copy error message. + | stw CARG2, 4(BASE) + |.endif + | b <7 + |.else + | mr CARG1, L + | mr CARG2, L:SAVE0 + | bl extern lj_ffh_coroutine_wrap_err // (lua_State *L, lua_State *co) + |.endif + | + |9: // Handle stack expansion on return from yield. + | mr CARG1, L + | srwi CARG2, RD, 3 + | bl extern lj_state_growstack // (lua_State *L, int n) + | li CRET1, 0 + | b <4 + |.endmacro + | + | coroutine_resume_wrap 1 // coroutine.resume + | coroutine_resume_wrap 0 // coroutine.wrap + | + |.ffunc coroutine_yield + | lp TMP0, L->cframe + | add TMP1, BASE, NARGS8:RC + | stp BASE, L->base + | andix. TMP0, TMP0, CFRAME_RESUME + | stp TMP1, L->top + | li CRET1, LUA_YIELD + | beq ->fff_fallback + | stp ZERO, L->cframe + | stb CRET1, L->status + | b ->vm_leave_unw + | + |//-- Math library ------------------------------------------------------- + | + |.ffunc_1 math_abs + | checknum CARG3 + |.if DUALNUM + | bne >2 + | srawi TMP1, CARG1, 31 + | xor TMP2, TMP1, CARG1 + |.if GPR64 + | lus TMP0, 0x8000 + | sub CARG1, TMP2, TMP1 + | cmplw CARG1, TMP0 + | beq >1 + |.else + | sub. CARG1, TMP2, TMP1 + | blt >1 + |.endif + |->fff_resi: + | lwz PC, FRAME_PC(BASE) + | la RA, -8(BASE) + | stw TISNUM, -8(BASE) + | stw CRET1, -4(BASE) + | b ->fff_res1 + |1: + | lus CARG3, 0x41e0 // 2^31. + | li CARG1, 0 + | b ->fff_restv + |2: + |.endif + | bge ->fff_fallback + | rlwinm CARG3, CARG3, 0, 1, 31 + | // Fallthrough. + | + |->fff_restv: + | // CARG3/CARG1 = TValue result. + | lwz PC, FRAME_PC(BASE) + | stw CARG3, -8(BASE) + | la RA, -8(BASE) + | stw CARG1, -4(BASE) + |->fff_res1: + | // RA = results, PC = return. + | li RD, (1+1)*8 + |->fff_res: + | // RA = results, RD = (nresults+1)*8, PC = return. + | andix. TMP0, PC, FRAME_TYPE + | mr MULTRES, RD + | bney ->vm_return + | lwz INS, -4(PC) + | decode_RB8 RB, INS + |5: + | cmplw RB, RD // More results expected? + | decode_RA8 TMP0, INS + | bgt >6 + | ins_next1 + | // Adjust BASE. KBASE is assumed to be set for the calling frame. + | sub BASE, RA, TMP0 + | ins_next2 + | + |6: // Fill up results with nil. + | subi TMP1, RD, 8 + | addi RD, RD, 8 + | stwx TISNIL, RA, TMP1 + | b <5 + | + |.macro math_extern, func + | .ffunc_n math_ .. func + | blex func + | b ->fff_resn + |.endmacro + | + |.macro math_extern2, func + | .ffunc_nn math_ .. func + | blex func + | b ->fff_resn + |.endmacro + | + |.macro math_round, func + | .ffunc_1 math_ .. func + | checknum CARG3; beqy ->fff_restv + | rlwinm TMP2, CARG3, 12, 21, 31 + | bge ->fff_fallback + | addic. TMP2, TMP2, -1023 // exp = exponent(x) - 1023 + | cmplwi cr1, TMP2, 31 // 0 <= exp < 31? + | subfic TMP0, TMP2, 31 + | blt >3 + | slwi TMP1, CARG3, 11 + | srwi TMP3, CARG1, 21 + | oris TMP1, TMP1, 0x8000 + | addi TMP2, TMP2, 1 + | or TMP1, TMP1, TMP3 + | slwi CARG2, CARG1, 11 + | bge cr1, >4 + | slw TMP3, TMP1, TMP2 + | srw RD, TMP1, TMP0 + | or TMP3, TMP3, CARG2 + | srawi TMP2, CARG3, 31 + |.if "func" == "floor" + | and TMP1, TMP3, TMP2 + | addic TMP0, TMP1, -1 + | subfe TMP1, TMP0, TMP1 + | add CARG1, RD, TMP1 + | xor CARG1, CARG1, TMP2 + | sub CARG1, CARG1, TMP2 + | b ->fff_resi + |.else + | andc TMP1, TMP3, TMP2 + | addic TMP0, TMP1, -1 + | subfe TMP1, TMP0, TMP1 + | add CARG1, RD, TMP1 + | cmpw CARG1, RD + | xor CARG1, CARG1, TMP2 + | sub CARG1, CARG1, TMP2 + | bge ->fff_resi + | // Overflow to 2^31. + | lus CARG3, 0x41e0 // 2^31. + | li CARG1, 0 + | b ->fff_restv + |.endif + |3: // |x| < 1 + | slwi TMP2, CARG3, 1 + | srawi TMP1, CARG3, 31 + | or TMP2, CARG1, TMP2 // ztest = (hi+hi) | lo + |.if "func" == "floor" + | and TMP1, TMP2, TMP1 // (ztest & sign) == 0 ? 0 : -1 + | subfic TMP2, TMP1, 0 + | subfe CARG1, CARG1, CARG1 + |.else + | andc TMP1, TMP2, TMP1 // (ztest & ~sign) == 0 ? 0 : 1 + | addic TMP2, TMP1, -1 + | subfe CARG1, TMP2, TMP1 + |.endif + | b ->fff_resi + |4: // exp >= 31. Check for -(2^31). + | xoris TMP1, TMP1, 0x8000 + | srawi TMP2, CARG3, 31 + |.if "func" == "floor" + | or TMP1, TMP1, CARG2 + |.endif + |.if PPE + | orc TMP1, TMP1, TMP2 + | cmpwi TMP1, 0 + |.else + | orc. TMP1, TMP1, TMP2 + |.endif + | crand 4*cr0+eq, 4*cr0+eq, 4*cr1+eq + | lus CARG1, 0x8000 // -(2^31). + | beqy ->fff_resi + |5: + |.if FPU + | lfd FARG1, 0(BASE) + |.else + | lwz CARG1, 0(BASE) + | lwz CARG2, 4(BASE) + |.endif + | blex func + | b ->fff_resn + |.endmacro + | + |.if DUALNUM + | math_round floor + | math_round ceil + |.else + | // NYI: use internal implementation. + | math_extern floor + | math_extern ceil + |.endif + | + |.if SQRT + |.ffunc_n math_sqrt + | fsqrt FARG1, FARG1 + | b ->fff_resn + |.else + | math_extern sqrt + |.endif + | + |.ffunc math_log + | cmplwi NARGS8:RC, 8 + | lwz CARG1, 0(BASE) + | bne ->fff_fallback // Need exactly 1 argument. + | checknum CARG1; bge ->fff_fallback + |.if FPU + | lfd FARG1, 0(BASE) + |.else + | lwz CARG2, 4(BASE) + |.endif + | blex log + | b ->fff_resn + | + | math_extern log10 + | math_extern exp + | math_extern sin + | math_extern cos + | math_extern tan + | math_extern asin + | math_extern acos + | math_extern atan + | math_extern sinh + | math_extern cosh + | math_extern tanh + | math_extern2 pow + | math_extern2 atan2 + | math_extern2 fmod + | + |.if DUALNUM + |.ffunc math_ldexp + | cmplwi NARGS8:RC, 16 + | lwz TMP0, 0(BASE) + |.if FPU + | lfd FARG1, 0(BASE) + |.else + | lwz CARG1, 0(BASE) + | lwz CARG2, 4(BASE) + |.endif + | lwz TMP1, 8(BASE) + |.if GPR64 + | lwz CARG2, 12(BASE) + |.elif FPU + | lwz CARG1, 12(BASE) + |.else + | lwz CARG3, 12(BASE) + |.endif + | blt ->fff_fallback + | checknum TMP0; bge ->fff_fallback + | checknum TMP1; bne ->fff_fallback + |.else + |.ffunc_nn math_ldexp + |.if GPR64 + | toint CARG2, FARG2 + |.else + | toint CARG1, FARG2 + |.endif + |.endif + | blex ldexp + | b ->fff_resn + | + |.ffunc_n math_frexp + |.if GPR64 + | la CARG2, DISPATCH_GL(tmptv)(DISPATCH) + |.elif FPU + | la CARG1, DISPATCH_GL(tmptv)(DISPATCH) + |.else + | la CARG3, DISPATCH_GL(tmptv)(DISPATCH) + |.endif + | lwz PC, FRAME_PC(BASE) + | blex frexp + | lwz TMP1, DISPATCH_GL(tmptv)(DISPATCH) + | la RA, -8(BASE) + |.if not DUALNUM + | tonum_i FARG2, TMP1 + |.endif + |.if FPU + | stfd FARG1, 0(RA) + |.else + | stw CRET1, 0(RA) + | stw CRET2, 4(RA) + |.endif + | li RD, (2+1)*8 + |.if DUALNUM + | stw TISNUM, 8(RA) + | stw TMP1, 12(RA) + |.else + | stfd FARG2, 8(RA) + |.endif + | b ->fff_res + | + |.ffunc_n math_modf + |.if GPR64 + | la CARG2, -8(BASE) + |.elif FPU + | la CARG1, -8(BASE) + |.else + | la CARG3, -8(BASE) + |.endif + | lwz PC, FRAME_PC(BASE) + | blex modf + | la RA, -8(BASE) + |.if FPU + | stfd FARG1, 0(BASE) + |.else + | stw CRET1, 0(BASE) + | stw CRET2, 4(BASE) + |.endif + | li RD, (2+1)*8 + | b ->fff_res + | + |.macro math_minmax, name, ismax + |.if DUALNUM + | .ffunc_1 name + | checknum CARG3 + | addi SAVE0, BASE, 8 + | add SAVE1, BASE, NARGS8:RC + | bne >4 + |1: // Handle integers. + | lwz CARG4, 0(SAVE0) + | cmplw cr1, SAVE0, SAVE1 + | lwz CARG2, 4(SAVE0) + | bge cr1, ->fff_resi + | checknum CARG4 + | xoris TMP0, CARG1, 0x8000 + | xoris TMP3, CARG2, 0x8000 + | bne >3 + | subfc TMP3, TMP3, TMP0 + | subfe TMP0, TMP0, TMP0 + |.if ismax + | andc TMP3, TMP3, TMP0 + |.else + | and TMP3, TMP3, TMP0 + |.endif + | add CARG1, TMP3, CARG2 + |.if GPR64 + | rldicl CARG1, CARG1, 0, 32 + |.endif + | addi SAVE0, SAVE0, 8 + | b <1 + |3: + | bge ->fff_fallback + | // Convert intermediate result to number and continue below. + |.if FPU + | tonum_i FARG1, CARG1 + | lfd FARG2, 0(SAVE0) + |.else + | mr CARG2, CARG1 + | bl ->vm_sfi2d_1 + | lwz CARG3, 0(SAVE0) + | lwz CARG4, 4(SAVE0) + |.endif + | b >6 + |4: + |.if FPU + | lfd FARG1, 0(BASE) + |.else + | lwz CARG1, 0(BASE) + | lwz CARG2, 4(BASE) + |.endif + | bge ->fff_fallback + |5: // Handle numbers. + | lwz CARG3, 0(SAVE0) + | cmplw cr1, SAVE0, SAVE1 + |.if FPU + | lfd FARG2, 0(SAVE0) + |.else + | lwz CARG4, 4(SAVE0) + |.endif + | bge cr1, ->fff_resn + | checknum CARG3; bge >7 + |6: + | addi SAVE0, SAVE0, 8 + |.if FPU + | fsub f0, FARG1, FARG2 + |.if ismax + | fsel FARG1, f0, FARG1, FARG2 + |.else + | fsel FARG1, f0, FARG2, FARG1 + |.endif + |.else + | stw CARG1, SFSAVE_1 + | stw CARG2, SFSAVE_2 + | stw CARG3, SFSAVE_3 + | stw CARG4, SFSAVE_4 + | blex __ledf2 + | cmpwi CRET1, 0 + |.if ismax + | blt >8 + |.else + | bge >8 + |.endif + | lwz CARG1, SFSAVE_1 + | lwz CARG2, SFSAVE_2 + | b <5 + |8: + | lwz CARG1, SFSAVE_3 + | lwz CARG2, SFSAVE_4 + |.endif + | b <5 + |7: // Convert integer to number and continue above. + | lwz CARG3, 4(SAVE0) + | bne ->fff_fallback + |.if FPU + | tonum_i FARG2, CARG3 + |.else + | bl ->vm_sfi2d_2 + |.endif + | b <6 + |.else + | .ffunc_n name + | li TMP1, 8 + |1: + | lwzx CARG2, BASE, TMP1 + | lfdx FARG2, BASE, TMP1 + | cmplw cr1, TMP1, NARGS8:RC + | checknum CARG2 + | bge cr1, ->fff_resn + | bge ->fff_fallback + | fsub f0, FARG1, FARG2 + | addi TMP1, TMP1, 8 + |.if ismax + | fsel FARG1, f0, FARG1, FARG2 + |.else + | fsel FARG1, f0, FARG2, FARG1 + |.endif + | b <1 + |.endif + |.endmacro + | + | math_minmax math_min, 0 + | math_minmax math_max, 1 + | + |//-- String library ----------------------------------------------------- + | + |.ffunc string_byte // Only handle the 1-arg case here. + | cmplwi NARGS8:RC, 8 + | lwz CARG3, 0(BASE) + | lwz STR:CARG1, 4(BASE) + | bne ->fff_fallback // Need exactly 1 argument. + | checkstr CARG3 + | bne ->fff_fallback + | lwz TMP0, STR:CARG1->len + |.if DUALNUM + | lbz CARG1, STR:CARG1[1] // Access is always ok (NUL at end). + | li RD, (0+1)*8 + | lwz PC, FRAME_PC(BASE) + | cmplwi TMP0, 0 + | la RA, -8(BASE) + | beqy ->fff_res + | b ->fff_resi + |.else + | lbz TMP1, STR:CARG1[1] // Access is always ok (NUL at end). + | addic TMP3, TMP0, -1 // RD = ((str->len != 0)+1)*8 + | subfe RD, TMP3, TMP0 + | stw TMP1, TONUM_LO // Inlined tonum_u f0, TMP1. + | addi RD, RD, 1 + | lfd f0, TONUM_D + | la RA, -8(BASE) + | lwz PC, FRAME_PC(BASE) + | fsub f0, f0, TOBIT + | slwi RD, RD, 3 + | stfd f0, 0(RA) + | b ->fff_res + |.endif + | + |.ffunc string_char // Only handle the 1-arg case here. + | ffgccheck + | cmplwi NARGS8:RC, 8 + | lwz CARG3, 0(BASE) + |.if DUALNUM + | lwz TMP0, 4(BASE) + | bne ->fff_fallback // Exactly 1 argument. + | checknum CARG3; bne ->fff_fallback + | la CARG2, 7(BASE) + |.else + | lfd FARG1, 0(BASE) + | bne ->fff_fallback // Exactly 1 argument. + | checknum CARG3; bge ->fff_fallback + | toint TMP0, FARG1 + | la CARG2, TMPD_BLO + |.endif + | li CARG3, 1 + | cmplwi TMP0, 255; bgt ->fff_fallback + |->fff_newstr: + | mr CARG1, L + | stp BASE, L->base + | stw PC, SAVE_PC + | bl extern lj_str_new // (lua_State *L, char *str, size_t l) + |->fff_resstr: + | // Returns GCstr *. + | lp BASE, L->base + | li CARG3, LJ_TSTR + | b ->fff_restv + | + |.ffunc string_sub + | ffgccheck + | cmplwi NARGS8:RC, 16 + | lwz CARG3, 16(BASE) + |.if not DUALNUM + | lfd f0, 16(BASE) + |.endif + | lwz TMP0, 0(BASE) + | lwz STR:CARG1, 4(BASE) + | blt ->fff_fallback + | lwz CARG2, 8(BASE) + |.if DUALNUM + | lwz TMP1, 12(BASE) + |.else + | lfd f1, 8(BASE) + |.endif + | li TMP2, -1 + | beq >1 + |.if DUALNUM + | checknum CARG3 + | lwz TMP2, 20(BASE) + | bne ->fff_fallback + |1: + | checknum CARG2; bne ->fff_fallback + |.else + | checknum CARG3; bge ->fff_fallback + | toint TMP2, f0 + |1: + | checknum CARG2; bge ->fff_fallback + |.endif + | checkstr TMP0; bne ->fff_fallback + |.if not DUALNUM + | toint TMP1, f1 + |.endif + | lwz TMP0, STR:CARG1->len + | cmplw TMP0, TMP2 // len < end? (unsigned compare) + | addi TMP3, TMP2, 1 + | blt >5 + |2: + | cmpwi TMP1, 0 // start <= 0? + | add TMP3, TMP1, TMP0 + | ble >7 + |3: + | sub CARG3, TMP2, TMP1 + | addi CARG2, STR:CARG1, #STR-1 + | srawi TMP0, CARG3, 31 + | addi CARG3, CARG3, 1 + | add CARG2, CARG2, TMP1 + | andc CARG3, CARG3, TMP0 + |.if GPR64 + | rldicl CARG2, CARG2, 0, 32 + | rldicl CARG3, CARG3, 0, 32 + |.endif + | b ->fff_newstr + | + |5: // Negative end or overflow. + | cmpw TMP0, TMP2 // len >= end? (signed compare) + | add TMP2, TMP0, TMP3 // Negative end: end = end+len+1. + | bge <2 + | mr TMP2, TMP0 // Overflow: end = len. + | b <2 + | + |7: // Negative start or underflow. + | .gpr64 extsw TMP1, TMP1 + | addic CARG3, TMP1, -1 + | subfe CARG3, CARG3, CARG3 + | srawi CARG2, TMP3, 31 // Note: modifies carry. + | andc TMP3, TMP3, CARG3 + | andc TMP1, TMP3, CARG2 + | addi TMP1, TMP1, 1 // start = 1 + (start ? start+len : 0) + | b <3 + | + |.macro ffstring_op, name + | .ffunc string_ .. name + | ffgccheck + | cmplwi NARGS8:RC, 8 + | lwz CARG3, 0(BASE) + | lwz STR:CARG2, 4(BASE) + | blt ->fff_fallback + | checkstr CARG3 + | la SBUF:CARG1, DISPATCH_GL(tmpbuf)(DISPATCH) + | bne ->fff_fallback + | lwz TMP0, SBUF:CARG1->b + | stw L, SBUF:CARG1->L + | stp BASE, L->base + | stw PC, SAVE_PC + | stw TMP0, SBUF:CARG1->p + | bl extern lj_buf_putstr_ .. name + | bl extern lj_buf_tostr + | b ->fff_resstr + |.endmacro + | + |ffstring_op reverse + |ffstring_op lower + |ffstring_op upper + | + |//-- Bit library -------------------------------------------------------- + | + |.macro .ffunc_bit, name + |.if DUALNUM + | .ffunc_1 bit_..name + | checknum CARG3; bnel ->fff_tobit_fb + |.else + | .ffunc_n bit_..name + | fadd FARG1, FARG1, TOBIT + | stfd FARG1, TMPD + | lwz CARG1, TMPD_LO + |.endif + |.endmacro + | + |.macro .ffunc_bit_op, name, ins + | .ffunc_bit name + | addi SAVE0, BASE, 8 + | add SAVE1, BASE, NARGS8:RC + |1: + | lwz CARG4, 0(SAVE0) + | cmplw cr1, SAVE0, SAVE1 + |.if DUALNUM + | lwz CARG2, 4(SAVE0) + |.else + | lfd FARG1, 0(SAVE0) + |.endif + | bgey cr1, ->fff_resi + | checknum CARG4 + |.if DUALNUM + |.if FPU + | bnel ->fff_bitop_fb + |.else + | beq >3 + | stw CARG1, SFSAVE_1 + | bl ->fff_bitop_fb + | mr CARG2, CARG1 + | lwz CARG1, SFSAVE_1 + |3: + |.endif + |.else + | fadd FARG1, FARG1, TOBIT + | bge ->fff_fallback + | stfd FARG1, TMPD + | lwz CARG2, TMPD_LO + |.endif + | ins CARG1, CARG1, CARG2 + | addi SAVE0, SAVE0, 8 + | b <1 + |.endmacro + | + |.ffunc_bit_op band, and + |.ffunc_bit_op bor, or + |.ffunc_bit_op bxor, xor + | + |.ffunc_bit bswap + | rotlwi TMP0, CARG1, 8 + | rlwimi TMP0, CARG1, 24, 0, 7 + | rlwimi TMP0, CARG1, 24, 16, 23 + | mr CRET1, TMP0 + | b ->fff_resi + | + |.ffunc_bit bnot + | not CRET1, CARG1 + | b ->fff_resi + | + |.macro .ffunc_bit_sh, name, ins, shmod + |.if DUALNUM + | .ffunc_2 bit_..name + |.if FPU + | checknum CARG3; bnel ->fff_tobit_fb + |.else + | checknum CARG3; beq >1 + | bl ->fff_tobit_fb + | lwz CARG2, 12(BASE) // Conversion polluted CARG2. + |1: + |.endif + | // Note: no inline conversion from number for 2nd argument! + | checknum CARG4; bne ->fff_fallback + |.else + | .ffunc_nn bit_..name + | fadd FARG1, FARG1, TOBIT + | fadd FARG2, FARG2, TOBIT + | stfd FARG1, TMPD + | lwz CARG1, TMPD_LO + | stfd FARG2, TMPD + | lwz CARG2, TMPD_LO + |.endif + |.if shmod == 1 + | rlwinm CARG2, CARG2, 0, 27, 31 + |.elif shmod == 2 + | neg CARG2, CARG2 + |.endif + | ins CRET1, CARG1, CARG2 + | b ->fff_resi + |.endmacro + | + |.ffunc_bit_sh lshift, slw, 1 + |.ffunc_bit_sh rshift, srw, 1 + |.ffunc_bit_sh arshift, sraw, 1 + |.ffunc_bit_sh rol, rotlw, 0 + |.ffunc_bit_sh ror, rotlw, 2 + | + |.ffunc_bit tobit + |.if DUALNUM + | b ->fff_resi + |.else + |->fff_resi: + | tonum_i FARG1, CRET1 + |.endif + |->fff_resn: + | lwz PC, FRAME_PC(BASE) + | la RA, -8(BASE) + |.if FPU + | stfd FARG1, -8(BASE) + |.else + | stw CARG1, -8(BASE) + | stw CARG2, -4(BASE) + |.endif + | b ->fff_res1 + | + |// Fallback FP number to bit conversion. + |->fff_tobit_fb: + |.if DUALNUM + |.if FPU + | lfd FARG1, 0(BASE) + | bgt ->fff_fallback + | fadd FARG1, FARG1, TOBIT + | stfd FARG1, TMPD + | lwz CARG1, TMPD_LO + | blr + |.else + | bgt ->fff_fallback + | mr CARG2, CARG1 + | mr CARG1, CARG3 + |// Modifies: CARG1, CARG2, TMP0, TMP1, TMP2. + |->vm_tobit: + | slwi TMP2, CARG1, 1 + | addis TMP2, TMP2, 0x0020 + | cmpwi TMP2, 0 + | bge >2 + | li TMP1, 0x3e0 + | srawi TMP2, TMP2, 21 + | not TMP1, TMP1 + | sub. TMP2, TMP1, TMP2 + | cmpwi cr7, CARG1, 0 + | blt >1 + | slwi TMP1, CARG1, 11 + | srwi TMP0, CARG2, 21 + | oris TMP1, TMP1, 0x8000 + | or TMP1, TMP1, TMP0 + | srw CARG1, TMP1, TMP2 + | bclr 4, 28 // Return if cr7[lt] == 0, no hint. + | neg CARG1, CARG1 + | blr + |1: + | addi TMP2, TMP2, 21 + | srw TMP1, CARG2, TMP2 + | slwi CARG2, CARG1, 12 + | subfic TMP2, TMP2, 20 + | slw TMP0, CARG2, TMP2 + | or CARG1, TMP1, TMP0 + | bclr 4, 28 // Return if cr7[lt] == 0, no hint. + | neg CARG1, CARG1 + | blr + |2: + | li CARG1, 0 + | blr + |.endif + |.endif + |->fff_bitop_fb: + |.if DUALNUM + |.if FPU + | lfd FARG1, 0(SAVE0) + | bgt ->fff_fallback + | fadd FARG1, FARG1, TOBIT + | stfd FARG1, TMPD + | lwz CARG2, TMPD_LO + | blr + |.else + | bgt ->fff_fallback + | mr CARG1, CARG4 + | b ->vm_tobit + |.endif + |.endif + | + |//----------------------------------------------------------------------- + | + |->fff_fallback: // Call fast function fallback handler. + | // BASE = new base, RB = CFUNC, RC = nargs*8 + | lp TMP3, CFUNC:RB->f + | add TMP1, BASE, NARGS8:RC + | lwz PC, FRAME_PC(BASE) // Fallback may overwrite PC. + | addi TMP0, TMP1, 8*LUA_MINSTACK + | lwz TMP2, L->maxstack + | stw PC, SAVE_PC // Redundant (but a defined value). + | .toc lp TMP3, 0(TMP3) + | cmplw TMP0, TMP2 + | stp BASE, L->base + | stp TMP1, L->top + | mr CARG1, L + | bgt >5 // Need to grow stack. + | mtctr TMP3 + | bctrl // (lua_State *L) + | // Either throws an error, or recovers and returns -1, 0 or nresults+1. + | lp BASE, L->base + | cmpwi CRET1, 0 + | slwi RD, CRET1, 3 + | la RA, -8(BASE) + | bgt ->fff_res // Returned nresults+1? + |1: // Returned 0 or -1: retry fast path. + | lp TMP0, L->top + | lwz LFUNC:RB, FRAME_FUNC(BASE) + | sub NARGS8:RC, TMP0, BASE + | bne ->vm_call_tail // Returned -1? + | ins_callt // Returned 0: retry fast path. + | + |// Reconstruct previous base for vmeta_call during tailcall. + |->vm_call_tail: + | andix. TMP0, PC, FRAME_TYPE + | rlwinm TMP1, PC, 0, 0, 28 + | bne >3 + | lwz INS, -4(PC) + | decode_RA8 TMP1, INS + | addi TMP1, TMP1, 8 + |3: + | sub TMP2, BASE, TMP1 + | b ->vm_call_dispatch // Resolve again for tailcall. + | + |5: // Grow stack for fallback handler. + | li CARG2, LUA_MINSTACK + | bl extern lj_state_growstack // (lua_State *L, int n) + | lp BASE, L->base + | cmpw TMP0, TMP0 // Set 4*cr0+eq to force retry. + | b <1 + | + |->fff_gcstep: // Call GC step function. + | // BASE = new base, RC = nargs*8 + | mflr SAVE0 + | stp BASE, L->base + | add TMP0, BASE, NARGS8:RC + | stw PC, SAVE_PC // Redundant (but a defined value). + | stp TMP0, L->top + | mr CARG1, L + | bl extern lj_gc_step // (lua_State *L) + | lp BASE, L->base + | mtlr SAVE0 + | lp TMP0, L->top + | sub NARGS8:RC, TMP0, BASE + | lwz CFUNC:RB, FRAME_FUNC(BASE) + | blr + | + |//----------------------------------------------------------------------- + |//-- Special dispatch targets ------------------------------------------- + |//----------------------------------------------------------------------- + | + |->vm_record: // Dispatch target for recording phase. + |.if JIT + | lbz TMP3, DISPATCH_GL(hookmask)(DISPATCH) + | andix. TMP0, TMP3, HOOK_VMEVENT // No recording while in vmevent. + | bne >5 + | // Decrement the hookcount for consistency, but always do the call. + | lwz TMP2, DISPATCH_GL(hookcount)(DISPATCH) + | andix. TMP0, TMP3, HOOK_ACTIVE + | bne >1 + | subi TMP2, TMP2, 1 + | andi. TMP0, TMP3, LUA_MASKLINE|LUA_MASKCOUNT + | beqy >1 + | stw TMP2, DISPATCH_GL(hookcount)(DISPATCH) + | b >1 + |.endif + | + |->vm_rethook: // Dispatch target for return hooks. + | lbz TMP3, DISPATCH_GL(hookmask)(DISPATCH) + | andix. TMP0, TMP3, HOOK_ACTIVE // Hook already active? + | beq >1 + |5: // Re-dispatch to static ins. + | addi TMP1, TMP1, GG_DISP2STATIC // Assumes decode_OPP TMP1, INS. + | lpx TMP0, DISPATCH, TMP1 + | mtctr TMP0 + | bctr + | + |->vm_inshook: // Dispatch target for instr/line hooks. + | lbz TMP3, DISPATCH_GL(hookmask)(DISPATCH) + | lwz TMP2, DISPATCH_GL(hookcount)(DISPATCH) + | andix. TMP0, TMP3, HOOK_ACTIVE // Hook already active? + | rlwinm TMP0, TMP3, 31-LUA_HOOKLINE, 31, 0 + | bne <5 + | + | cmpwi cr1, TMP0, 0 + | addic. TMP2, TMP2, -1 + | beq cr1, <5 + | stw TMP2, DISPATCH_GL(hookcount)(DISPATCH) + | beq >1 + | bge cr1, <5 + |1: + | mr CARG1, L + | stw MULTRES, SAVE_MULTRES + | mr CARG2, PC + | stp BASE, L->base + | // SAVE_PC must hold the _previous_ PC. The callee updates it with PC. + | bl extern lj_dispatch_ins // (lua_State *L, const BCIns *pc) + |3: + | lp BASE, L->base + |4: // Re-dispatch to static ins. + | lwz INS, -4(PC) + | decode_OPP TMP1, INS + | decode_RB8 RB, INS + | addi TMP1, TMP1, GG_DISP2STATIC + | decode_RD8 RD, INS + | lpx TMP0, DISPATCH, TMP1 + | decode_RA8 RA, INS + | decode_RC8 RC, INS + | mtctr TMP0 + | bctr + | + |->cont_hook: // Continue from hook yield. + | addi PC, PC, 4 + | lwz MULTRES, -20(RB) // Restore MULTRES for *M ins. + | b <4 + | + |->vm_hotloop: // Hot loop counter underflow. + |.if JIT + | lwz LFUNC:TMP1, FRAME_FUNC(BASE) + | addi CARG1, DISPATCH, GG_DISP2J + | stw PC, SAVE_PC + | lwz TMP1, LFUNC:TMP1->pc + | mr CARG2, PC + | stw L, DISPATCH_J(L)(DISPATCH) + | lbz TMP1, PC2PROTO(framesize)(TMP1) + | stp BASE, L->base + | slwi TMP1, TMP1, 3 + | add TMP1, BASE, TMP1 + | stp TMP1, L->top + | bl extern lj_trace_hot // (jit_State *J, const BCIns *pc) + | b <3 + |.endif + | + |->vm_callhook: // Dispatch target for call hooks. + | mr CARG2, PC + |.if JIT + | b >1 + |.endif + | + |->vm_hotcall: // Hot call counter underflow. + |.if JIT + | ori CARG2, PC, 1 + |1: + |.endif + | add TMP0, BASE, RC + | stw PC, SAVE_PC + | mr CARG1, L + | stp BASE, L->base + | sub RA, RA, BASE + | stp TMP0, L->top + | bl extern lj_dispatch_call // (lua_State *L, const BCIns *pc) + | // Returns ASMFunction. + | lp BASE, L->base + | lp TMP0, L->top + | stw ZERO, SAVE_PC // Invalidate for subsequent line hook. + | sub NARGS8:RC, TMP0, BASE + | add RA, BASE, RA + | lwz LFUNC:RB, FRAME_FUNC(BASE) + | lwz INS, -4(PC) + | mtctr CRET1 + | bctr + | + |->cont_stitch: // Trace stitching. + |.if JIT + | // RA = resultptr, RB = meta base + | lwz INS, -4(PC) + | lwz TRACE:TMP2, -20(RB) // Save previous trace. + | addic. TMP1, MULTRES, -8 + | decode_RA8 RC, INS // Call base. + | beq >2 + |1: // Move results down. + |.if FPU + | lfd f0, 0(RA) + |.else + | lwz CARG1, 0(RA) + | lwz CARG2, 4(RA) + |.endif + | addic. TMP1, TMP1, -8 + | addi RA, RA, 8 + |.if FPU + | stfdx f0, BASE, RC + |.else + | add CARG3, BASE, RC + | stw CARG1, 0(CARG3) + | stw CARG2, 4(CARG3) + |.endif + | addi RC, RC, 8 + | bne <1 + |2: + | decode_RA8 RA, INS + | decode_RB8 RB, INS + | add RA, RA, RB + |3: + | cmplw RA, RC + | bgt >9 // More results wanted? + | + | lhz TMP3, TRACE:TMP2->traceno + | lhz RD, TRACE:TMP2->link + | cmpw RD, TMP3 + | cmpwi cr1, RD, 0 + | beq ->cont_nop // Blacklisted. + | slwi RD, RD, 3 + | bne cr1, =>BC_JLOOP // Jump to stitched trace. + | + | // Stitch a new trace to the previous trace. + | stw TMP3, DISPATCH_J(exitno)(DISPATCH) + | stp L, DISPATCH_J(L)(DISPATCH) + | stp BASE, L->base + | addi CARG1, DISPATCH, GG_DISP2J + | mr CARG2, PC + | bl extern lj_dispatch_stitch // (jit_State *J, const BCIns *pc) + | lp BASE, L->base + | b ->cont_nop + | + |9: + | stwx TISNIL, BASE, RC + | addi RC, RC, 8 + | b <3 + |.endif + | + |->vm_profhook: // Dispatch target for profiler hook. +#if LJ_HASPROFILE + | mr CARG1, L + | stw MULTRES, SAVE_MULTRES + | mr CARG2, PC + | stp BASE, L->base + | bl extern lj_dispatch_profile // (lua_State *L, const BCIns *pc) + | // HOOK_PROFILE is off again, so re-dispatch to dynamic instruction. + | lp BASE, L->base + | subi PC, PC, 4 + | b ->cont_nop +#endif + | + |//----------------------------------------------------------------------- + |//-- Trace exit handler ------------------------------------------------- + |//----------------------------------------------------------------------- + | + |.macro savex_, a, b, c, d + |.if FPU + | stfd f..a, 16+a*8(sp) + | stfd f..b, 16+b*8(sp) + | stfd f..c, 16+c*8(sp) + | stfd f..d, 16+d*8(sp) + |.endif + |.endmacro + | + |->vm_exit_handler: + |.if JIT + | addi sp, sp, -(16+32*8+32*4) + | stmw r2, 16+32*8+2*4(sp) + | addi DISPATCH, JGL, -GG_DISP2G-32768 + | li CARG2, ~LJ_VMST_EXIT + | lwz CARG1, 16+32*8+32*4(sp) // Get stack chain. + | stw CARG2, DISPATCH_GL(vmstate)(DISPATCH) + | savex_ 0,1,2,3 + | stw CARG1, 0(sp) // Store extended stack chain. + | clrso TMP1 + | savex_ 4,5,6,7 + | addi CARG2, sp, 16+32*8+32*4 // Recompute original value of sp. + | savex_ 8,9,10,11 + | stw CARG2, 16+32*8+1*4(sp) // Store sp in RID_SP. + | savex_ 12,13,14,15 + | mflr CARG3 + | li TMP1, 0 + | savex_ 16,17,18,19 + | stw TMP1, 16+32*8+0*4(sp) // Clear RID_TMP. + | savex_ 20,21,22,23 + | lhz CARG4, 2(CARG3) // Load trace number. + | savex_ 24,25,26,27 + | lwz L, DISPATCH_GL(cur_L)(DISPATCH) + | savex_ 28,29,30,31 + | sub CARG3, TMP0, CARG3 // Compute exit number. + | lp BASE, DISPATCH_GL(jit_base)(DISPATCH) + | srwi CARG3, CARG3, 2 + | stp L, DISPATCH_J(L)(DISPATCH) + | subi CARG3, CARG3, 2 + | stp BASE, L->base + | stw CARG4, DISPATCH_J(parent)(DISPATCH) + | stw TMP1, DISPATCH_GL(jit_base)(DISPATCH) + | addi CARG1, DISPATCH, GG_DISP2J + | stw CARG3, DISPATCH_J(exitno)(DISPATCH) + | addi CARG2, sp, 16 + | bl extern lj_trace_exit // (jit_State *J, ExitState *ex) + | // Returns MULTRES (unscaled) or negated error code. + | lp TMP1, L->cframe + | lwz TMP2, 0(sp) + | lp BASE, L->base + |.if GPR64 + | rldicr sp, TMP1, 0, 61 + |.else + | rlwinm sp, TMP1, 0, 0, 29 + |.endif + | lwz PC, SAVE_PC // Get SAVE_PC. + | stw TMP2, 0(sp) + | stw L, SAVE_L // Set SAVE_L (on-trace resume/yield). + | b >1 + |.endif + |->vm_exit_interp: + |.if JIT + | // CARG1 = MULTRES or negated error code, BASE, PC and JGL set. + | lwz L, SAVE_L + | addi DISPATCH, JGL, -GG_DISP2G-32768 + | stp BASE, L->base + |1: + | cmpwi CARG1, 0 + | blt >9 // Check for error from exit. + | lwz LFUNC:RB, FRAME_FUNC(BASE) + | slwi MULTRES, CARG1, 3 + | li TMP2, 0 + | stw MULTRES, SAVE_MULTRES + | lwz TMP1, LFUNC:RB->pc + | stw TMP2, DISPATCH_GL(jit_base)(DISPATCH) + | lwz KBASE, PC2PROTO(k)(TMP1) + | // Setup type comparison constants. + | li TISNUM, LJ_TISNUM + | .FPU lus TMP3, 0x59c0 // TOBIT = 2^52 + 2^51 (float). + | .FPU stw TMP3, TMPD + | li ZERO, 0 + | .FPU ori TMP3, TMP3, 0x0004 // TONUM = 2^52 + 2^51 + 2^31 (float). + | .FPU lfs TOBIT, TMPD + | .FPU stw TMP3, TMPD + | .FPU lus TMP0, 0x4338 // Hiword of 2^52 + 2^51 (double) + | li TISNIL, LJ_TNIL + | .FPU stw TMP0, TONUM_HI + | .FPU lfs TONUM, TMPD + | // Modified copy of ins_next which handles function header dispatch, too. + | lwz INS, 0(PC) + | addi PC, PC, 4 + | // Assumes TISNIL == ~LJ_VMST_INTERP == -1. + | stw TISNIL, DISPATCH_GL(vmstate)(DISPATCH) + | decode_OPP TMP1, INS + | decode_RA8 RA, INS + | lpx TMP0, DISPATCH, TMP1 + | mtctr TMP0 + | cmplwi TMP1, BC_FUNCF*4 // Function header? + | bge >2 + | decode_RB8 RB, INS + | decode_RD8 RD, INS + | decode_RC8 RC, INS + | bctr + |2: + | cmplwi TMP1, (BC_FUNCC+2)*4 // Fast function? + | blt >3 + | // Check frame below fast function. + | lwz TMP1, FRAME_PC(BASE) + | andix. TMP0, TMP1, FRAME_TYPE + | bney >3 // Trace stitching continuation? + | // Otherwise set KBASE for Lua function below fast function. + | lwz TMP2, -4(TMP1) + | decode_RA8 TMP0, TMP2 + | sub TMP1, BASE, TMP0 + | lwz LFUNC:TMP2, -12(TMP1) + | lwz TMP1, LFUNC:TMP2->pc + | lwz KBASE, PC2PROTO(k)(TMP1) + |3: + | subi RC, MULTRES, 8 + | add RA, RA, BASE + | bctr + | + |9: // Rethrow error from the right C frame. + | neg CARG2, CARG1 + | mr CARG1, L + | bl extern lj_err_throw // (lua_State *L, int errcode) + |.endif + | + |//----------------------------------------------------------------------- + |//-- Math helper functions ---------------------------------------------- + |//----------------------------------------------------------------------- + | + |// NYI: Use internal implementations of floor, ceil, trunc, sfcmp. + | + |.macro sfi2d, AHI, ALO + |.if not FPU + | mr. AHI, ALO + | bclr 12, 2 // Handle zero first. + | srawi TMP0, ALO, 31 + | xor TMP1, ALO, TMP0 + | sub TMP1, TMP1, TMP0 // Absolute value in TMP1. + | cntlzw AHI, TMP1 + | andix. TMP0, TMP0, 0x800 // Mask sign bit. + | slw TMP1, TMP1, AHI // Align mantissa left with leading 1. + | subfic AHI, AHI, 0x3ff+31-1 // Exponent -1 in AHI. + | slwi ALO, TMP1, 21 + | or AHI, AHI, TMP0 // Sign | Exponent. + | srwi TMP1, TMP1, 11 + | slwi AHI, AHI, 20 // Align left. + | add AHI, AHI, TMP1 // Add mantissa, increment exponent. + | blr + |.endif + |.endmacro + | + |// Input: CARG2. Output: CARG1, CARG2. Temporaries: TMP0, TMP1. + |->vm_sfi2d_1: + | sfi2d CARG1, CARG2 + | + |// Input: CARG4. Output: CARG3, CARG4. Temporaries: TMP0, TMP1. + |->vm_sfi2d_2: + | sfi2d CARG3, CARG4 + | + |->vm_modi: + | divwo. TMP0, CARG1, CARG2 + | bso >1 + |.if GPR64 + | xor CARG3, CARG1, CARG2 + | cmpwi CARG3, 0 + |.else + | xor. CARG3, CARG1, CARG2 + |.endif + | mullw TMP0, TMP0, CARG2 + | sub CARG1, CARG1, TMP0 + | bgelr + | cmpwi CARG1, 0; beqlr + | add CARG1, CARG1, CARG2 + | blr + |1: + | cmpwi CARG2, 0 + | li CARG1, 0 + | beqlr + | clrso TMP0 // Clear SO for -2147483648 % -1 and return 0. + | blr + | + |//----------------------------------------------------------------------- + |//-- Miscellaneous functions -------------------------------------------- + |//----------------------------------------------------------------------- + | + |// void lj_vm_cachesync(void *start, void *end) + |// Flush D-Cache and invalidate I-Cache. Assumes 32 byte cache line size. + |// This is a good lower bound, except for very ancient PPC models. + |->vm_cachesync: + |.if JIT or FFI + | // Compute start of first cache line and number of cache lines. + | rlwinm CARG1, CARG1, 0, 0, 26 + | sub CARG2, CARG2, CARG1 + | addi CARG2, CARG2, 31 + | rlwinm. CARG2, CARG2, 27, 5, 31 + | beqlr + | mtctr CARG2 + | mr CARG3, CARG1 + |1: // Flush D-Cache. + | dcbst r0, CARG1 + | addi CARG1, CARG1, 32 + | bdnz <1 + | sync + | mtctr CARG2 + |1: // Invalidate I-Cache. + | icbi r0, CARG3 + | addi CARG3, CARG3, 32 + | bdnz <1 + | isync + | blr + |.endif + | + |//----------------------------------------------------------------------- + |//-- FFI helper functions ----------------------------------------------- + |//----------------------------------------------------------------------- + | + |// Handler for callback functions. Callback slot number in r11, g in r12. + |->vm_ffi_callback: + |.if FFI + |.type CTSTATE, CTState, PC + | saveregs + | lwz CTSTATE, GL:r12->ctype_state + | addi DISPATCH, r12, GG_G2DISP + | stw r11, CTSTATE->cb.slot + | stw r3, CTSTATE->cb.gpr[0] + | .FPU stfd f1, CTSTATE->cb.fpr[0] + | stw r4, CTSTATE->cb.gpr[1] + | .FPU stfd f2, CTSTATE->cb.fpr[1] + | stw r5, CTSTATE->cb.gpr[2] + | .FPU stfd f3, CTSTATE->cb.fpr[2] + | stw r6, CTSTATE->cb.gpr[3] + | .FPU stfd f4, CTSTATE->cb.fpr[3] + | stw r7, CTSTATE->cb.gpr[4] + | .FPU stfd f5, CTSTATE->cb.fpr[4] + | stw r8, CTSTATE->cb.gpr[5] + | .FPU stfd f6, CTSTATE->cb.fpr[5] + | stw r9, CTSTATE->cb.gpr[6] + | .FPU stfd f7, CTSTATE->cb.fpr[6] + | stw r10, CTSTATE->cb.gpr[7] + | .FPU stfd f8, CTSTATE->cb.fpr[7] + | addi TMP0, sp, CFRAME_SPACE+8 + | stw TMP0, CTSTATE->cb.stack + | mr CARG1, CTSTATE + | stw CTSTATE, SAVE_PC // Any value outside of bytecode is ok. + | mr CARG2, sp + | bl extern lj_ccallback_enter // (CTState *cts, void *cf) + | // Returns lua_State *. + | lp BASE, L:CRET1->base + | li TISNUM, LJ_TISNUM // Setup type comparison constants. + | lp RC, L:CRET1->top + | .FPU lus TMP3, 0x59c0 // TOBIT = 2^52 + 2^51 (float). + | li ZERO, 0 + | mr L, CRET1 + | .FPU stw TMP3, TMPD + | .FPU lus TMP0, 0x4338 // Hiword of 2^52 + 2^51 (double) + | lwz LFUNC:RB, FRAME_FUNC(BASE) + | .FPU ori TMP3, TMP3, 0x0004 // TONUM = 2^52 + 2^51 + 2^31 (float). + | .FPU stw TMP0, TONUM_HI + | li TISNIL, LJ_TNIL + | li_vmstate INTERP + | .FPU lfs TOBIT, TMPD + | .FPU stw TMP3, TMPD + | sub RC, RC, BASE + | st_vmstate + | .FPU lfs TONUM, TMPD + | ins_callt + |.endif + | + |->cont_ffi_callback: // Return from FFI callback. + |.if FFI + | lwz CTSTATE, DISPATCH_GL(ctype_state)(DISPATCH) + | stp BASE, L->base + | stp RB, L->top + | stp L, CTSTATE->L + | mr CARG1, CTSTATE + | mr CARG2, RA + | bl extern lj_ccallback_leave // (CTState *cts, TValue *o) + | lwz CRET1, CTSTATE->cb.gpr[0] + | .FPU lfd FARG1, CTSTATE->cb.fpr[0] + | lwz CRET2, CTSTATE->cb.gpr[1] + | b ->vm_leave_unw + |.endif + | + |->vm_ffi_call: // Call C function via FFI. + | // Caveat: needs special frame unwinding, see below. + |.if FFI + | .type CCSTATE, CCallState, CARG1 + | lwz TMP1, CCSTATE->spadj + | mflr TMP0 + | lbz CARG2, CCSTATE->nsp + | lbz CARG3, CCSTATE->nfpr + | neg TMP1, TMP1 + | stw TMP0, 4(sp) + | cmpwi cr1, CARG3, 0 + | mr TMP2, sp + | addic. CARG2, CARG2, -1 + | stwux sp, sp, TMP1 + | crnot 4*cr1+eq, 4*cr1+eq // For vararg calls. + | stw r14, -4(TMP2) + | stw CCSTATE, -8(TMP2) + | mr r14, TMP2 + | la TMP1, CCSTATE->stack + | slwi CARG2, CARG2, 2 + | blty >2 + | la TMP2, 8(sp) + |1: + | lwzx TMP0, TMP1, CARG2 + | stwx TMP0, TMP2, CARG2 + | addic. CARG2, CARG2, -4 + | bge <1 + |2: + | bney cr1, >3 + | .FPU lfd f1, CCSTATE->fpr[0] + | .FPU lfd f2, CCSTATE->fpr[1] + | .FPU lfd f3, CCSTATE->fpr[2] + | .FPU lfd f4, CCSTATE->fpr[3] + | .FPU lfd f5, CCSTATE->fpr[4] + | .FPU lfd f6, CCSTATE->fpr[5] + | .FPU lfd f7, CCSTATE->fpr[6] + | .FPU lfd f8, CCSTATE->fpr[7] + |3: + | lp TMP0, CCSTATE->func + | lwz CARG2, CCSTATE->gpr[1] + | lwz CARG3, CCSTATE->gpr[2] + | lwz CARG4, CCSTATE->gpr[3] + | lwz CARG5, CCSTATE->gpr[4] + | mtctr TMP0 + | lwz r8, CCSTATE->gpr[5] + | lwz r9, CCSTATE->gpr[6] + | lwz r10, CCSTATE->gpr[7] + | lwz CARG1, CCSTATE->gpr[0] // Do this last, since CCSTATE is CARG1. + | bctrl + | lwz CCSTATE:TMP1, -8(r14) + | lwz TMP2, -4(r14) + | lwz TMP0, 4(r14) + | stw CARG1, CCSTATE:TMP1->gpr[0] + | .FPU stfd FARG1, CCSTATE:TMP1->fpr[0] + | stw CARG2, CCSTATE:TMP1->gpr[1] + | mtlr TMP0 + | stw CARG3, CCSTATE:TMP1->gpr[2] + | mr sp, r14 + | stw CARG4, CCSTATE:TMP1->gpr[3] + | mr r14, TMP2 + | blr + |.endif + |// Note: vm_ffi_call must be the last function in this object file! + | + |//----------------------------------------------------------------------- +} + +/* Generate the code for a single instruction. */ +static void build_ins(BuildCtx *ctx, BCOp op, int defop) +{ + int vk = 0; + |=>defop: + + switch (op) { + + /* -- Comparison ops ---------------------------------------------------- */ + + /* Remember: all ops branch for a true comparison, fall through otherwise. */ + + case BC_ISLT: case BC_ISGE: case BC_ISLE: case BC_ISGT: + | // RA = src1*8, RD = src2*8, JMP with RD = target + |.if DUALNUM + | lwzux CARG1, RA, BASE + | addi PC, PC, 4 + | lwz CARG2, 4(RA) + | lwzux CARG3, RD, BASE + | lwz TMP2, -4(PC) + | checknum cr0, CARG1 + | lwz CARG4, 4(RD) + | decode_RD4 TMP2, TMP2 + | checknum cr1, CARG3 + | addis SAVE0, TMP2, -(BCBIAS_J*4 >> 16) + | bne cr0, >7 + | bne cr1, >8 + | cmpw CARG2, CARG4 + if (op == BC_ISLT) { + | bge >2 + } else if (op == BC_ISGE) { + | blt >2 + } else if (op == BC_ISLE) { + | bgt >2 + } else { + | ble >2 + } + |1: + | add PC, PC, SAVE0 + |2: + | ins_next + | + |7: // RA is not an integer. + | bgt cr0, ->vmeta_comp + | // RA is a number. + | .FPU lfd f0, 0(RA) + | bgt cr1, ->vmeta_comp + | blt cr1, >4 + | // RA is a number, RD is an integer. + |.if FPU + | tonum_i f1, CARG4 + |.else + | bl ->vm_sfi2d_2 + |.endif + | b >5 + | + |8: // RA is an integer, RD is not an integer. + | bgt cr1, ->vmeta_comp + | // RA is an integer, RD is a number. + |.if FPU + | tonum_i f0, CARG2 + |.else + | bl ->vm_sfi2d_1 + |.endif + |4: + | .FPU lfd f1, 0(RD) + |5: + |.if FPU + | fcmpu cr0, f0, f1 + |.else + | blex __ledf2 + | cmpwi CRET1, 0 + |.endif + if (op == BC_ISLT) { + | bge <2 + } else if (op == BC_ISGE) { + | blt <2 + } else if (op == BC_ISLE) { + | cror 4*cr0+lt, 4*cr0+lt, 4*cr0+eq + | bge <2 + } else { + | cror 4*cr0+lt, 4*cr0+lt, 4*cr0+eq + | blt <2 + } + | b <1 + |.else + | lwzx TMP0, BASE, RA + | addi PC, PC, 4 + | lfdx f0, BASE, RA + | lwzx TMP1, BASE, RD + | checknum cr0, TMP0 + | lwz TMP2, -4(PC) + | lfdx f1, BASE, RD + | checknum cr1, TMP1 + | decode_RD4 TMP2, TMP2 + | bge cr0, ->vmeta_comp + | addis TMP2, TMP2, -(BCBIAS_J*4 >> 16) + | bge cr1, ->vmeta_comp + | fcmpu cr0, f0, f1 + if (op == BC_ISLT) { + | bge >1 + } else if (op == BC_ISGE) { + | blt >1 + } else if (op == BC_ISLE) { + | cror 4*cr0+lt, 4*cr0+lt, 4*cr0+eq + | bge >1 + } else { + | cror 4*cr0+lt, 4*cr0+lt, 4*cr0+eq + | blt >1 + } + | add PC, PC, TMP2 + |1: + | ins_next + |.endif + break; + + case BC_ISEQV: case BC_ISNEV: + vk = op == BC_ISEQV; + | // RA = src1*8, RD = src2*8, JMP with RD = target + |.if DUALNUM + | lwzux CARG1, RA, BASE + | addi PC, PC, 4 + | lwz CARG2, 4(RA) + | lwzux CARG3, RD, BASE + | checknum cr0, CARG1 + | lwz SAVE0, -4(PC) + | checknum cr1, CARG3 + | decode_RD4 SAVE0, SAVE0 + | lwz CARG4, 4(RD) + | cror 4*cr7+gt, 4*cr0+gt, 4*cr1+gt + | addis SAVE0, SAVE0, -(BCBIAS_J*4 >> 16) + if (vk) { + | ble cr7, ->BC_ISEQN_Z + } else { + | ble cr7, ->BC_ISNEN_Z + } + |.else + | lwzux CARG1, RA, BASE + | lwz SAVE0, 0(PC) + | lfd f0, 0(RA) + | addi PC, PC, 4 + | lwzux CARG3, RD, BASE + | checknum cr0, CARG1 + | decode_RD4 SAVE0, SAVE0 + | lfd f1, 0(RD) + | checknum cr1, CARG3 + | addis SAVE0, SAVE0, -(BCBIAS_J*4 >> 16) + | bge cr0, >5 + | bge cr1, >5 + | fcmpu cr0, f0, f1 + if (vk) { + | bne >1 + | add PC, PC, SAVE0 + } else { + | beq >1 + | add PC, PC, SAVE0 + } + |1: + | ins_next + |.endif + |5: // Either or both types are not numbers. + |.if not DUALNUM + | lwz CARG2, 4(RA) + | lwz CARG4, 4(RD) + |.endif + |.if FFI + | cmpwi cr7, CARG1, LJ_TCDATA + | cmpwi cr5, CARG3, LJ_TCDATA + |.endif + | not TMP2, CARG1 + | cmplw CARG1, CARG3 + | cmplwi cr1, TMP2, ~LJ_TISPRI // Primitive? + |.if FFI + | cror 4*cr7+eq, 4*cr7+eq, 4*cr5+eq + |.endif + | cmplwi cr6, TMP2, ~LJ_TISTABUD // Table or userdata? + |.if FFI + | beq cr7, ->vmeta_equal_cd + |.endif + | cmplw cr5, CARG2, CARG4 + | crandc 4*cr0+gt, 4*cr0+eq, 4*cr1+gt // 2: Same type and primitive. + | crorc 4*cr0+lt, 4*cr5+eq, 4*cr0+eq // 1: Same tv or different type. + | crand 4*cr0+eq, 4*cr0+eq, 4*cr5+eq // 0: Same type and same tv. + | mr SAVE1, PC + | cror 4*cr0+eq, 4*cr0+eq, 4*cr0+gt // 0 or 2. + | cror 4*cr0+lt, 4*cr0+lt, 4*cr0+gt // 1 or 2. + if (vk) { + | bne cr0, >6 + | add PC, PC, SAVE0 + |6: + } else { + | beq cr0, >6 + | add PC, PC, SAVE0 + |6: + } + |.if DUALNUM + | bge cr0, >2 // Done if 1 or 2. + |1: + | ins_next + |2: + |.else + | blt cr0, <1 // Done if 1 or 2. + |.endif + | blt cr6, <1 // Done if not tab/ud. + | + | // Different tables or userdatas. Need to check __eq metamethod. + | // Field metatable must be at same offset for GCtab and GCudata! + | mr CARG3, CARG4 + | lwz TAB:TMP2, TAB:CARG2->metatable + | li CARG4, 1-vk // ne = 0 or 1. + | cmplwi TAB:TMP2, 0 + | beq <1 // No metatable? + | lbz TMP2, TAB:TMP2->nomm + | andix. TMP2, TMP2, 1<vmeta_equal // Handle __eq metamethod. + break; + + case BC_ISEQS: case BC_ISNES: + vk = op == BC_ISEQS; + | // RA = src*8, RD = str_const*8 (~), JMP with RD = target + | lwzux TMP0, RA, BASE + | srwi RD, RD, 1 + | lwz STR:TMP3, 4(RA) + | lwz TMP2, 0(PC) + | subfic RD, RD, -4 + | addi PC, PC, 4 + |.if FFI + | cmpwi TMP0, LJ_TCDATA + |.endif + | lwzx STR:TMP1, KBASE, RD // KBASE-4-str_const*4 + | .gpr64 extsw TMP0, TMP0 + | subfic TMP0, TMP0, LJ_TSTR + |.if FFI + | beq ->vmeta_equal_cd + |.endif + | sub TMP1, STR:TMP1, STR:TMP3 + | or TMP0, TMP0, TMP1 + | decode_RD4 TMP2, TMP2 + | subfic TMP0, TMP0, 0 + | addis TMP2, TMP2, -(BCBIAS_J*4 >> 16) + | subfe TMP1, TMP1, TMP1 + if (vk) { + | andc TMP2, TMP2, TMP1 + } else { + | and TMP2, TMP2, TMP1 + } + | add PC, PC, TMP2 + | ins_next + break; + + case BC_ISEQN: case BC_ISNEN: + vk = op == BC_ISEQN; + | // RA = src*8, RD = num_const*8, JMP with RD = target + |.if DUALNUM + | lwzux CARG1, RA, BASE + | addi PC, PC, 4 + | lwz CARG2, 4(RA) + | lwzux CARG3, RD, KBASE + | checknum cr0, CARG1 + | lwz SAVE0, -4(PC) + | checknum cr1, CARG3 + | decode_RD4 SAVE0, SAVE0 + | lwz CARG4, 4(RD) + | addis SAVE0, SAVE0, -(BCBIAS_J*4 >> 16) + if (vk) { + |->BC_ISEQN_Z: + } else { + |->BC_ISNEN_Z: + } + | bne cr0, >7 + | bne cr1, >8 + | cmpw CARG2, CARG4 + |4: + |.else + if (vk) { + |->BC_ISEQN_Z: // Dummy label. + } else { + |->BC_ISNEN_Z: // Dummy label. + } + | lwzx CARG1, BASE, RA + | addi PC, PC, 4 + | lfdx f0, BASE, RA + | lwz SAVE0, -4(PC) + | lfdx f1, KBASE, RD + | decode_RD4 SAVE0, SAVE0 + | checknum CARG1 + | addis SAVE0, SAVE0, -(BCBIAS_J*4 >> 16) + | bge >3 + | fcmpu cr0, f0, f1 + |.endif + if (vk) { + | bne >1 + | add PC, PC, SAVE0 + |1: + |.if not FFI + |3: + |.endif + } else { + | beq >2 + |1: + |.if not FFI + |3: + |.endif + | add PC, PC, SAVE0 + |2: + } + | ins_next + |.if FFI + |3: + | cmpwi CARG1, LJ_TCDATA + | beq ->vmeta_equal_cd + | b <1 + |.endif + |.if DUALNUM + |7: // RA is not an integer. + | bge cr0, <3 + | // RA is a number. + | .FPU lfd f0, 0(RA) + | blt cr1, >1 + | // RA is a number, RD is an integer. + |.if FPU + | tonum_i f1, CARG4 + |.else + | bl ->vm_sfi2d_2 + |.endif + | b >2 + | + |8: // RA is an integer, RD is a number. + |.if FPU + | tonum_i f0, CARG2 + |.else + | bl ->vm_sfi2d_1 + |.endif + |1: + | .FPU lfd f1, 0(RD) + |2: + |.if FPU + | fcmpu cr0, f0, f1 + |.else + | blex __ledf2 + | cmpwi CRET1, 0 + |.endif + | b <4 + |.endif + break; + + case BC_ISEQP: case BC_ISNEP: + vk = op == BC_ISEQP; + | // RA = src*8, RD = primitive_type*8 (~), JMP with RD = target + | lwzx TMP0, BASE, RA + | srwi TMP1, RD, 3 + | lwz TMP2, 0(PC) + | not TMP1, TMP1 + | addi PC, PC, 4 + |.if FFI + | cmpwi TMP0, LJ_TCDATA + |.endif + | sub TMP0, TMP0, TMP1 + |.if FFI + | beq ->vmeta_equal_cd + |.endif + | decode_RD4 TMP2, TMP2 + | .gpr64 extsw TMP0, TMP0 + | addic TMP0, TMP0, -1 + | addis TMP2, TMP2, -(BCBIAS_J*4 >> 16) + | subfe TMP1, TMP1, TMP1 + if (vk) { + | and TMP2, TMP2, TMP1 + } else { + | andc TMP2, TMP2, TMP1 + } + | add PC, PC, TMP2 + | ins_next + break; + + /* -- Unary test and copy ops ------------------------------------------- */ + + case BC_ISTC: case BC_ISFC: case BC_IST: case BC_ISF: + | // RA = dst*8 or unused, RD = src*8, JMP with RD = target + | lwzx TMP0, BASE, RD + | lwz INS, 0(PC) + | addi PC, PC, 4 + if (op == BC_IST || op == BC_ISF) { + | .gpr64 extsw TMP0, TMP0 + | subfic TMP0, TMP0, LJ_TTRUE + | decode_RD4 TMP2, INS + | subfe TMP1, TMP1, TMP1 + | addis TMP2, TMP2, -(BCBIAS_J*4 >> 16) + if (op == BC_IST) { + | andc TMP2, TMP2, TMP1 + } else { + | and TMP2, TMP2, TMP1 + } + | add PC, PC, TMP2 + } else { + | li TMP1, LJ_TFALSE + |.if FPU + | lfdx f0, BASE, RD + |.else + | lwzux CARG1, RD, BASE + | lwz CARG2, 4(RD) + |.endif + | cmplw TMP0, TMP1 + if (op == BC_ISTC) { + | bge >1 + } else { + | blt >1 + } + | addis PC, PC, -(BCBIAS_J*4 >> 16) + | decode_RD4 TMP2, INS + |.if FPU + | stfdx f0, BASE, RA + |.else + | stwux CARG1, RA, BASE + | stw CARG2, 4(RA) + |.endif + | add PC, PC, TMP2 + |1: + } + | ins_next + break; + + case BC_ISTYPE: + | // RA = src*8, RD = -type*8 + | lwzx TMP0, BASE, RA + | srwi TMP1, RD, 3 + | ins_next1 + |.if not PPE and not GPR64 + | add. TMP0, TMP0, TMP1 + |.else + | neg TMP1, TMP1 + | cmpw TMP0, TMP1 + |.endif + | bne ->vmeta_istype + | ins_next2 + break; + case BC_ISNUM: + | // RA = src*8, RD = -(TISNUM-1)*8 + | lwzx TMP0, BASE, RA + | ins_next1 + | checknum TMP0 + | bge ->vmeta_istype + | ins_next2 + break; + + /* -- Unary ops --------------------------------------------------------- */ + + case BC_MOV: + | // RA = dst*8, RD = src*8 + | ins_next1 + |.if FPU + | lfdx f0, BASE, RD + | stfdx f0, BASE, RA + |.else + | lwzux TMP0, RD, BASE + | lwz TMP1, 4(RD) + | stwux TMP0, RA, BASE + | stw TMP1, 4(RA) + |.endif + | ins_next2 + break; + case BC_NOT: + | // RA = dst*8, RD = src*8 + | ins_next1 + | lwzx TMP0, BASE, RD + | .gpr64 extsw TMP0, TMP0 + | subfic TMP1, TMP0, LJ_TTRUE + | adde TMP0, TMP0, TMP1 + | stwx TMP0, BASE, RA + | ins_next2 + break; + case BC_UNM: + | // RA = dst*8, RD = src*8 + | lwzux TMP1, RD, BASE + | lwz TMP0, 4(RD) + | checknum TMP1 + |.if DUALNUM + | bne >5 + |.if GPR64 + | lus TMP2, 0x8000 + | neg TMP0, TMP0 + | cmplw TMP0, TMP2 + | beq >4 + |.else + | nego. TMP0, TMP0 + | bso >4 + |1: + |.endif + | ins_next1 + | stwux TISNUM, RA, BASE + | stw TMP0, 4(RA) + |3: + | ins_next2 + |4: + |.if not GPR64 + | // Potential overflow. + | checkov TMP1, <1 // Ignore unrelated overflow. + |.endif + | lus TMP1, 0x41e0 // 2^31. + | li TMP0, 0 + | b >7 + |.endif + |5: + | bge ->vmeta_unm + | xoris TMP1, TMP1, 0x8000 + |7: + | ins_next1 + | stwux TMP1, RA, BASE + | stw TMP0, 4(RA) + |.if DUALNUM + | b <3 + |.else + | ins_next2 + |.endif + break; + case BC_LEN: + | // RA = dst*8, RD = src*8 + | lwzux TMP0, RD, BASE + | lwz CARG1, 4(RD) + | checkstr TMP0; bne >2 + | lwz CRET1, STR:CARG1->len + |1: + |.if DUALNUM + | ins_next1 + | stwux TISNUM, RA, BASE + | stw CRET1, 4(RA) + |.else + | tonum_u f0, CRET1 // Result is a non-negative integer. + | ins_next1 + | stfdx f0, BASE, RA + |.endif + | ins_next2 + |2: + | checktab TMP0; bne ->vmeta_len +#if LJ_52 + | lwz TAB:TMP2, TAB:CARG1->metatable + | cmplwi TAB:TMP2, 0 + | bne >9 + |3: +#endif + |->BC_LEN_Z: + | bl extern lj_tab_len // (GCtab *t) + | // Returns uint32_t (but less than 2^31). + | b <1 +#if LJ_52 + |9: + | lbz TMP0, TAB:TMP2->nomm + | andix. TMP0, TMP0, 1<vmeta_len +#endif + break; + + /* -- Binary ops -------------------------------------------------------- */ + + |.macro ins_arithpre + | // RA = dst*8, RB = src1*8, RC = src2*8 | num_const*8 + ||vk = ((int)op - BC_ADDVN) / (BC_ADDNV-BC_ADDVN); + ||switch (vk) { + ||case 0: + | lwzx CARG1, BASE, RB + | .if DUALNUM + | lwzx CARG3, KBASE, RC + | .endif + | .if FPU + | lfdx f14, BASE, RB + | lfdx f15, KBASE, RC + | .else + | add TMP1, BASE, RB + | add TMP2, KBASE, RC + | lwz CARG2, 4(TMP1) + | lwz CARG4, 4(TMP2) + | .endif + | .if DUALNUM + | checknum cr0, CARG1 + | checknum cr1, CARG3 + | crand 4*cr0+lt, 4*cr0+lt, 4*cr1+lt + | bge ->vmeta_arith_vn + | .else + | checknum CARG1; bge ->vmeta_arith_vn + | .endif + || break; + ||case 1: + | lwzx CARG1, BASE, RB + | .if DUALNUM + | lwzx CARG3, KBASE, RC + | .endif + | .if FPU + | lfdx f15, BASE, RB + | lfdx f14, KBASE, RC + | .else + | add TMP1, BASE, RB + | add TMP2, KBASE, RC + | lwz CARG2, 4(TMP1) + | lwz CARG4, 4(TMP2) + | .endif + | .if DUALNUM + | checknum cr0, CARG1 + | checknum cr1, CARG3 + | crand 4*cr0+lt, 4*cr0+lt, 4*cr1+lt + | bge ->vmeta_arith_nv + | .else + | checknum CARG1; bge ->vmeta_arith_nv + | .endif + || break; + ||default: + | lwzx CARG1, BASE, RB + | lwzx CARG3, BASE, RC + | .if FPU + | lfdx f14, BASE, RB + | lfdx f15, BASE, RC + | .else + | add TMP1, BASE, RB + | add TMP2, BASE, RC + | lwz CARG2, 4(TMP1) + | lwz CARG4, 4(TMP2) + | .endif + | checknum cr0, CARG1 + | checknum cr1, CARG3 + | crand 4*cr0+lt, 4*cr0+lt, 4*cr1+lt + | bge ->vmeta_arith_vv + || break; + ||} + |.endmacro + | + |.macro ins_arithfallback, ins + ||switch (vk) { + ||case 0: + | ins ->vmeta_arith_vn2 + || break; + ||case 1: + | ins ->vmeta_arith_nv2 + || break; + ||default: + | ins ->vmeta_arith_vv2 + || break; + ||} + |.endmacro + | + |.macro intmod, a, b, c + | bl ->vm_modi + |.endmacro + | + |.macro fpmod, a, b, c + |->BC_MODVN_Z: + | fdiv FARG1, b, c + | // NYI: Use internal implementation of floor. + | blex floor // floor(b/c) + | fmul a, FARG1, c + | fsub a, b, a // b - floor(b/c)*c + |.endmacro + | + |.macro sfpmod + |->BC_MODVN_Z: + | stw CARG1, SFSAVE_1 + | stw CARG2, SFSAVE_2 + | mr SAVE0, CARG3 + | mr SAVE1, CARG4 + | blex __divdf3 + | blex floor + | mr CARG3, SAVE0 + | mr CARG4, SAVE1 + | blex __muldf3 + | mr CARG3, CRET1 + | mr CARG4, CRET2 + | lwz CARG1, SFSAVE_1 + | lwz CARG2, SFSAVE_2 + | blex __subdf3 + |.endmacro + | + |.macro ins_arithfp, fpins + | ins_arithpre + |.if "fpins" == "fpmod_" + | b ->BC_MODVN_Z // Avoid 3 copies. It's slow anyway. + |.elif FPU + | fpins f0, f14, f15 + | ins_next1 + | stfdx f0, BASE, RA + | ins_next2 + |.else + | blex __divdf3 // Only soft-float div uses this macro. + | ins_next1 + | stwux CRET1, RA, BASE + | stw CRET2, 4(RA) + | ins_next2 + |.endif + |.endmacro + | + |.macro ins_arithdn, intins, fpins, fpcall + | // RA = dst*8, RB = src1*8, RC = src2*8 | num_const*8 + ||vk = ((int)op - BC_ADDVN) / (BC_ADDNV-BC_ADDVN); + ||switch (vk) { + ||case 0: + | lwzux CARG1, RB, BASE + | lwzux CARG3, RC, KBASE + | lwz CARG2, 4(RB) + | checknum cr0, CARG1 + | lwz CARG4, 4(RC) + | checknum cr1, CARG3 + || break; + ||case 1: + | lwzux CARG3, RB, BASE + | lwzux CARG1, RC, KBASE + | lwz CARG4, 4(RB) + | checknum cr0, CARG3 + | lwz CARG2, 4(RC) + | checknum cr1, CARG1 + || break; + ||default: + | lwzux CARG1, RB, BASE + | lwzux CARG3, RC, BASE + | lwz CARG2, 4(RB) + | checknum cr0, CARG1 + | lwz CARG4, 4(RC) + | checknum cr1, CARG3 + || break; + ||} + | bne >5 + | bne cr1, >5 + |.if "intins" == "intmod" + | mr CARG1, CARG2 + | mr CARG2, CARG4 + |.endif + | intins CARG1, CARG2, CARG4 + | bso >4 + |1: + | ins_next1 + | stwux TISNUM, RA, BASE + | stw CARG1, 4(RA) + |2: + | ins_next2 + |4: // Overflow. + | checkov TMP0, <1 // Ignore unrelated overflow. + | ins_arithfallback b + |5: // FP variant. + |.if FPU + ||if (vk == 1) { + | lfd f15, 0(RB) + | lfd f14, 0(RC) + ||} else { + | lfd f14, 0(RB) + | lfd f15, 0(RC) + ||} + |.endif + | crand 4*cr0+lt, 4*cr0+lt, 4*cr1+lt + | ins_arithfallback bge + |.if "fpins" == "fpmod_" + | b ->BC_MODVN_Z // Avoid 3 copies. It's slow anyway. + |.else + |.if FPU + | fpins f0, f14, f15 + | stfdx f0, BASE, RA + |.else + |.if "fpcall" == "sfpmod" + | sfpmod + |.else + | blex fpcall + |.endif + | stwux CRET1, RA, BASE + | stw CRET2, 4(RA) + |.endif + | ins_next1 + | b <2 + |.endif + |.endmacro + | + |.macro ins_arith, intins, fpins, fpcall + |.if DUALNUM + | ins_arithdn intins, fpins, fpcall + |.else + | ins_arithfp fpins + |.endif + |.endmacro + + case BC_ADDVN: case BC_ADDNV: case BC_ADDVV: + |.if GPR64 + |.macro addo32., y, a, b + | // Need to check overflow for (a<<32) + (b<<32). + | rldicr TMP0, a, 32, 31 + | rldicr TMP3, b, 32, 31 + | addo. TMP0, TMP0, TMP3 + | add y, a, b + |.endmacro + | ins_arith addo32., fadd, __adddf3 + |.else + | ins_arith addo., fadd, __adddf3 + |.endif + break; + case BC_SUBVN: case BC_SUBNV: case BC_SUBVV: + |.if GPR64 + |.macro subo32., y, a, b + | // Need to check overflow for (a<<32) - (b<<32). + | rldicr TMP0, a, 32, 31 + | rldicr TMP3, b, 32, 31 + | subo. TMP0, TMP0, TMP3 + | sub y, a, b + |.endmacro + | ins_arith subo32., fsub, __subdf3 + |.else + | ins_arith subo., fsub, __subdf3 + |.endif + break; + case BC_MULVN: case BC_MULNV: case BC_MULVV: + | ins_arith mullwo., fmul, __muldf3 + break; + case BC_DIVVN: case BC_DIVNV: case BC_DIVVV: + | ins_arithfp fdiv + break; + case BC_MODVN: + | ins_arith intmod, fpmod, sfpmod + break; + case BC_MODNV: case BC_MODVV: + | ins_arith intmod, fpmod_, sfpmod + break; + case BC_POW: + | // NYI: (partial) integer arithmetic. + | lwzx CARG1, BASE, RB + | lwzx CARG3, BASE, RC + |.if FPU + | lfdx FARG1, BASE, RB + | lfdx FARG2, BASE, RC + |.else + | add TMP1, BASE, RB + | add TMP2, BASE, RC + | lwz CARG2, 4(TMP1) + | lwz CARG4, 4(TMP2) + |.endif + | checknum cr0, CARG1 + | checknum cr1, CARG3 + | crand 4*cr0+lt, 4*cr0+lt, 4*cr1+lt + | bge ->vmeta_arith_vv + | blex pow + | ins_next1 + |.if FPU + | stfdx FARG1, BASE, RA + |.else + | stwux CARG1, RA, BASE + | stw CARG2, 4(RA) + |.endif + | ins_next2 + break; + + case BC_CAT: + | // RA = dst*8, RB = src_start*8, RC = src_end*8 + | sub CARG3, RC, RB + | stp BASE, L->base + | add CARG2, BASE, RC + | mr SAVE0, RB + |->BC_CAT_Z: + | stw PC, SAVE_PC + | mr CARG1, L + | srwi CARG3, CARG3, 3 + | bl extern lj_meta_cat // (lua_State *L, TValue *top, int left) + | // Returns NULL (finished) or TValue * (metamethod). + | cmplwi CRET1, 0 + | lp BASE, L->base + | bne ->vmeta_binop + | ins_next1 + |.if FPU + | lfdx f0, BASE, SAVE0 // Copy result from RB to RA. + | stfdx f0, BASE, RA + |.else + | lwzux TMP0, SAVE0, BASE + | lwz TMP1, 4(SAVE0) + | stwux TMP0, RA, BASE + | stw TMP1, 4(RA) + |.endif + | ins_next2 + break; + + /* -- Constant ops ------------------------------------------------------ */ + + case BC_KSTR: + | // RA = dst*8, RD = str_const*8 (~) + | srwi TMP1, RD, 1 + | subfic TMP1, TMP1, -4 + | ins_next1 + | lwzx TMP0, KBASE, TMP1 // KBASE-4-str_const*4 + | li TMP2, LJ_TSTR + | stwux TMP2, RA, BASE + | stw TMP0, 4(RA) + | ins_next2 + break; + case BC_KCDATA: + |.if FFI + | // RA = dst*8, RD = cdata_const*8 (~) + | srwi TMP1, RD, 1 + | subfic TMP1, TMP1, -4 + | ins_next1 + | lwzx TMP0, KBASE, TMP1 // KBASE-4-cdata_const*4 + | li TMP2, LJ_TCDATA + | stwux TMP2, RA, BASE + | stw TMP0, 4(RA) + | ins_next2 + |.endif + break; + case BC_KSHORT: + | // RA = dst*8, RD = int16_literal*8 + |.if DUALNUM + | slwi RD, RD, 13 + | srawi RD, RD, 16 + | ins_next1 + | stwux TISNUM, RA, BASE + | stw RD, 4(RA) + | ins_next2 + |.else + | // The soft-float approach is faster. + | slwi RD, RD, 13 + | srawi TMP1, RD, 31 + | xor TMP2, TMP1, RD + | sub TMP2, TMP2, TMP1 // TMP2 = abs(x) + | cntlzw TMP3, TMP2 + | subfic TMP1, TMP3, 0x40d // TMP1 = exponent-1 + | slw TMP2, TMP2, TMP3 // TMP2 = left aligned mantissa + | subfic TMP3, RD, 0 + | slwi TMP1, TMP1, 20 + | rlwimi RD, TMP2, 21, 1, 31 // hi = sign(x) | (mantissa>>11) + | subfe TMP0, TMP0, TMP0 + | add RD, RD, TMP1 // hi = hi + exponent-1 + | and RD, RD, TMP0 // hi = x == 0 ? 0 : hi + | ins_next1 + | stwux RD, RA, BASE + | stw ZERO, 4(RA) + | ins_next2 + |.endif + break; + case BC_KNUM: + | // RA = dst*8, RD = num_const*8 + | ins_next1 + |.if FPU + | lfdx f0, KBASE, RD + | stfdx f0, BASE, RA + |.else + | lwzux TMP0, RD, KBASE + | lwz TMP1, 4(RD) + | stwux TMP0, RA, BASE + | stw TMP1, 4(RA) + |.endif + | ins_next2 + break; + case BC_KPRI: + | // RA = dst*8, RD = primitive_type*8 (~) + | srwi TMP1, RD, 3 + | not TMP0, TMP1 + | ins_next1 + | stwx TMP0, BASE, RA + | ins_next2 + break; + case BC_KNIL: + | // RA = base*8, RD = end*8 + | stwx TISNIL, BASE, RA + | addi RA, RA, 8 + |1: + | stwx TISNIL, BASE, RA + | cmpw RA, RD + | addi RA, RA, 8 + | blt <1 + | ins_next_ + break; + + /* -- Upvalue and function ops ------------------------------------------ */ + + case BC_UGET: + | // RA = dst*8, RD = uvnum*8 + | lwz LFUNC:RB, FRAME_FUNC(BASE) + | srwi RD, RD, 1 + | addi RD, RD, offsetof(GCfuncL, uvptr) + | lwzx UPVAL:RB, LFUNC:RB, RD + | ins_next1 + | lwz TMP1, UPVAL:RB->v + |.if FPU + | lfd f0, 0(TMP1) + | stfdx f0, BASE, RA + |.else + | lwz TMP2, 0(TMP1) + | lwz TMP3, 4(TMP1) + | stwux TMP2, RA, BASE + | stw TMP3, 4(RA) + |.endif + | ins_next2 + break; + case BC_USETV: + | // RA = uvnum*8, RD = src*8 + | lwz LFUNC:RB, FRAME_FUNC(BASE) + | srwi RA, RA, 1 + | addi RA, RA, offsetof(GCfuncL, uvptr) + |.if FPU + | lfdux f0, RD, BASE + |.else + | lwzux CARG1, RD, BASE + | lwz CARG3, 4(RD) + |.endif + | lwzx UPVAL:RB, LFUNC:RB, RA + | lbz TMP3, UPVAL:RB->marked + | lwz CARG2, UPVAL:RB->v + | andix. TMP3, TMP3, LJ_GC_BLACK // isblack(uv) + | lbz TMP0, UPVAL:RB->closed + | lwz TMP2, 0(RD) + |.if FPU + | stfd f0, 0(CARG2) + |.else + | stw CARG1, 0(CARG2) + | stw CARG3, 4(CARG2) + |.endif + | cmplwi cr1, TMP0, 0 + | lwz TMP1, 4(RD) + | cror 4*cr0+eq, 4*cr0+eq, 4*cr1+eq + | subi TMP2, TMP2, (LJ_TNUMX+1) + | bne >2 // Upvalue is closed and black? + |1: + | ins_next + | + |2: // Check if new value is collectable. + | cmplwi TMP2, LJ_TISGCV - (LJ_TNUMX+1) + | bge <1 // tvisgcv(v) + | lbz TMP3, GCOBJ:TMP1->gch.marked + | andix. TMP3, TMP3, LJ_GC_WHITES // iswhite(v) + | la CARG1, GG_DISP2G(DISPATCH) + | // Crossed a write barrier. Move the barrier forward. + | beq <1 + | bl extern lj_gc_barrieruv // (global_State *g, TValue *tv) + | b <1 + break; + case BC_USETS: + | // RA = uvnum*8, RD = str_const*8 (~) + | lwz LFUNC:RB, FRAME_FUNC(BASE) + | srwi TMP1, RD, 1 + | srwi RA, RA, 1 + | subfic TMP1, TMP1, -4 + | addi RA, RA, offsetof(GCfuncL, uvptr) + | lwzx STR:TMP1, KBASE, TMP1 // KBASE-4-str_const*4 + | lwzx UPVAL:RB, LFUNC:RB, RA + | lbz TMP3, UPVAL:RB->marked + | lwz CARG2, UPVAL:RB->v + | andix. TMP3, TMP3, LJ_GC_BLACK // isblack(uv) + | lbz TMP3, STR:TMP1->marked + | lbz TMP2, UPVAL:RB->closed + | li TMP0, LJ_TSTR + | stw STR:TMP1, 4(CARG2) + | stw TMP0, 0(CARG2) + | bne >2 + |1: + | ins_next + | + |2: // Check if string is white and ensure upvalue is closed. + | andix. TMP3, TMP3, LJ_GC_WHITES // iswhite(str) + | cmplwi cr1, TMP2, 0 + | cror 4*cr0+eq, 4*cr0+eq, 4*cr1+eq + | la CARG1, GG_DISP2G(DISPATCH) + | // Crossed a write barrier. Move the barrier forward. + | beq <1 + | bl extern lj_gc_barrieruv // (global_State *g, TValue *tv) + | b <1 + break; + case BC_USETN: + | // RA = uvnum*8, RD = num_const*8 + | lwz LFUNC:RB, FRAME_FUNC(BASE) + | srwi RA, RA, 1 + | addi RA, RA, offsetof(GCfuncL, uvptr) + |.if FPU + | lfdx f0, KBASE, RD + |.else + | lwzux TMP2, RD, KBASE + | lwz TMP3, 4(RD) + |.endif + | lwzx UPVAL:RB, LFUNC:RB, RA + | ins_next1 + | lwz TMP1, UPVAL:RB->v + |.if FPU + | stfd f0, 0(TMP1) + |.else + | stw TMP2, 0(TMP1) + | stw TMP3, 4(TMP1) + |.endif + | ins_next2 + break; + case BC_USETP: + | // RA = uvnum*8, RD = primitive_type*8 (~) + | lwz LFUNC:RB, FRAME_FUNC(BASE) + | srwi RA, RA, 1 + | srwi TMP0, RD, 3 + | addi RA, RA, offsetof(GCfuncL, uvptr) + | not TMP0, TMP0 + | lwzx UPVAL:RB, LFUNC:RB, RA + | ins_next1 + | lwz TMP1, UPVAL:RB->v + | stw TMP0, 0(TMP1) + | ins_next2 + break; + + case BC_UCLO: + | // RA = level*8, RD = target + | lwz TMP1, L->openupval + | branch_RD // Do this first since RD is not saved. + | stp BASE, L->base + | cmplwi TMP1, 0 + | mr CARG1, L + | beq >1 + | add CARG2, BASE, RA + | bl extern lj_func_closeuv // (lua_State *L, TValue *level) + | lp BASE, L->base + |1: + | ins_next + break; + + case BC_FNEW: + | // RA = dst*8, RD = proto_const*8 (~) (holding function prototype) + | srwi TMP1, RD, 1 + | stp BASE, L->base + | subfic TMP1, TMP1, -4 + | stw PC, SAVE_PC + | lwzx CARG2, KBASE, TMP1 // KBASE-4-tab_const*4 + | mr CARG1, L + | lwz CARG3, FRAME_FUNC(BASE) + | // (lua_State *L, GCproto *pt, GCfuncL *parent) + | bl extern lj_func_newL_gc + | // Returns GCfuncL *. + | lp BASE, L->base + | li TMP0, LJ_TFUNC + | stwux TMP0, RA, BASE + | stw LFUNC:CRET1, 4(RA) + | ins_next + break; + + /* -- Table ops --------------------------------------------------------- */ + + case BC_TNEW: + case BC_TDUP: + | // RA = dst*8, RD = (hbits|asize)*8 | tab_const*8 (~) + | lwz TMP0, DISPATCH_GL(gc.total)(DISPATCH) + | mr CARG1, L + | lwz TMP1, DISPATCH_GL(gc.threshold)(DISPATCH) + | stp BASE, L->base + | cmplw TMP0, TMP1 + | stw PC, SAVE_PC + | bge >5 + |1: + if (op == BC_TNEW) { + | rlwinm CARG2, RD, 29, 21, 31 + | rlwinm CARG3, RD, 18, 27, 31 + | cmpwi CARG2, 0x7ff; beq >3 + |2: + | bl extern lj_tab_new // (lua_State *L, int32_t asize, uint32_t hbits) + | // Returns Table *. + } else { + | srwi TMP1, RD, 1 + | subfic TMP1, TMP1, -4 + | lwzx CARG2, KBASE, TMP1 // KBASE-4-tab_const*4 + | bl extern lj_tab_dup // (lua_State *L, Table *kt) + | // Returns Table *. + } + | lp BASE, L->base + | li TMP0, LJ_TTAB + | stwux TMP0, RA, BASE + | stw TAB:CRET1, 4(RA) + | ins_next + if (op == BC_TNEW) { + |3: + | li CARG2, 0x801 + | b <2 + } + |5: + | mr SAVE0, RD + | bl extern lj_gc_step_fixtop // (lua_State *L) + | mr RD, SAVE0 + | mr CARG1, L + | b <1 + break; + + case BC_GGET: + | // RA = dst*8, RD = str_const*8 (~) + case BC_GSET: + | // RA = src*8, RD = str_const*8 (~) + | lwz LFUNC:TMP2, FRAME_FUNC(BASE) + | srwi TMP1, RD, 1 + | lwz TAB:RB, LFUNC:TMP2->env + | subfic TMP1, TMP1, -4 + | lwzx STR:RC, KBASE, TMP1 // KBASE-4-str_const*4 + if (op == BC_GGET) { + | b ->BC_TGETS_Z + } else { + | b ->BC_TSETS_Z + } + break; + + case BC_TGETV: + | // RA = dst*8, RB = table*8, RC = key*8 + | lwzux CARG1, RB, BASE + | lwzux CARG2, RC, BASE + | lwz TAB:RB, 4(RB) + |.if DUALNUM + | lwz RC, 4(RC) + |.else + | lfd f0, 0(RC) + |.endif + | checktab CARG1 + | checknum cr1, CARG2 + | bne ->vmeta_tgetv + |.if DUALNUM + | lwz TMP0, TAB:RB->asize + | bne cr1, >5 + | lwz TMP1, TAB:RB->array + | cmplw TMP0, RC + | slwi TMP2, RC, 3 + |.else + | bge cr1, >5 + | // Convert number key to integer, check for integerness and range. + | fctiwz f1, f0 + | fadd f2, f0, TOBIT + | stfd f1, TMPD + | lwz TMP0, TAB:RB->asize + | fsub f2, f2, TOBIT + | lwz TMP2, TMPD_LO + | lwz TMP1, TAB:RB->array + | fcmpu cr1, f0, f2 + | cmplw cr0, TMP0, TMP2 + | crand 4*cr0+gt, 4*cr0+gt, 4*cr1+eq + | slwi TMP2, TMP2, 3 + |.endif + | ble ->vmeta_tgetv // Integer key and in array part? + | lwzx TMP0, TMP1, TMP2 + |.if FPU + | lfdx f14, TMP1, TMP2 + |.else + | lwzux SAVE0, TMP1, TMP2 + | lwz SAVE1, 4(TMP1) + |.endif + | checknil TMP0; beq >2 + |1: + | ins_next1 + |.if FPU + | stfdx f14, BASE, RA + |.else + | stwux SAVE0, RA, BASE + | stw SAVE1, 4(RA) + |.endif + | ins_next2 + | + |2: // Check for __index if table value is nil. + | lwz TAB:TMP2, TAB:RB->metatable + | cmplwi TAB:TMP2, 0 + | beq <1 // No metatable: done. + | lbz TMP0, TAB:TMP2->nomm + | andix. TMP0, TMP0, 1<vmeta_tgetv + | + |5: + | checkstr CARG2; bne ->vmeta_tgetv + |.if not DUALNUM + | lwz STR:RC, 4(RC) + |.endif + | b ->BC_TGETS_Z // String key? + break; + case BC_TGETS: + | // RA = dst*8, RB = table*8, RC = str_const*8 (~) + | lwzux CARG1, RB, BASE + | srwi TMP1, RC, 1 + | lwz TAB:RB, 4(RB) + | subfic TMP1, TMP1, -4 + | checktab CARG1 + | lwzx STR:RC, KBASE, TMP1 // KBASE-4-str_const*4 + | bne ->vmeta_tgets1 + |->BC_TGETS_Z: + | // TAB:RB = GCtab *, STR:RC = GCstr *, RA = dst*8 + | lwz TMP0, TAB:RB->hmask + | lwz TMP1, STR:RC->hash + | lwz NODE:TMP2, TAB:RB->node + | and TMP1, TMP1, TMP0 // idx = str->hash & tab->hmask + | slwi TMP0, TMP1, 5 + | slwi TMP1, TMP1, 3 + | sub TMP1, TMP0, TMP1 + | add NODE:TMP2, NODE:TMP2, TMP1 // node = tab->node + (idx*32-idx*8) + |1: + | lwz CARG1, NODE:TMP2->key + | lwz TMP0, 4+offsetof(Node, key)(NODE:TMP2) + | lwz CARG2, NODE:TMP2->val + | lwz TMP1, 4+offsetof(Node, val)(NODE:TMP2) + | checkstr CARG1; bne >4 + | cmpw TMP0, STR:RC; bne >4 + | checknil CARG2; beq >5 // Key found, but nil value? + |3: + | stwux CARG2, RA, BASE + | stw TMP1, 4(RA) + | ins_next + | + |4: // Follow hash chain. + | lwz NODE:TMP2, NODE:TMP2->next + | cmplwi NODE:TMP2, 0 + | bne <1 + | // End of hash chain: key not found, nil result. + | li CARG2, LJ_TNIL + | + |5: // Check for __index if table value is nil. + | lwz TAB:TMP2, TAB:RB->metatable + | cmplwi TAB:TMP2, 0 + | beq <3 // No metatable: done. + | lbz TMP0, TAB:TMP2->nomm + | andix. TMP0, TMP0, 1<vmeta_tgets + break; + case BC_TGETB: + | // RA = dst*8, RB = table*8, RC = index*8 + | lwzux CARG1, RB, BASE + | srwi TMP0, RC, 3 + | lwz TAB:RB, 4(RB) + | checktab CARG1; bne ->vmeta_tgetb + | lwz TMP1, TAB:RB->asize + | lwz TMP2, TAB:RB->array + | cmplw TMP0, TMP1; bge ->vmeta_tgetb + |.if FPU + | lwzx TMP1, TMP2, RC + | lfdx f0, TMP2, RC + |.else + | lwzux TMP1, TMP2, RC + | lwz TMP3, 4(TMP2) + |.endif + | checknil TMP1; beq >5 + |1: + | ins_next1 + |.if FPU + | stfdx f0, BASE, RA + |.else + | stwux TMP1, RA, BASE + | stw TMP3, 4(RA) + |.endif + | ins_next2 + | + |5: // Check for __index if table value is nil. + | lwz TAB:TMP2, TAB:RB->metatable + | cmplwi TAB:TMP2, 0 + | beq <1 // No metatable: done. + | lbz TMP2, TAB:TMP2->nomm + | andix. TMP2, TMP2, 1<vmeta_tgetb // Caveat: preserve TMP0! + break; + case BC_TGETR: + | // RA = dst*8, RB = table*8, RC = key*8 + | add RB, BASE, RB + | lwz TAB:CARG1, 4(RB) + |.if DUALNUM + | add RC, BASE, RC + | lwz TMP0, TAB:CARG1->asize + | lwz CARG2, 4(RC) + | lwz TMP1, TAB:CARG1->array + |.else + | lfdx f0, BASE, RC + | lwz TMP0, TAB:CARG1->asize + | toint CARG2, f0 + | lwz TMP1, TAB:CARG1->array + |.endif + | cmplw TMP0, CARG2 + | slwi TMP2, CARG2, 3 + | ble ->vmeta_tgetr // In array part? + |.if FPU + | lfdx f14, TMP1, TMP2 + |.else + | lwzux SAVE0, TMP2, TMP1 + | lwz SAVE1, 4(TMP2) + |.endif + |->BC_TGETR_Z: + | ins_next1 + |.if FPU + | stfdx f14, BASE, RA + |.else + | stwux SAVE0, RA, BASE + | stw SAVE1, 4(RA) + |.endif + | ins_next2 + break; + + case BC_TSETV: + | // RA = src*8, RB = table*8, RC = key*8 + | lwzux CARG1, RB, BASE + | lwzux CARG2, RC, BASE + | lwz TAB:RB, 4(RB) + |.if DUALNUM + | lwz RC, 4(RC) + |.else + | lfd f0, 0(RC) + |.endif + | checktab CARG1 + | checknum cr1, CARG2 + | bne ->vmeta_tsetv + |.if DUALNUM + | lwz TMP0, TAB:RB->asize + | bne cr1, >5 + | lwz TMP1, TAB:RB->array + | cmplw TMP0, RC + | slwi TMP0, RC, 3 + |.else + | bge cr1, >5 + | // Convert number key to integer, check for integerness and range. + | fctiwz f1, f0 + | fadd f2, f0, TOBIT + | stfd f1, TMPD + | lwz TMP0, TAB:RB->asize + | fsub f2, f2, TOBIT + | lwz TMP2, TMPD_LO + | lwz TMP1, TAB:RB->array + | fcmpu cr1, f0, f2 + | cmplw cr0, TMP0, TMP2 + | crand 4*cr0+gt, 4*cr0+gt, 4*cr1+eq + | slwi TMP0, TMP2, 3 + |.endif + | ble ->vmeta_tsetv // Integer key and in array part? + | lwzx TMP2, TMP1, TMP0 + | lbz TMP3, TAB:RB->marked + |.if FPU + | lfdx f14, BASE, RA + |.else + | add SAVE1, BASE, RA + | lwz SAVE0, 0(SAVE1) + | lwz SAVE1, 4(SAVE1) + |.endif + | checknil TMP2; beq >3 + |1: + | andix. TMP2, TMP3, LJ_GC_BLACK // isblack(table) + |.if FPU + | stfdx f14, TMP1, TMP0 + |.else + | stwux SAVE0, TMP1, TMP0 + | stw SAVE1, 4(TMP1) + |.endif + | bne >7 + |2: + | ins_next + | + |3: // Check for __newindex if previous value is nil. + | lwz TAB:TMP2, TAB:RB->metatable + | cmplwi TAB:TMP2, 0 + | beq <1 // No metatable: done. + | lbz TMP2, TAB:TMP2->nomm + | andix. TMP2, TMP2, 1<vmeta_tsetv + | + |5: + | checkstr CARG2; bne ->vmeta_tsetv + |.if not DUALNUM + | lwz STR:RC, 4(RC) + |.endif + | b ->BC_TSETS_Z // String key? + | + |7: // Possible table write barrier for the value. Skip valiswhite check. + | barrierback TAB:RB, TMP3, TMP0 + | b <2 + break; + case BC_TSETS: + | // RA = src*8, RB = table*8, RC = str_const*8 (~) + | lwzux CARG1, RB, BASE + | srwi TMP1, RC, 1 + | lwz TAB:RB, 4(RB) + | subfic TMP1, TMP1, -4 + | checktab CARG1 + | lwzx STR:RC, KBASE, TMP1 // KBASE-4-str_const*4 + | bne ->vmeta_tsets1 + |->BC_TSETS_Z: + | // TAB:RB = GCtab *, STR:RC = GCstr *, RA = src*8 + | lwz TMP0, TAB:RB->hmask + | lwz TMP1, STR:RC->hash + | lwz NODE:TMP2, TAB:RB->node + | stb ZERO, TAB:RB->nomm // Clear metamethod cache. + | and TMP1, TMP1, TMP0 // idx = str->hash & tab->hmask + |.if FPU + | lfdx f14, BASE, RA + |.else + | add CARG2, BASE, RA + | lwz SAVE0, 0(CARG2) + | lwz SAVE1, 4(CARG2) + |.endif + | slwi TMP0, TMP1, 5 + | slwi TMP1, TMP1, 3 + | sub TMP1, TMP0, TMP1 + | lbz TMP3, TAB:RB->marked + | add NODE:TMP2, NODE:TMP2, TMP1 // node = tab->node + (idx*32-idx*8) + |1: + | lwz CARG1, NODE:TMP2->key + | lwz TMP0, 4+offsetof(Node, key)(NODE:TMP2) + | lwz CARG2, NODE:TMP2->val + | lwz NODE:TMP1, NODE:TMP2->next + | checkstr CARG1; bne >5 + | cmpw TMP0, STR:RC; bne >5 + | checknil CARG2; beq >4 // Key found, but nil value? + |2: + | andix. TMP0, TMP3, LJ_GC_BLACK // isblack(table) + |.if FPU + | stfd f14, NODE:TMP2->val + |.else + | stw SAVE0, NODE:TMP2->val.u32.hi + | stw SAVE1, NODE:TMP2->val.u32.lo + |.endif + | bne >7 + |3: + | ins_next + | + |4: // Check for __newindex if previous value is nil. + | lwz TAB:TMP1, TAB:RB->metatable + | cmplwi TAB:TMP1, 0 + | beq <2 // No metatable: done. + | lbz TMP0, TAB:TMP1->nomm + | andix. TMP0, TMP0, 1<vmeta_tsets + | + |5: // Follow hash chain. + | cmplwi NODE:TMP1, 0 + | mr NODE:TMP2, NODE:TMP1 + | bne <1 + | // End of hash chain: key not found, add a new one. + | + | // But check for __newindex first. + | lwz TAB:TMP1, TAB:RB->metatable + | la CARG3, DISPATCH_GL(tmptv)(DISPATCH) + | stw PC, SAVE_PC + | mr CARG1, L + | cmplwi TAB:TMP1, 0 + | stp BASE, L->base + | beq >6 // No metatable: continue. + | lbz TMP0, TAB:TMP1->nomm + | andix. TMP0, TMP0, 1<vmeta_tsets // 'no __newindex' flag NOT set: check. + |6: + | li TMP0, LJ_TSTR + | stw STR:RC, 4(CARG3) + | mr CARG2, TAB:RB + | stw TMP0, 0(CARG3) + | bl extern lj_tab_newkey // (lua_State *L, GCtab *t, TValue *k) + | // Returns TValue *. + | lp BASE, L->base + |.if FPU + | stfd f14, 0(CRET1) + |.else + | stw SAVE0, 0(CRET1) + | stw SAVE1, 4(CRET1) + |.endif + | b <3 // No 2nd write barrier needed. + | + |7: // Possible table write barrier for the value. Skip valiswhite check. + | barrierback TAB:RB, TMP3, TMP0 + | b <3 + break; + case BC_TSETB: + | // RA = src*8, RB = table*8, RC = index*8 + | lwzux CARG1, RB, BASE + | srwi TMP0, RC, 3 + | lwz TAB:RB, 4(RB) + | checktab CARG1; bne ->vmeta_tsetb + | lwz TMP1, TAB:RB->asize + | lwz TMP2, TAB:RB->array + | lbz TMP3, TAB:RB->marked + | cmplw TMP0, TMP1 + |.if FPU + | lfdx f14, BASE, RA + |.else + | add CARG2, BASE, RA + | lwz SAVE0, 0(CARG2) + | lwz SAVE1, 4(CARG2) + |.endif + | bge ->vmeta_tsetb + | lwzx TMP1, TMP2, RC + | checknil TMP1; beq >5 + |1: + | andix. TMP0, TMP3, LJ_GC_BLACK // isblack(table) + |.if FPU + | stfdx f14, TMP2, RC + |.else + | stwux SAVE0, RC, TMP2 + | stw SAVE1, 4(RC) + |.endif + | bne >7 + |2: + | ins_next + | + |5: // Check for __newindex if previous value is nil. + | lwz TAB:TMP1, TAB:RB->metatable + | cmplwi TAB:TMP1, 0 + | beq <1 // No metatable: done. + | lbz TMP1, TAB:TMP1->nomm + | andix. TMP1, TMP1, 1<vmeta_tsetb // Caveat: preserve TMP0! + | + |7: // Possible table write barrier for the value. Skip valiswhite check. + | barrierback TAB:RB, TMP3, TMP0 + | b <2 + break; + case BC_TSETR: + | // RA = dst*8, RB = table*8, RC = key*8 + | add RB, BASE, RB + | lwz TAB:CARG2, 4(RB) + |.if DUALNUM + | add RC, BASE, RC + | lbz TMP3, TAB:CARG2->marked + | lwz TMP0, TAB:CARG2->asize + | lwz CARG3, 4(RC) + | lwz TMP1, TAB:CARG2->array + |.else + | lfdx f0, BASE, RC + | lbz TMP3, TAB:CARG2->marked + | lwz TMP0, TAB:CARG2->asize + | toint CARG3, f0 + | lwz TMP1, TAB:CARG2->array + |.endif + | andix. TMP2, TMP3, LJ_GC_BLACK // isblack(table) + | bne >7 + |2: + | cmplw TMP0, CARG3 + | slwi TMP2, CARG3, 3 + |.if FPU + | lfdx f14, BASE, RA + |.else + | lwzux SAVE0, RA, BASE + | lwz SAVE1, 4(RA) + |.endif + | ble ->vmeta_tsetr // In array part? + | ins_next1 + |.if FPU + | stfdx f14, TMP1, TMP2 + |.else + | stwux SAVE0, TMP1, TMP2 + | stw SAVE1, 4(TMP1) + |.endif + | ins_next2 + | + |7: // Possible table write barrier for the value. Skip valiswhite check. + | barrierback TAB:CARG2, TMP3, TMP2 + | b <2 + break; + + + case BC_TSETM: + | // RA = base*8 (table at base-1), RD = num_const*8 (start index) + | add RA, BASE, RA + |1: + | add TMP3, KBASE, RD + | lwz TAB:CARG2, -4(RA) // Guaranteed to be a table. + | addic. TMP0, MULTRES, -8 + | lwz TMP3, 4(TMP3) // Integer constant is in lo-word. + | srwi CARG3, TMP0, 3 + | beq >4 // Nothing to copy? + | add CARG3, CARG3, TMP3 + | lwz TMP2, TAB:CARG2->asize + | slwi TMP1, TMP3, 3 + | lbz TMP3, TAB:CARG2->marked + | cmplw CARG3, TMP2 + | add TMP2, RA, TMP0 + | lwz TMP0, TAB:CARG2->array + | bgt >5 + | add TMP1, TMP1, TMP0 + | andix. TMP0, TMP3, LJ_GC_BLACK // isblack(table) + |3: // Copy result slots to table. + |.if FPU + | lfd f0, 0(RA) + |.else + | lwz SAVE0, 0(RA) + | lwz SAVE1, 4(RA) + |.endif + | addi RA, RA, 8 + | cmpw cr1, RA, TMP2 + |.if FPU + | stfd f0, 0(TMP1) + |.else + | stw SAVE0, 0(TMP1) + | stw SAVE1, 4(TMP1) + |.endif + | addi TMP1, TMP1, 8 + | blt cr1, <3 + | bne >7 + |4: + | ins_next + | + |5: // Need to resize array part. + | stp BASE, L->base + | mr CARG1, L + | stw PC, SAVE_PC + | mr SAVE0, RD + | bl extern lj_tab_reasize // (lua_State *L, GCtab *t, int nasize) + | // Must not reallocate the stack. + | mr RD, SAVE0 + | b <1 + | + |7: // Possible table write barrier for any value. Skip valiswhite check. + | barrierback TAB:CARG2, TMP3, TMP0 + | b <4 + break; + + /* -- Calls and vararg handling ----------------------------------------- */ + + case BC_CALLM: + | // RA = base*8, (RB = (nresults+1)*8,) RC = extra_nargs*8 + | add NARGS8:RC, NARGS8:RC, MULTRES + | // Fall through. Assumes BC_CALL follows. + break; + case BC_CALL: + | // RA = base*8, (RB = (nresults+1)*8,) RC = (nargs+1)*8 + | mr TMP2, BASE + | lwzux TMP0, BASE, RA + | lwz LFUNC:RB, 4(BASE) + | subi NARGS8:RC, NARGS8:RC, 8 + | addi BASE, BASE, 8 + | checkfunc TMP0; bne ->vmeta_call + | ins_call + break; + + case BC_CALLMT: + | // RA = base*8, (RB = 0,) RC = extra_nargs*8 + | add NARGS8:RC, NARGS8:RC, MULTRES + | // Fall through. Assumes BC_CALLT follows. + break; + case BC_CALLT: + | // RA = base*8, (RB = 0,) RC = (nargs+1)*8 + | lwzux TMP0, RA, BASE + | lwz LFUNC:RB, 4(RA) + | subi NARGS8:RC, NARGS8:RC, 8 + | lwz TMP1, FRAME_PC(BASE) + | checkfunc TMP0 + | addi RA, RA, 8 + | bne ->vmeta_callt + |->BC_CALLT_Z: + | andix. TMP0, TMP1, FRAME_TYPE // Caveat: preserve cr0 until the crand. + | lbz TMP3, LFUNC:RB->ffid + | xori TMP2, TMP1, FRAME_VARG + | cmplwi cr1, NARGS8:RC, 0 + | bne >7 + |1: + | stw LFUNC:RB, FRAME_FUNC(BASE) // Copy function down, but keep PC. + | li TMP2, 0 + | cmplwi cr7, TMP3, 1 // (> FF_C) Calling a fast function? + | beq cr1, >3 + |2: + | addi TMP3, TMP2, 8 + |.if FPU + | lfdx f0, RA, TMP2 + |.else + | add CARG3, RA, TMP2 + | lwz CARG1, 0(CARG3) + | lwz CARG2, 4(CARG3) + |.endif + | cmplw cr1, TMP3, NARGS8:RC + |.if FPU + | stfdx f0, BASE, TMP2 + |.else + | stwux CARG1, TMP2, BASE + | stw CARG2, 4(TMP2) + |.endif + | mr TMP2, TMP3 + | bne cr1, <2 + |3: + | crand 4*cr0+eq, 4*cr0+eq, 4*cr7+gt + | beq >5 + |4: + | ins_callt + | + |5: // Tailcall to a fast function with a Lua frame below. + | lwz INS, -4(TMP1) + | decode_RA8 RA, INS + | sub TMP1, BASE, RA + | lwz LFUNC:TMP1, FRAME_FUNC-8(TMP1) + | lwz TMP1, LFUNC:TMP1->pc + | lwz KBASE, PC2PROTO(k)(TMP1) // Need to prepare KBASE. + | b <4 + | + |7: // Tailcall from a vararg function. + | andix. TMP0, TMP2, FRAME_TYPEP + | bne <1 // Vararg frame below? + | sub BASE, BASE, TMP2 // Relocate BASE down. + | lwz TMP1, FRAME_PC(BASE) + | andix. TMP0, TMP1, FRAME_TYPE + | b <1 + break; + + case BC_ITERC: + | // RA = base*8, (RB = (nresults+1)*8, RC = (nargs+1)*8 ((2+1)*8)) + | mr TMP2, BASE + | add BASE, BASE, RA + | lwz TMP1, -24(BASE) + | lwz LFUNC:RB, -20(BASE) + |.if FPU + | lfd f1, -8(BASE) + | lfd f0, -16(BASE) + |.else + | lwz CARG1, -8(BASE) + | lwz CARG2, -4(BASE) + | lwz CARG3, -16(BASE) + | lwz CARG4, -12(BASE) + |.endif + | stw TMP1, 0(BASE) // Copy callable. + | stw LFUNC:RB, 4(BASE) + | checkfunc TMP1 + | li NARGS8:RC, 16 // Iterators get 2 arguments. + |.if FPU + | stfd f1, 16(BASE) // Copy control var. + | stfdu f0, 8(BASE) // Copy state. + |.else + | stw CARG1, 16(BASE) // Copy control var. + | stw CARG2, 20(BASE) + | stwu CARG3, 8(BASE) // Copy state. + | stw CARG4, 4(BASE) + |.endif + | bne ->vmeta_call + | ins_call + break; + + case BC_ITERN: + | // RA = base*8, (RB = (nresults+1)*8, RC = (nargs+1)*8 (2+1)*8) + |.if JIT + | // NYI: add hotloop, record BC_ITERN. + |.endif + | add RA, BASE, RA + | lwz TAB:RB, -12(RA) + | lwz RC, -4(RA) // Get index from control var. + | lwz TMP0, TAB:RB->asize + | lwz TMP1, TAB:RB->array + | addi PC, PC, 4 + |1: // Traverse array part. + | cmplw RC, TMP0 + | slwi TMP3, RC, 3 + | bge >5 // Index points after array part? + | lwzx TMP2, TMP1, TMP3 + |.if FPU + | lfdx f0, TMP1, TMP3 + |.else + | lwzux CARG1, TMP3, TMP1 + | lwz CARG2, 4(TMP3) + |.endif + | checknil TMP2 + | lwz INS, -4(PC) + | beq >4 + |.if DUALNUM + | stw RC, 4(RA) + | stw TISNUM, 0(RA) + |.else + | tonum_u f1, RC + |.endif + | addi RC, RC, 1 + | addis TMP3, PC, -(BCBIAS_J*4 >> 16) + |.if FPU + | stfd f0, 8(RA) + |.else + | stw CARG1, 8(RA) + | stw CARG2, 12(RA) + |.endif + | decode_RD4 TMP1, INS + | stw RC, -4(RA) // Update control var. + | add PC, TMP1, TMP3 + |.if not DUALNUM + | stfd f1, 0(RA) + |.endif + |3: + | ins_next + | + |4: // Skip holes in array part. + | addi RC, RC, 1 + | b <1 + | + |5: // Traverse hash part. + | lwz TMP1, TAB:RB->hmask + | sub RC, RC, TMP0 + | lwz TMP2, TAB:RB->node + |6: + | cmplw RC, TMP1 // End of iteration? Branch to ITERL+1. + | slwi TMP3, RC, 5 + | bgty <3 + | slwi RB, RC, 3 + | sub TMP3, TMP3, RB + | lwzx RB, TMP2, TMP3 + |.if FPU + | lfdx f0, TMP2, TMP3 + |.else + | add CARG3, TMP2, TMP3 + | lwz CARG1, 0(CARG3) + | lwz CARG2, 4(CARG3) + |.endif + | add NODE:TMP3, TMP2, TMP3 + | checknil RB + | lwz INS, -4(PC) + | beq >7 + |.if FPU + | lfd f1, NODE:TMP3->key + |.else + | lwz CARG3, NODE:TMP3->key.u32.hi + | lwz CARG4, NODE:TMP3->key.u32.lo + |.endif + | addis TMP2, PC, -(BCBIAS_J*4 >> 16) + |.if FPU + | stfd f0, 8(RA) + |.else + | stw CARG1, 8(RA) + | stw CARG2, 12(RA) + |.endif + | add RC, RC, TMP0 + | decode_RD4 TMP1, INS + |.if FPU + | stfd f1, 0(RA) + |.else + | stw CARG3, 0(RA) + | stw CARG4, 4(RA) + |.endif + | addi RC, RC, 1 + | add PC, TMP1, TMP2 + | stw RC, -4(RA) // Update control var. + | b <3 + | + |7: // Skip holes in hash part. + | addi RC, RC, 1 + | b <6 + break; + + case BC_ISNEXT: + | // RA = base*8, RD = target (points to ITERN) + | add RA, BASE, RA + | lwz TMP0, -24(RA) + | lwz CFUNC:TMP1, -20(RA) + | lwz TMP2, -16(RA) + | lwz TMP3, -8(RA) + | cmpwi cr0, TMP2, LJ_TTAB + | cmpwi cr1, TMP0, LJ_TFUNC + | cmpwi cr6, TMP3, LJ_TNIL + | bne cr1, >5 + | lbz TMP1, CFUNC:TMP1->ffid + | crand 4*cr0+eq, 4*cr0+eq, 4*cr6+eq + | cmpwi cr7, TMP1, FF_next_N + | srwi TMP0, RD, 1 + | crand 4*cr0+eq, 4*cr0+eq, 4*cr7+eq + | add TMP3, PC, TMP0 + | bne cr0, >5 + | lus TMP1, 0xfffe + | ori TMP1, TMP1, 0x7fff + | stw ZERO, -4(RA) // Initialize control var. + | stw TMP1, -8(RA) + | addis PC, TMP3, -(BCBIAS_J*4 >> 16) + |1: + | ins_next + |5: // Despecialize bytecode if any of the checks fail. + | li TMP0, BC_JMP + | li TMP1, BC_ITERC + | stb TMP0, -1(PC) + | addis PC, TMP3, -(BCBIAS_J*4 >> 16) + | stb TMP1, 3(PC) + | b <1 + break; + + case BC_VARG: + | // RA = base*8, RB = (nresults+1)*8, RC = numparams*8 + | lwz TMP0, FRAME_PC(BASE) + | add RC, BASE, RC + | add RA, BASE, RA + | addi RC, RC, FRAME_VARG + | add TMP2, RA, RB + | subi TMP3, BASE, 8 // TMP3 = vtop + | sub RC, RC, TMP0 // RC = vbase + | // Note: RC may now be even _above_ BASE if nargs was < numparams. + | cmplwi cr1, RB, 0 + |.if PPE + | sub TMP1, TMP3, RC + | cmpwi TMP1, 0 + |.else + | sub. TMP1, TMP3, RC + |.endif + | beq cr1, >5 // Copy all varargs? + | subi TMP2, TMP2, 16 + | ble >2 // No vararg slots? + |1: // Copy vararg slots to destination slots. + |.if FPU + | lfd f0, 0(RC) + |.else + | lwz CARG1, 0(RC) + | lwz CARG2, 4(RC) + |.endif + | addi RC, RC, 8 + |.if FPU + | stfd f0, 0(RA) + |.else + | stw CARG1, 0(RA) + | stw CARG2, 4(RA) + |.endif + | cmplw RA, TMP2 + | cmplw cr1, RC, TMP3 + | bge >3 // All destination slots filled? + | addi RA, RA, 8 + | blt cr1, <1 // More vararg slots? + |2: // Fill up remainder with nil. + | stw TISNIL, 0(RA) + | cmplw RA, TMP2 + | addi RA, RA, 8 + | blt <2 + |3: + | ins_next + | + |5: // Copy all varargs. + | lwz TMP0, L->maxstack + | li MULTRES, 8 // MULTRES = (0+1)*8 + | bley <3 // No vararg slots? + | add TMP2, RA, TMP1 + | cmplw TMP2, TMP0 + | addi MULTRES, TMP1, 8 + | bgt >7 + |6: + |.if FPU + | lfd f0, 0(RC) + |.else + | lwz CARG1, 0(RC) + | lwz CARG2, 4(RC) + |.endif + | addi RC, RC, 8 + |.if FPU + | stfd f0, 0(RA) + |.else + | stw CARG1, 0(RA) + | stw CARG2, 4(RA) + |.endif + | cmplw RC, TMP3 + | addi RA, RA, 8 + | blt <6 // More vararg slots? + | b <3 + | + |7: // Grow stack for varargs. + | mr CARG1, L + | stp RA, L->top + | sub SAVE0, RC, BASE // Need delta, because BASE may change. + | stp BASE, L->base + | sub RA, RA, BASE + | stw PC, SAVE_PC + | srwi CARG2, TMP1, 3 + | bl extern lj_state_growstack // (lua_State *L, int n) + | lp BASE, L->base + | add RA, BASE, RA + | add RC, BASE, SAVE0 + | subi TMP3, BASE, 8 + | b <6 + break; + + /* -- Returns ----------------------------------------------------------- */ + + case BC_RETM: + | // RA = results*8, RD = extra_nresults*8 + | add RD, RD, MULTRES // MULTRES >= 8, so RD >= 8. + | // Fall through. Assumes BC_RET follows. + break; + + case BC_RET: + | // RA = results*8, RD = (nresults+1)*8 + | lwz PC, FRAME_PC(BASE) + | add RA, BASE, RA + | mr MULTRES, RD + |1: + | andix. TMP0, PC, FRAME_TYPE + | xori TMP1, PC, FRAME_VARG + | bne ->BC_RETV_Z + | + |->BC_RET_Z: + | // BASE = base, RA = resultptr, RD = (nresults+1)*8, PC = return + | lwz INS, -4(PC) + | cmpwi RD, 8 + | subi TMP2, BASE, 8 + | subi RC, RD, 8 + | decode_RB8 RB, INS + | beq >3 + | li TMP1, 0 + |2: + | addi TMP3, TMP1, 8 + |.if FPU + | lfdx f0, RA, TMP1 + |.else + | add CARG3, RA, TMP1 + | lwz CARG1, 0(CARG3) + | lwz CARG2, 4(CARG3) + |.endif + | cmpw TMP3, RC + |.if FPU + | stfdx f0, TMP2, TMP1 + |.else + | add CARG3, TMP2, TMP1 + | stw CARG1, 0(CARG3) + | stw CARG2, 4(CARG3) + |.endif + | beq >3 + | addi TMP1, TMP3, 8 + |.if FPU + | lfdx f1, RA, TMP3 + |.else + | add CARG3, RA, TMP3 + | lwz CARG1, 0(CARG3) + | lwz CARG2, 4(CARG3) + |.endif + | cmpw TMP1, RC + |.if FPU + | stfdx f1, TMP2, TMP3 + |.else + | add CARG3, TMP2, TMP3 + | stw CARG1, 0(CARG3) + | stw CARG2, 4(CARG3) + |.endif + | bne <2 + |3: + |5: + | cmplw RB, RD + | decode_RA8 RA, INS + | bgt >6 + | sub BASE, TMP2, RA + | lwz LFUNC:TMP1, FRAME_FUNC(BASE) + | ins_next1 + | lwz TMP1, LFUNC:TMP1->pc + | lwz KBASE, PC2PROTO(k)(TMP1) + | ins_next2 + | + |6: // Fill up results with nil. + | subi TMP1, RD, 8 + | addi RD, RD, 8 + | stwx TISNIL, TMP2, TMP1 + | b <5 + | + |->BC_RETV_Z: // Non-standard return case. + | andix. TMP2, TMP1, FRAME_TYPEP + | bne ->vm_return + | // Return from vararg function: relocate BASE down. + | sub BASE, BASE, TMP1 + | lwz PC, FRAME_PC(BASE) + | b <1 + break; + + case BC_RET0: case BC_RET1: + | // RA = results*8, RD = (nresults+1)*8 + | lwz PC, FRAME_PC(BASE) + | add RA, BASE, RA + | mr MULTRES, RD + | andix. TMP0, PC, FRAME_TYPE + | xori TMP1, PC, FRAME_VARG + | bney ->BC_RETV_Z + | + | lwz INS, -4(PC) + | subi TMP2, BASE, 8 + | decode_RB8 RB, INS + if (op == BC_RET1) { + |.if FPU + | lfd f0, 0(RA) + | stfd f0, 0(TMP2) + |.else + | lwz CARG1, 0(RA) + | lwz CARG2, 4(RA) + | stw CARG1, 0(TMP2) + | stw CARG2, 4(TMP2) + |.endif + } + |5: + | cmplw RB, RD + | decode_RA8 RA, INS + | bgt >6 + | sub BASE, TMP2, RA + | lwz LFUNC:TMP1, FRAME_FUNC(BASE) + | ins_next1 + | lwz TMP1, LFUNC:TMP1->pc + | lwz KBASE, PC2PROTO(k)(TMP1) + | ins_next2 + | + |6: // Fill up results with nil. + | subi TMP1, RD, 8 + | addi RD, RD, 8 + | stwx TISNIL, TMP2, TMP1 + | b <5 + break; + + /* -- Loops and branches ------------------------------------------------ */ + + case BC_FORL: + |.if JIT + | hotloop + |.endif + | // Fall through. Assumes BC_IFORL follows. + break; + + case BC_JFORI: + case BC_JFORL: +#if !LJ_HASJIT + break; +#endif + case BC_FORI: + case BC_IFORL: + | // RA = base*8, RD = target (after end of loop or start of loop) + vk = (op == BC_IFORL || op == BC_JFORL); + |.if DUALNUM + | // Integer loop. + | lwzux TMP1, RA, BASE + | lwz CARG1, FORL_IDX*8+4(RA) + | cmplw cr0, TMP1, TISNUM + if (vk) { + | lwz CARG3, FORL_STEP*8+4(RA) + | bne >9 + |.if GPR64 + | // Need to check overflow for (a<<32) + (b<<32). + | rldicr TMP0, CARG1, 32, 31 + | rldicr TMP2, CARG3, 32, 31 + | add CARG1, CARG1, CARG3 + | addo. TMP0, TMP0, TMP2 + |.else + | addo. CARG1, CARG1, CARG3 + |.endif + | cmpwi cr6, CARG3, 0 + | lwz CARG2, FORL_STOP*8+4(RA) + | bso >6 + |4: + | stw CARG1, FORL_IDX*8+4(RA) + } else { + | lwz SAVE0, FORL_STEP*8(RA) + | lwz CARG3, FORL_STEP*8+4(RA) + | lwz TMP2, FORL_STOP*8(RA) + | lwz CARG2, FORL_STOP*8+4(RA) + | cmplw cr7, SAVE0, TISNUM + | cmplw cr1, TMP2, TISNUM + | crand 4*cr0+eq, 4*cr0+eq, 4*cr7+eq + | crand 4*cr0+eq, 4*cr0+eq, 4*cr1+eq + | cmpwi cr6, CARG3, 0 + | bne >9 + } + | blt cr6, >5 + | cmpw CARG1, CARG2 + |1: + | stw TISNUM, FORL_EXT*8(RA) + if (op != BC_JFORL) { + | srwi RD, RD, 1 + } + | stw CARG1, FORL_EXT*8+4(RA) + if (op != BC_JFORL) { + | add RD, PC, RD + } + if (op == BC_FORI) { + | bgt >3 // See FP loop below. + } else if (op == BC_JFORI) { + | addis PC, RD, -(BCBIAS_J*4 >> 16) + | bley >7 + } else if (op == BC_IFORL) { + | bgt >2 + | addis PC, RD, -(BCBIAS_J*4 >> 16) + } else { + | bley =>BC_JLOOP + } + |2: + | ins_next + |5: // Invert check for negative step. + | cmpw CARG2, CARG1 + | b <1 + if (vk) { + |6: // Potential overflow. + | checkov TMP0, <4 // Ignore unrelated overflow. + | b <2 + } + |.endif + if (vk) { + |.if DUALNUM + |9: // FP loop. + |.if FPU + | lfd f1, FORL_IDX*8(RA) + |.else + | lwz CARG1, FORL_IDX*8(RA) + | lwz CARG2, FORL_IDX*8+4(RA) + |.endif + |.else + | lfdux f1, RA, BASE + |.endif + |.if FPU + | lfd f3, FORL_STEP*8(RA) + | lfd f2, FORL_STOP*8(RA) + | fadd f1, f1, f3 + | stfd f1, FORL_IDX*8(RA) + |.else + | lwz CARG3, FORL_STEP*8(RA) + | lwz CARG4, FORL_STEP*8+4(RA) + | mr SAVE1, RD + | blex __adddf3 + | mr RD, SAVE1 + | stw CRET1, FORL_IDX*8(RA) + | stw CRET2, FORL_IDX*8+4(RA) + | lwz CARG3, FORL_STOP*8(RA) + | lwz CARG4, FORL_STOP*8+4(RA) + |.endif + | lwz SAVE0, FORL_STEP*8(RA) + } else { + |.if DUALNUM + |9: // FP loop. + |.else + | lwzux TMP1, RA, BASE + | lwz SAVE0, FORL_STEP*8(RA) + | lwz TMP2, FORL_STOP*8(RA) + | cmplw cr0, TMP1, TISNUM + | cmplw cr7, SAVE0, TISNUM + | cmplw cr1, TMP2, TISNUM + |.endif + |.if FPU + | lfd f1, FORL_IDX*8(RA) + |.else + | lwz CARG1, FORL_IDX*8(RA) + | lwz CARG2, FORL_IDX*8+4(RA) + |.endif + | crand 4*cr0+lt, 4*cr0+lt, 4*cr7+lt + | crand 4*cr0+lt, 4*cr0+lt, 4*cr1+lt + |.if FPU + | lfd f2, FORL_STOP*8(RA) + |.else + | lwz CARG3, FORL_STOP*8(RA) + | lwz CARG4, FORL_STOP*8+4(RA) + |.endif + | bge ->vmeta_for + } + | cmpwi cr6, SAVE0, 0 + if (op != BC_JFORL) { + | srwi RD, RD, 1 + } + |.if FPU + | stfd f1, FORL_EXT*8(RA) + |.else + | stw CARG1, FORL_EXT*8(RA) + | stw CARG2, FORL_EXT*8+4(RA) + |.endif + if (op != BC_JFORL) { + | add RD, PC, RD + } + |.if FPU + | fcmpu cr0, f1, f2 + |.else + | mr SAVE1, RD + | blex __ledf2 + | cmpwi CRET1, 0 + | mr RD, SAVE1 + |.endif + if (op == BC_JFORI) { + | addis PC, RD, -(BCBIAS_J*4 >> 16) + } + | blt cr6, >5 + if (op == BC_FORI) { + | bgt >3 + } else if (op == BC_IFORL) { + |.if DUALNUM + | bgty <2 + |.else + | bgt >2 + |.endif + |1: + | addis PC, RD, -(BCBIAS_J*4 >> 16) + } else if (op == BC_JFORI) { + | bley >7 + } else { + | bley =>BC_JLOOP + } + |.if DUALNUM + | b <2 + |.else + |2: + | ins_next + |.endif + |5: // Negative step. + if (op == BC_FORI) { + | bge <2 + |3: // Used by integer loop, too. + | addis PC, RD, -(BCBIAS_J*4 >> 16) + } else if (op == BC_IFORL) { + | bgey <1 + } else if (op == BC_JFORI) { + | bgey >7 + } else { + | bgey =>BC_JLOOP + } + | b <2 + if (op == BC_JFORI) { + |7: + | lwz INS, -4(PC) + | decode_RD8 RD, INS + | b =>BC_JLOOP + } + break; + + case BC_ITERL: + |.if JIT + | hotloop + |.endif + | // Fall through. Assumes BC_IITERL follows. + break; + + case BC_JITERL: +#if !LJ_HASJIT + break; +#endif + case BC_IITERL: + | // RA = base*8, RD = target + | lwzux TMP1, RA, BASE + | lwz TMP2, 4(RA) + | checknil TMP1; beq >1 // Stop if iterator returned nil. + if (op == BC_JITERL) { + | stw TMP1, -8(RA) + | stw TMP2, -4(RA) + | b =>BC_JLOOP + } else { + | branch_RD // Otherwise save control var + branch. + | stw TMP1, -8(RA) + | stw TMP2, -4(RA) + } + |1: + | ins_next + break; + + case BC_LOOP: + | // RA = base*8, RD = target (loop extent) + | // Note: RA/RD is only used by trace recorder to determine scope/extent + | // This opcode does NOT jump, it's only purpose is to detect a hot loop. + |.if JIT + | hotloop + |.endif + | // Fall through. Assumes BC_ILOOP follows. + break; + + case BC_ILOOP: + | // RA = base*8, RD = target (loop extent) + | ins_next + break; + + case BC_JLOOP: + |.if JIT + | // RA = base*8 (ignored), RD = traceno*8 + | lwz TMP1, DISPATCH_J(trace)(DISPATCH) + | srwi RD, RD, 1 + | // Traces on PPC don't store the trace number, so use 0. + | stw ZERO, DISPATCH_GL(vmstate)(DISPATCH) + | lwzx TRACE:TMP2, TMP1, RD + | clrso TMP1 + | lp TMP2, TRACE:TMP2->mcode + | stw BASE, DISPATCH_GL(jit_base)(DISPATCH) + | mtctr TMP2 + | addi JGL, DISPATCH, GG_DISP2G+32768 + | stw L, DISPATCH_GL(tmpbuf.L)(DISPATCH) + | bctr + |.endif + break; + + case BC_JMP: + | // RA = base*8 (only used by trace recorder), RD = target + | branch_RD + | ins_next + break; + + /* -- Function headers -------------------------------------------------- */ + + case BC_FUNCF: + |.if JIT + | hotcall + |.endif + case BC_FUNCV: /* NYI: compiled vararg functions. */ + | // Fall through. Assumes BC_IFUNCF/BC_IFUNCV follow. + break; + + case BC_JFUNCF: +#if !LJ_HASJIT + break; +#endif + case BC_IFUNCF: + | // BASE = new base, RA = BASE+framesize*8, RB = LFUNC, RC = nargs*8 + | lwz TMP2, L->maxstack + | lbz TMP1, -4+PC2PROTO(numparams)(PC) + | lwz KBASE, -4+PC2PROTO(k)(PC) + | cmplw RA, TMP2 + | slwi TMP1, TMP1, 3 + | bgt ->vm_growstack_l + if (op != BC_JFUNCF) { + | ins_next1 + } + |2: + | cmplw NARGS8:RC, TMP1 // Check for missing parameters. + | blt >3 + if (op == BC_JFUNCF) { + | decode_RD8 RD, INS + | b =>BC_JLOOP + } else { + | ins_next2 + } + | + |3: // Clear missing parameters. + | stwx TISNIL, BASE, NARGS8:RC + | addi NARGS8:RC, NARGS8:RC, 8 + | b <2 + break; + + case BC_JFUNCV: +#if !LJ_HASJIT + break; +#endif + | NYI // NYI: compiled vararg functions + break; /* NYI: compiled vararg functions. */ + + case BC_IFUNCV: + | // BASE = new base, RA = BASE+framesize*8, RB = LFUNC, RC = nargs*8 + | lwz TMP2, L->maxstack + | add TMP1, BASE, RC + | add TMP0, RA, RC + | stw LFUNC:RB, 4(TMP1) // Store copy of LFUNC. + | addi TMP3, RC, 8+FRAME_VARG + | lwz KBASE, -4+PC2PROTO(k)(PC) + | cmplw TMP0, TMP2 + | stw TMP3, 0(TMP1) // Store delta + FRAME_VARG. + | bge ->vm_growstack_l + | lbz TMP2, -4+PC2PROTO(numparams)(PC) + | mr RA, BASE + | mr RC, TMP1 + | ins_next1 + | cmpwi TMP2, 0 + | addi BASE, TMP1, 8 + | beq >3 + |1: + | cmplw RA, RC // Less args than parameters? + | lwz TMP0, 0(RA) + | lwz TMP3, 4(RA) + | bge >4 + | stw TISNIL, 0(RA) // Clear old fixarg slot (help the GC). + | addi RA, RA, 8 + |2: + | addic. TMP2, TMP2, -1 + | stw TMP0, 8(TMP1) + | stw TMP3, 12(TMP1) + | addi TMP1, TMP1, 8 + | bne <1 + |3: + | ins_next2 + | + |4: // Clear missing parameters. + | li TMP0, LJ_TNIL + | b <2 + break; + + case BC_FUNCC: + case BC_FUNCCW: + | // BASE = new base, RA = BASE+framesize*8, RB = CFUNC, RC = nargs*8 + if (op == BC_FUNCC) { + | lp RD, CFUNC:RB->f + } else { + | lp RD, DISPATCH_GL(wrapf)(DISPATCH) + } + | add TMP1, RA, NARGS8:RC + | lwz TMP2, L->maxstack + | .toc lp TMP3, 0(RD) + | add RC, BASE, NARGS8:RC + | stp BASE, L->base + | cmplw TMP1, TMP2 + | stp RC, L->top + | li_vmstate C + |.if TOC + | mtctr TMP3 + |.else + | mtctr RD + |.endif + if (op == BC_FUNCCW) { + | lp CARG2, CFUNC:RB->f + } + | mr CARG1, L + | bgt ->vm_growstack_c // Need to grow stack. + | .toc lp TOCREG, TOC_OFS(RD) + | .tocenv lp ENVREG, ENV_OFS(RD) + | st_vmstate + | bctrl // (lua_State *L [, lua_CFunction f]) + | // Returns nresults. + | lp BASE, L->base + | .toc ld TOCREG, SAVE_TOC + | slwi RD, CRET1, 3 + | lp TMP1, L->top + | li_vmstate INTERP + | lwz PC, FRAME_PC(BASE) // Fetch PC of caller. + | stw L, DISPATCH_GL(cur_L)(DISPATCH) + | sub RA, TMP1, RD // RA = L->top - nresults*8 + | st_vmstate + | b ->vm_returnc + break; + + /* ---------------------------------------------------------------------- */ + + default: + fprintf(stderr, "Error: undefined opcode BC_%s\n", bc_names[op]); + exit(2); + break; + } +} + +static int build_backend(BuildCtx *ctx) +{ + int op; + + dasm_growpc(Dst, BC__MAX); + + build_subroutines(ctx); + + |.code_op + for (op = 0; op < BC__MAX; op++) + build_ins(ctx, (BCOp)op, op); + + return BC__MAX; +} + +/* Emit pseudo frame-info for all assembler functions. */ +static void emit_asm_debug(BuildCtx *ctx) +{ + int fcofs = (int)((uint8_t *)ctx->glob[GLOB_vm_ffi_call] - ctx->code); + int i; + switch (ctx->mode) { + case BUILD_elfasm: + fprintf(ctx->fp, "\t.section .debug_frame,\"\",@progbits\n"); + fprintf(ctx->fp, + ".Lframe0:\n" + "\t.long .LECIE0-.LSCIE0\n" + ".LSCIE0:\n" + "\t.long 0xffffffff\n" + "\t.byte 0x1\n" + "\t.string \"\"\n" + "\t.uleb128 0x1\n" + "\t.sleb128 -4\n" + "\t.byte 65\n" + "\t.byte 0xc\n\t.uleb128 1\n\t.uleb128 0\n" + "\t.align 2\n" + ".LECIE0:\n\n"); + fprintf(ctx->fp, + ".LSFDE0:\n" + "\t.long .LEFDE0-.LASFDE0\n" + ".LASFDE0:\n" + "\t.long .Lframe0\n" + "\t.long .Lbegin\n" + "\t.long %d\n" + "\t.byte 0xe\n\t.uleb128 %d\n" + "\t.byte 0x11\n\t.uleb128 65\n\t.sleb128 -1\n" + "\t.byte 0x5\n\t.uleb128 70\n\t.uleb128 55\n", + fcofs, CFRAME_SIZE); + for (i = 14; i <= 31; i++) + fprintf(ctx->fp, + "\t.byte %d\n\t.uleb128 %d\n" + "\t.byte %d\n\t.uleb128 %d\n", + 0x80+i, 37+(31-i), 0x80+32+i, 2+2*(31-i)); + fprintf(ctx->fp, + "\t.align 2\n" + ".LEFDE0:\n\n"); +#if LJ_HASFFI + fprintf(ctx->fp, + ".LSFDE1:\n" + "\t.long .LEFDE1-.LASFDE1\n" + ".LASFDE1:\n" + "\t.long .Lframe0\n" +#if LJ_TARGET_PS3 + "\t.long .lj_vm_ffi_call\n" +#else + "\t.long lj_vm_ffi_call\n" +#endif + "\t.long %d\n" + "\t.byte 0x11\n\t.uleb128 65\n\t.sleb128 -1\n" + "\t.byte 0x8e\n\t.uleb128 2\n" + "\t.byte 0xd\n\t.uleb128 0xe\n" + "\t.align 2\n" + ".LEFDE1:\n\n", (int)ctx->codesz - fcofs); +#endif +#if !LJ_NO_UNWIND + fprintf(ctx->fp, "\t.section .eh_frame,\"a\",@progbits\n"); + fprintf(ctx->fp, + ".Lframe1:\n" + "\t.long .LECIE1-.LSCIE1\n" + ".LSCIE1:\n" + "\t.long 0\n" + "\t.byte 0x1\n" + "\t.string \"zPR\"\n" + "\t.uleb128 0x1\n" + "\t.sleb128 -4\n" + "\t.byte 65\n" + "\t.uleb128 6\n" /* augmentation length */ + "\t.byte 0x1b\n" /* pcrel|sdata4 */ + "\t.long lj_err_unwind_dwarf-.\n" + "\t.byte 0x1b\n" /* pcrel|sdata4 */ + "\t.byte 0xc\n\t.uleb128 1\n\t.uleb128 0\n" + "\t.align 2\n" + ".LECIE1:\n\n"); + fprintf(ctx->fp, + ".LSFDE2:\n" + "\t.long .LEFDE2-.LASFDE2\n" + ".LASFDE2:\n" + "\t.long .LASFDE2-.Lframe1\n" + "\t.long .Lbegin-.\n" + "\t.long %d\n" + "\t.uleb128 0\n" /* augmentation length */ + "\t.byte 0xe\n\t.uleb128 %d\n" + "\t.byte 0x11\n\t.uleb128 65\n\t.sleb128 -1\n" + "\t.byte 0x5\n\t.uleb128 70\n\t.uleb128 55\n", + fcofs, CFRAME_SIZE); + for (i = 14; i <= 31; i++) + fprintf(ctx->fp, + "\t.byte %d\n\t.uleb128 %d\n" + "\t.byte %d\n\t.uleb128 %d\n", + 0x80+i, 37+(31-i), 0x80+32+i, 2+2*(31-i)); + fprintf(ctx->fp, + "\t.align 2\n" + ".LEFDE2:\n\n"); +#if LJ_HASFFI + fprintf(ctx->fp, + ".Lframe2:\n" + "\t.long .LECIE2-.LSCIE2\n" + ".LSCIE2:\n" + "\t.long 0\n" + "\t.byte 0x1\n" + "\t.string \"zR\"\n" + "\t.uleb128 0x1\n" + "\t.sleb128 -4\n" + "\t.byte 65\n" + "\t.uleb128 1\n" /* augmentation length */ + "\t.byte 0x1b\n" /* pcrel|sdata4 */ + "\t.byte 0xc\n\t.uleb128 1\n\t.uleb128 0\n" + "\t.align 2\n" + ".LECIE2:\n\n"); + fprintf(ctx->fp, + ".LSFDE3:\n" + "\t.long .LEFDE3-.LASFDE3\n" + ".LASFDE3:\n" + "\t.long .LASFDE3-.Lframe2\n" + "\t.long lj_vm_ffi_call-.\n" + "\t.long %d\n" + "\t.uleb128 0\n" /* augmentation length */ + "\t.byte 0x11\n\t.uleb128 65\n\t.sleb128 -1\n" + "\t.byte 0x8e\n\t.uleb128 2\n" + "\t.byte 0xd\n\t.uleb128 0xe\n" + "\t.align 2\n" + ".LEFDE3:\n\n", (int)ctx->codesz - fcofs); +#endif +#endif + break; + default: + break; + } +} + diff --git a/lib/LuaJIT/vm_x64.dasc b/lib/LuaJIT/vm_x64.dasc new file mode 100644 index 0000000..a003fb4 --- /dev/null +++ b/lib/LuaJIT/vm_x64.dasc @@ -0,0 +1,4909 @@ +|// Low-level VM code for x64 CPUs in LJ_GC64 mode. +|// Bytecode interpreter, fast functions and helper functions. +|// Copyright (C) 2005-2017 Mike Pall. See Copyright Notice in luajit.h +| +|.arch x64 +|.section code_op, code_sub +| +|.actionlist build_actionlist +|.globals GLOB_ +|.globalnames globnames +|.externnames extnames +| +|//----------------------------------------------------------------------- +| +|.if WIN +|.define X64WIN, 1 // Windows/x64 calling conventions. +|.endif +| +|// Fixed register assignments for the interpreter. +|// This is very fragile and has many dependencies. Caveat emptor. +|.define BASE, rdx // Not C callee-save, refetched anyway. +|.if X64WIN +|.define KBASE, rdi // Must be C callee-save. +|.define PC, rsi // Must be C callee-save. +|.define DISPATCH, rbx // Must be C callee-save. +|.define KBASEd, edi +|.define PCd, esi +|.define DISPATCHd, ebx +|.else +|.define KBASE, r15 // Must be C callee-save. +|.define PC, rbx // Must be C callee-save. +|.define DISPATCH, r14 // Must be C callee-save. +|.define KBASEd, r15d +|.define PCd, ebx +|.define DISPATCHd, r14d +|.endif +| +|.define RA, rcx +|.define RAd, ecx +|.define RAH, ch +|.define RAL, cl +|.define RB, rbp // Must be rbp (C callee-save). +|.define RBd, ebp +|.define RC, rax // Must be rax. +|.define RCd, eax +|.define RCW, ax +|.define RCH, ah +|.define RCL, al +|.define OP, RBd +|.define RD, RC +|.define RDd, RCd +|.define RDW, RCW +|.define RDL, RCL +|.define TMPR, r10 +|.define TMPRd, r10d +|.define ITYPE, r11 +|.define ITYPEd, r11d +| +|.if X64WIN +|.define CARG1, rcx // x64/WIN64 C call arguments. +|.define CARG2, rdx +|.define CARG3, r8 +|.define CARG4, r9 +|.define CARG1d, ecx +|.define CARG2d, edx +|.define CARG3d, r8d +|.define CARG4d, r9d +|.else +|.define CARG1, rdi // x64/POSIX C call arguments. +|.define CARG2, rsi +|.define CARG3, rdx +|.define CARG4, rcx +|.define CARG5, r8 +|.define CARG6, r9 +|.define CARG1d, edi +|.define CARG2d, esi +|.define CARG3d, edx +|.define CARG4d, ecx +|.define CARG5d, r8d +|.define CARG6d, r9d +|.endif +| +|// Type definitions. Some of these are only used for documentation. +|.type L, lua_State +|.type GL, global_State +|.type TVALUE, TValue +|.type GCOBJ, GCobj +|.type STR, GCstr +|.type TAB, GCtab +|.type LFUNC, GCfuncL +|.type CFUNC, GCfuncC +|.type PROTO, GCproto +|.type UPVAL, GCupval +|.type NODE, Node +|.type NARGS, int +|.type TRACE, GCtrace +|.type SBUF, SBuf +| +|// Stack layout while in interpreter. Must match with lj_frame.h. +|//----------------------------------------------------------------------- +|.if X64WIN // x64/Windows stack layout +| +|.define CFRAME_SPACE, aword*5 // Delta for rsp (see <--). +|.macro saveregs_ +| push rdi; push rsi; push rbx +| sub rsp, CFRAME_SPACE +|.endmacro +|.macro saveregs +| push rbp; saveregs_ +|.endmacro +|.macro restoreregs +| add rsp, CFRAME_SPACE +| pop rbx; pop rsi; pop rdi; pop rbp +|.endmacro +| +|.define SAVE_CFRAME, aword [rsp+aword*13] +|.define SAVE_PC, aword [rsp+aword*12] +|.define SAVE_L, aword [rsp+aword*11] +|.define SAVE_ERRF, dword [rsp+dword*21] +|.define SAVE_NRES, dword [rsp+dword*20] +|//----- 16 byte aligned, ^^^ 32 byte register save area, owned by interpreter +|.define SAVE_RET, aword [rsp+aword*9] //<-- rsp entering interpreter. +|.define SAVE_R4, aword [rsp+aword*8] +|.define SAVE_R3, aword [rsp+aword*7] +|.define SAVE_R2, aword [rsp+aword*6] +|.define SAVE_R1, aword [rsp+aword*5] //<-- rsp after register saves. +|.define ARG5, aword [rsp+aword*4] +|.define CSAVE_4, aword [rsp+aword*3] +|.define CSAVE_3, aword [rsp+aword*2] +|.define CSAVE_2, aword [rsp+aword*1] +|.define CSAVE_1, aword [rsp] //<-- rsp while in interpreter. +|//----- 16 byte aligned, ^^^ 32 byte register save area, owned by callee +| +|.define ARG5d, dword [rsp+dword*8] +|.define TMP1, ARG5 // TMP1 overlaps ARG5 +|.define TMP1d, ARG5d +|.define TMP1hi, dword [rsp+dword*9] +|.define MULTRES, TMP1d // MULTRES overlaps TMP1d. +| +|//----------------------------------------------------------------------- +|.else // x64/POSIX stack layout +| +|.define CFRAME_SPACE, aword*5 // Delta for rsp (see <--). +|.macro saveregs_ +| push rbx; push r15; push r14 +|.if NO_UNWIND +| push r13; push r12 +|.endif +| sub rsp, CFRAME_SPACE +|.endmacro +|.macro saveregs +| push rbp; saveregs_ +|.endmacro +|.macro restoreregs +| add rsp, CFRAME_SPACE +|.if NO_UNWIND +| pop r12; pop r13 +|.endif +| pop r14; pop r15; pop rbx; pop rbp +|.endmacro +| +|//----- 16 byte aligned, +|.if NO_UNWIND +|.define SAVE_RET, aword [rsp+aword*11] //<-- rsp entering interpreter. +|.define SAVE_R4, aword [rsp+aword*10] +|.define SAVE_R3, aword [rsp+aword*9] +|.define SAVE_R2, aword [rsp+aword*8] +|.define SAVE_R1, aword [rsp+aword*7] +|.define SAVE_RU2, aword [rsp+aword*6] +|.define SAVE_RU1, aword [rsp+aword*5] //<-- rsp after register saves. +|.else +|.define SAVE_RET, aword [rsp+aword*9] //<-- rsp entering interpreter. +|.define SAVE_R4, aword [rsp+aword*8] +|.define SAVE_R3, aword [rsp+aword*7] +|.define SAVE_R2, aword [rsp+aword*6] +|.define SAVE_R1, aword [rsp+aword*5] //<-- rsp after register saves. +|.endif +|.define SAVE_CFRAME, aword [rsp+aword*4] +|.define SAVE_PC, aword [rsp+aword*3] +|.define SAVE_L, aword [rsp+aword*2] +|.define SAVE_ERRF, dword [rsp+dword*3] +|.define SAVE_NRES, dword [rsp+dword*2] +|.define TMP1, aword [rsp] //<-- rsp while in interpreter. +|//----- 16 byte aligned +| +|.define TMP1d, dword [rsp] +|.define TMP1hi, dword [rsp+dword*1] +|.define MULTRES, TMP1d // MULTRES overlaps TMP1d. +| +|.endif +| +|//----------------------------------------------------------------------- +| +|// Instruction headers. +|.macro ins_A; .endmacro +|.macro ins_AD; .endmacro +|.macro ins_AJ; .endmacro +|.macro ins_ABC; movzx RBd, RCH; movzx RCd, RCL; .endmacro +|.macro ins_AB_; movzx RBd, RCH; .endmacro +|.macro ins_A_C; movzx RCd, RCL; .endmacro +|.macro ins_AND; not RD; .endmacro +| +|// Instruction decode+dispatch. Carefully tuned (nope, lodsd is not faster). +|.macro ins_NEXT +| mov RCd, [PC] +| movzx RAd, RCH +| movzx OP, RCL +| add PC, 4 +| shr RCd, 16 +| jmp aword [DISPATCH+OP*8] +|.endmacro +| +|// Instruction footer. +|.if 1 +| // Replicated dispatch. Less unpredictable branches, but higher I-Cache use. +| .define ins_next, ins_NEXT +| .define ins_next_, ins_NEXT +|.else +| // Common dispatch. Lower I-Cache use, only one (very) unpredictable branch. +| // Affects only certain kinds of benchmarks (and only with -j off). +| // Around 10%-30% slower on Core2, a lot more slower on P4. +| .macro ins_next +| jmp ->ins_next +| .endmacro +| .macro ins_next_ +| ->ins_next: +| ins_NEXT +| .endmacro +|.endif +| +|// Call decode and dispatch. +|.macro ins_callt +| // BASE = new base, RB = LFUNC, RD = nargs+1, [BASE-8] = PC +| mov PC, LFUNC:RB->pc +| mov RAd, [PC] +| movzx OP, RAL +| movzx RAd, RAH +| add PC, 4 +| jmp aword [DISPATCH+OP*8] +|.endmacro +| +|.macro ins_call +| // BASE = new base, RB = LFUNC, RD = nargs+1 +| mov [BASE-8], PC +| ins_callt +|.endmacro +| +|//----------------------------------------------------------------------- +| +|// Macros to clear or set tags. +|.macro cleartp, reg; shl reg, 17; shr reg, 17; .endmacro +|.macro settp, reg, tp +| mov64 ITYPE, ((uint64_t)tp<<47) +| or reg, ITYPE +|.endmacro +|.macro settp, dst, reg, tp +| mov64 dst, ((uint64_t)tp<<47) +| or dst, reg +|.endmacro +|.macro setint, reg +| settp reg, LJ_TISNUM +|.endmacro +|.macro setint, dst, reg +| settp dst, reg, LJ_TISNUM +|.endmacro +| +|// Macros to test operand types. +|.macro checktp_nc, reg, tp, target +| mov ITYPE, reg +| sar ITYPE, 47 +| cmp ITYPEd, tp +| jne target +|.endmacro +|.macro checktp, reg, tp, target +| mov ITYPE, reg +| cleartp reg +| sar ITYPE, 47 +| cmp ITYPEd, tp +| jne target +|.endmacro +|.macro checktptp, src, tp, target +| mov ITYPE, src +| sar ITYPE, 47 +| cmp ITYPEd, tp +| jne target +|.endmacro +|.macro checkstr, reg, target; checktp reg, LJ_TSTR, target; .endmacro +|.macro checktab, reg, target; checktp reg, LJ_TTAB, target; .endmacro +|.macro checkfunc, reg, target; checktp reg, LJ_TFUNC, target; .endmacro +| +|.macro checknumx, reg, target, jump +| mov ITYPE, reg +| sar ITYPE, 47 +| cmp ITYPEd, LJ_TISNUM +| jump target +|.endmacro +|.macro checkint, reg, target; checknumx reg, target, jne; .endmacro +|.macro checkinttp, src, target; checknumx src, target, jne; .endmacro +|.macro checknum, reg, target; checknumx reg, target, jae; .endmacro +|.macro checknumtp, src, target; checknumx src, target, jae; .endmacro +|.macro checknumber, src, target; checknumx src, target, ja; .endmacro +| +|.macro mov_false, reg; mov64 reg, (int64_t)~((uint64_t)1<<47); .endmacro +|.macro mov_true, reg; mov64 reg, (int64_t)~((uint64_t)2<<47); .endmacro +| +|// These operands must be used with movzx. +|.define PC_OP, byte [PC-4] +|.define PC_RA, byte [PC-3] +|.define PC_RB, byte [PC-1] +|.define PC_RC, byte [PC-2] +|.define PC_RD, word [PC-2] +| +|.macro branchPC, reg +| lea PC, [PC+reg*4-BCBIAS_J*4] +|.endmacro +| +|// Assumes DISPATCH is relative to GL. +#define DISPATCH_GL(field) (GG_DISP2G + (int)offsetof(global_State, field)) +#define DISPATCH_J(field) (GG_DISP2J + (int)offsetof(jit_State, field)) +| +#define PC2PROTO(field) ((int)offsetof(GCproto, field)-(int)sizeof(GCproto)) +| +|// Decrement hashed hotcount and trigger trace recorder if zero. +|.macro hotloop, reg +| mov reg, PCd +| shr reg, 1 +| and reg, HOTCOUNT_PCMASK +| sub word [DISPATCH+reg+GG_DISP2HOT], HOTCOUNT_LOOP +| jb ->vm_hotloop +|.endmacro +| +|.macro hotcall, reg +| mov reg, PCd +| shr reg, 1 +| and reg, HOTCOUNT_PCMASK +| sub word [DISPATCH+reg+GG_DISP2HOT], HOTCOUNT_CALL +| jb ->vm_hotcall +|.endmacro +| +|// Set current VM state. +|.macro set_vmstate, st +| mov dword [DISPATCH+DISPATCH_GL(vmstate)], ~LJ_VMST_..st +|.endmacro +| +|.macro fpop1; fstp st1; .endmacro +| +|// Synthesize SSE FP constants. +|.macro sseconst_abs, reg, tmp // Synthesize abs mask. +| mov64 tmp, U64x(7fffffff,ffffffff); movd reg, tmp +|.endmacro +| +|.macro sseconst_hi, reg, tmp, val // Synthesize hi-32 bit const. +| mov64 tmp, U64x(val,00000000); movd reg, tmp +|.endmacro +| +|.macro sseconst_sign, reg, tmp // Synthesize sign mask. +| sseconst_hi reg, tmp, 80000000 +|.endmacro +|.macro sseconst_1, reg, tmp // Synthesize 1.0. +| sseconst_hi reg, tmp, 3ff00000 +|.endmacro +|.macro sseconst_m1, reg, tmp // Synthesize -1.0. +| sseconst_hi reg, tmp, bff00000 +|.endmacro +|.macro sseconst_2p52, reg, tmp // Synthesize 2^52. +| sseconst_hi reg, tmp, 43300000 +|.endmacro +|.macro sseconst_tobit, reg, tmp // Synthesize 2^52 + 2^51. +| sseconst_hi reg, tmp, 43380000 +|.endmacro +| +|// Move table write barrier back. Overwrites reg. +|.macro barrierback, tab, reg +| and byte tab->marked, (uint8_t)~LJ_GC_BLACK // black2gray(tab) +| mov reg, [DISPATCH+DISPATCH_GL(gc.grayagain)] +| mov [DISPATCH+DISPATCH_GL(gc.grayagain)], tab +| mov tab->gclist, reg +|.endmacro +| +|//----------------------------------------------------------------------- + +/* Generate subroutines used by opcodes and other parts of the VM. */ +/* The .code_sub section should be last to help static branch prediction. */ +static void build_subroutines(BuildCtx *ctx) +{ + |.code_sub + | + |//----------------------------------------------------------------------- + |//-- Return handling ---------------------------------------------------- + |//----------------------------------------------------------------------- + | + |->vm_returnp: + | test PCd, FRAME_P + | jz ->cont_dispatch + | + | // Return from pcall or xpcall fast func. + | and PC, -8 + | sub BASE, PC // Restore caller base. + | lea RA, [RA+PC-8] // Rebase RA and prepend one result. + | mov PC, [BASE-8] // Fetch PC of previous frame. + | // Prepending may overwrite the pcall frame, so do it at the end. + | mov_true ITYPE + | mov aword [BASE+RA], ITYPE // Prepend true to results. + | + |->vm_returnc: + | add RDd, 1 // RD = nresults+1 + | jz ->vm_unwind_yield + | mov MULTRES, RDd + | test PC, FRAME_TYPE + | jz ->BC_RET_Z // Handle regular return to Lua. + | + |->vm_return: + | // BASE = base, RA = resultofs, RD = nresults+1 (= MULTRES), PC = return + | xor PC, FRAME_C + | test PCd, FRAME_TYPE + | jnz ->vm_returnp + | + | // Return to C. + | set_vmstate C + | and PC, -8 + | sub PC, BASE + | neg PC // Previous base = BASE - delta. + | + | sub RDd, 1 + | jz >2 + |1: // Move results down. + | mov RB, [BASE+RA] + | mov [BASE-16], RB + | add BASE, 8 + | sub RDd, 1 + | jnz <1 + |2: + | mov L:RB, SAVE_L + | mov L:RB->base, PC + |3: + | mov RDd, MULTRES + | mov RAd, SAVE_NRES // RA = wanted nresults+1 + |4: + | cmp RAd, RDd + | jne >6 // More/less results wanted? + |5: + | sub BASE, 16 + | mov L:RB->top, BASE + | + |->vm_leave_cp: + | mov RA, SAVE_CFRAME // Restore previous C frame. + | mov L:RB->cframe, RA + | xor eax, eax // Ok return status for vm_pcall. + | + |->vm_leave_unw: + | restoreregs + | ret + | + |6: + | jb >7 // Less results wanted? + | // More results wanted. Check stack size and fill up results with nil. + | cmp BASE, L:RB->maxstack + | ja >8 + | mov aword [BASE-16], LJ_TNIL + | add BASE, 8 + | add RDd, 1 + | jmp <4 + | + |7: // Less results wanted. + | test RAd, RAd + | jz <5 // But check for LUA_MULTRET+1. + | sub RA, RD // Negative result! + | lea BASE, [BASE+RA*8] // Correct top. + | jmp <5 + | + |8: // Corner case: need to grow stack for filling up results. + | // This can happen if: + | // - A C function grows the stack (a lot). + | // - The GC shrinks the stack in between. + | // - A return back from a lua_call() with (high) nresults adjustment. + | mov L:RB->top, BASE // Save current top held in BASE (yes). + | mov MULTRES, RDd // Need to fill only remainder with nil. + | mov CARG2d, RAd + | mov CARG1, L:RB + | call extern lj_state_growstack // (lua_State *L, int n) + | mov BASE, L:RB->top // Need the (realloced) L->top in BASE. + | jmp <3 + | + |->vm_unwind_yield: + | mov al, LUA_YIELD + | jmp ->vm_unwind_c_eh + | + |->vm_unwind_c: // Unwind C stack, return from vm_pcall. + | // (void *cframe, int errcode) + | mov eax, CARG2d // Error return status for vm_pcall. + | mov rsp, CARG1 + |->vm_unwind_c_eh: // Landing pad for external unwinder. + | mov L:RB, SAVE_L + | mov GL:RB, L:RB->glref + | mov dword GL:RB->vmstate, ~LJ_VMST_C + | jmp ->vm_leave_unw + | + |->vm_unwind_rethrow: + |.if not X64WIN + | mov CARG1, SAVE_L + | mov CARG2d, eax + | restoreregs + | jmp extern lj_err_throw // (lua_State *L, int errcode) + |.endif + | + |->vm_unwind_ff: // Unwind C stack, return from ff pcall. + | // (void *cframe) + | and CARG1, CFRAME_RAWMASK + | mov rsp, CARG1 + |->vm_unwind_ff_eh: // Landing pad for external unwinder. + | mov L:RB, SAVE_L + | mov RDd, 1+1 // Really 1+2 results, incr. later. + | mov BASE, L:RB->base + | mov DISPATCH, L:RB->glref // Setup pointer to dispatch table. + | add DISPATCH, GG_G2DISP + | mov PC, [BASE-8] // Fetch PC of previous frame. + | mov_false RA + | mov RB, [BASE] + | mov [BASE-16], RA // Prepend false to error message. + | mov [BASE-8], RB + | mov RA, -16 // Results start at BASE+RA = BASE-16. + | set_vmstate INTERP + | jmp ->vm_returnc // Increments RD/MULTRES and returns. + | + |//----------------------------------------------------------------------- + |//-- Grow stack for calls ----------------------------------------------- + |//----------------------------------------------------------------------- + | + |->vm_growstack_c: // Grow stack for C function. + | mov CARG2d, LUA_MINSTACK + | jmp >2 + | + |->vm_growstack_v: // Grow stack for vararg Lua function. + | sub RD, 16 // LJ_FR2 + | jmp >1 + | + |->vm_growstack_f: // Grow stack for fixarg Lua function. + | // BASE = new base, RD = nargs+1, RB = L, PC = first PC + | lea RD, [BASE+NARGS:RD*8-8] + |1: + | movzx RAd, byte [PC-4+PC2PROTO(framesize)] + | add PC, 4 // Must point after first instruction. + | mov L:RB->base, BASE + | mov L:RB->top, RD + | mov SAVE_PC, PC + | mov CARG2, RA + |2: + | // RB = L, L->base = new base, L->top = top + | mov CARG1, L:RB + | call extern lj_state_growstack // (lua_State *L, int n) + | mov BASE, L:RB->base + | mov RD, L:RB->top + | mov LFUNC:RB, [BASE-16] + | cleartp LFUNC:RB + | sub RD, BASE + | shr RDd, 3 + | add NARGS:RDd, 1 + | // BASE = new base, RB = LFUNC, RD = nargs+1 + | ins_callt // Just retry the call. + | + |//----------------------------------------------------------------------- + |//-- Entry points into the assembler VM --------------------------------- + |//----------------------------------------------------------------------- + | + |->vm_resume: // Setup C frame and resume thread. + | // (lua_State *L, TValue *base, int nres1 = 0, ptrdiff_t ef = 0) + | saveregs + | mov L:RB, CARG1 // Caveat: CARG1 may be RA. + | mov SAVE_L, CARG1 + | mov RA, CARG2 + | mov PCd, FRAME_CP + | xor RDd, RDd + | lea KBASE, [esp+CFRAME_RESUME] + | mov DISPATCH, L:RB->glref // Setup pointer to dispatch table. + | add DISPATCH, GG_G2DISP + | mov SAVE_PC, RD // Any value outside of bytecode is ok. + | mov SAVE_CFRAME, RD + | mov SAVE_NRES, RDd + | mov SAVE_ERRF, RDd + | mov L:RB->cframe, KBASE + | cmp byte L:RB->status, RDL + | je >2 // Initial resume (like a call). + | + | // Resume after yield (like a return). + | mov [DISPATCH+DISPATCH_GL(cur_L)], L:RB + | set_vmstate INTERP + | mov byte L:RB->status, RDL + | mov BASE, L:RB->base + | mov RD, L:RB->top + | sub RD, RA + | shr RDd, 3 + | add RDd, 1 // RD = nresults+1 + | sub RA, BASE // RA = resultofs + | mov PC, [BASE-8] + | mov MULTRES, RDd + | test PCd, FRAME_TYPE + | jz ->BC_RET_Z + | jmp ->vm_return + | + |->vm_pcall: // Setup protected C frame and enter VM. + | // (lua_State *L, TValue *base, int nres1, ptrdiff_t ef) + | saveregs + | mov PCd, FRAME_CP + | mov SAVE_ERRF, CARG4d + | jmp >1 + | + |->vm_call: // Setup C frame and enter VM. + | // (lua_State *L, TValue *base, int nres1) + | saveregs + | mov PCd, FRAME_C + | + |1: // Entry point for vm_pcall above (PC = ftype). + | mov SAVE_NRES, CARG3d + | mov L:RB, CARG1 // Caveat: CARG1 may be RA. + | mov SAVE_L, CARG1 + | mov RA, CARG2 + | + | mov DISPATCH, L:RB->glref // Setup pointer to dispatch table. + | mov KBASE, L:RB->cframe // Add our C frame to cframe chain. + | mov SAVE_CFRAME, KBASE + | mov SAVE_PC, L:RB // Any value outside of bytecode is ok. + | add DISPATCH, GG_G2DISP + | mov L:RB->cframe, rsp + | + |2: // Entry point for vm_resume/vm_cpcall (RA = base, RB = L, PC = ftype). + | mov [DISPATCH+DISPATCH_GL(cur_L)], L:RB + | set_vmstate INTERP + | mov BASE, L:RB->base // BASE = old base (used in vmeta_call). + | add PC, RA + | sub PC, BASE // PC = frame delta + frame type + | + | mov RD, L:RB->top + | sub RD, RA + | shr NARGS:RDd, 3 + | add NARGS:RDd, 1 // RD = nargs+1 + | + |->vm_call_dispatch: + | mov LFUNC:RB, [RA-16] + | checkfunc LFUNC:RB, ->vmeta_call // Ensure KBASE defined and != BASE. + | + |->vm_call_dispatch_f: + | mov BASE, RA + | ins_call + | // BASE = new base, RB = func, RD = nargs+1, PC = caller PC + | + |->vm_cpcall: // Setup protected C frame, call C. + | // (lua_State *L, lua_CFunction func, void *ud, lua_CPFunction cp) + | saveregs + | mov L:RB, CARG1 // Caveat: CARG1 may be RA. + | mov SAVE_L, CARG1 + | mov SAVE_PC, L:RB // Any value outside of bytecode is ok. + | + | mov KBASE, L:RB->stack // Compute -savestack(L, L->top). + | sub KBASE, L:RB->top + | mov DISPATCH, L:RB->glref // Setup pointer to dispatch table. + | mov SAVE_ERRF, 0 // No error function. + | mov SAVE_NRES, KBASEd // Neg. delta means cframe w/o frame. + | add DISPATCH, GG_G2DISP + | // Handler may change cframe_nres(L->cframe) or cframe_errfunc(L->cframe). + | + | mov KBASE, L:RB->cframe // Add our C frame to cframe chain. + | mov SAVE_CFRAME, KBASE + | mov L:RB->cframe, rsp + | mov [DISPATCH+DISPATCH_GL(cur_L)], L:RB + | + | call CARG4 // (lua_State *L, lua_CFunction func, void *ud) + | // TValue * (new base) or NULL returned in eax (RC). + | test RC, RC + | jz ->vm_leave_cp // No base? Just remove C frame. + | mov RA, RC + | mov PCd, FRAME_CP + | jmp <2 // Else continue with the call. + | + |//----------------------------------------------------------------------- + |//-- Metamethod handling ------------------------------------------------ + |//----------------------------------------------------------------------- + | + |//-- Continuation dispatch ---------------------------------------------- + | + |->cont_dispatch: + | // BASE = meta base, RA = resultofs, RD = nresults+1 (also in MULTRES) + | add RA, BASE + | and PC, -8 + | mov RB, BASE + | sub BASE, PC // Restore caller BASE. + | mov aword [RA+RD*8-8], LJ_TNIL // Ensure one valid arg. + | mov RC, RA // ... in [RC] + | mov PC, [RB-24] // Restore PC from [cont|PC]. + | mov RA, qword [RB-32] // May be negative on WIN64 with debug. + |.if FFI + | cmp RA, 1 + | jbe >1 + |.endif + | mov LFUNC:KBASE, [BASE-16] + | cleartp LFUNC:KBASE + | mov KBASE, LFUNC:KBASE->pc + | mov KBASE, [KBASE+PC2PROTO(k)] + | // BASE = base, RC = result, RB = meta base + | jmp RA // Jump to continuation. + | + |.if FFI + |1: + | je ->cont_ffi_callback // cont = 1: return from FFI callback. + | // cont = 0: Tail call from C function. + | sub RB, BASE + | shr RBd, 3 + | lea RDd, [RBd-3] + | jmp ->vm_call_tail + |.endif + | + |->cont_cat: // BASE = base, RC = result, RB = mbase + | movzx RAd, PC_RB + | sub RB, 32 + | lea RA, [BASE+RA*8] + | sub RA, RB + | je ->cont_ra + | neg RA + | shr RAd, 3 + |.if X64WIN + | mov CARG3d, RAd + | mov L:CARG1, SAVE_L + | mov L:CARG1->base, BASE + | mov RC, [RC] + | mov [RB], RC + | mov CARG2, RB + |.else + | mov L:CARG1, SAVE_L + | mov L:CARG1->base, BASE + | mov CARG3d, RAd + | mov RA, [RC] + | mov [RB], RA + | mov CARG2, RB + |.endif + | jmp ->BC_CAT_Z + | + |//-- Table indexing metamethods ----------------------------------------- + | + |->vmeta_tgets: + | settp STR:RC, LJ_TSTR // STR:RC = GCstr * + | mov TMP1, STR:RC + | lea RC, TMP1 + | cmp PC_OP, BC_GGET + | jne >1 + | settp TAB:RA, TAB:RB, LJ_TTAB // TAB:RB = GCtab * + | lea RB, [DISPATCH+DISPATCH_GL(tmptv)] // Store fn->l.env in g->tmptv. + | mov [RB], TAB:RA + | jmp >2 + | + |->vmeta_tgetb: + | movzx RCd, PC_RC + |.if DUALNUM + | setint RC + | mov TMP1, RC + |.else + | cvtsi2sd xmm0, RCd + | movsd TMP1, xmm0 + |.endif + | lea RC, TMP1 + | jmp >1 + | + |->vmeta_tgetv: + | movzx RCd, PC_RC // Reload TValue *k from RC. + | lea RC, [BASE+RC*8] + |1: + | movzx RBd, PC_RB // Reload TValue *t from RB. + | lea RB, [BASE+RB*8] + |2: + | mov L:CARG1, SAVE_L + | mov L:CARG1->base, BASE // Caveat: CARG2/CARG3 may be BASE. + | mov CARG2, RB + | mov CARG3, RC + | mov L:RB, L:CARG1 + | mov SAVE_PC, PC + | call extern lj_meta_tget // (lua_State *L, TValue *o, TValue *k) + | // TValue * (finished) or NULL (metamethod) returned in eax (RC). + | mov BASE, L:RB->base + | test RC, RC + | jz >3 + |->cont_ra: // BASE = base, RC = result + | movzx RAd, PC_RA + | mov RB, [RC] + | mov [BASE+RA*8], RB + | ins_next + | + |3: // Call __index metamethod. + | // BASE = base, L->top = new base, stack = cont/func/t/k + | mov RA, L:RB->top + | mov [RA-24], PC // [cont|PC] + | lea PC, [RA+FRAME_CONT] + | sub PC, BASE + | mov LFUNC:RB, [RA-16] // Guaranteed to be a function here. + | mov NARGS:RDd, 2+1 // 2 args for func(t, k). + | cleartp LFUNC:RB + | jmp ->vm_call_dispatch_f + | + |->vmeta_tgetr: + | mov CARG1, TAB:RB + | mov RB, BASE // Save BASE. + | mov CARG2d, RCd // Caveat: CARG2 == BASE + | call extern lj_tab_getinth // (GCtab *t, int32_t key) + | // cTValue * or NULL returned in eax (RC). + | movzx RAd, PC_RA + | mov BASE, RB // Restore BASE. + | test RC, RC + | jnz ->BC_TGETR_Z + | mov ITYPE, LJ_TNIL + | jmp ->BC_TGETR2_Z + | + |//----------------------------------------------------------------------- + | + |->vmeta_tsets: + | settp STR:RC, LJ_TSTR // STR:RC = GCstr * + | mov TMP1, STR:RC + | lea RC, TMP1 + | cmp PC_OP, BC_GSET + | jne >1 + | settp TAB:RA, TAB:RB, LJ_TTAB // TAB:RB = GCtab * + | lea RB, [DISPATCH+DISPATCH_GL(tmptv)] // Store fn->l.env in g->tmptv. + | mov [RB], TAB:RA + | jmp >2 + | + |->vmeta_tsetb: + | movzx RCd, PC_RC + |.if DUALNUM + | setint RC + | mov TMP1, RC + |.else + | cvtsi2sd xmm0, RCd + | movsd TMP1, xmm0 + |.endif + | lea RC, TMP1 + | jmp >1 + | + |->vmeta_tsetv: + | movzx RCd, PC_RC // Reload TValue *k from RC. + | lea RC, [BASE+RC*8] + |1: + | movzx RBd, PC_RB // Reload TValue *t from RB. + | lea RB, [BASE+RB*8] + |2: + | mov L:CARG1, SAVE_L + | mov L:CARG1->base, BASE // Caveat: CARG2/CARG3 may be BASE. + | mov CARG2, RB + | mov CARG3, RC + | mov L:RB, L:CARG1 + | mov SAVE_PC, PC + | call extern lj_meta_tset // (lua_State *L, TValue *o, TValue *k) + | // TValue * (finished) or NULL (metamethod) returned in eax (RC). + | mov BASE, L:RB->base + | test RC, RC + | jz >3 + | // NOBARRIER: lj_meta_tset ensures the table is not black. + | movzx RAd, PC_RA + | mov RB, [BASE+RA*8] + | mov [RC], RB + |->cont_nop: // BASE = base, (RC = result) + | ins_next + | + |3: // Call __newindex metamethod. + | // BASE = base, L->top = new base, stack = cont/func/t/k/(v) + | mov RA, L:RB->top + | mov [RA-24], PC // [cont|PC] + | movzx RCd, PC_RA + | // Copy value to third argument. + | mov RB, [BASE+RC*8] + | mov [RA+16], RB + | lea PC, [RA+FRAME_CONT] + | sub PC, BASE + | mov LFUNC:RB, [RA-16] // Guaranteed to be a function here. + | mov NARGS:RDd, 3+1 // 3 args for func(t, k, v). + | cleartp LFUNC:RB + | jmp ->vm_call_dispatch_f + | + |->vmeta_tsetr: + |.if X64WIN + | mov L:CARG1, SAVE_L + | mov CARG3d, RCd + | mov L:CARG1->base, BASE + | xchg CARG2, TAB:RB // Caveat: CARG2 == BASE. + |.else + | mov L:CARG1, SAVE_L + | mov CARG2, TAB:RB + | mov L:CARG1->base, BASE + | mov RB, BASE // Save BASE. + | mov CARG3d, RCd // Caveat: CARG3 == BASE. + |.endif + | mov SAVE_PC, PC + | call extern lj_tab_setinth // (lua_State *L, GCtab *t, int32_t key) + | // TValue * returned in eax (RC). + | movzx RAd, PC_RA + | mov BASE, RB // Restore BASE. + | jmp ->BC_TSETR_Z + | + |//-- Comparison metamethods --------------------------------------------- + | + |->vmeta_comp: + | movzx RDd, PC_RD + | movzx RAd, PC_RA + | mov L:RB, SAVE_L + | mov L:RB->base, BASE // Caveat: CARG2/CARG3 == BASE. + |.if X64WIN + | lea CARG3, [BASE+RD*8] + | lea CARG2, [BASE+RA*8] + |.else + | lea CARG2, [BASE+RA*8] + | lea CARG3, [BASE+RD*8] + |.endif + | mov CARG1, L:RB // Caveat: CARG1/CARG4 == RA. + | movzx CARG4d, PC_OP + | mov SAVE_PC, PC + | call extern lj_meta_comp // (lua_State *L, TValue *o1, *o2, int op) + | // 0/1 or TValue * (metamethod) returned in eax (RC). + |3: + | mov BASE, L:RB->base + | cmp RC, 1 + | ja ->vmeta_binop + |4: + | lea PC, [PC+4] + | jb >6 + |5: + | movzx RDd, PC_RD + | branchPC RD + |6: + | ins_next + | + |->cont_condt: // BASE = base, RC = result + | add PC, 4 + | mov ITYPE, [RC] + | sar ITYPE, 47 + | cmp ITYPEd, LJ_TISTRUECOND // Branch if result is true. + | jb <5 + | jmp <6 + | + |->cont_condf: // BASE = base, RC = result + | mov ITYPE, [RC] + | sar ITYPE, 47 + | cmp ITYPEd, LJ_TISTRUECOND // Branch if result is false. + | jmp <4 + | + |->vmeta_equal: + | cleartp TAB:RD + | sub PC, 4 + |.if X64WIN + | mov CARG3, RD + | mov CARG4d, RBd + | mov L:RB, SAVE_L + | mov L:RB->base, BASE // Caveat: CARG2 == BASE. + | mov CARG2, RA + | mov CARG1, L:RB // Caveat: CARG1 == RA. + |.else + | mov CARG2, RA + | mov CARG4d, RBd // Caveat: CARG4 == RA. + | mov L:RB, SAVE_L + | mov L:RB->base, BASE // Caveat: CARG3 == BASE. + | mov CARG3, RD + | mov CARG1, L:RB + |.endif + | mov SAVE_PC, PC + | call extern lj_meta_equal // (lua_State *L, GCobj *o1, *o2, int ne) + | // 0/1 or TValue * (metamethod) returned in eax (RC). + | jmp <3 + | + |->vmeta_equal_cd: + |.if FFI + | sub PC, 4 + | mov L:RB, SAVE_L + | mov L:RB->base, BASE + | mov CARG1, L:RB + | mov CARG2d, dword [PC-4] + | mov SAVE_PC, PC + | call extern lj_meta_equal_cd // (lua_State *L, BCIns ins) + | // 0/1 or TValue * (metamethod) returned in eax (RC). + | jmp <3 + |.endif + | + |->vmeta_istype: + | mov L:RB, SAVE_L + | mov L:RB->base, BASE // Caveat: CARG2/CARG3 may be BASE. + | mov CARG2d, RAd + | mov CARG3d, RDd + | mov L:CARG1, L:RB + | mov SAVE_PC, PC + | call extern lj_meta_istype // (lua_State *L, BCReg ra, BCReg tp) + | mov BASE, L:RB->base + | jmp <6 + | + |//-- Arithmetic metamethods --------------------------------------------- + | + |->vmeta_arith_vno: + |.if DUALNUM + | movzx RBd, PC_RB + | movzx RCd, PC_RC + |.endif + |->vmeta_arith_vn: + | lea RC, [KBASE+RC*8] + | jmp >1 + | + |->vmeta_arith_nvo: + |.if DUALNUM + | movzx RBd, PC_RB + | movzx RCd, PC_RC + |.endif + |->vmeta_arith_nv: + | lea TMPR, [KBASE+RC*8] + | lea RC, [BASE+RB*8] + | mov RB, TMPR + | jmp >2 + | + |->vmeta_unm: + | lea RC, [BASE+RD*8] + | mov RB, RC + | jmp >2 + | + |->vmeta_arith_vvo: + |.if DUALNUM + | movzx RBd, PC_RB + | movzx RCd, PC_RC + |.endif + |->vmeta_arith_vv: + | lea RC, [BASE+RC*8] + |1: + | lea RB, [BASE+RB*8] + |2: + | lea RA, [BASE+RA*8] + |.if X64WIN + | mov CARG3, RB + | mov CARG4, RC + | movzx RCd, PC_OP + | mov ARG5d, RCd + | mov L:RB, SAVE_L + | mov L:RB->base, BASE // Caveat: CARG2 == BASE. + | mov CARG2, RA + | mov CARG1, L:RB // Caveat: CARG1 == RA. + |.else + | movzx CARG5d, PC_OP + | mov CARG2, RA + | mov CARG4, RC // Caveat: CARG4 == RA. + | mov L:CARG1, SAVE_L + | mov L:CARG1->base, BASE // Caveat: CARG3 == BASE. + | mov CARG3, RB + | mov L:RB, L:CARG1 + |.endif + | mov SAVE_PC, PC + | call extern lj_meta_arith // (lua_State *L, TValue *ra,*rb,*rc, BCReg op) + | // NULL (finished) or TValue * (metamethod) returned in eax (RC). + | mov BASE, L:RB->base + | test RC, RC + | jz ->cont_nop + | + | // Call metamethod for binary op. + |->vmeta_binop: + | // BASE = base, RC = new base, stack = cont/func/o1/o2 + | mov RA, RC + | sub RC, BASE + | mov [RA-24], PC // [cont|PC] + | lea PC, [RC+FRAME_CONT] + | mov NARGS:RDd, 2+1 // 2 args for func(o1, o2). + | jmp ->vm_call_dispatch + | + |->vmeta_len: + | movzx RDd, PC_RD + | mov L:RB, SAVE_L + | mov L:RB->base, BASE + | lea CARG2, [BASE+RD*8] // Caveat: CARG2 == BASE + | mov L:CARG1, L:RB + | mov SAVE_PC, PC + | call extern lj_meta_len // (lua_State *L, TValue *o) + | // NULL (retry) or TValue * (metamethod) returned in eax (RC). + | mov BASE, L:RB->base +#if LJ_52 + | test RC, RC + | jne ->vmeta_binop // Binop call for compatibility. + | movzx RDd, PC_RD + | mov TAB:CARG1, [BASE+RD*8] + | cleartp TAB:CARG1 + | jmp ->BC_LEN_Z +#else + | jmp ->vmeta_binop // Binop call for compatibility. +#endif + | + |//-- Call metamethod ---------------------------------------------------- + | + |->vmeta_call_ra: + | lea RA, [BASE+RA*8+16] + |->vmeta_call: // Resolve and call __call metamethod. + | // BASE = old base, RA = new base, RC = nargs+1, PC = return + | mov TMP1d, NARGS:RDd // Save RA, RC for us. + | mov RB, RA + |.if X64WIN + | mov L:TMPR, SAVE_L + | mov L:TMPR->base, BASE // Caveat: CARG2 is BASE. + | lea CARG2, [RA-16] + | lea CARG3, [RA+NARGS:RD*8-8] + | mov CARG1, L:TMPR // Caveat: CARG1 is RA. + |.else + | mov L:CARG1, SAVE_L + | mov L:CARG1->base, BASE // Caveat: CARG3 is BASE. + | lea CARG2, [RA-16] + | lea CARG3, [RA+NARGS:RD*8-8] + |.endif + | mov SAVE_PC, PC + | call extern lj_meta_call // (lua_State *L, TValue *func, TValue *top) + | mov RA, RB + | mov L:RB, SAVE_L + | mov BASE, L:RB->base + | mov NARGS:RDd, TMP1d + | mov LFUNC:RB, [RA-16] + | add NARGS:RDd, 1 + | // This is fragile. L->base must not move, KBASE must always be defined. + | cmp KBASE, BASE // Continue with CALLT if flag set. + | je ->BC_CALLT_Z + | cleartp LFUNC:RB + | mov BASE, RA + | ins_call // Otherwise call resolved metamethod. + | + |//-- Argument coercion for 'for' statement ------------------------------ + | + |->vmeta_for: + | mov L:RB, SAVE_L + | mov L:RB->base, BASE + | mov CARG2, RA // Caveat: CARG2 == BASE + | mov L:CARG1, L:RB // Caveat: CARG1 == RA + | mov SAVE_PC, PC + | call extern lj_meta_for // (lua_State *L, TValue *base) + | mov BASE, L:RB->base + | mov RCd, [PC-4] + | movzx RAd, RCH + | movzx OP, RCL + | shr RCd, 16 + | jmp aword [DISPATCH+OP*8+GG_DISP2STATIC] // Retry FORI or JFORI. + | + |//----------------------------------------------------------------------- + |//-- Fast functions ----------------------------------------------------- + |//----------------------------------------------------------------------- + | + |.macro .ffunc, name + |->ff_ .. name: + |.endmacro + | + |.macro .ffunc_1, name + |->ff_ .. name: + | cmp NARGS:RDd, 1+1; jb ->fff_fallback + |.endmacro + | + |.macro .ffunc_2, name + |->ff_ .. name: + | cmp NARGS:RDd, 2+1; jb ->fff_fallback + |.endmacro + | + |.macro .ffunc_n, name, op + | .ffunc_1 name + | checknumtp [BASE], ->fff_fallback + | op xmm0, qword [BASE] + |.endmacro + | + |.macro .ffunc_n, name + | .ffunc_n name, movsd + |.endmacro + | + |.macro .ffunc_nn, name + | .ffunc_2 name + | checknumtp [BASE], ->fff_fallback + | checknumtp [BASE+8], ->fff_fallback + | movsd xmm0, qword [BASE] + | movsd xmm1, qword [BASE+8] + |.endmacro + | + |// Inlined GC threshold check. Caveat: uses label 1. + |.macro ffgccheck + | mov RB, [DISPATCH+DISPATCH_GL(gc.total)] + | cmp RB, [DISPATCH+DISPATCH_GL(gc.threshold)] + | jb >1 + | call ->fff_gcstep + |1: + |.endmacro + | + |//-- Base library: checks ----------------------------------------------- + | + |.ffunc_1 assert + | mov ITYPE, [BASE] + | mov RB, ITYPE + | sar ITYPE, 47 + | cmp ITYPEd, LJ_TISTRUECOND; jae ->fff_fallback + | mov PC, [BASE-8] + | mov MULTRES, RDd + | mov RB, [BASE] + | mov [BASE-16], RB + | sub RDd, 2 + | jz >2 + | mov RA, BASE + |1: + | add RA, 8 + | mov RB, [RA] + | mov [RA-16], RB + | sub RDd, 1 + | jnz <1 + |2: + | mov RDd, MULTRES + | jmp ->fff_res_ + | + |.ffunc_1 type + | mov RC, [BASE] + | sar RC, 47 + | mov RBd, LJ_TISNUM + | cmp RCd, RBd + | cmovb RCd, RBd + | not RCd + |2: + | mov CFUNC:RB, [BASE-16] + | cleartp CFUNC:RB + | mov STR:RC, [CFUNC:RB+RC*8+((char *)(&((GCfuncC *)0)->upvalue))] + | mov PC, [BASE-8] + | settp STR:RC, LJ_TSTR + | mov [BASE-16], STR:RC + | jmp ->fff_res1 + | + |//-- Base library: getters and setters --------------------------------- + | + |.ffunc_1 getmetatable + | mov TAB:RB, [BASE] + | mov PC, [BASE-8] + | checktab TAB:RB, >6 + |1: // Field metatable must be at same offset for GCtab and GCudata! + | mov TAB:RB, TAB:RB->metatable + |2: + | test TAB:RB, TAB:RB + | mov aword [BASE-16], LJ_TNIL + | jz ->fff_res1 + | settp TAB:RC, TAB:RB, LJ_TTAB + | mov [BASE-16], TAB:RC // Store metatable as default result. + | mov STR:RC, [DISPATCH+DISPATCH_GL(gcroot)+8*(GCROOT_MMNAME+MM_metatable)] + | mov RAd, TAB:RB->hmask + | and RAd, STR:RC->hash + | settp STR:RC, LJ_TSTR + | imul RAd, #NODE + | add NODE:RA, TAB:RB->node + |3: // Rearranged logic, because we expect _not_ to find the key. + | cmp NODE:RA->key, STR:RC + | je >5 + |4: + | mov NODE:RA, NODE:RA->next + | test NODE:RA, NODE:RA + | jnz <3 + | jmp ->fff_res1 // Not found, keep default result. + |5: + | mov RB, NODE:RA->val + | cmp RB, LJ_TNIL; je ->fff_res1 // Ditto for nil value. + | mov [BASE-16], RB // Return value of mt.__metatable. + | jmp ->fff_res1 + | + |6: + | cmp ITYPEd, LJ_TUDATA; je <1 + | cmp ITYPEd, LJ_TISNUM; ja >7 + | mov ITYPEd, LJ_TISNUM + |7: + | not ITYPEd + | mov TAB:RB, [DISPATCH+ITYPE*8+DISPATCH_GL(gcroot[GCROOT_BASEMT])] + | jmp <2 + | + |.ffunc_2 setmetatable + | mov TAB:RB, [BASE] + | mov TAB:TMPR, TAB:RB + | checktab TAB:RB, ->fff_fallback + | // Fast path: no mt for table yet and not clearing the mt. + | cmp aword TAB:RB->metatable, 0; jne ->fff_fallback + | mov TAB:RA, [BASE+8] + | checktab TAB:RA, ->fff_fallback + | mov TAB:RB->metatable, TAB:RA + | mov PC, [BASE-8] + | mov [BASE-16], TAB:TMPR // Return original table. + | test byte TAB:RB->marked, LJ_GC_BLACK // isblack(table) + | jz >1 + | // Possible write barrier. Table is black, but skip iswhite(mt) check. + | barrierback TAB:RB, RC + |1: + | jmp ->fff_res1 + | + |.ffunc_2 rawget + |.if X64WIN + | mov TAB:RA, [BASE] + | checktab TAB:RA, ->fff_fallback + | mov RB, BASE // Save BASE. + | lea CARG3, [BASE+8] + | mov CARG2, TAB:RA // Caveat: CARG2 == BASE. + | mov CARG1, SAVE_L + |.else + | mov TAB:CARG2, [BASE] + | checktab TAB:CARG2, ->fff_fallback + | mov RB, BASE // Save BASE. + | lea CARG3, [BASE+8] // Caveat: CARG3 == BASE. + | mov CARG1, SAVE_L + |.endif + | call extern lj_tab_get // (lua_State *L, GCtab *t, cTValue *key) + | // cTValue * returned in eax (RD). + | mov BASE, RB // Restore BASE. + | // Copy table slot. + | mov RB, [RD] + | mov PC, [BASE-8] + | mov [BASE-16], RB + | jmp ->fff_res1 + | + |//-- Base library: conversions ------------------------------------------ + | + |.ffunc tonumber + | // Only handles the number case inline (without a base argument). + | cmp NARGS:RDd, 1+1; jne ->fff_fallback // Exactly one argument. + | mov RB, [BASE] + | checknumber RB, ->fff_fallback + | mov PC, [BASE-8] + | mov [BASE-16], RB + | jmp ->fff_res1 + | + |.ffunc_1 tostring + | // Only handles the string or number case inline. + | mov PC, [BASE-8] + | mov STR:RB, [BASE] + | checktp_nc STR:RB, LJ_TSTR, >3 + | // A __tostring method in the string base metatable is ignored. + |2: + | mov [BASE-16], STR:RB + | jmp ->fff_res1 + |3: // Handle numbers inline, unless a number base metatable is present. + | cmp ITYPEd, LJ_TISNUM; ja ->fff_fallback_1 + | cmp aword [DISPATCH+DISPATCH_GL(gcroot[GCROOT_BASEMT_NUM])], 0 + | jne ->fff_fallback + | ffgccheck // Caveat: uses label 1. + | mov L:RB, SAVE_L + | mov L:RB->base, BASE // Add frame since C call can throw. + | mov SAVE_PC, PC // Redundant (but a defined value). + |.if not X64WIN + | mov CARG2, BASE // Otherwise: CARG2 == BASE + |.endif + | mov L:CARG1, L:RB + |.if DUALNUM + | call extern lj_strfmt_number // (lua_State *L, cTValue *o) + |.else + | call extern lj_strfmt_num // (lua_State *L, lua_Number *np) + |.endif + | // GCstr returned in eax (RD). + | mov BASE, L:RB->base + | settp STR:RB, RD, LJ_TSTR + | jmp <2 + | + |//-- Base library: iterators ------------------------------------------- + | + |.ffunc_1 next + | je >2 // Missing 2nd arg? + |1: + |.if X64WIN + | mov RA, [BASE] + | checktab RA, ->fff_fallback + |.else + | mov CARG2, [BASE] + | checktab CARG2, ->fff_fallback + |.endif + | mov L:RB, SAVE_L + | mov L:RB->base, BASE // Add frame since C call can throw. + | mov L:RB->top, BASE // Dummy frame length is ok. + | mov PC, [BASE-8] + |.if X64WIN + | lea CARG3, [BASE+8] + | mov CARG2, RA // Caveat: CARG2 == BASE. + | mov CARG1, L:RB + |.else + | lea CARG3, [BASE+8] // Caveat: CARG3 == BASE. + | mov CARG1, L:RB + |.endif + | mov SAVE_PC, PC // Needed for ITERN fallback. + | call extern lj_tab_next // (lua_State *L, GCtab *t, TValue *key) + | // Flag returned in eax (RD). + | mov BASE, L:RB->base + | test RDd, RDd; jz >3 // End of traversal? + | // Copy key and value to results. + | mov RB, [BASE+8] + | mov RD, [BASE+16] + | mov [BASE-16], RB + | mov [BASE-8], RD + |->fff_res2: + | mov RDd, 1+2 + | jmp ->fff_res + |2: // Set missing 2nd arg to nil. + | mov aword [BASE+8], LJ_TNIL + | jmp <1 + |3: // End of traversal: return nil. + | mov aword [BASE-16], LJ_TNIL + | jmp ->fff_res1 + | + |.ffunc_1 pairs + | mov TAB:RB, [BASE] + | mov TMPR, TAB:RB + | checktab TAB:RB, ->fff_fallback +#if LJ_52 + | cmp aword TAB:RB->metatable, 0; jne ->fff_fallback +#endif + | mov CFUNC:RD, [BASE-16] + | cleartp CFUNC:RD + | mov CFUNC:RD, CFUNC:RD->upvalue[0] + | settp CFUNC:RD, LJ_TFUNC + | mov PC, [BASE-8] + | mov [BASE-16], CFUNC:RD + | mov [BASE-8], TMPR + | mov aword [BASE], LJ_TNIL + | mov RDd, 1+3 + | jmp ->fff_res + | + |.ffunc_2 ipairs_aux + | mov TAB:RB, [BASE] + | checktab TAB:RB, ->fff_fallback + |.if DUALNUM + | mov RA, [BASE+8] + | checkint RA, ->fff_fallback + |.else + | checknumtp [BASE+8], ->fff_fallback + | movsd xmm0, qword [BASE+8] + |.endif + | mov PC, [BASE-8] + |.if DUALNUM + | add RAd, 1 + | setint ITYPE, RA + | mov [BASE-16], ITYPE + |.else + | sseconst_1 xmm1, TMPR + | addsd xmm0, xmm1 + | cvttsd2si RAd, xmm0 + | movsd qword [BASE-16], xmm0 + |.endif + | cmp RAd, TAB:RB->asize; jae >2 // Not in array part? + | mov RD, TAB:RB->array + | lea RD, [RD+RA*8] + |1: + | cmp aword [RD], LJ_TNIL; je ->fff_res0 + | // Copy array slot. + | mov RB, [RD] + | mov [BASE-8], RB + | jmp ->fff_res2 + |2: // Check for empty hash part first. Otherwise call C function. + | cmp dword TAB:RB->hmask, 0; je ->fff_res0 + |.if X64WIN + | mov TMPR, BASE + | mov CARG2d, RAd + | mov CARG1, TAB:RB + | mov RB, TMPR + |.else + | mov CARG1, TAB:RB + | mov RB, BASE // Save BASE. + | mov CARG2d, RAd // Caveat: CARG2 == BASE + |.endif + | call extern lj_tab_getinth // (GCtab *t, int32_t key) + | // cTValue * or NULL returned in eax (RD). + | mov BASE, RB + | test RD, RD + | jnz <1 + |->fff_res0: + | mov RDd, 1+0 + | jmp ->fff_res + | + |.ffunc_1 ipairs + | mov TAB:RB, [BASE] + | mov TMPR, TAB:RB + | checktab TAB:RB, ->fff_fallback +#if LJ_52 + | cmp aword TAB:RB->metatable, 0; jne ->fff_fallback +#endif + | mov CFUNC:RD, [BASE-16] + | cleartp CFUNC:RD + | mov CFUNC:RD, CFUNC:RD->upvalue[0] + | settp CFUNC:RD, LJ_TFUNC + | mov PC, [BASE-8] + | mov [BASE-16], CFUNC:RD + | mov [BASE-8], TMPR + |.if DUALNUM + | mov64 RD, ((uint64_t)LJ_TISNUM<<47) + | mov [BASE], RD + |.else + | mov qword [BASE], 0 + |.endif + | mov RDd, 1+3 + | jmp ->fff_res + | + |//-- Base library: catch errors ---------------------------------------- + | + |.ffunc_1 pcall + | lea RA, [BASE+16] + | sub NARGS:RDd, 1 + | mov PCd, 16+FRAME_PCALL + |1: + | movzx RBd, byte [DISPATCH+DISPATCH_GL(hookmask)] + | shr RB, HOOK_ACTIVE_SHIFT + | and RB, 1 + | add PC, RB // Remember active hook before pcall. + | // Note: this does a (harmless) copy of the function to the PC slot, too. + | mov KBASE, RD + |2: + | mov RB, [RA+KBASE*8-24] + | mov [RA+KBASE*8-16], RB + | sub KBASE, 1 + | ja <2 + | jmp ->vm_call_dispatch + | + |.ffunc_2 xpcall + | mov LFUNC:RA, [BASE+8] + | checktp_nc LFUNC:RA, LJ_TFUNC, ->fff_fallback + | mov LFUNC:RB, [BASE] // Swap function and traceback. + | mov [BASE], LFUNC:RA + | mov [BASE+8], LFUNC:RB + | lea RA, [BASE+24] + | sub NARGS:RDd, 2 + | mov PCd, 24+FRAME_PCALL + | jmp <1 + | + |//-- Coroutine library -------------------------------------------------- + | + |.macro coroutine_resume_wrap, resume + |.if resume + |.ffunc_1 coroutine_resume + | mov L:RB, [BASE] + | cleartp L:RB + |.else + |.ffunc coroutine_wrap_aux + | mov CFUNC:RB, [BASE-16] + | cleartp CFUNC:RB + | mov L:RB, CFUNC:RB->upvalue[0].gcr + | cleartp L:RB + |.endif + | mov PC, [BASE-8] + | mov SAVE_PC, PC + | mov TMP1, L:RB + |.if resume + | checktptp [BASE], LJ_TTHREAD, ->fff_fallback + |.endif + | cmp aword L:RB->cframe, 0; jne ->fff_fallback + | cmp byte L:RB->status, LUA_YIELD; ja ->fff_fallback + | mov RA, L:RB->top + | je >1 // Status != LUA_YIELD (i.e. 0)? + | cmp RA, L:RB->base // Check for presence of initial func. + | je ->fff_fallback + | mov PC, [RA-8] // Move initial function up. + | mov [RA], PC + | add RA, 8 + |1: + |.if resume + | lea PC, [RA+NARGS:RD*8-16] // Check stack space (-1-thread). + |.else + | lea PC, [RA+NARGS:RD*8-8] // Check stack space (-1). + |.endif + | cmp PC, L:RB->maxstack; ja ->fff_fallback + | mov L:RB->top, PC + | + | mov L:RB, SAVE_L + | mov L:RB->base, BASE + |.if resume + | add BASE, 8 // Keep resumed thread in stack for GC. + |.endif + | mov L:RB->top, BASE + |.if resume + | lea RB, [BASE+NARGS:RD*8-24] // RB = end of source for stack move. + |.else + | lea RB, [BASE+NARGS:RD*8-16] // RB = end of source for stack move. + |.endif + | sub RB, PC // Relative to PC. + | + | cmp PC, RA + | je >3 + |2: // Move args to coroutine. + | mov RC, [PC+RB] + | mov [PC-8], RC + | sub PC, 8 + | cmp PC, RA + | jne <2 + |3: + | mov CARG2, RA + | mov CARG1, TMP1 + | call ->vm_resume // (lua_State *L, TValue *base, 0, 0) + | + | mov L:RB, SAVE_L + | mov L:PC, TMP1 + | mov BASE, L:RB->base + | mov [DISPATCH+DISPATCH_GL(cur_L)], L:RB + | set_vmstate INTERP + | + | cmp eax, LUA_YIELD + | ja >8 + |4: + | mov RA, L:PC->base + | mov KBASE, L:PC->top + | mov L:PC->top, RA // Clear coroutine stack. + | mov PC, KBASE + | sub PC, RA + | je >6 // No results? + | lea RD, [BASE+PC] + | shr PCd, 3 + | cmp RD, L:RB->maxstack + | ja >9 // Need to grow stack? + | + | mov RB, BASE + | sub RB, RA + |5: // Move results from coroutine. + | mov RD, [RA] + | mov [RA+RB], RD + | add RA, 8 + | cmp RA, KBASE + | jne <5 + |6: + |.if resume + | lea RDd, [PCd+2] // nresults+1 = 1 + true + results. + | mov_true ITYPE // Prepend true to results. + | mov [BASE-8], ITYPE + |.else + | lea RDd, [PCd+1] // nresults+1 = 1 + results. + |.endif + |7: + | mov PC, SAVE_PC + | mov MULTRES, RDd + |.if resume + | mov RA, -8 + |.else + | xor RAd, RAd + |.endif + | test PCd, FRAME_TYPE + | jz ->BC_RET_Z + | jmp ->vm_return + | + |8: // Coroutine returned with error (at co->top-1). + |.if resume + | mov_false ITYPE // Prepend false to results. + | mov [BASE-8], ITYPE + | mov RA, L:PC->top + | sub RA, 8 + | mov L:PC->top, RA // Clear error from coroutine stack. + | // Copy error message. + | mov RD, [RA] + | mov [BASE], RD + | mov RDd, 1+2 // nresults+1 = 1 + false + error. + | jmp <7 + |.else + | mov CARG2, L:PC + | mov CARG1, L:RB + | call extern lj_ffh_coroutine_wrap_err // (lua_State *L, lua_State *co) + | // Error function does not return. + |.endif + | + |9: // Handle stack expansion on return from yield. + | mov L:RA, TMP1 + | mov L:RA->top, KBASE // Undo coroutine stack clearing. + | mov CARG2, PC + | mov CARG1, L:RB + | call extern lj_state_growstack // (lua_State *L, int n) + | mov L:PC, TMP1 + | mov BASE, L:RB->base + | jmp <4 // Retry the stack move. + |.endmacro + | + | coroutine_resume_wrap 1 // coroutine.resume + | coroutine_resume_wrap 0 // coroutine.wrap + | + |.ffunc coroutine_yield + | mov L:RB, SAVE_L + | test aword L:RB->cframe, CFRAME_RESUME + | jz ->fff_fallback + | mov L:RB->base, BASE + | lea RD, [BASE+NARGS:RD*8-8] + | mov L:RB->top, RD + | xor RDd, RDd + | mov aword L:RB->cframe, RD + | mov al, LUA_YIELD + | mov byte L:RB->status, al + | jmp ->vm_leave_unw + | + |//-- Math library ------------------------------------------------------- + | + | .ffunc_1 math_abs + | mov RB, [BASE] + |.if DUALNUM + | checkint RB, >3 + | cmp RBd, 0; jns ->fff_resi + | neg RBd; js >2 + |->fff_resbit: + |->fff_resi: + | setint RB + |->fff_resRB: + | mov PC, [BASE-8] + | mov [BASE-16], RB + | jmp ->fff_res1 + |2: + | mov64 RB, U64x(41e00000,00000000) // 2^31. + | jmp ->fff_resRB + |3: + | ja ->fff_fallback + |.else + | checknum RB, ->fff_fallback + |.endif + | shl RB, 1 + | shr RB, 1 + | mov PC, [BASE-8] + | mov [BASE-16], RB + | jmp ->fff_res1 + | + |.ffunc_n math_sqrt, sqrtsd + |->fff_resxmm0: + | mov PC, [BASE-8] + | movsd qword [BASE-16], xmm0 + | // fallthrough + | + |->fff_res1: + | mov RDd, 1+1 + |->fff_res: + | mov MULTRES, RDd + |->fff_res_: + | test PCd, FRAME_TYPE + | jnz >7 + |5: + | cmp PC_RB, RDL // More results expected? + | ja >6 + | // Adjust BASE. KBASE is assumed to be set for the calling frame. + | movzx RAd, PC_RA + | neg RA + | lea BASE, [BASE+RA*8-16] // base = base - (RA+2)*8 + | ins_next + | + |6: // Fill up results with nil. + | mov aword [BASE+RD*8-24], LJ_TNIL + | add RD, 1 + | jmp <5 + | + |7: // Non-standard return case. + | mov RA, -16 // Results start at BASE+RA = BASE-16. + | jmp ->vm_return + | + |.macro math_round, func + | .ffunc math_ .. func + |.if DUALNUM + | mov RB, [BASE] + | checknumx RB, ->fff_resRB, je + | ja ->fff_fallback + |.else + | checknumtp [BASE], ->fff_fallback + |.endif + | movsd xmm0, qword [BASE] + | call ->vm_ .. func .. _sse + |.if DUALNUM + | cvttsd2si RBd, xmm0 + | cmp RBd, 0x80000000 + | jne ->fff_resi + | cvtsi2sd xmm1, RBd + | ucomisd xmm0, xmm1 + | jp ->fff_resxmm0 + | je ->fff_resi + |.endif + | jmp ->fff_resxmm0 + |.endmacro + | + | math_round floor + | math_round ceil + | + |.ffunc math_log + | cmp NARGS:RDd, 1+1; jne ->fff_fallback // Exactly one argument. + | checknumtp [BASE], ->fff_fallback + | movsd xmm0, qword [BASE] + | mov RB, BASE + | call extern log + | mov BASE, RB + | jmp ->fff_resxmm0 + | + |.macro math_extern, func + | .ffunc_n math_ .. func + | mov RB, BASE + | call extern func + | mov BASE, RB + | jmp ->fff_resxmm0 + |.endmacro + | + |.macro math_extern2, func + | .ffunc_nn math_ .. func + | mov RB, BASE + | call extern func + | mov BASE, RB + | jmp ->fff_resxmm0 + |.endmacro + | + | math_extern log10 + | math_extern exp + | math_extern sin + | math_extern cos + | math_extern tan + | math_extern asin + | math_extern acos + | math_extern atan + | math_extern sinh + | math_extern cosh + | math_extern tanh + | math_extern2 pow + | math_extern2 atan2 + | math_extern2 fmod + | + |.ffunc_2 math_ldexp + | checknumtp [BASE], ->fff_fallback + | checknumtp [BASE+8], ->fff_fallback + | fld qword [BASE+8] + | fld qword [BASE] + | fscale + | fpop1 + | mov PC, [BASE-8] + | fstp qword [BASE-16] + | jmp ->fff_res1 + | + |.ffunc_n math_frexp + | mov RB, BASE + |.if X64WIN + | lea CARG2, TMP1 // Caveat: CARG2 == BASE + |.else + | lea CARG1, TMP1 + |.endif + | call extern frexp + | mov BASE, RB + | mov RBd, TMP1d + | mov PC, [BASE-8] + | movsd qword [BASE-16], xmm0 + |.if DUALNUM + | setint RB + | mov [BASE-8], RB + |.else + | cvtsi2sd xmm1, RBd + | movsd qword [BASE-8], xmm1 + |.endif + | mov RDd, 1+2 + | jmp ->fff_res + | + |.ffunc_n math_modf + | mov RB, BASE + |.if X64WIN + | lea CARG2, [BASE-16] // Caveat: CARG2 == BASE + |.else + | lea CARG1, [BASE-16] + |.endif + | call extern modf + | mov BASE, RB + | mov PC, [BASE-8] + | movsd qword [BASE-8], xmm0 + | mov RDd, 1+2 + | jmp ->fff_res + | + |.macro math_minmax, name, cmovop, sseop + | .ffunc name + | mov RAd, 2 + |.if DUALNUM + | mov RB, [BASE] + | checkint RB, >4 + |1: // Handle integers. + | cmp RAd, RDd; jae ->fff_resRB + | mov TMPR, [BASE+RA*8-8] + | checkint TMPR, >3 + | cmp RBd, TMPRd + | cmovop RB, TMPR + | add RAd, 1 + | jmp <1 + |3: + | ja ->fff_fallback + | // Convert intermediate result to number and continue below. + | cvtsi2sd xmm0, RBd + | jmp >6 + |4: + | ja ->fff_fallback + |.else + | checknumtp [BASE], ->fff_fallback + |.endif + | + | movsd xmm0, qword [BASE] + |5: // Handle numbers or integers. + | cmp RAd, RDd; jae ->fff_resxmm0 + |.if DUALNUM + | mov RB, [BASE+RA*8-8] + | checknumx RB, >6, jb + | ja ->fff_fallback + | cvtsi2sd xmm1, RBd + | jmp >7 + |.else + | checknumtp [BASE+RA*8-8], ->fff_fallback + |.endif + |6: + | movsd xmm1, qword [BASE+RA*8-8] + |7: + | sseop xmm0, xmm1 + | add RAd, 1 + | jmp <5 + |.endmacro + | + | math_minmax math_min, cmovg, minsd + | math_minmax math_max, cmovl, maxsd + | + |//-- String library ----------------------------------------------------- + | + |.ffunc string_byte // Only handle the 1-arg case here. + | cmp NARGS:RDd, 1+1; jne ->fff_fallback + | mov STR:RB, [BASE] + | checkstr STR:RB, ->fff_fallback + | mov PC, [BASE-8] + | cmp dword STR:RB->len, 1 + | jb ->fff_res0 // Return no results for empty string. + | movzx RBd, byte STR:RB[1] + |.if DUALNUM + | jmp ->fff_resi + |.else + | cvtsi2sd xmm0, RBd; jmp ->fff_resxmm0 + |.endif + | + |.ffunc string_char // Only handle the 1-arg case here. + | ffgccheck + | cmp NARGS:RDd, 1+1; jne ->fff_fallback // *Exactly* 1 arg. + |.if DUALNUM + | mov RB, [BASE] + | checkint RB, ->fff_fallback + |.else + | checknumtp [BASE], ->fff_fallback + | cvttsd2si RBd, qword [BASE] + |.endif + | cmp RBd, 255; ja ->fff_fallback + | mov TMP1d, RBd + | mov TMPRd, 1 + | lea RD, TMP1 // Points to stack. Little-endian. + |->fff_newstr: + | mov L:RB, SAVE_L + | mov L:RB->base, BASE + | mov CARG3d, TMPRd // Zero-extended to size_t. + | mov CARG2, RD + | mov CARG1, L:RB + | mov SAVE_PC, PC + | call extern lj_str_new // (lua_State *L, char *str, size_t l) + |->fff_resstr: + | // GCstr * returned in eax (RD). + | mov BASE, L:RB->base + | mov PC, [BASE-8] + | settp STR:RD, LJ_TSTR + | mov [BASE-16], STR:RD + | jmp ->fff_res1 + | + |.ffunc string_sub + | ffgccheck + | mov TMPRd, -1 + | cmp NARGS:RDd, 1+2; jb ->fff_fallback + | jna >1 + |.if DUALNUM + | mov TMPR, [BASE+16] + | checkint TMPR, ->fff_fallback + |.else + | checknumtp [BASE+16], ->fff_fallback + | cvttsd2si TMPRd, qword [BASE+16] + |.endif + |1: + | mov STR:RB, [BASE] + | checkstr STR:RB, ->fff_fallback + |.if DUALNUM + | mov ITYPE, [BASE+8] + | mov RAd, ITYPEd // Must clear hiword for lea below. + | sar ITYPE, 47 + | cmp ITYPEd, LJ_TISNUM + | jne ->fff_fallback + |.else + | checknumtp [BASE+8], ->fff_fallback + | cvttsd2si RAd, qword [BASE+8] + |.endif + | mov RCd, STR:RB->len + | cmp RCd, TMPRd // len < end? (unsigned compare) + | jb >5 + |2: + | test RAd, RAd // start <= 0? + | jle >7 + |3: + | sub TMPRd, RAd // start > end? + | jl ->fff_emptystr + | lea RD, [STR:RB+RAd+#STR-1] + | add TMPRd, 1 + |4: + | jmp ->fff_newstr + | + |5: // Negative end or overflow. + | jl >6 + | lea TMPRd, [TMPRd+RCd+1] // end = end+(len+1) + | jmp <2 + |6: // Overflow. + | mov TMPRd, RCd // end = len + | jmp <2 + | + |7: // Negative start or underflow. + | je >8 + | add RAd, RCd // start = start+(len+1) + | add RAd, 1 + | jg <3 // start > 0? + |8: // Underflow. + | mov RAd, 1 // start = 1 + | jmp <3 + | + |->fff_emptystr: // Range underflow. + | xor TMPRd, TMPRd // Zero length. Any ptr in RD is ok. + | jmp <4 + | + |.macro ffstring_op, name + | .ffunc_1 string_ .. name + | ffgccheck + |.if X64WIN + | mov STR:TMPR, [BASE] + | checkstr STR:TMPR, ->fff_fallback + |.else + | mov STR:CARG2, [BASE] + | checkstr STR:CARG2, ->fff_fallback + |.endif + | mov L:RB, SAVE_L + | lea SBUF:CARG1, [DISPATCH+DISPATCH_GL(tmpbuf)] + | mov L:RB->base, BASE + |.if X64WIN + | mov STR:CARG2, STR:TMPR // Caveat: CARG2 == BASE + |.endif + | mov RC, SBUF:CARG1->b + | mov SBUF:CARG1->L, L:RB + | mov SBUF:CARG1->p, RC + | mov SAVE_PC, PC + | call extern lj_buf_putstr_ .. name + | mov CARG1, rax + | call extern lj_buf_tostr + | jmp ->fff_resstr + |.endmacro + | + |ffstring_op reverse + |ffstring_op lower + |ffstring_op upper + | + |//-- Bit library -------------------------------------------------------- + | + |.macro .ffunc_bit, name, kind, fdef + | fdef name + |.if kind == 2 + | sseconst_tobit xmm1, RB + |.endif + |.if DUALNUM + | mov RB, [BASE] + | checkint RB, >1 + |.if kind > 0 + | jmp >2 + |.else + | jmp ->fff_resbit + |.endif + |1: + | ja ->fff_fallback + | movd xmm0, RB + |.else + | checknumtp [BASE], ->fff_fallback + | movsd xmm0, qword [BASE] + |.endif + |.if kind < 2 + | sseconst_tobit xmm1, RB + |.endif + | addsd xmm0, xmm1 + | movd RBd, xmm0 + |2: + |.endmacro + | + |.macro .ffunc_bit, name, kind + | .ffunc_bit name, kind, .ffunc_1 + |.endmacro + | + |.ffunc_bit bit_tobit, 0 + | jmp ->fff_resbit + | + |.macro .ffunc_bit_op, name, ins + | .ffunc_bit name, 2 + | mov TMPRd, NARGS:RDd // Save for fallback. + | lea RD, [BASE+NARGS:RD*8-16] + |1: + | cmp RD, BASE + | jbe ->fff_resbit + |.if DUALNUM + | mov RA, [RD] + | checkint RA, >2 + | ins RBd, RAd + | sub RD, 8 + | jmp <1 + |2: + | ja ->fff_fallback_bit_op + | movd xmm0, RA + |.else + | checknumtp [RD], ->fff_fallback_bit_op + | movsd xmm0, qword [RD] + |.endif + | addsd xmm0, xmm1 + | movd RAd, xmm0 + | ins RBd, RAd + | sub RD, 8 + | jmp <1 + |.endmacro + | + |.ffunc_bit_op bit_band, and + |.ffunc_bit_op bit_bor, or + |.ffunc_bit_op bit_bxor, xor + | + |.ffunc_bit bit_bswap, 1 + | bswap RBd + | jmp ->fff_resbit + | + |.ffunc_bit bit_bnot, 1 + | not RBd + |.if DUALNUM + | jmp ->fff_resbit + |.else + |->fff_resbit: + | cvtsi2sd xmm0, RBd + | jmp ->fff_resxmm0 + |.endif + | + |->fff_fallback_bit_op: + | mov NARGS:RDd, TMPRd // Restore for fallback + | jmp ->fff_fallback + | + |.macro .ffunc_bit_sh, name, ins + |.if DUALNUM + | .ffunc_bit name, 1, .ffunc_2 + | // Note: no inline conversion from number for 2nd argument! + | mov RA, [BASE+8] + | checkint RA, ->fff_fallback + |.else + | .ffunc_nn name + | sseconst_tobit xmm2, RB + | addsd xmm0, xmm2 + | addsd xmm1, xmm2 + | movd RBd, xmm0 + | movd RAd, xmm1 + |.endif + | ins RBd, cl // Assumes RA is ecx. + | jmp ->fff_resbit + |.endmacro + | + |.ffunc_bit_sh bit_lshift, shl + |.ffunc_bit_sh bit_rshift, shr + |.ffunc_bit_sh bit_arshift, sar + |.ffunc_bit_sh bit_rol, rol + |.ffunc_bit_sh bit_ror, ror + | + |//----------------------------------------------------------------------- + | + |->fff_fallback_2: + | mov NARGS:RDd, 1+2 // Other args are ignored, anyway. + | jmp ->fff_fallback + |->fff_fallback_1: + | mov NARGS:RDd, 1+1 // Other args are ignored, anyway. + |->fff_fallback: // Call fast function fallback handler. + | // BASE = new base, RD = nargs+1 + | mov L:RB, SAVE_L + | mov PC, [BASE-8] // Fallback may overwrite PC. + | mov SAVE_PC, PC // Redundant (but a defined value). + | mov L:RB->base, BASE + | lea RD, [BASE+NARGS:RD*8-8] + | lea RA, [RD+8*LUA_MINSTACK] // Ensure enough space for handler. + | mov L:RB->top, RD + | mov CFUNC:RD, [BASE-16] + | cleartp CFUNC:RD + | cmp RA, L:RB->maxstack + | ja >5 // Need to grow stack. + | mov CARG1, L:RB + | call aword CFUNC:RD->f // (lua_State *L) + | mov BASE, L:RB->base + | // Either throws an error, or recovers and returns -1, 0 or nresults+1. + | test RDd, RDd; jg ->fff_res // Returned nresults+1? + |1: + | mov RA, L:RB->top + | sub RA, BASE + | shr RAd, 3 + | test RDd, RDd + | lea NARGS:RDd, [RAd+1] + | mov LFUNC:RB, [BASE-16] + | jne ->vm_call_tail // Returned -1? + | cleartp LFUNC:RB + | ins_callt // Returned 0: retry fast path. + | + |// Reconstruct previous base for vmeta_call during tailcall. + |->vm_call_tail: + | mov RA, BASE + | test PCd, FRAME_TYPE + | jnz >3 + | movzx RBd, PC_RA + | neg RB + | lea BASE, [BASE+RB*8-16] // base = base - (RB+2)*8 + | jmp ->vm_call_dispatch // Resolve again for tailcall. + |3: + | mov RB, PC + | and RB, -8 + | sub BASE, RB + | jmp ->vm_call_dispatch // Resolve again for tailcall. + | + |5: // Grow stack for fallback handler. + | mov CARG2d, LUA_MINSTACK + | mov CARG1, L:RB + | call extern lj_state_growstack // (lua_State *L, int n) + | mov BASE, L:RB->base + | xor RDd, RDd // Simulate a return 0. + | jmp <1 // Dumb retry (goes through ff first). + | + |->fff_gcstep: // Call GC step function. + | // BASE = new base, RD = nargs+1 + | pop RB // Must keep stack at same level. + | mov TMP1, RB // Save return address + | mov L:RB, SAVE_L + | mov SAVE_PC, PC // Redundant (but a defined value). + | mov L:RB->base, BASE + | lea RD, [BASE+NARGS:RD*8-8] + | mov CARG1, L:RB + | mov L:RB->top, RD + | call extern lj_gc_step // (lua_State *L) + | mov BASE, L:RB->base + | mov RD, L:RB->top + | sub RD, BASE + | shr RDd, 3 + | add NARGS:RDd, 1 + | mov RB, TMP1 + | push RB // Restore return address. + | ret + | + |//----------------------------------------------------------------------- + |//-- Special dispatch targets ------------------------------------------- + |//----------------------------------------------------------------------- + | + |->vm_record: // Dispatch target for recording phase. + |.if JIT + | movzx RDd, byte [DISPATCH+DISPATCH_GL(hookmask)] + | test RDL, HOOK_VMEVENT // No recording while in vmevent. + | jnz >5 + | // Decrement the hookcount for consistency, but always do the call. + | test RDL, HOOK_ACTIVE + | jnz >1 + | test RDL, LUA_MASKLINE|LUA_MASKCOUNT + | jz >1 + | dec dword [DISPATCH+DISPATCH_GL(hookcount)] + | jmp >1 + |.endif + | + |->vm_rethook: // Dispatch target for return hooks. + | movzx RDd, byte [DISPATCH+DISPATCH_GL(hookmask)] + | test RDL, HOOK_ACTIVE // Hook already active? + | jnz >5 + | jmp >1 + | + |->vm_inshook: // Dispatch target for instr/line hooks. + | movzx RDd, byte [DISPATCH+DISPATCH_GL(hookmask)] + | test RDL, HOOK_ACTIVE // Hook already active? + | jnz >5 + | + | test RDL, LUA_MASKLINE|LUA_MASKCOUNT + | jz >5 + | dec dword [DISPATCH+DISPATCH_GL(hookcount)] + | jz >1 + | test RDL, LUA_MASKLINE + | jz >5 + |1: + | mov L:RB, SAVE_L + | mov L:RB->base, BASE + | mov CARG2, PC // Caveat: CARG2 == BASE + | mov CARG1, L:RB + | // SAVE_PC must hold the _previous_ PC. The callee updates it with PC. + | call extern lj_dispatch_ins // (lua_State *L, const BCIns *pc) + |3: + | mov BASE, L:RB->base + |4: + | movzx RAd, PC_RA + |5: + | movzx OP, PC_OP + | movzx RDd, PC_RD + | jmp aword [DISPATCH+OP*8+GG_DISP2STATIC] // Re-dispatch to static ins. + | + |->cont_hook: // Continue from hook yield. + | add PC, 4 + | mov RA, [RB-40] + | mov MULTRES, RAd // Restore MULTRES for *M ins. + | jmp <4 + | + |->vm_hotloop: // Hot loop counter underflow. + |.if JIT + | mov LFUNC:RB, [BASE-16] // Same as curr_topL(L). + | cleartp LFUNC:RB + | mov RB, LFUNC:RB->pc + | movzx RDd, byte [RB+PC2PROTO(framesize)] + | lea RD, [BASE+RD*8] + | mov L:RB, SAVE_L + | mov L:RB->base, BASE + | mov L:RB->top, RD + | mov CARG2, PC + | lea CARG1, [DISPATCH+GG_DISP2J] + | mov aword [DISPATCH+DISPATCH_J(L)], L:RB + | mov SAVE_PC, PC + | call extern lj_trace_hot // (jit_State *J, const BCIns *pc) + | jmp <3 + |.endif + | + |->vm_callhook: // Dispatch target for call hooks. + | mov SAVE_PC, PC + |.if JIT + | jmp >1 + |.endif + | + |->vm_hotcall: // Hot call counter underflow. + |.if JIT + | mov SAVE_PC, PC + | or PC, 1 // Marker for hot call. + |1: + |.endif + | lea RD, [BASE+NARGS:RD*8-8] + | mov L:RB, SAVE_L + | mov L:RB->base, BASE + | mov L:RB->top, RD + | mov CARG2, PC + | mov CARG1, L:RB + | call extern lj_dispatch_call // (lua_State *L, const BCIns *pc) + | // ASMFunction returned in eax/rax (RD). + | mov SAVE_PC, 0 // Invalidate for subsequent line hook. + |.if JIT + | and PC, -2 + |.endif + | mov BASE, L:RB->base + | mov RA, RD + | mov RD, L:RB->top + | sub RD, BASE + | mov RB, RA + | movzx RAd, PC_RA + | shr RDd, 3 + | add NARGS:RDd, 1 + | jmp RB + | + |->cont_stitch: // Trace stitching. + |.if JIT + | // BASE = base, RC = result, RB = mbase + | mov TRACE:ITYPE, [RB-40] // Save previous trace. + | cleartp TRACE:ITYPE + | mov TMPRd, MULTRES + | movzx RAd, PC_RA + | lea RA, [BASE+RA*8] // Call base. + | sub TMPRd, 1 + | jz >2 + |1: // Move results down. + | mov RB, [RC] + | mov [RA], RB + | add RC, 8 + | add RA, 8 + | sub TMPRd, 1 + | jnz <1 + |2: + | movzx RCd, PC_RA + | movzx RBd, PC_RB + | add RC, RB + | lea RC, [BASE+RC*8-8] + |3: + | cmp RC, RA + | ja >9 // More results wanted? + | + | test TRACE:ITYPE, TRACE:ITYPE + | jz ->cont_nop + | movzx RBd, word TRACE:ITYPE->traceno + | movzx RDd, word TRACE:ITYPE->link + | cmp RDd, RBd + | je ->cont_nop // Blacklisted. + | test RDd, RDd + | jne =>BC_JLOOP // Jump to stitched trace. + | + | // Stitch a new trace to the previous trace. + | mov [DISPATCH+DISPATCH_J(exitno)], RB + | mov L:RB, SAVE_L + | mov L:RB->base, BASE + | mov CARG2, PC + | lea CARG1, [DISPATCH+GG_DISP2J] + | mov aword [DISPATCH+DISPATCH_J(L)], L:RB + | call extern lj_dispatch_stitch // (jit_State *J, const BCIns *pc) + | mov BASE, L:RB->base + | jmp ->cont_nop + | + |9: // Fill up results with nil. + | mov aword [RA], LJ_TNIL + | add RA, 8 + | jmp <3 + |.endif + | + |->vm_profhook: // Dispatch target for profiler hook. +#if LJ_HASPROFILE + | mov L:RB, SAVE_L + | mov L:RB->base, BASE + | mov CARG2, PC // Caveat: CARG2 == BASE + | mov CARG1, L:RB + | call extern lj_dispatch_profile // (lua_State *L, const BCIns *pc) + | mov BASE, L:RB->base + | // HOOK_PROFILE is off again, so re-dispatch to dynamic instruction. + | sub PC, 4 + | jmp ->cont_nop +#endif + | + |//----------------------------------------------------------------------- + |//-- Trace exit handler ------------------------------------------------- + |//----------------------------------------------------------------------- + | + |// Called from an exit stub with the exit number on the stack. + |// The 16 bit exit number is stored with two (sign-extended) push imm8. + |->vm_exit_handler: + |.if JIT + | push r13; push r12 + | push r11; push r10; push r9; push r8 + | push rdi; push rsi; push rbp; lea rbp, [rsp+88]; push rbp + | push rbx; push rdx; push rcx; push rax + | movzx RCd, byte [rbp-8] // Reconstruct exit number. + | mov RCH, byte [rbp-16] + | mov [rbp-8], r15; mov [rbp-16], r14 + | // DISPATCH is preserved on-trace in LJ_GC64 mode. + | mov RAd, [DISPATCH+DISPATCH_GL(vmstate)] // Get trace number. + | set_vmstate EXIT + | mov [DISPATCH+DISPATCH_J(exitno)], RCd + | mov [DISPATCH+DISPATCH_J(parent)], RAd + |.if X64WIN + | sub rsp, 16*8+4*8 // Room for SSE regs + save area. + |.else + | sub rsp, 16*8 // Room for SSE regs. + |.endif + | add rbp, -128 + | movsd qword [rbp-8], xmm15; movsd qword [rbp-16], xmm14 + | movsd qword [rbp-24], xmm13; movsd qword [rbp-32], xmm12 + | movsd qword [rbp-40], xmm11; movsd qword [rbp-48], xmm10 + | movsd qword [rbp-56], xmm9; movsd qword [rbp-64], xmm8 + | movsd qword [rbp-72], xmm7; movsd qword [rbp-80], xmm6 + | movsd qword [rbp-88], xmm5; movsd qword [rbp-96], xmm4 + | movsd qword [rbp-104], xmm3; movsd qword [rbp-112], xmm2 + | movsd qword [rbp-120], xmm1; movsd qword [rbp-128], xmm0 + | // Caveat: RB is rbp. + | mov L:RB, [DISPATCH+DISPATCH_GL(cur_L)] + | mov BASE, [DISPATCH+DISPATCH_GL(jit_base)] + | mov aword [DISPATCH+DISPATCH_J(L)], L:RB + | mov L:RB->base, BASE + |.if X64WIN + | lea CARG2, [rsp+4*8] + |.else + | mov CARG2, rsp + |.endif + | lea CARG1, [DISPATCH+GG_DISP2J] + | mov qword [DISPATCH+DISPATCH_GL(jit_base)], 0 + | call extern lj_trace_exit // (jit_State *J, ExitState *ex) + | // MULTRES or negated error code returned in eax (RD). + | mov RA, L:RB->cframe + | and RA, CFRAME_RAWMASK + | mov [RA+CFRAME_OFS_L], L:RB // Set SAVE_L (on-trace resume/yield). + | mov BASE, L:RB->base + | mov PC, [RA+CFRAME_OFS_PC] // Get SAVE_PC. + | jmp >1 + |.endif + |->vm_exit_interp: + | // RD = MULTRES or negated error code, BASE, PC and DISPATCH set. + |.if JIT + | // Restore additional callee-save registers only used in compiled code. + |.if X64WIN + | lea RA, [rsp+10*16+4*8] + |1: + | movdqa xmm15, [RA-10*16] + | movdqa xmm14, [RA-9*16] + | movdqa xmm13, [RA-8*16] + | movdqa xmm12, [RA-7*16] + | movdqa xmm11, [RA-6*16] + | movdqa xmm10, [RA-5*16] + | movdqa xmm9, [RA-4*16] + | movdqa xmm8, [RA-3*16] + | movdqa xmm7, [RA-2*16] + | mov rsp, RA // Reposition stack to C frame. + | movdqa xmm6, [RA-1*16] + | mov r15, CSAVE_1 + | mov r14, CSAVE_2 + | mov r13, CSAVE_3 + | mov r12, CSAVE_4 + |.else + | lea RA, [rsp+16] + |1: + | mov r13, [RA-8] + | mov r12, [RA] + | mov rsp, RA // Reposition stack to C frame. + |.endif + | test RDd, RDd; js >9 // Check for error from exit. + | mov L:RB, SAVE_L + | mov MULTRES, RDd + | mov LFUNC:KBASE, [BASE-16] + | cleartp LFUNC:KBASE + | mov KBASE, LFUNC:KBASE->pc + | mov KBASE, [KBASE+PC2PROTO(k)] + | mov L:RB->base, BASE + | mov qword [DISPATCH+DISPATCH_GL(jit_base)], 0 + | set_vmstate INTERP + | // Modified copy of ins_next which handles function header dispatch, too. + | mov RCd, [PC] + | movzx RAd, RCH + | movzx OP, RCL + | add PC, 4 + | shr RCd, 16 + | cmp OP, BC_FUNCF // Function header? + | jb >3 + | cmp OP, BC_FUNCC+2 // Fast function? + | jae >4 + |2: + | mov RCd, MULTRES // RC/RD holds nres+1. + |3: + | jmp aword [DISPATCH+OP*8] + | + |4: // Check frame below fast function. + | mov RC, [BASE-8] + | test RCd, FRAME_TYPE + | jnz <2 // Trace stitching continuation? + | // Otherwise set KBASE for Lua function below fast function. + | movzx RCd, byte [RC-3] + | neg RC + | mov LFUNC:KBASE, [BASE+RC*8-32] + | cleartp LFUNC:KBASE + | mov KBASE, LFUNC:KBASE->pc + | mov KBASE, [KBASE+PC2PROTO(k)] + | jmp <2 + | + |9: // Rethrow error from the right C frame. + | neg RD + | mov CARG1, L:RB + | mov CARG2, RD + | call extern lj_err_throw // (lua_State *L, int errcode) + |.endif + | + |//----------------------------------------------------------------------- + |//-- Math helper functions ---------------------------------------------- + |//----------------------------------------------------------------------- + | + |// FP value rounding. Called by math.floor/math.ceil fast functions + |// and from JIT code. arg/ret is xmm0. xmm0-xmm3 and RD (eax) modified. + |.macro vm_round, name, mode, cond + |->name: + |->name .. _sse: + | sseconst_abs xmm2, RD + | sseconst_2p52 xmm3, RD + | movaps xmm1, xmm0 + | andpd xmm1, xmm2 // |x| + | ucomisd xmm3, xmm1 // No truncation if 2^52 <= |x|. + | jbe >1 + | andnpd xmm2, xmm0 // Isolate sign bit. + |.if mode == 2 // trunc(x)? + | movaps xmm0, xmm1 + | addsd xmm1, xmm3 // (|x| + 2^52) - 2^52 + | subsd xmm1, xmm3 + | sseconst_1 xmm3, RD + | cmpsd xmm0, xmm1, 1 // |x| < result? + | andpd xmm0, xmm3 + | subsd xmm1, xmm0 // If yes, subtract -1. + | orpd xmm1, xmm2 // Merge sign bit back in. + |.else + | addsd xmm1, xmm3 // (|x| + 2^52) - 2^52 + | subsd xmm1, xmm3 + | orpd xmm1, xmm2 // Merge sign bit back in. + | .if mode == 1 // ceil(x)? + | sseconst_m1 xmm2, RD // Must subtract -1 to preserve -0. + | cmpsd xmm0, xmm1, 6 // x > result? + | .else // floor(x)? + | sseconst_1 xmm2, RD + | cmpsd xmm0, xmm1, 1 // x < result? + | .endif + | andpd xmm0, xmm2 + | subsd xmm1, xmm0 // If yes, subtract +-1. + |.endif + | movaps xmm0, xmm1 + |1: + | ret + |.endmacro + | + | vm_round vm_floor, 0, 1 + | vm_round vm_ceil, 1, JIT + | vm_round vm_trunc, 2, JIT + | + |// FP modulo x%y. Called by BC_MOD* and vm_arith. + |->vm_mod: + |// Args in xmm0/xmm1, return value in xmm0. + |// Caveat: xmm0-xmm5 and RC (eax) modified! + | movaps xmm5, xmm0 + | divsd xmm0, xmm1 + | sseconst_abs xmm2, RD + | sseconst_2p52 xmm3, RD + | movaps xmm4, xmm0 + | andpd xmm4, xmm2 // |x/y| + | ucomisd xmm3, xmm4 // No truncation if 2^52 <= |x/y|. + | jbe >1 + | andnpd xmm2, xmm0 // Isolate sign bit. + | addsd xmm4, xmm3 // (|x/y| + 2^52) - 2^52 + | subsd xmm4, xmm3 + | orpd xmm4, xmm2 // Merge sign bit back in. + | sseconst_1 xmm2, RD + | cmpsd xmm0, xmm4, 1 // x/y < result? + | andpd xmm0, xmm2 + | subsd xmm4, xmm0 // If yes, subtract 1.0. + | movaps xmm0, xmm5 + | mulsd xmm1, xmm4 + | subsd xmm0, xmm1 + | ret + |1: + | mulsd xmm1, xmm0 + | movaps xmm0, xmm5 + | subsd xmm0, xmm1 + | ret + | + |// Args in xmm0/eax. Ret in xmm0. xmm0-xmm1 and eax modified. + |->vm_powi_sse: + | cmp eax, 1; jle >6 // i<=1? + | // Now 1 < (unsigned)i <= 0x80000000. + |1: // Handle leading zeros. + | test eax, 1; jnz >2 + | mulsd xmm0, xmm0 + | shr eax, 1 + | jmp <1 + |2: + | shr eax, 1; jz >5 + | movaps xmm1, xmm0 + |3: // Handle trailing bits. + | mulsd xmm0, xmm0 + | shr eax, 1; jz >4 + | jnc <3 + | mulsd xmm1, xmm0 + | jmp <3 + |4: + | mulsd xmm0, xmm1 + |5: + | ret + |6: + | je <5 // x^1 ==> x + | jb >7 // x^0 ==> 1 + | neg eax + | call <1 + | sseconst_1 xmm1, RD + | divsd xmm1, xmm0 + | movaps xmm0, xmm1 + | ret + |7: + | sseconst_1 xmm0, RD + | ret + | + |//----------------------------------------------------------------------- + |//-- Miscellaneous functions -------------------------------------------- + |//----------------------------------------------------------------------- + | + |// int lj_vm_cpuid(uint32_t f, uint32_t res[4]) + |->vm_cpuid: + | mov eax, CARG1d + | .if X64WIN; push rsi; mov rsi, CARG2; .endif + | push rbx + | xor ecx, ecx + | cpuid + | mov [rsi], eax + | mov [rsi+4], ebx + | mov [rsi+8], ecx + | mov [rsi+12], edx + | pop rbx + | .if X64WIN; pop rsi; .endif + | ret + | + |//----------------------------------------------------------------------- + |//-- Assertions --------------------------------------------------------- + |//----------------------------------------------------------------------- + | + |->assert_bad_for_arg_type: +#ifdef LUA_USE_ASSERT + | int3 +#endif + | int3 + | + |//----------------------------------------------------------------------- + |//-- FFI helper functions ----------------------------------------------- + |//----------------------------------------------------------------------- + | + |// Handler for callback functions. Callback slot number in ah/al. + |->vm_ffi_callback: + |.if FFI + |.type CTSTATE, CTState, PC + | saveregs_ // ebp/rbp already saved. ebp now holds global_State *. + | lea DISPATCH, [ebp+GG_G2DISP] + | mov CTSTATE, GL:ebp->ctype_state + | movzx eax, ax + | mov CTSTATE->cb.slot, eax + | mov CTSTATE->cb.gpr[0], CARG1 + | mov CTSTATE->cb.gpr[1], CARG2 + | mov CTSTATE->cb.gpr[2], CARG3 + | mov CTSTATE->cb.gpr[3], CARG4 + | movsd qword CTSTATE->cb.fpr[0], xmm0 + | movsd qword CTSTATE->cb.fpr[1], xmm1 + | movsd qword CTSTATE->cb.fpr[2], xmm2 + | movsd qword CTSTATE->cb.fpr[3], xmm3 + |.if X64WIN + | lea rax, [rsp+CFRAME_SIZE+4*8] + |.else + | lea rax, [rsp+CFRAME_SIZE] + | mov CTSTATE->cb.gpr[4], CARG5 + | mov CTSTATE->cb.gpr[5], CARG6 + | movsd qword CTSTATE->cb.fpr[4], xmm4 + | movsd qword CTSTATE->cb.fpr[5], xmm5 + | movsd qword CTSTATE->cb.fpr[6], xmm6 + | movsd qword CTSTATE->cb.fpr[7], xmm7 + |.endif + | mov CTSTATE->cb.stack, rax + | mov CARG2, rsp + | mov SAVE_PC, CTSTATE // Any value outside of bytecode is ok. + | mov CARG1, CTSTATE + | call extern lj_ccallback_enter // (CTState *cts, void *cf) + | // lua_State * returned in eax (RD). + | set_vmstate INTERP + | mov BASE, L:RD->base + | mov RD, L:RD->top + | sub RD, BASE + | mov LFUNC:RB, [BASE-16] + | cleartp LFUNC:RB + | shr RD, 3 + | add RD, 1 + | ins_callt + |.endif + | + |->cont_ffi_callback: // Return from FFI callback. + |.if FFI + | mov L:RA, SAVE_L + | mov CTSTATE, [DISPATCH+DISPATCH_GL(ctype_state)] + | mov aword CTSTATE->L, L:RA + | mov L:RA->base, BASE + | mov L:RA->top, RB + | mov CARG1, CTSTATE + | mov CARG2, RC + | call extern lj_ccallback_leave // (CTState *cts, TValue *o) + | mov rax, CTSTATE->cb.gpr[0] + | movsd xmm0, qword CTSTATE->cb.fpr[0] + | jmp ->vm_leave_unw + |.endif + | + |->vm_ffi_call: // Call C function via FFI. + | // Caveat: needs special frame unwinding, see below. + |.if FFI + | .type CCSTATE, CCallState, rbx + | push rbp; mov rbp, rsp; push rbx; mov CCSTATE, CARG1 + | + | // Readjust stack. + | mov eax, CCSTATE->spadj + | sub rsp, rax + | + | // Copy stack slots. + | movzx ecx, byte CCSTATE->nsp + | sub ecx, 1 + | js >2 + |1: + | mov rax, [CCSTATE+rcx*8+offsetof(CCallState, stack)] + | mov [rsp+rcx*8+CCALL_SPS_EXTRA*8], rax + | sub ecx, 1 + | jns <1 + |2: + | + | movzx eax, byte CCSTATE->nfpr + | mov CARG1, CCSTATE->gpr[0] + | mov CARG2, CCSTATE->gpr[1] + | mov CARG3, CCSTATE->gpr[2] + | mov CARG4, CCSTATE->gpr[3] + |.if not X64WIN + | mov CARG5, CCSTATE->gpr[4] + | mov CARG6, CCSTATE->gpr[5] + |.endif + | test eax, eax; jz >5 + | movaps xmm0, CCSTATE->fpr[0] + | movaps xmm1, CCSTATE->fpr[1] + | movaps xmm2, CCSTATE->fpr[2] + | movaps xmm3, CCSTATE->fpr[3] + |.if not X64WIN + | cmp eax, 4; jbe >5 + | movaps xmm4, CCSTATE->fpr[4] + | movaps xmm5, CCSTATE->fpr[5] + | movaps xmm6, CCSTATE->fpr[6] + | movaps xmm7, CCSTATE->fpr[7] + |.endif + |5: + | + | call aword CCSTATE->func + | + | mov CCSTATE->gpr[0], rax + | movaps CCSTATE->fpr[0], xmm0 + |.if not X64WIN + | mov CCSTATE->gpr[1], rdx + | movaps CCSTATE->fpr[1], xmm1 + |.endif + | + | mov rbx, [rbp-8]; leave; ret + |.endif + |// Note: vm_ffi_call must be the last function in this object file! + | + |//----------------------------------------------------------------------- +} + +/* Generate the code for a single instruction. */ +static void build_ins(BuildCtx *ctx, BCOp op, int defop) +{ + int vk = 0; + |// Note: aligning all instructions does not pay off. + |=>defop: + + switch (op) { + + /* -- Comparison ops ---------------------------------------------------- */ + + /* Remember: all ops branch for a true comparison, fall through otherwise. */ + + |.macro jmp_comp, lt, ge, le, gt, target + ||switch (op) { + ||case BC_ISLT: + | lt target + ||break; + ||case BC_ISGE: + | ge target + ||break; + ||case BC_ISLE: + | le target + ||break; + ||case BC_ISGT: + | gt target + ||break; + ||default: break; /* Shut up GCC. */ + ||} + |.endmacro + + case BC_ISLT: case BC_ISGE: case BC_ISLE: case BC_ISGT: + | // RA = src1, RD = src2, JMP with RD = target + | ins_AD + | mov ITYPE, [BASE+RA*8] + | mov RB, [BASE+RD*8] + | mov RA, ITYPE + | mov RD, RB + | sar ITYPE, 47 + | sar RB, 47 + |.if DUALNUM + | cmp ITYPEd, LJ_TISNUM; jne >7 + | cmp RBd, LJ_TISNUM; jne >8 + | add PC, 4 + | cmp RAd, RDd + | jmp_comp jge, jl, jg, jle, >9 + |6: + | movzx RDd, PC_RD + | branchPC RD + |9: + | ins_next + | + |7: // RA is not an integer. + | ja ->vmeta_comp + | // RA is a number. + | cmp RBd, LJ_TISNUM; jb >1; jne ->vmeta_comp + | // RA is a number, RD is an integer. + | cvtsi2sd xmm0, RDd + | jmp >2 + | + |8: // RA is an integer, RD is not an integer. + | ja ->vmeta_comp + | // RA is an integer, RD is a number. + | cvtsi2sd xmm1, RAd + | movd xmm0, RD + | jmp >3 + |.else + | cmp ITYPEd, LJ_TISNUM; jae ->vmeta_comp + | cmp RBd, LJ_TISNUM; jae ->vmeta_comp + |.endif + |1: + | movd xmm0, RD + |2: + | movd xmm1, RA + |3: + | add PC, 4 + | ucomisd xmm0, xmm1 + | // Unordered: all of ZF CF PF set, ordered: PF clear. + | // To preserve NaN semantics GE/GT branch on unordered, but LT/LE don't. + |.if DUALNUM + | jmp_comp jbe, ja, jb, jae, <9 + | jmp <6 + |.else + | jmp_comp jbe, ja, jb, jae, >1 + | movzx RDd, PC_RD + | branchPC RD + |1: + | ins_next + |.endif + break; + + case BC_ISEQV: case BC_ISNEV: + vk = op == BC_ISEQV; + | ins_AD // RA = src1, RD = src2, JMP with RD = target + | mov RB, [BASE+RD*8] + | mov ITYPE, [BASE+RA*8] + | add PC, 4 + | mov RD, RB + | mov RA, ITYPE + | sar RB, 47 + | sar ITYPE, 47 + |.if DUALNUM + | cmp RBd, LJ_TISNUM; jne >7 + | cmp ITYPEd, LJ_TISNUM; jne >8 + | cmp RDd, RAd + if (vk) { + | jne >9 + } else { + | je >9 + } + | movzx RDd, PC_RD + | branchPC RD + |9: + | ins_next + | + |7: // RD is not an integer. + | ja >5 + | // RD is a number. + | movd xmm1, RD + | cmp ITYPEd, LJ_TISNUM; jb >1; jne >5 + | // RD is a number, RA is an integer. + | cvtsi2sd xmm0, RAd + | jmp >2 + | + |8: // RD is an integer, RA is not an integer. + | ja >5 + | // RD is an integer, RA is a number. + | cvtsi2sd xmm1, RDd + | jmp >1 + | + |.else + | cmp RBd, LJ_TISNUM; jae >5 + | cmp ITYPEd, LJ_TISNUM; jae >5 + | movd xmm1, RD + |.endif + |1: + | movd xmm0, RA + |2: + | ucomisd xmm0, xmm1 + |4: + iseqne_fp: + if (vk) { + | jp >2 // Unordered means not equal. + | jne >2 + } else { + | jp >2 // Unordered means not equal. + | je >1 + } + iseqne_end: + if (vk) { + |1: // EQ: Branch to the target. + | movzx RDd, PC_RD + | branchPC RD + |2: // NE: Fallthrough to next instruction. + |.if not FFI + |3: + |.endif + } else { + |.if not FFI + |3: + |.endif + |2: // NE: Branch to the target. + | movzx RDd, PC_RD + | branchPC RD + |1: // EQ: Fallthrough to next instruction. + } + if (LJ_DUALNUM && (op == BC_ISEQV || op == BC_ISNEV || + op == BC_ISEQN || op == BC_ISNEN)) { + | jmp <9 + } else { + | ins_next + } + | + if (op == BC_ISEQV || op == BC_ISNEV) { + |5: // Either or both types are not numbers. + |.if FFI + | cmp RBd, LJ_TCDATA; je ->vmeta_equal_cd + | cmp ITYPEd, LJ_TCDATA; je ->vmeta_equal_cd + |.endif + | cmp RA, RD + | je <1 // Same GCobjs or pvalues? + | cmp RBd, ITYPEd + | jne <2 // Not the same type? + | cmp RBd, LJ_TISTABUD + | ja <2 // Different objects and not table/ud? + | + | // Different tables or userdatas. Need to check __eq metamethod. + | // Field metatable must be at same offset for GCtab and GCudata! + | cleartp TAB:RA + | mov TAB:RB, TAB:RA->metatable + | test TAB:RB, TAB:RB + | jz <2 // No metatable? + | test byte TAB:RB->nomm, 1<vmeta_equal // Handle __eq metamethod. + } else { + |.if FFI + |3: + | cmp ITYPEd, LJ_TCDATA + if (LJ_DUALNUM && vk) { + | jne <9 + } else { + | jne <2 + } + | jmp ->vmeta_equal_cd + |.endif + } + break; + case BC_ISEQS: case BC_ISNES: + vk = op == BC_ISEQS; + | ins_AND // RA = src, RD = str const, JMP with RD = target + | mov RB, [BASE+RA*8] + | add PC, 4 + | checkstr RB, >3 + | cmp RB, [KBASE+RD*8] + iseqne_test: + if (vk) { + | jne >2 + } else { + | je >1 + } + goto iseqne_end; + case BC_ISEQN: case BC_ISNEN: + vk = op == BC_ISEQN; + | ins_AD // RA = src, RD = num const, JMP with RD = target + | mov RB, [BASE+RA*8] + | add PC, 4 + |.if DUALNUM + | checkint RB, >7 + | mov RD, [KBASE+RD*8] + | checkint RD, >8 + | cmp RBd, RDd + if (vk) { + | jne >9 + } else { + | je >9 + } + | movzx RDd, PC_RD + | branchPC RD + |9: + | ins_next + | + |7: // RA is not an integer. + | ja >3 + | // RA is a number. + | mov RD, [KBASE+RD*8] + | checkint RD, >1 + | // RA is a number, RD is an integer. + | cvtsi2sd xmm0, RDd + | jmp >2 + | + |8: // RA is an integer, RD is a number. + | cvtsi2sd xmm0, RBd + | movd xmm1, RD + | ucomisd xmm0, xmm1 + | jmp >4 + |1: + | movd xmm0, RD + |.else + | checknum RB, >3 + |1: + | movsd xmm0, qword [KBASE+RD*8] + |.endif + |2: + | ucomisd xmm0, qword [BASE+RA*8] + |4: + goto iseqne_fp; + case BC_ISEQP: case BC_ISNEP: + vk = op == BC_ISEQP; + | ins_AND // RA = src, RD = primitive type (~), JMP with RD = target + | mov RB, [BASE+RA*8] + | sar RB, 47 + | add PC, 4 + | cmp RBd, RDd + if (!LJ_HASFFI) goto iseqne_test; + if (vk) { + | jne >3 + | movzx RDd, PC_RD + | branchPC RD + |2: + | ins_next + |3: + | cmp RBd, LJ_TCDATA; jne <2 + | jmp ->vmeta_equal_cd + } else { + | je >2 + | cmp RBd, LJ_TCDATA; je ->vmeta_equal_cd + | movzx RDd, PC_RD + | branchPC RD + |2: + | ins_next + } + break; + + /* -- Unary test and copy ops ------------------------------------------- */ + + case BC_ISTC: case BC_ISFC: case BC_IST: case BC_ISF: + | ins_AD // RA = dst or unused, RD = src, JMP with RD = target + | mov ITYPE, [BASE+RD*8] + | add PC, 4 + if (op == BC_ISTC || op == BC_ISFC) { + | mov RB, ITYPE + } + | sar ITYPE, 47 + | cmp ITYPEd, LJ_TISTRUECOND + if (op == BC_IST || op == BC_ISTC) { + | jae >1 + } else { + | jb >1 + } + if (op == BC_ISTC || op == BC_ISFC) { + | mov [BASE+RA*8], RB + } + | movzx RDd, PC_RD + | branchPC RD + |1: // Fallthrough to the next instruction. + | ins_next + break; + + case BC_ISTYPE: + | ins_AD // RA = src, RD = -type + | mov RB, [BASE+RA*8] + | sar RB, 47 + | add RBd, RDd + | jne ->vmeta_istype + | ins_next + break; + case BC_ISNUM: + | ins_AD // RA = src, RD = -(TISNUM-1) + | checknumtp [BASE+RA*8], ->vmeta_istype + | ins_next + break; + + /* -- Unary ops --------------------------------------------------------- */ + + case BC_MOV: + | ins_AD // RA = dst, RD = src + | mov RB, [BASE+RD*8] + | mov [BASE+RA*8], RB + | ins_next_ + break; + case BC_NOT: + | ins_AD // RA = dst, RD = src + | mov RB, [BASE+RD*8] + | sar RB, 47 + | mov RCd, 2 + | cmp RB, LJ_TISTRUECOND + | sbb RCd, 0 + | shl RC, 47 + | not RC + | mov [BASE+RA*8], RC + | ins_next + break; + case BC_UNM: + | ins_AD // RA = dst, RD = src + | mov RB, [BASE+RD*8] + |.if DUALNUM + | checkint RB, >5 + | neg RBd + | jo >4 + | setint RB + |9: + | mov [BASE+RA*8], RB + | ins_next + |4: + | mov64 RB, U64x(41e00000,00000000) // 2^31. + | jmp <9 + |5: + | ja ->vmeta_unm + |.else + | checknum RB, ->vmeta_unm + |.endif + | mov64 RD, U64x(80000000,00000000) + | xor RB, RD + |.if DUALNUM + | jmp <9 + |.else + | mov [BASE+RA*8], RB + | ins_next + |.endif + break; + case BC_LEN: + | ins_AD // RA = dst, RD = src + | mov RD, [BASE+RD*8] + | checkstr RD, >2 + |.if DUALNUM + | mov RDd, dword STR:RD->len + |1: + | setint RD + | mov [BASE+RA*8], RD + |.else + | xorps xmm0, xmm0 + | cvtsi2sd xmm0, dword STR:RD->len + |1: + | movsd qword [BASE+RA*8], xmm0 + |.endif + | ins_next + |2: + | cmp ITYPEd, LJ_TTAB; jne ->vmeta_len + | mov TAB:CARG1, TAB:RD +#if LJ_52 + | mov TAB:RB, TAB:RD->metatable + | cmp TAB:RB, 0 + | jnz >9 + |3: +#endif + |->BC_LEN_Z: + | mov RB, BASE // Save BASE. + | call extern lj_tab_len // (GCtab *t) + | // Length of table returned in eax (RD). + |.if DUALNUM + | // Nothing to do. + |.else + | cvtsi2sd xmm0, RDd + |.endif + | mov BASE, RB // Restore BASE. + | movzx RAd, PC_RA + | jmp <1 +#if LJ_52 + |9: // Check for __len. + | test byte TAB:RB->nomm, 1<vmeta_len // 'no __len' flag NOT set: check. +#endif + break; + + /* -- Binary ops -------------------------------------------------------- */ + + |.macro ins_arithpre, sseins, ssereg + | ins_ABC + ||vk = ((int)op - BC_ADDVN) / (BC_ADDNV-BC_ADDVN); + ||switch (vk) { + ||case 0: + | checknumtp [BASE+RB*8], ->vmeta_arith_vn + | .if DUALNUM + | checknumtp [KBASE+RC*8], ->vmeta_arith_vn + | .endif + | movsd xmm0, qword [BASE+RB*8] + | sseins ssereg, qword [KBASE+RC*8] + || break; + ||case 1: + | checknumtp [BASE+RB*8], ->vmeta_arith_nv + | .if DUALNUM + | checknumtp [KBASE+RC*8], ->vmeta_arith_nv + | .endif + | movsd xmm0, qword [KBASE+RC*8] + | sseins ssereg, qword [BASE+RB*8] + || break; + ||default: + | checknumtp [BASE+RB*8], ->vmeta_arith_vv + | checknumtp [BASE+RC*8], ->vmeta_arith_vv + | movsd xmm0, qword [BASE+RB*8] + | sseins ssereg, qword [BASE+RC*8] + || break; + ||} + |.endmacro + | + |.macro ins_arithdn, intins + | ins_ABC + ||vk = ((int)op - BC_ADDVN) / (BC_ADDNV-BC_ADDVN); + ||switch (vk) { + ||case 0: + | mov RB, [BASE+RB*8] + | mov RC, [KBASE+RC*8] + | checkint RB, ->vmeta_arith_vno + | checkint RC, ->vmeta_arith_vno + | intins RBd, RCd; jo ->vmeta_arith_vno + || break; + ||case 1: + | mov RB, [BASE+RB*8] + | mov RC, [KBASE+RC*8] + | checkint RB, ->vmeta_arith_nvo + | checkint RC, ->vmeta_arith_nvo + | intins RCd, RBd; jo ->vmeta_arith_nvo + || break; + ||default: + | mov RB, [BASE+RB*8] + | mov RC, [BASE+RC*8] + | checkint RB, ->vmeta_arith_vvo + | checkint RC, ->vmeta_arith_vvo + | intins RBd, RCd; jo ->vmeta_arith_vvo + || break; + ||} + ||if (vk == 1) { + | setint RC + | mov [BASE+RA*8], RC + ||} else { + | setint RB + | mov [BASE+RA*8], RB + ||} + | ins_next + |.endmacro + | + |.macro ins_arithpost + | movsd qword [BASE+RA*8], xmm0 + |.endmacro + | + |.macro ins_arith, sseins + | ins_arithpre sseins, xmm0 + | ins_arithpost + | ins_next + |.endmacro + | + |.macro ins_arith, intins, sseins + |.if DUALNUM + | ins_arithdn intins + |.else + | ins_arith, sseins + |.endif + |.endmacro + + | // RA = dst, RB = src1 or num const, RC = src2 or num const + case BC_ADDVN: case BC_ADDNV: case BC_ADDVV: + | ins_arith add, addsd + break; + case BC_SUBVN: case BC_SUBNV: case BC_SUBVV: + | ins_arith sub, subsd + break; + case BC_MULVN: case BC_MULNV: case BC_MULVV: + | ins_arith imul, mulsd + break; + case BC_DIVVN: case BC_DIVNV: case BC_DIVVV: + | ins_arith divsd + break; + case BC_MODVN: + | ins_arithpre movsd, xmm1 + |->BC_MODVN_Z: + | call ->vm_mod + | ins_arithpost + | ins_next + break; + case BC_MODNV: case BC_MODVV: + | ins_arithpre movsd, xmm1 + | jmp ->BC_MODVN_Z // Avoid 3 copies. It's slow anyway. + break; + case BC_POW: + | ins_arithpre movsd, xmm1 + | mov RB, BASE + | call extern pow + | movzx RAd, PC_RA + | mov BASE, RB + | ins_arithpost + | ins_next + break; + + case BC_CAT: + | ins_ABC // RA = dst, RB = src_start, RC = src_end + | mov L:CARG1, SAVE_L + | mov L:CARG1->base, BASE + | lea CARG2, [BASE+RC*8] + | mov CARG3d, RCd + | sub CARG3d, RBd + |->BC_CAT_Z: + | mov L:RB, L:CARG1 + | mov SAVE_PC, PC + | call extern lj_meta_cat // (lua_State *L, TValue *top, int left) + | // NULL (finished) or TValue * (metamethod) returned in eax (RC). + | mov BASE, L:RB->base + | test RC, RC + | jnz ->vmeta_binop + | movzx RBd, PC_RB // Copy result to Stk[RA] from Stk[RB]. + | movzx RAd, PC_RA + | mov RC, [BASE+RB*8] + | mov [BASE+RA*8], RC + | ins_next + break; + + /* -- Constant ops ------------------------------------------------------ */ + + case BC_KSTR: + | ins_AND // RA = dst, RD = str const (~) + | mov RD, [KBASE+RD*8] + | settp RD, LJ_TSTR + | mov [BASE+RA*8], RD + | ins_next + break; + case BC_KCDATA: + |.if FFI + | ins_AND // RA = dst, RD = cdata const (~) + | mov RD, [KBASE+RD*8] + | settp RD, LJ_TCDATA + | mov [BASE+RA*8], RD + | ins_next + |.endif + break; + case BC_KSHORT: + | ins_AD // RA = dst, RD = signed int16 literal + |.if DUALNUM + | movsx RDd, RDW + | setint RD + | mov [BASE+RA*8], RD + |.else + | movsx RDd, RDW // Sign-extend literal. + | cvtsi2sd xmm0, RDd + | movsd qword [BASE+RA*8], xmm0 + |.endif + | ins_next + break; + case BC_KNUM: + | ins_AD // RA = dst, RD = num const + | movsd xmm0, qword [KBASE+RD*8] + | movsd qword [BASE+RA*8], xmm0 + | ins_next + break; + case BC_KPRI: + | ins_AD // RA = dst, RD = primitive type (~) + | shl RD, 47 + | not RD + | mov [BASE+RA*8], RD + | ins_next + break; + case BC_KNIL: + | ins_AD // RA = dst_start, RD = dst_end + | lea RA, [BASE+RA*8+8] + | lea RD, [BASE+RD*8] + | mov RB, LJ_TNIL + | mov [RA-8], RB // Sets minimum 2 slots. + |1: + | mov [RA], RB + | add RA, 8 + | cmp RA, RD + | jbe <1 + | ins_next + break; + + /* -- Upvalue and function ops ------------------------------------------ */ + + case BC_UGET: + | ins_AD // RA = dst, RD = upvalue # + | mov LFUNC:RB, [BASE-16] + | cleartp LFUNC:RB + | mov UPVAL:RB, [LFUNC:RB+RD*8+offsetof(GCfuncL, uvptr)] + | mov RB, UPVAL:RB->v + | mov RD, [RB] + | mov [BASE+RA*8], RD + | ins_next + break; + case BC_USETV: +#define TV2MARKOFS \ + ((int32_t)offsetof(GCupval, marked)-(int32_t)offsetof(GCupval, tv)) + | ins_AD // RA = upvalue #, RD = src + | mov LFUNC:RB, [BASE-16] + | cleartp LFUNC:RB + | mov UPVAL:RB, [LFUNC:RB+RA*8+offsetof(GCfuncL, uvptr)] + | cmp byte UPVAL:RB->closed, 0 + | mov RB, UPVAL:RB->v + | mov RA, [BASE+RD*8] + | mov [RB], RA + | jz >1 + | // Check barrier for closed upvalue. + | test byte [RB+TV2MARKOFS], LJ_GC_BLACK // isblack(uv) + | jnz >2 + |1: + | ins_next + | + |2: // Upvalue is black. Check if new value is collectable and white. + | mov RD, RA + | sar RD, 47 + | sub RDd, LJ_TISGCV + | cmp RDd, LJ_TNUMX - LJ_TISGCV // tvisgcv(v) + | jbe <1 + | cleartp GCOBJ:RA + | test byte GCOBJ:RA->gch.marked, LJ_GC_WHITES // iswhite(v) + | jz <1 + | // Crossed a write barrier. Move the barrier forward. + |.if not X64WIN + | mov CARG2, RB + | mov RB, BASE // Save BASE. + |.else + | xchg CARG2, RB // Save BASE (CARG2 == BASE). + |.endif + | lea GL:CARG1, [DISPATCH+GG_DISP2G] + | call extern lj_gc_barrieruv // (global_State *g, TValue *tv) + | mov BASE, RB // Restore BASE. + | jmp <1 + break; +#undef TV2MARKOFS + case BC_USETS: + | ins_AND // RA = upvalue #, RD = str const (~) + | mov LFUNC:RB, [BASE-16] + | cleartp LFUNC:RB + | mov UPVAL:RB, [LFUNC:RB+RA*8+offsetof(GCfuncL, uvptr)] + | mov STR:RA, [KBASE+RD*8] + | mov RD, UPVAL:RB->v + | settp STR:ITYPE, STR:RA, LJ_TSTR + | mov [RD], STR:ITYPE + | test byte UPVAL:RB->marked, LJ_GC_BLACK // isblack(uv) + | jnz >2 + |1: + | ins_next + | + |2: // Check if string is white and ensure upvalue is closed. + | test byte GCOBJ:RA->gch.marked, LJ_GC_WHITES // iswhite(str) + | jz <1 + | cmp byte UPVAL:RB->closed, 0 + | jz <1 + | // Crossed a write barrier. Move the barrier forward. + | mov RB, BASE // Save BASE (CARG2 == BASE). + | mov CARG2, RD + | lea GL:CARG1, [DISPATCH+GG_DISP2G] + | call extern lj_gc_barrieruv // (global_State *g, TValue *tv) + | mov BASE, RB // Restore BASE. + | jmp <1 + break; + case BC_USETN: + | ins_AD // RA = upvalue #, RD = num const + | mov LFUNC:RB, [BASE-16] + | cleartp LFUNC:RB + | movsd xmm0, qword [KBASE+RD*8] + | mov UPVAL:RB, [LFUNC:RB+RA*8+offsetof(GCfuncL, uvptr)] + | mov RA, UPVAL:RB->v + | movsd qword [RA], xmm0 + | ins_next + break; + case BC_USETP: + | ins_AD // RA = upvalue #, RD = primitive type (~) + | mov LFUNC:RB, [BASE-16] + | cleartp LFUNC:RB + | mov UPVAL:RB, [LFUNC:RB+RA*8+offsetof(GCfuncL, uvptr)] + | shl RD, 47 + | not RD + | mov RA, UPVAL:RB->v + | mov [RA], RD + | ins_next + break; + case BC_UCLO: + | ins_AD // RA = level, RD = target + | branchPC RD // Do this first to free RD. + | mov L:RB, SAVE_L + | cmp aword L:RB->openupval, 0 + | je >1 + | mov L:RB->base, BASE + | lea CARG2, [BASE+RA*8] // Caveat: CARG2 == BASE + | mov L:CARG1, L:RB // Caveat: CARG1 == RA + | call extern lj_func_closeuv // (lua_State *L, TValue *level) + | mov BASE, L:RB->base + |1: + | ins_next + break; + + case BC_FNEW: + | ins_AND // RA = dst, RD = proto const (~) (holding function prototype) + | mov L:RB, SAVE_L + | mov L:RB->base, BASE // Caveat: CARG2/CARG3 may be BASE. + | mov CARG3, [BASE-16] + | cleartp CARG3 + | mov CARG2, [KBASE+RD*8] // Fetch GCproto *. + | mov CARG1, L:RB + | mov SAVE_PC, PC + | // (lua_State *L, GCproto *pt, GCfuncL *parent) + | call extern lj_func_newL_gc + | // GCfuncL * returned in eax (RC). + | mov BASE, L:RB->base + | movzx RAd, PC_RA + | settp LFUNC:RC, LJ_TFUNC + | mov [BASE+RA*8], LFUNC:RC + | ins_next + break; + + /* -- Table ops --------------------------------------------------------- */ + + case BC_TNEW: + | ins_AD // RA = dst, RD = hbits|asize + | mov L:RB, SAVE_L + | mov L:RB->base, BASE + | mov RA, [DISPATCH+DISPATCH_GL(gc.total)] + | cmp RA, [DISPATCH+DISPATCH_GL(gc.threshold)] + | mov SAVE_PC, PC + | jae >5 + |1: + | mov CARG3d, RDd + | and RDd, 0x7ff + | shr CARG3d, 11 + | cmp RDd, 0x7ff + | je >3 + |2: + | mov L:CARG1, L:RB + | mov CARG2d, RDd + | call extern lj_tab_new // (lua_State *L, int32_t asize, uint32_t hbits) + | // Table * returned in eax (RC). + | mov BASE, L:RB->base + | movzx RAd, PC_RA + | settp TAB:RC, LJ_TTAB + | mov [BASE+RA*8], TAB:RC + | ins_next + |3: // Turn 0x7ff into 0x801. + | mov RDd, 0x801 + | jmp <2 + |5: + | mov L:CARG1, L:RB + | call extern lj_gc_step_fixtop // (lua_State *L) + | movzx RDd, PC_RD + | jmp <1 + break; + case BC_TDUP: + | ins_AND // RA = dst, RD = table const (~) (holding template table) + | mov L:RB, SAVE_L + | mov RA, [DISPATCH+DISPATCH_GL(gc.total)] + | mov SAVE_PC, PC + | cmp RA, [DISPATCH+DISPATCH_GL(gc.threshold)] + | mov L:RB->base, BASE + | jae >3 + |2: + | mov TAB:CARG2, [KBASE+RD*8] // Caveat: CARG2 == BASE + | mov L:CARG1, L:RB // Caveat: CARG1 == RA + | call extern lj_tab_dup // (lua_State *L, Table *kt) + | // Table * returned in eax (RC). + | mov BASE, L:RB->base + | movzx RAd, PC_RA + | settp TAB:RC, LJ_TTAB + | mov [BASE+RA*8], TAB:RC + | ins_next + |3: + | mov L:CARG1, L:RB + | call extern lj_gc_step_fixtop // (lua_State *L) + | movzx RDd, PC_RD // Need to reload RD. + | not RD + | jmp <2 + break; + + case BC_GGET: + | ins_AND // RA = dst, RD = str const (~) + | mov LFUNC:RB, [BASE-16] + | cleartp LFUNC:RB + | mov TAB:RB, LFUNC:RB->env + | mov STR:RC, [KBASE+RD*8] + | jmp ->BC_TGETS_Z + break; + case BC_GSET: + | ins_AND // RA = src, RD = str const (~) + | mov LFUNC:RB, [BASE-16] + | cleartp LFUNC:RB + | mov TAB:RB, LFUNC:RB->env + | mov STR:RC, [KBASE+RD*8] + | jmp ->BC_TSETS_Z + break; + + case BC_TGETV: + | ins_ABC // RA = dst, RB = table, RC = key + | mov TAB:RB, [BASE+RB*8] + | mov RC, [BASE+RC*8] + | checktab TAB:RB, ->vmeta_tgetv + | + | // Integer key? + |.if DUALNUM + | checkint RC, >5 + |.else + | // Convert number to int and back and compare. + | checknum RC, >5 + | movd xmm0, RC + | cvttsd2si RCd, xmm0 + | cvtsi2sd xmm1, RCd + | ucomisd xmm0, xmm1 + | jne ->vmeta_tgetv // Generic numeric key? Use fallback. + |.endif + | cmp RCd, TAB:RB->asize // Takes care of unordered, too. + | jae ->vmeta_tgetv // Not in array part? Use fallback. + | shl RCd, 3 + | add RC, TAB:RB->array + | // Get array slot. + | mov ITYPE, [RC] + | cmp ITYPE, LJ_TNIL // Avoid overwriting RB in fastpath. + | je >2 + |1: + | mov [BASE+RA*8], ITYPE + | ins_next + | + |2: // Check for __index if table value is nil. + | mov TAB:TMPR, TAB:RB->metatable + | test TAB:TMPR, TAB:TMPR + | jz <1 + | test byte TAB:TMPR->nomm, 1<vmeta_tgetv // 'no __index' flag NOT set: check. + | jmp <1 + | + |5: // String key? + | cmp ITYPEd, LJ_TSTR; jne ->vmeta_tgetv + | cleartp STR:RC + | jmp ->BC_TGETS_Z + break; + case BC_TGETS: + | ins_ABC // RA = dst, RB = table, RC = str const (~) + | mov TAB:RB, [BASE+RB*8] + | not RC + | mov STR:RC, [KBASE+RC*8] + | checktab TAB:RB, ->vmeta_tgets + |->BC_TGETS_Z: // RB = GCtab *, RC = GCstr * + | mov TMPRd, TAB:RB->hmask + | and TMPRd, STR:RC->hash + | imul TMPRd, #NODE + | add NODE:TMPR, TAB:RB->node + | settp ITYPE, STR:RC, LJ_TSTR + |1: + | cmp NODE:TMPR->key, ITYPE + | jne >4 + | // Get node value. + | mov ITYPE, NODE:TMPR->val + | cmp ITYPE, LJ_TNIL + | je >5 // Key found, but nil value? + |2: + | mov [BASE+RA*8], ITYPE + | ins_next + | + |4: // Follow hash chain. + | mov NODE:TMPR, NODE:TMPR->next + | test NODE:TMPR, NODE:TMPR + | jnz <1 + | // End of hash chain: key not found, nil result. + | mov ITYPE, LJ_TNIL + | + |5: // Check for __index if table value is nil. + | mov TAB:TMPR, TAB:RB->metatable + | test TAB:TMPR, TAB:TMPR + | jz <2 // No metatable: done. + | test byte TAB:TMPR->nomm, 1<vmeta_tgets // Caveat: preserve STR:RC. + break; + case BC_TGETB: + | ins_ABC // RA = dst, RB = table, RC = byte literal + | mov TAB:RB, [BASE+RB*8] + | checktab TAB:RB, ->vmeta_tgetb + | cmp RCd, TAB:RB->asize + | jae ->vmeta_tgetb + | shl RCd, 3 + | add RC, TAB:RB->array + | // Get array slot. + | mov ITYPE, [RC] + | cmp ITYPE, LJ_TNIL + | je >2 + |1: + | mov [BASE+RA*8], ITYPE + | ins_next + | + |2: // Check for __index if table value is nil. + | mov TAB:TMPR, TAB:RB->metatable + | test TAB:TMPR, TAB:TMPR + | jz <1 + | test byte TAB:TMPR->nomm, 1<vmeta_tgetb // 'no __index' flag NOT set: check. + | jmp <1 + break; + case BC_TGETR: + | ins_ABC // RA = dst, RB = table, RC = key + | mov TAB:RB, [BASE+RB*8] + | cleartp TAB:RB + |.if DUALNUM + | mov RCd, dword [BASE+RC*8] + |.else + | cvttsd2si RCd, qword [BASE+RC*8] + |.endif + | cmp RCd, TAB:RB->asize + | jae ->vmeta_tgetr // Not in array part? Use fallback. + | shl RCd, 3 + | add RC, TAB:RB->array + | // Get array slot. + |->BC_TGETR_Z: + | mov ITYPE, [RC] + |->BC_TGETR2_Z: + | mov [BASE+RA*8], ITYPE + | ins_next + break; + + case BC_TSETV: + | ins_ABC // RA = src, RB = table, RC = key + | mov TAB:RB, [BASE+RB*8] + | mov RC, [BASE+RC*8] + | checktab TAB:RB, ->vmeta_tsetv + | + | // Integer key? + |.if DUALNUM + | checkint RC, >5 + |.else + | // Convert number to int and back and compare. + | checknum RC, >5 + | movd xmm0, RC + | cvttsd2si RCd, xmm0 + | cvtsi2sd xmm1, RCd + | ucomisd xmm0, xmm1 + | jne ->vmeta_tsetv // Generic numeric key? Use fallback. + |.endif + | cmp RCd, TAB:RB->asize // Takes care of unordered, too. + | jae ->vmeta_tsetv + | shl RCd, 3 + | add RC, TAB:RB->array + | cmp aword [RC], LJ_TNIL + | je >3 // Previous value is nil? + |1: + | test byte TAB:RB->marked, LJ_GC_BLACK // isblack(table) + | jnz >7 + |2: // Set array slot. + | mov RB, [BASE+RA*8] + | mov [RC], RB + | ins_next + | + |3: // Check for __newindex if previous value is nil. + | mov TAB:TMPR, TAB:RB->metatable + | test TAB:TMPR, TAB:TMPR + | jz <1 + | test byte TAB:TMPR->nomm, 1<vmeta_tsetv // 'no __newindex' flag NOT set: check. + | jmp <1 + | + |5: // String key? + | cmp ITYPEd, LJ_TSTR; jne ->vmeta_tsetv + | cleartp STR:RC + | jmp ->BC_TSETS_Z + | + |7: // Possible table write barrier for the value. Skip valiswhite check. + | barrierback TAB:RB, TMPR + | jmp <2 + break; + case BC_TSETS: + | ins_ABC // RA = src, RB = table, RC = str const (~) + | mov TAB:RB, [BASE+RB*8] + | not RC + | mov STR:RC, [KBASE+RC*8] + | checktab TAB:RB, ->vmeta_tsets + |->BC_TSETS_Z: // RB = GCtab *, RC = GCstr * + | mov TMPRd, TAB:RB->hmask + | and TMPRd, STR:RC->hash + | imul TMPRd, #NODE + | mov byte TAB:RB->nomm, 0 // Clear metamethod cache. + | add NODE:TMPR, TAB:RB->node + | settp ITYPE, STR:RC, LJ_TSTR + |1: + | cmp NODE:TMPR->key, ITYPE + | jne >5 + | // Ok, key found. Assumes: offsetof(Node, val) == 0 + | cmp aword [TMPR], LJ_TNIL + | je >4 // Previous value is nil? + |2: + | test byte TAB:RB->marked, LJ_GC_BLACK // isblack(table) + | jnz >7 + |3: // Set node value. + | mov ITYPE, [BASE+RA*8] + | mov [TMPR], ITYPE + | ins_next + | + |4: // Check for __newindex if previous value is nil. + | mov TAB:ITYPE, TAB:RB->metatable + | test TAB:ITYPE, TAB:ITYPE + | jz <2 + | test byte TAB:ITYPE->nomm, 1<vmeta_tsets // 'no __newindex' flag NOT set: check. + | jmp <2 + | + |5: // Follow hash chain. + | mov NODE:TMPR, NODE:TMPR->next + | test NODE:TMPR, NODE:TMPR + | jnz <1 + | // End of hash chain: key not found, add a new one. + | + | // But check for __newindex first. + | mov TAB:TMPR, TAB:RB->metatable + | test TAB:TMPR, TAB:TMPR + | jz >6 // No metatable: continue. + | test byte TAB:TMPR->nomm, 1<vmeta_tsets // 'no __newindex' flag NOT set: check. + |6: + | mov TMP1, ITYPE + | mov L:CARG1, SAVE_L + | mov L:CARG1->base, BASE + | lea CARG3, TMP1 + | mov CARG2, TAB:RB + | mov SAVE_PC, PC + | call extern lj_tab_newkey // (lua_State *L, GCtab *t, TValue *k) + | // Handles write barrier for the new key. TValue * returned in eax (RC). + | mov L:CARG1, SAVE_L + | mov BASE, L:CARG1->base + | mov TMPR, rax + | movzx RAd, PC_RA + | jmp <2 // Must check write barrier for value. + | + |7: // Possible table write barrier for the value. Skip valiswhite check. + | barrierback TAB:RB, ITYPE + | jmp <3 + break; + case BC_TSETB: + | ins_ABC // RA = src, RB = table, RC = byte literal + | mov TAB:RB, [BASE+RB*8] + | checktab TAB:RB, ->vmeta_tsetb + | cmp RCd, TAB:RB->asize + | jae ->vmeta_tsetb + | shl RCd, 3 + | add RC, TAB:RB->array + | cmp aword [RC], LJ_TNIL + | je >3 // Previous value is nil? + |1: + | test byte TAB:RB->marked, LJ_GC_BLACK // isblack(table) + | jnz >7 + |2: // Set array slot. + | mov ITYPE, [BASE+RA*8] + | mov [RC], ITYPE + | ins_next + | + |3: // Check for __newindex if previous value is nil. + | mov TAB:TMPR, TAB:RB->metatable + | test TAB:TMPR, TAB:TMPR + | jz <1 + | test byte TAB:TMPR->nomm, 1<vmeta_tsetb // 'no __newindex' flag NOT set: check. + | jmp <1 + | + |7: // Possible table write barrier for the value. Skip valiswhite check. + | barrierback TAB:RB, TMPR + | jmp <2 + break; + case BC_TSETR: + | ins_ABC // RA = src, RB = table, RC = key + | mov TAB:RB, [BASE+RB*8] + | cleartp TAB:RB + |.if DUALNUM + | mov RC, [BASE+RC*8] + |.else + | cvttsd2si RCd, qword [BASE+RC*8] + |.endif + | test byte TAB:RB->marked, LJ_GC_BLACK // isblack(table) + | jnz >7 + |2: + | cmp RCd, TAB:RB->asize + | jae ->vmeta_tsetr + | shl RCd, 3 + | add RC, TAB:RB->array + | // Set array slot. + |->BC_TSETR_Z: + | mov ITYPE, [BASE+RA*8] + | mov [RC], ITYPE + | ins_next + | + |7: // Possible table write barrier for the value. Skip valiswhite check. + | barrierback TAB:RB, TMPR + | jmp <2 + break; + + case BC_TSETM: + | ins_AD // RA = base (table at base-1), RD = num const (start index) + |1: + | mov TMPRd, dword [KBASE+RD*8] // Integer constant is in lo-word. + | lea RA, [BASE+RA*8] + | mov TAB:RB, [RA-8] // Guaranteed to be a table. + | cleartp TAB:RB + | test byte TAB:RB->marked, LJ_GC_BLACK // isblack(table) + | jnz >7 + |2: + | mov RDd, MULTRES + | sub RDd, 1 + | jz >4 // Nothing to copy? + | add RDd, TMPRd // Compute needed size. + | cmp RDd, TAB:RB->asize + | ja >5 // Doesn't fit into array part? + | sub RDd, TMPRd + | shl TMPRd, 3 + | add TMPR, TAB:RB->array + |3: // Copy result slots to table. + | mov RB, [RA] + | add RA, 8 + | mov [TMPR], RB + | add TMPR, 8 + | sub RDd, 1 + | jnz <3 + |4: + | ins_next + | + |5: // Need to resize array part. + | mov L:CARG1, SAVE_L + | mov L:CARG1->base, BASE // Caveat: CARG2/CARG3 may be BASE. + | mov CARG2, TAB:RB + | mov CARG3d, RDd + | mov L:RB, L:CARG1 + | mov SAVE_PC, PC + | call extern lj_tab_reasize // (lua_State *L, GCtab *t, int nasize) + | mov BASE, L:RB->base + | movzx RAd, PC_RA // Restore RA. + | movzx RDd, PC_RD // Restore RD. + | jmp <1 // Retry. + | + |7: // Possible table write barrier for any value. Skip valiswhite check. + | barrierback TAB:RB, RD + | jmp <2 + break; + + /* -- Calls and vararg handling ----------------------------------------- */ + + case BC_CALL: case BC_CALLM: + | ins_A_C // RA = base, (RB = nresults+1,) RC = nargs+1 | extra_nargs + if (op == BC_CALLM) { + | add NARGS:RDd, MULTRES + } + | mov LFUNC:RB, [BASE+RA*8] + | checkfunc LFUNC:RB, ->vmeta_call_ra + | lea BASE, [BASE+RA*8+16] + | ins_call + break; + + case BC_CALLMT: + | ins_AD // RA = base, RD = extra_nargs + | add NARGS:RDd, MULTRES + | // Fall through. Assumes BC_CALLT follows and ins_AD is a no-op. + break; + case BC_CALLT: + | ins_AD // RA = base, RD = nargs+1 + | lea RA, [BASE+RA*8+16] + | mov KBASE, BASE // Use KBASE for move + vmeta_call hint. + | mov LFUNC:RB, [RA-16] + | checktp_nc LFUNC:RB, LJ_TFUNC, ->vmeta_call + |->BC_CALLT_Z: + | mov PC, [BASE-8] + | test PCd, FRAME_TYPE + | jnz >7 + |1: + | mov [BASE-16], LFUNC:RB // Copy func+tag down, reloaded below. + | mov MULTRES, NARGS:RDd + | sub NARGS:RDd, 1 + | jz >3 + |2: // Move args down. + | mov RB, [RA] + | add RA, 8 + | mov [KBASE], RB + | add KBASE, 8 + | sub NARGS:RDd, 1 + | jnz <2 + | + | mov LFUNC:RB, [BASE-16] + |3: + | cleartp LFUNC:RB + | mov NARGS:RDd, MULTRES + | cmp byte LFUNC:RB->ffid, 1 // (> FF_C) Calling a fast function? + | ja >5 + |4: + | ins_callt + | + |5: // Tailcall to a fast function. + | test PCd, FRAME_TYPE // Lua frame below? + | jnz <4 + | movzx RAd, PC_RA + | neg RA + | mov LFUNC:KBASE, [BASE+RA*8-32] // Need to prepare KBASE. + | cleartp LFUNC:KBASE + | mov KBASE, LFUNC:KBASE->pc + | mov KBASE, [KBASE+PC2PROTO(k)] + | jmp <4 + | + |7: // Tailcall from a vararg function. + | sub PC, FRAME_VARG + | test PCd, FRAME_TYPEP + | jnz >8 // Vararg frame below? + | sub BASE, PC // Need to relocate BASE/KBASE down. + | mov KBASE, BASE + | mov PC, [BASE-8] + | jmp <1 + |8: + | add PCd, FRAME_VARG + | jmp <1 + break; + + case BC_ITERC: + | ins_A // RA = base, (RB = nresults+1,) RC = nargs+1 (2+1) + | lea RA, [BASE+RA*8+16] // fb = base+2 + | mov RB, [RA-32] // Copy state. fb[0] = fb[-4]. + | mov RC, [RA-24] // Copy control var. fb[1] = fb[-3]. + | mov [RA], RB + | mov [RA+8], RC + | mov LFUNC:RB, [RA-40] // Copy callable. fb[-2] = fb[-5] + | mov [RA-16], LFUNC:RB + | mov NARGS:RDd, 2+1 // Handle like a regular 2-arg call. + | checkfunc LFUNC:RB, ->vmeta_call + | mov BASE, RA + | ins_call + break; + + case BC_ITERN: + | ins_A // RA = base, (RB = nresults+1, RC = nargs+1 (2+1)) + |.if JIT + | // NYI: add hotloop, record BC_ITERN. + |.endif + | mov TAB:RB, [BASE+RA*8-16] + | cleartp TAB:RB + | mov RCd, [BASE+RA*8-8] // Get index from control var. + | mov TMPRd, TAB:RB->asize + | add PC, 4 + | mov ITYPE, TAB:RB->array + |1: // Traverse array part. + | cmp RCd, TMPRd; jae >5 // Index points after array part? + | cmp aword [ITYPE+RC*8], LJ_TNIL; je >4 + |.if not DUALNUM + | cvtsi2sd xmm0, RCd + |.endif + | // Copy array slot to returned value. + | mov RB, [ITYPE+RC*8] + | mov [BASE+RA*8+8], RB + | // Return array index as a numeric key. + |.if DUALNUM + | setint ITYPE, RC + | mov [BASE+RA*8], ITYPE + |.else + | movsd qword [BASE+RA*8], xmm0 + |.endif + | add RCd, 1 + | mov [BASE+RA*8-8], RCd // Update control var. + |2: + | movzx RDd, PC_RD // Get target from ITERL. + | branchPC RD + |3: + | ins_next + | + |4: // Skip holes in array part. + | add RCd, 1 + | jmp <1 + | + |5: // Traverse hash part. + | sub RCd, TMPRd + |6: + | cmp RCd, TAB:RB->hmask; ja <3 // End of iteration? Branch to ITERL+1. + | imul ITYPEd, RCd, #NODE + | add NODE:ITYPE, TAB:RB->node + | cmp aword NODE:ITYPE->val, LJ_TNIL; je >7 + | lea TMPRd, [RCd+TMPRd+1] + | // Copy key and value from hash slot. + | mov RB, NODE:ITYPE->key + | mov RC, NODE:ITYPE->val + | mov [BASE+RA*8], RB + | mov [BASE+RA*8+8], RC + | mov [BASE+RA*8-8], TMPRd + | jmp <2 + | + |7: // Skip holes in hash part. + | add RCd, 1 + | jmp <6 + break; + + case BC_ISNEXT: + | ins_AD // RA = base, RD = target (points to ITERN) + | mov CFUNC:RB, [BASE+RA*8-24] + | checkfunc CFUNC:RB, >5 + | checktptp [BASE+RA*8-16], LJ_TTAB, >5 + | cmp aword [BASE+RA*8-8], LJ_TNIL; jne >5 + | cmp byte CFUNC:RB->ffid, FF_next_N; jne >5 + | branchPC RD + | mov64 TMPR, U64x(fffe7fff, 00000000) + | mov [BASE+RA*8-8], TMPR // Initialize control var. + |1: + | ins_next + |5: // Despecialize bytecode if any of the checks fail. + | mov PC_OP, BC_JMP + | branchPC RD + | mov byte [PC], BC_ITERC + | jmp <1 + break; + + case BC_VARG: + | ins_ABC // RA = base, RB = nresults+1, RC = numparams + | lea TMPR, [BASE+RC*8+(16+FRAME_VARG)] + | lea RA, [BASE+RA*8] + | sub TMPR, [BASE-8] + | // Note: TMPR may now be even _above_ BASE if nargs was < numparams. + | test RB, RB + | jz >5 // Copy all varargs? + | lea RB, [RA+RB*8-8] + | cmp TMPR, BASE // No vararg slots? + | jnb >2 + |1: // Copy vararg slots to destination slots. + | mov RC, [TMPR-16] + | add TMPR, 8 + | mov [RA], RC + | add RA, 8 + | cmp RA, RB // All destination slots filled? + | jnb >3 + | cmp TMPR, BASE // No more vararg slots? + | jb <1 + |2: // Fill up remainder with nil. + | mov aword [RA], LJ_TNIL + | add RA, 8 + | cmp RA, RB + | jb <2 + |3: + | ins_next + | + |5: // Copy all varargs. + | mov MULTRES, 1 // MULTRES = 0+1 + | mov RC, BASE + | sub RC, TMPR + | jbe <3 // No vararg slots? + | mov RBd, RCd + | shr RBd, 3 + | add RBd, 1 + | mov MULTRES, RBd // MULTRES = #varargs+1 + | mov L:RB, SAVE_L + | add RC, RA + | cmp RC, L:RB->maxstack + | ja >7 // Need to grow stack? + |6: // Copy all vararg slots. + | mov RC, [TMPR-16] + | add TMPR, 8 + | mov [RA], RC + | add RA, 8 + | cmp TMPR, BASE // No more vararg slots? + | jb <6 + | jmp <3 + | + |7: // Grow stack for varargs. + | mov L:RB->base, BASE + | mov L:RB->top, RA + | mov SAVE_PC, PC + | sub TMPR, BASE // Need delta, because BASE may change. + | mov TMP1hi, TMPRd + | mov CARG2d, MULTRES + | sub CARG2d, 1 + | mov CARG1, L:RB + | call extern lj_state_growstack // (lua_State *L, int n) + | mov BASE, L:RB->base + | movsxd TMPR, TMP1hi + | mov RA, L:RB->top + | add TMPR, BASE + | jmp <6 + break; + + /* -- Returns ----------------------------------------------------------- */ + + case BC_RETM: + | ins_AD // RA = results, RD = extra_nresults + | add RDd, MULTRES // MULTRES >=1, so RD >=1. + | // Fall through. Assumes BC_RET follows and ins_AD is a no-op. + break; + + case BC_RET: case BC_RET0: case BC_RET1: + | ins_AD // RA = results, RD = nresults+1 + if (op != BC_RET0) { + | shl RAd, 3 + } + |1: + | mov PC, [BASE-8] + | mov MULTRES, RDd // Save nresults+1. + | test PCd, FRAME_TYPE // Check frame type marker. + | jnz >7 // Not returning to a fixarg Lua func? + switch (op) { + case BC_RET: + |->BC_RET_Z: + | mov KBASE, BASE // Use KBASE for result move. + | sub RDd, 1 + | jz >3 + |2: // Move results down. + | mov RB, [KBASE+RA] + | mov [KBASE-16], RB + | add KBASE, 8 + | sub RDd, 1 + | jnz <2 + |3: + | mov RDd, MULTRES // Note: MULTRES may be >255. + | movzx RBd, PC_RB // So cannot compare with RDL! + |5: + | cmp RBd, RDd // More results expected? + | ja >6 + break; + case BC_RET1: + | mov RB, [BASE+RA] + | mov [BASE-16], RB + /* fallthrough */ + case BC_RET0: + |5: + | cmp PC_RB, RDL // More results expected? + | ja >6 + default: + break; + } + | movzx RAd, PC_RA + | neg RA + | lea BASE, [BASE+RA*8-16] // base = base - (RA+2)*8 + | mov LFUNC:KBASE, [BASE-16] + | cleartp LFUNC:KBASE + | mov KBASE, LFUNC:KBASE->pc + | mov KBASE, [KBASE+PC2PROTO(k)] + | ins_next + | + |6: // Fill up results with nil. + if (op == BC_RET) { + | mov aword [KBASE-16], LJ_TNIL // Note: relies on shifted base. + | add KBASE, 8 + } else { + | mov aword [BASE+RD*8-24], LJ_TNIL + } + | add RD, 1 + | jmp <5 + | + |7: // Non-standard return case. + | lea RB, [PC-FRAME_VARG] + | test RBd, FRAME_TYPEP + | jnz ->vm_return + | // Return from vararg function: relocate BASE down and RA up. + | sub BASE, RB + if (op != BC_RET0) { + | add RA, RB + } + | jmp <1 + break; + + /* -- Loops and branches ------------------------------------------------ */ + + |.define FOR_IDX, [RA] + |.define FOR_STOP, [RA+8] + |.define FOR_STEP, [RA+16] + |.define FOR_EXT, [RA+24] + + case BC_FORL: + |.if JIT + | hotloop RBd + |.endif + | // Fall through. Assumes BC_IFORL follows and ins_AJ is a no-op. + break; + + case BC_JFORI: + case BC_JFORL: +#if !LJ_HASJIT + break; +#endif + case BC_FORI: + case BC_IFORL: + vk = (op == BC_IFORL || op == BC_JFORL); + | ins_AJ // RA = base, RD = target (after end of loop or start of loop) + | lea RA, [BASE+RA*8] + if (LJ_DUALNUM) { + | mov RB, FOR_IDX + | checkint RB, >9 + | mov TMPR, FOR_STOP + if (!vk) { + | checkint TMPR, ->vmeta_for + | mov ITYPE, FOR_STEP + | test ITYPEd, ITYPEd; js >5 + | sar ITYPE, 47; + | cmp ITYPEd, LJ_TISNUM; jne ->vmeta_for + } else { +#ifdef LUA_USE_ASSERT + | checkinttp FOR_STOP, ->assert_bad_for_arg_type + | checkinttp FOR_STEP, ->assert_bad_for_arg_type +#endif + | mov ITYPE, FOR_STEP + | test ITYPEd, ITYPEd; js >5 + | add RBd, ITYPEd; jo >1 + | setint RB + | mov FOR_IDX, RB + } + | cmp RBd, TMPRd + | mov FOR_EXT, RB + if (op == BC_FORI) { + | jle >7 + |1: + |6: + | branchPC RD + } else if (op == BC_JFORI) { + | branchPC RD + | movzx RDd, PC_RD + | jle =>BC_JLOOP + |1: + |6: + } else if (op == BC_IFORL) { + | jg >7 + |6: + | branchPC RD + |1: + } else { + | jle =>BC_JLOOP + |1: + |6: + } + |7: + | ins_next + | + |5: // Invert check for negative step. + if (!vk) { + | sar ITYPE, 47; + | cmp ITYPEd, LJ_TISNUM; jne ->vmeta_for + } else { + | add RBd, ITYPEd; jo <1 + | setint RB + | mov FOR_IDX, RB + } + | cmp RBd, TMPRd + | mov FOR_EXT, RB + if (op == BC_FORI) { + | jge <7 + } else if (op == BC_JFORI) { + | branchPC RD + | movzx RDd, PC_RD + | jge =>BC_JLOOP + } else if (op == BC_IFORL) { + | jl <7 + } else { + | jge =>BC_JLOOP + } + | jmp <6 + |9: // Fallback to FP variant. + if (!vk) { + | jae ->vmeta_for + } + } else if (!vk) { + | checknumtp FOR_IDX, ->vmeta_for + } + if (!vk) { + | checknumtp FOR_STOP, ->vmeta_for + } else { +#ifdef LUA_USE_ASSERT + | checknumtp FOR_STOP, ->assert_bad_for_arg_type + | checknumtp FOR_STEP, ->assert_bad_for_arg_type +#endif + } + | mov RB, FOR_STEP + if (!vk) { + | checknum RB, ->vmeta_for + } + | movsd xmm0, qword FOR_IDX + | movsd xmm1, qword FOR_STOP + if (vk) { + | addsd xmm0, qword FOR_STEP + | movsd qword FOR_IDX, xmm0 + | test RB, RB; js >3 + } else { + | jl >3 + } + | ucomisd xmm1, xmm0 + |1: + | movsd qword FOR_EXT, xmm0 + if (op == BC_FORI) { + |.if DUALNUM + | jnb <7 + |.else + | jnb >2 + | branchPC RD + |.endif + } else if (op == BC_JFORI) { + | branchPC RD + | movzx RDd, PC_RD + | jnb =>BC_JLOOP + } else if (op == BC_IFORL) { + |.if DUALNUM + | jb <7 + |.else + | jb >2 + | branchPC RD + |.endif + } else { + | jnb =>BC_JLOOP + } + |.if DUALNUM + | jmp <6 + |.else + |2: + | ins_next + |.endif + | + |3: // Invert comparison if step is negative. + | ucomisd xmm0, xmm1 + | jmp <1 + break; + + case BC_ITERL: + |.if JIT + | hotloop RBd + |.endif + | // Fall through. Assumes BC_IITERL follows and ins_AJ is a no-op. + break; + + case BC_JITERL: +#if !LJ_HASJIT + break; +#endif + case BC_IITERL: + | ins_AJ // RA = base, RD = target + | lea RA, [BASE+RA*8] + | mov RB, [RA] + | cmp RB, LJ_TNIL; je >1 // Stop if iterator returned nil. + if (op == BC_JITERL) { + | mov [RA-8], RB + | jmp =>BC_JLOOP + } else { + | branchPC RD // Otherwise save control var + branch. + | mov [RA-8], RB + } + |1: + | ins_next + break; + + case BC_LOOP: + | ins_A // RA = base, RD = target (loop extent) + | // Note: RA/RD is only used by trace recorder to determine scope/extent + | // This opcode does NOT jump, it's only purpose is to detect a hot loop. + |.if JIT + | hotloop RBd + |.endif + | // Fall through. Assumes BC_ILOOP follows and ins_A is a no-op. + break; + + case BC_ILOOP: + | ins_A // RA = base, RD = target (loop extent) + | ins_next + break; + + case BC_JLOOP: + |.if JIT + | ins_AD // RA = base (ignored), RD = traceno + | mov RA, [DISPATCH+DISPATCH_J(trace)] + | mov TRACE:RD, [RA+RD*8] + | mov RD, TRACE:RD->mcode + | mov L:RB, SAVE_L + | mov [DISPATCH+DISPATCH_GL(jit_base)], BASE + | mov [DISPATCH+DISPATCH_GL(tmpbuf.L)], L:RB + | // Save additional callee-save registers only used in compiled code. + |.if X64WIN + | mov CSAVE_4, r12 + | mov CSAVE_3, r13 + | mov CSAVE_2, r14 + | mov CSAVE_1, r15 + | mov RA, rsp + | sub rsp, 10*16+4*8 + | movdqa [RA-1*16], xmm6 + | movdqa [RA-2*16], xmm7 + | movdqa [RA-3*16], xmm8 + | movdqa [RA-4*16], xmm9 + | movdqa [RA-5*16], xmm10 + | movdqa [RA-6*16], xmm11 + | movdqa [RA-7*16], xmm12 + | movdqa [RA-8*16], xmm13 + | movdqa [RA-9*16], xmm14 + | movdqa [RA-10*16], xmm15 + |.else + | sub rsp, 16 + | mov [rsp+16], r12 + | mov [rsp+8], r13 + |.endif + | jmp RD + |.endif + break; + + case BC_JMP: + | ins_AJ // RA = unused, RD = target + | branchPC RD + | ins_next + break; + + /* -- Function headers -------------------------------------------------- */ + + /* + ** Reminder: A function may be called with func/args above L->maxstack, + ** i.e. occupying EXTRA_STACK slots. And vmeta_call may add one extra slot, + ** too. This means all FUNC* ops (including fast functions) must check + ** for stack overflow _before_ adding more slots! + */ + + case BC_FUNCF: + |.if JIT + | hotcall RBd + |.endif + case BC_FUNCV: /* NYI: compiled vararg functions. */ + | // Fall through. Assumes BC_IFUNCF/BC_IFUNCV follow and ins_AD is a no-op. + break; + + case BC_JFUNCF: +#if !LJ_HASJIT + break; +#endif + case BC_IFUNCF: + | ins_AD // BASE = new base, RA = framesize, RD = nargs+1 + | mov KBASE, [PC-4+PC2PROTO(k)] + | mov L:RB, SAVE_L + | lea RA, [BASE+RA*8] // Top of frame. + | cmp RA, L:RB->maxstack + | ja ->vm_growstack_f + | movzx RAd, byte [PC-4+PC2PROTO(numparams)] + | cmp NARGS:RDd, RAd // Check for missing parameters. + | jbe >3 + |2: + if (op == BC_JFUNCF) { + | movzx RDd, PC_RD + | jmp =>BC_JLOOP + } else { + | ins_next + } + | + |3: // Clear missing parameters. + | mov aword [BASE+NARGS:RD*8-8], LJ_TNIL + | add NARGS:RDd, 1 + | cmp NARGS:RDd, RAd + | jbe <3 + | jmp <2 + break; + + case BC_JFUNCV: +#if !LJ_HASJIT + break; +#endif + | int3 // NYI: compiled vararg functions + break; /* NYI: compiled vararg functions. */ + + case BC_IFUNCV: + | ins_AD // BASE = new base, RA = framesize, RD = nargs+1 + | lea RBd, [NARGS:RD*8+FRAME_VARG+8] + | lea RD, [BASE+NARGS:RD*8+8] + | mov LFUNC:KBASE, [BASE-16] + | mov [RD-8], RB // Store delta + FRAME_VARG. + | mov [RD-16], LFUNC:KBASE // Store copy of LFUNC. + | mov L:RB, SAVE_L + | lea RA, [RD+RA*8] + | cmp RA, L:RB->maxstack + | ja ->vm_growstack_v // Need to grow stack. + | mov RA, BASE + | mov BASE, RD + | movzx RBd, byte [PC-4+PC2PROTO(numparams)] + | test RBd, RBd + | jz >2 + | add RA, 8 + |1: // Copy fixarg slots up to new frame. + | add RA, 8 + | cmp RA, BASE + | jnb >3 // Less args than parameters? + | mov KBASE, [RA-16] + | mov [RD], KBASE + | add RD, 8 + | mov aword [RA-16], LJ_TNIL // Clear old fixarg slot (help the GC). + | sub RBd, 1 + | jnz <1 + |2: + if (op == BC_JFUNCV) { + | movzx RDd, PC_RD + | jmp =>BC_JLOOP + } else { + | mov KBASE, [PC-4+PC2PROTO(k)] + | ins_next + } + | + |3: // Clear missing parameters. + | mov aword [RD], LJ_TNIL + | add RD, 8 + | sub RBd, 1 + | jnz <3 + | jmp <2 + break; + + case BC_FUNCC: + case BC_FUNCCW: + | ins_AD // BASE = new base, RA = ins RA|RD (unused), RD = nargs+1 + | mov CFUNC:RB, [BASE-16] + | cleartp CFUNC:RB + | mov KBASE, CFUNC:RB->f + | mov L:RB, SAVE_L + | lea RD, [BASE+NARGS:RD*8-8] + | mov L:RB->base, BASE + | lea RA, [RD+8*LUA_MINSTACK] + | cmp RA, L:RB->maxstack + | mov L:RB->top, RD + if (op == BC_FUNCC) { + | mov CARG1, L:RB // Caveat: CARG1 may be RA. + } else { + | mov CARG2, KBASE + | mov CARG1, L:RB // Caveat: CARG1 may be RA. + } + | ja ->vm_growstack_c // Need to grow stack. + | set_vmstate C + if (op == BC_FUNCC) { + | call KBASE // (lua_State *L) + } else { + | // (lua_State *L, lua_CFunction f) + | call aword [DISPATCH+DISPATCH_GL(wrapf)] + } + | // nresults returned in eax (RD). + | mov BASE, L:RB->base + | mov [DISPATCH+DISPATCH_GL(cur_L)], L:RB + | set_vmstate INTERP + | lea RA, [BASE+RD*8] + | neg RA + | add RA, L:RB->top // RA = (L->top-(L->base+nresults))*8 + | mov PC, [BASE-8] // Fetch PC of caller. + | jmp ->vm_returnc + break; + + /* ---------------------------------------------------------------------- */ + + default: + fprintf(stderr, "Error: undefined opcode BC_%s\n", bc_names[op]); + exit(2); + break; + } +} + +static int build_backend(BuildCtx *ctx) +{ + int op; + dasm_growpc(Dst, BC__MAX); + build_subroutines(ctx); + |.code_op + for (op = 0; op < BC__MAX; op++) + build_ins(ctx, (BCOp)op, op); + return BC__MAX; +} + +/* Emit pseudo frame-info for all assembler functions. */ +static void emit_asm_debug(BuildCtx *ctx) +{ + int fcofs = (int)((uint8_t *)ctx->glob[GLOB_vm_ffi_call] - ctx->code); + switch (ctx->mode) { + case BUILD_elfasm: + fprintf(ctx->fp, "\t.section .debug_frame,\"\",@progbits\n"); + fprintf(ctx->fp, + ".Lframe0:\n" + "\t.long .LECIE0-.LSCIE0\n" + ".LSCIE0:\n" + "\t.long 0xffffffff\n" + "\t.byte 0x1\n" + "\t.string \"\"\n" + "\t.uleb128 0x1\n" + "\t.sleb128 -8\n" + "\t.byte 0x10\n" + "\t.byte 0xc\n\t.uleb128 0x7\n\t.uleb128 8\n" + "\t.byte 0x80+0x10\n\t.uleb128 0x1\n" + "\t.align 8\n" + ".LECIE0:\n\n"); + fprintf(ctx->fp, + ".LSFDE0:\n" + "\t.long .LEFDE0-.LASFDE0\n" + ".LASFDE0:\n" + "\t.long .Lframe0\n" + "\t.quad .Lbegin\n" + "\t.quad %d\n" + "\t.byte 0xe\n\t.uleb128 %d\n" /* def_cfa_offset */ + "\t.byte 0x86\n\t.uleb128 0x2\n" /* offset rbp */ + "\t.byte 0x83\n\t.uleb128 0x3\n" /* offset rbx */ + "\t.byte 0x8f\n\t.uleb128 0x4\n" /* offset r15 */ + "\t.byte 0x8e\n\t.uleb128 0x5\n" /* offset r14 */ +#if LJ_NO_UNWIND + "\t.byte 0x8d\n\t.uleb128 0x6\n" /* offset r13 */ + "\t.byte 0x8c\n\t.uleb128 0x7\n" /* offset r12 */ +#endif + "\t.align 8\n" + ".LEFDE0:\n\n", fcofs, CFRAME_SIZE); +#if LJ_HASFFI + fprintf(ctx->fp, + ".LSFDE1:\n" + "\t.long .LEFDE1-.LASFDE1\n" + ".LASFDE1:\n" + "\t.long .Lframe0\n" + "\t.quad lj_vm_ffi_call\n" + "\t.quad %d\n" + "\t.byte 0xe\n\t.uleb128 16\n" /* def_cfa_offset */ + "\t.byte 0x86\n\t.uleb128 0x2\n" /* offset rbp */ + "\t.byte 0xd\n\t.uleb128 0x6\n" /* def_cfa_register rbp */ + "\t.byte 0x83\n\t.uleb128 0x3\n" /* offset rbx */ + "\t.align 8\n" + ".LEFDE1:\n\n", (int)ctx->codesz - fcofs); +#endif +#if !LJ_NO_UNWIND +#if (defined(__sun__) && defined(__svr4__)) + fprintf(ctx->fp, "\t.section .eh_frame,\"a\",@unwind\n"); +#else + fprintf(ctx->fp, "\t.section .eh_frame,\"a\",@progbits\n"); +#endif + fprintf(ctx->fp, + ".Lframe1:\n" + "\t.long .LECIE1-.LSCIE1\n" + ".LSCIE1:\n" + "\t.long 0\n" + "\t.byte 0x1\n" + "\t.string \"zPR\"\n" + "\t.uleb128 0x1\n" + "\t.sleb128 -8\n" + "\t.byte 0x10\n" + "\t.uleb128 6\n" /* augmentation length */ + "\t.byte 0x1b\n" /* pcrel|sdata4 */ + "\t.long lj_err_unwind_dwarf-.\n" + "\t.byte 0x1b\n" /* pcrel|sdata4 */ + "\t.byte 0xc\n\t.uleb128 0x7\n\t.uleb128 8\n" + "\t.byte 0x80+0x10\n\t.uleb128 0x1\n" + "\t.align 8\n" + ".LECIE1:\n\n"); + fprintf(ctx->fp, + ".LSFDE2:\n" + "\t.long .LEFDE2-.LASFDE2\n" + ".LASFDE2:\n" + "\t.long .LASFDE2-.Lframe1\n" + "\t.long .Lbegin-.\n" + "\t.long %d\n" + "\t.uleb128 0\n" /* augmentation length */ + "\t.byte 0xe\n\t.uleb128 %d\n" /* def_cfa_offset */ + "\t.byte 0x86\n\t.uleb128 0x2\n" /* offset rbp */ + "\t.byte 0x83\n\t.uleb128 0x3\n" /* offset rbx */ + "\t.byte 0x8f\n\t.uleb128 0x4\n" /* offset r15 */ + "\t.byte 0x8e\n\t.uleb128 0x5\n" /* offset r14 */ + "\t.align 8\n" + ".LEFDE2:\n\n", fcofs, CFRAME_SIZE); +#if LJ_HASFFI + fprintf(ctx->fp, + ".Lframe2:\n" + "\t.long .LECIE2-.LSCIE2\n" + ".LSCIE2:\n" + "\t.long 0\n" + "\t.byte 0x1\n" + "\t.string \"zR\"\n" + "\t.uleb128 0x1\n" + "\t.sleb128 -8\n" + "\t.byte 0x10\n" + "\t.uleb128 1\n" /* augmentation length */ + "\t.byte 0x1b\n" /* pcrel|sdata4 */ + "\t.byte 0xc\n\t.uleb128 0x7\n\t.uleb128 8\n" + "\t.byte 0x80+0x10\n\t.uleb128 0x1\n" + "\t.align 8\n" + ".LECIE2:\n\n"); + fprintf(ctx->fp, + ".LSFDE3:\n" + "\t.long .LEFDE3-.LASFDE3\n" + ".LASFDE3:\n" + "\t.long .LASFDE3-.Lframe2\n" + "\t.long lj_vm_ffi_call-.\n" + "\t.long %d\n" + "\t.uleb128 0\n" /* augmentation length */ + "\t.byte 0xe\n\t.uleb128 16\n" /* def_cfa_offset */ + "\t.byte 0x86\n\t.uleb128 0x2\n" /* offset rbp */ + "\t.byte 0xd\n\t.uleb128 0x6\n" /* def_cfa_register rbp */ + "\t.byte 0x83\n\t.uleb128 0x3\n" /* offset rbx */ + "\t.align 8\n" + ".LEFDE3:\n\n", (int)ctx->codesz - fcofs); +#endif +#endif + break; +#if !LJ_NO_UNWIND + /* Mental note: never let Apple design an assembler. + ** Or a linker. Or a plastic case. But I digress. + */ + case BUILD_machasm: { +#if LJ_HASFFI + int fcsize = 0; +#endif + int i; + fprintf(ctx->fp, "\t.section __TEXT,__eh_frame,coalesced,no_toc+strip_static_syms+live_support\n"); + fprintf(ctx->fp, + "EH_frame1:\n" + "\t.set L$set$x,LECIEX-LSCIEX\n" + "\t.long L$set$x\n" + "LSCIEX:\n" + "\t.long 0\n" + "\t.byte 0x1\n" + "\t.ascii \"zPR\\0\"\n" + "\t.byte 0x1\n" + "\t.byte 128-8\n" + "\t.byte 0x10\n" + "\t.byte 6\n" /* augmentation length */ + "\t.byte 0x9b\n" /* indirect|pcrel|sdata4 */ + "\t.long _lj_err_unwind_dwarf+4@GOTPCREL\n" + "\t.byte 0x1b\n" /* pcrel|sdata4 */ + "\t.byte 0xc\n\t.byte 0x7\n\t.byte 8\n" + "\t.byte 0x80+0x10\n\t.byte 0x1\n" + "\t.align 3\n" + "LECIEX:\n\n"); + for (i = 0; i < ctx->nsym; i++) { + const char *name = ctx->sym[i].name; + int32_t size = ctx->sym[i+1].ofs - ctx->sym[i].ofs; + if (size == 0) continue; +#if LJ_HASFFI + if (!strcmp(name, "_lj_vm_ffi_call")) { fcsize = size; continue; } +#endif + fprintf(ctx->fp, + "%s.eh:\n" + "LSFDE%d:\n" + "\t.set L$set$%d,LEFDE%d-LASFDE%d\n" + "\t.long L$set$%d\n" + "LASFDE%d:\n" + "\t.long LASFDE%d-EH_frame1\n" + "\t.long %s-.\n" + "\t.long %d\n" + "\t.byte 0\n" /* augmentation length */ + "\t.byte 0xe\n\t.byte %d\n" /* def_cfa_offset */ + "\t.byte 0x86\n\t.byte 0x2\n" /* offset rbp */ + "\t.byte 0x83\n\t.byte 0x3\n" /* offset rbx */ + "\t.byte 0x8f\n\t.byte 0x4\n" /* offset r15 */ + "\t.byte 0x8e\n\t.byte 0x5\n" /* offset r14 */ + "\t.align 3\n" + "LEFDE%d:\n\n", + name, i, i, i, i, i, i, i, name, size, CFRAME_SIZE, i); + } +#if LJ_HASFFI + if (fcsize) { + fprintf(ctx->fp, + "EH_frame2:\n" + "\t.set L$set$y,LECIEY-LSCIEY\n" + "\t.long L$set$y\n" + "LSCIEY:\n" + "\t.long 0\n" + "\t.byte 0x1\n" + "\t.ascii \"zR\\0\"\n" + "\t.byte 0x1\n" + "\t.byte 128-8\n" + "\t.byte 0x10\n" + "\t.byte 1\n" /* augmentation length */ + "\t.byte 0x1b\n" /* pcrel|sdata4 */ + "\t.byte 0xc\n\t.byte 0x7\n\t.byte 8\n" + "\t.byte 0x80+0x10\n\t.byte 0x1\n" + "\t.align 3\n" + "LECIEY:\n\n"); + fprintf(ctx->fp, + "_lj_vm_ffi_call.eh:\n" + "LSFDEY:\n" + "\t.set L$set$yy,LEFDEY-LASFDEY\n" + "\t.long L$set$yy\n" + "LASFDEY:\n" + "\t.long LASFDEY-EH_frame2\n" + "\t.long _lj_vm_ffi_call-.\n" + "\t.long %d\n" + "\t.byte 0\n" /* augmentation length */ + "\t.byte 0xe\n\t.byte 16\n" /* def_cfa_offset */ + "\t.byte 0x86\n\t.byte 0x2\n" /* offset rbp */ + "\t.byte 0xd\n\t.byte 0x6\n" /* def_cfa_register rbp */ + "\t.byte 0x83\n\t.byte 0x3\n" /* offset rbx */ + "\t.align 3\n" + "LEFDEY:\n\n", fcsize); + } +#endif + fprintf(ctx->fp, ".subsections_via_symbols\n"); + } + break; +#endif + default: /* Difficult for other modes. */ + break; + } +} + diff --git a/lib/LuaJIT/vm_x86.dasc b/lib/LuaJIT/vm_x86.dasc new file mode 100644 index 0000000..211ae7b --- /dev/null +++ b/lib/LuaJIT/vm_x86.dasc @@ -0,0 +1,5780 @@ +|// Low-level VM code for x86 CPUs. +|// Bytecode interpreter, fast functions and helper functions. +|// Copyright (C) 2005-2017 Mike Pall. See Copyright Notice in luajit.h +| +|.if P64 +|.arch x64 +|.else +|.arch x86 +|.endif +|.section code_op, code_sub +| +|.actionlist build_actionlist +|.globals GLOB_ +|.globalnames globnames +|.externnames extnames +| +|//----------------------------------------------------------------------- +| +|.if P64 +|.define X64, 1 +|.if WIN +|.define X64WIN, 1 +|.endif +|.endif +| +|// Fixed register assignments for the interpreter. +|// This is very fragile and has many dependencies. Caveat emptor. +|.define BASE, edx // Not C callee-save, refetched anyway. +|.if not X64 +|.define KBASE, edi // Must be C callee-save. +|.define KBASEa, KBASE +|.define PC, esi // Must be C callee-save. +|.define PCa, PC +|.define DISPATCH, ebx // Must be C callee-save. +|.elif X64WIN +|.define KBASE, edi // Must be C callee-save. +|.define KBASEa, rdi +|.define PC, esi // Must be C callee-save. +|.define PCa, rsi +|.define DISPATCH, ebx // Must be C callee-save. +|.else +|.define KBASE, r15d // Must be C callee-save. +|.define KBASEa, r15 +|.define PC, ebx // Must be C callee-save. +|.define PCa, rbx +|.define DISPATCH, r14d // Must be C callee-save. +|.endif +| +|.define RA, ecx +|.define RAH, ch +|.define RAL, cl +|.define RB, ebp // Must be ebp (C callee-save). +|.define RC, eax // Must be eax. +|.define RCW, ax +|.define RCH, ah +|.define RCL, al +|.define OP, RB +|.define RD, RC +|.define RDW, RCW +|.define RDL, RCL +|.if X64 +|.define RAa, rcx +|.define RBa, rbp +|.define RCa, rax +|.define RDa, rax +|.else +|.define RAa, RA +|.define RBa, RB +|.define RCa, RC +|.define RDa, RD +|.endif +| +|.if not X64 +|.define FCARG1, ecx // x86 fastcall arguments. +|.define FCARG2, edx +|.elif X64WIN +|.define CARG1, rcx // x64/WIN64 C call arguments. +|.define CARG2, rdx +|.define CARG3, r8 +|.define CARG4, r9 +|.define CARG1d, ecx +|.define CARG2d, edx +|.define CARG3d, r8d +|.define CARG4d, r9d +|.define FCARG1, CARG1d // Upwards compatible to x86 fastcall. +|.define FCARG2, CARG2d +|.else +|.define CARG1, rdi // x64/POSIX C call arguments. +|.define CARG2, rsi +|.define CARG3, rdx +|.define CARG4, rcx +|.define CARG5, r8 +|.define CARG6, r9 +|.define CARG1d, edi +|.define CARG2d, esi +|.define CARG3d, edx +|.define CARG4d, ecx +|.define CARG5d, r8d +|.define CARG6d, r9d +|.define FCARG1, CARG1d // Simulate x86 fastcall. +|.define FCARG2, CARG2d +|.endif +| +|// Type definitions. Some of these are only used for documentation. +|.type L, lua_State +|.type GL, global_State +|.type TVALUE, TValue +|.type GCOBJ, GCobj +|.type STR, GCstr +|.type TAB, GCtab +|.type LFUNC, GCfuncL +|.type CFUNC, GCfuncC +|.type PROTO, GCproto +|.type UPVAL, GCupval +|.type NODE, Node +|.type NARGS, int +|.type TRACE, GCtrace +|.type SBUF, SBuf +| +|// Stack layout while in interpreter. Must match with lj_frame.h. +|//----------------------------------------------------------------------- +|.if not X64 // x86 stack layout. +| +|.if WIN +| +|.define CFRAME_SPACE, aword*9 // Delta for esp (see <--). +|.macro saveregs_ +| push edi; push esi; push ebx +| push extern lj_err_unwind_win +| fs; push dword [0] +| fs; mov [0], esp +| sub esp, CFRAME_SPACE +|.endmacro +|.macro restoreregs +| add esp, CFRAME_SPACE +| fs; pop dword [0] +| pop edi // Short for esp += 4. +| pop ebx; pop esi; pop edi; pop ebp +|.endmacro +| +|.else +| +|.define CFRAME_SPACE, aword*7 // Delta for esp (see <--). +|.macro saveregs_ +| push edi; push esi; push ebx +| sub esp, CFRAME_SPACE +|.endmacro +|.macro restoreregs +| add esp, CFRAME_SPACE +| pop ebx; pop esi; pop edi; pop ebp +|.endmacro +| +|.endif +| +|.macro saveregs +| push ebp; saveregs_ +|.endmacro +| +|.if WIN +|.define SAVE_ERRF, aword [esp+aword*19] // vm_pcall/vm_cpcall only. +|.define SAVE_NRES, aword [esp+aword*18] +|.define SAVE_CFRAME, aword [esp+aword*17] +|.define SAVE_L, aword [esp+aword*16] +|//----- 16 byte aligned, ^^^ arguments from C caller +|.define SAVE_RET, aword [esp+aword*15] //<-- esp entering interpreter. +|.define SAVE_R4, aword [esp+aword*14] +|.define SAVE_R3, aword [esp+aword*13] +|.define SAVE_R2, aword [esp+aword*12] +|//----- 16 byte aligned +|.define SAVE_R1, aword [esp+aword*11] +|.define SEH_FUNC, aword [esp+aword*10] +|.define SEH_NEXT, aword [esp+aword*9] //<-- esp after register saves. +|.define UNUSED2, aword [esp+aword*8] +|//----- 16 byte aligned +|.define UNUSED1, aword [esp+aword*7] +|.define SAVE_PC, aword [esp+aword*6] +|.define TMP2, aword [esp+aword*5] +|.define TMP1, aword [esp+aword*4] +|//----- 16 byte aligned +|.define ARG4, aword [esp+aword*3] +|.define ARG3, aword [esp+aword*2] +|.define ARG2, aword [esp+aword*1] +|.define ARG1, aword [esp] //<-- esp while in interpreter. +|//----- 16 byte aligned, ^^^ arguments for C callee +|.else +|.define SAVE_ERRF, aword [esp+aword*15] // vm_pcall/vm_cpcall only. +|.define SAVE_NRES, aword [esp+aword*14] +|.define SAVE_CFRAME, aword [esp+aword*13] +|.define SAVE_L, aword [esp+aword*12] +|//----- 16 byte aligned, ^^^ arguments from C caller +|.define SAVE_RET, aword [esp+aword*11] //<-- esp entering interpreter. +|.define SAVE_R4, aword [esp+aword*10] +|.define SAVE_R3, aword [esp+aword*9] +|.define SAVE_R2, aword [esp+aword*8] +|//----- 16 byte aligned +|.define SAVE_R1, aword [esp+aword*7] //<-- esp after register saves. +|.define SAVE_PC, aword [esp+aword*6] +|.define TMP2, aword [esp+aword*5] +|.define TMP1, aword [esp+aword*4] +|//----- 16 byte aligned +|.define ARG4, aword [esp+aword*3] +|.define ARG3, aword [esp+aword*2] +|.define ARG2, aword [esp+aword*1] +|.define ARG1, aword [esp] //<-- esp while in interpreter. +|//----- 16 byte aligned, ^^^ arguments for C callee +|.endif +| +|// FPARGx overlaps ARGx and ARG(x+1) on x86. +|.define FPARG3, qword [esp+qword*1] +|.define FPARG1, qword [esp] +|// TMPQ overlaps TMP1/TMP2. ARG5/MULTRES overlap TMP1/TMP2 (and TMPQ). +|.define TMPQ, qword [esp+aword*4] +|.define TMP3, ARG4 +|.define ARG5, TMP1 +|.define TMPa, TMP1 +|.define MULTRES, TMP2 +| +|// Arguments for vm_call and vm_pcall. +|.define INARG_BASE, SAVE_CFRAME // Overwritten by SAVE_CFRAME! +| +|// Arguments for vm_cpcall. +|.define INARG_CP_CALL, SAVE_ERRF +|.define INARG_CP_UD, SAVE_NRES +|.define INARG_CP_FUNC, SAVE_CFRAME +| +|//----------------------------------------------------------------------- +|.elif X64WIN // x64/Windows stack layout +| +|.define CFRAME_SPACE, aword*5 // Delta for rsp (see <--). +|.macro saveregs_ +| push rdi; push rsi; push rbx +| sub rsp, CFRAME_SPACE +|.endmacro +|.macro saveregs +| push rbp; saveregs_ +|.endmacro +|.macro restoreregs +| add rsp, CFRAME_SPACE +| pop rbx; pop rsi; pop rdi; pop rbp +|.endmacro +| +|.define SAVE_CFRAME, aword [rsp+aword*13] +|.define SAVE_PC, dword [rsp+dword*25] +|.define SAVE_L, dword [rsp+dword*24] +|.define SAVE_ERRF, dword [rsp+dword*23] +|.define SAVE_NRES, dword [rsp+dword*22] +|.define TMP2, dword [rsp+dword*21] +|.define TMP1, dword [rsp+dword*20] +|//----- 16 byte aligned, ^^^ 32 byte register save area, owned by interpreter +|.define SAVE_RET, aword [rsp+aword*9] //<-- rsp entering interpreter. +|.define SAVE_R4, aword [rsp+aword*8] +|.define SAVE_R3, aword [rsp+aword*7] +|.define SAVE_R2, aword [rsp+aword*6] +|.define SAVE_R1, aword [rsp+aword*5] //<-- rsp after register saves. +|.define ARG5, aword [rsp+aword*4] +|.define CSAVE_4, aword [rsp+aword*3] +|.define CSAVE_3, aword [rsp+aword*2] +|.define CSAVE_2, aword [rsp+aword*1] +|.define CSAVE_1, aword [rsp] //<-- rsp while in interpreter. +|//----- 16 byte aligned, ^^^ 32 byte register save area, owned by callee +| +|// TMPQ overlaps TMP1/TMP2. MULTRES overlaps TMP2 (and TMPQ). +|.define TMPQ, qword [rsp+aword*10] +|.define MULTRES, TMP2 +|.define TMPa, ARG5 +|.define ARG5d, dword [rsp+aword*4] +|.define TMP3, ARG5d +| +|//----------------------------------------------------------------------- +|.else // x64/POSIX stack layout +| +|.define CFRAME_SPACE, aword*5 // Delta for rsp (see <--). +|.macro saveregs_ +| push rbx; push r15; push r14 +|.if NO_UNWIND +| push r13; push r12 +|.endif +| sub rsp, CFRAME_SPACE +|.endmacro +|.macro saveregs +| push rbp; saveregs_ +|.endmacro +|.macro restoreregs +| add rsp, CFRAME_SPACE +|.if NO_UNWIND +| pop r12; pop r13 +|.endif +| pop r14; pop r15; pop rbx; pop rbp +|.endmacro +| +|//----- 16 byte aligned, +|.if NO_UNWIND +|.define SAVE_RET, aword [rsp+aword*11] //<-- rsp entering interpreter. +|.define SAVE_R4, aword [rsp+aword*10] +|.define SAVE_R3, aword [rsp+aword*9] +|.define SAVE_R2, aword [rsp+aword*8] +|.define SAVE_R1, aword [rsp+aword*7] +|.define SAVE_RU2, aword [rsp+aword*6] +|.define SAVE_RU1, aword [rsp+aword*5] //<-- rsp after register saves. +|.else +|.define SAVE_RET, aword [rsp+aword*9] //<-- rsp entering interpreter. +|.define SAVE_R4, aword [rsp+aword*8] +|.define SAVE_R3, aword [rsp+aword*7] +|.define SAVE_R2, aword [rsp+aword*6] +|.define SAVE_R1, aword [rsp+aword*5] //<-- rsp after register saves. +|.endif +|.define SAVE_CFRAME, aword [rsp+aword*4] +|.define SAVE_PC, dword [rsp+dword*7] +|.define SAVE_L, dword [rsp+dword*6] +|.define SAVE_ERRF, dword [rsp+dword*5] +|.define SAVE_NRES, dword [rsp+dword*4] +|.define TMPa, aword [rsp+aword*1] +|.define TMP2, dword [rsp+dword*1] +|.define TMP1, dword [rsp] //<-- rsp while in interpreter. +|//----- 16 byte aligned +| +|// TMPQ overlaps TMP1/TMP2. MULTRES overlaps TMP2 (and TMPQ). +|.define TMPQ, qword [rsp] +|.define TMP3, dword [rsp+aword*1] +|.define MULTRES, TMP2 +| +|.endif +| +|//----------------------------------------------------------------------- +| +|// Instruction headers. +|.macro ins_A; .endmacro +|.macro ins_AD; .endmacro +|.macro ins_AJ; .endmacro +|.macro ins_ABC; movzx RB, RCH; movzx RC, RCL; .endmacro +|.macro ins_AB_; movzx RB, RCH; .endmacro +|.macro ins_A_C; movzx RC, RCL; .endmacro +|.macro ins_AND; not RDa; .endmacro +| +|// Instruction decode+dispatch. Carefully tuned (nope, lodsd is not faster). +|.macro ins_NEXT +| mov RC, [PC] +| movzx RA, RCH +| movzx OP, RCL +| add PC, 4 +| shr RC, 16 +|.if X64 +| jmp aword [DISPATCH+OP*8] +|.else +| jmp aword [DISPATCH+OP*4] +|.endif +|.endmacro +| +|// Instruction footer. +|.if 1 +| // Replicated dispatch. Less unpredictable branches, but higher I-Cache use. +| .define ins_next, ins_NEXT +| .define ins_next_, ins_NEXT +|.else +| // Common dispatch. Lower I-Cache use, only one (very) unpredictable branch. +| // Affects only certain kinds of benchmarks (and only with -j off). +| // Around 10%-30% slower on Core2, a lot more slower on P4. +| .macro ins_next +| jmp ->ins_next +| .endmacro +| .macro ins_next_ +| ->ins_next: +| ins_NEXT +| .endmacro +|.endif +| +|// Call decode and dispatch. +|.macro ins_callt +| // BASE = new base, RB = LFUNC, RD = nargs+1, [BASE-4] = PC +| mov PC, LFUNC:RB->pc +| mov RA, [PC] +| movzx OP, RAL +| movzx RA, RAH +| add PC, 4 +|.if X64 +| jmp aword [DISPATCH+OP*8] +|.else +| jmp aword [DISPATCH+OP*4] +|.endif +|.endmacro +| +|.macro ins_call +| // BASE = new base, RB = LFUNC, RD = nargs+1 +| mov [BASE-4], PC +| ins_callt +|.endmacro +| +|//----------------------------------------------------------------------- +| +|// Macros to test operand types. +|.macro checktp, reg, tp; cmp dword [BASE+reg*8+4], tp; .endmacro +|.macro checknum, reg, target; checktp reg, LJ_TISNUM; jae target; .endmacro +|.macro checkint, reg, target; checktp reg, LJ_TISNUM; jne target; .endmacro +|.macro checkstr, reg, target; checktp reg, LJ_TSTR; jne target; .endmacro +|.macro checktab, reg, target; checktp reg, LJ_TTAB; jne target; .endmacro +| +|// These operands must be used with movzx. +|.define PC_OP, byte [PC-4] +|.define PC_RA, byte [PC-3] +|.define PC_RB, byte [PC-1] +|.define PC_RC, byte [PC-2] +|.define PC_RD, word [PC-2] +| +|.macro branchPC, reg +| lea PC, [PC+reg*4-BCBIAS_J*4] +|.endmacro +| +|// Assumes DISPATCH is relative to GL. +#define DISPATCH_GL(field) (GG_DISP2G + (int)offsetof(global_State, field)) +#define DISPATCH_J(field) (GG_DISP2J + (int)offsetof(jit_State, field)) +| +#define PC2PROTO(field) ((int)offsetof(GCproto, field)-(int)sizeof(GCproto)) +| +|// Decrement hashed hotcount and trigger trace recorder if zero. +|.macro hotloop, reg +| mov reg, PC +| shr reg, 1 +| and reg, HOTCOUNT_PCMASK +| sub word [DISPATCH+reg+GG_DISP2HOT], HOTCOUNT_LOOP +| jb ->vm_hotloop +|.endmacro +| +|.macro hotcall, reg +| mov reg, PC +| shr reg, 1 +| and reg, HOTCOUNT_PCMASK +| sub word [DISPATCH+reg+GG_DISP2HOT], HOTCOUNT_CALL +| jb ->vm_hotcall +|.endmacro +| +|// Set current VM state. +|.macro set_vmstate, st +| mov dword [DISPATCH+DISPATCH_GL(vmstate)], ~LJ_VMST_..st +|.endmacro +| +|// x87 compares. +|.macro fcomparepp // Compare and pop st0 >< st1. +| fucomip st1 +| fpop +|.endmacro +| +|.macro fpop1; fstp st1; .endmacro +| +|// Synthesize SSE FP constants. +|.macro sseconst_abs, reg, tmp // Synthesize abs mask. +|.if X64 +| mov64 tmp, U64x(7fffffff,ffffffff); movd reg, tmp +|.else +| pxor reg, reg; pcmpeqd reg, reg; psrlq reg, 1 +|.endif +|.endmacro +| +|.macro sseconst_hi, reg, tmp, val // Synthesize hi-32 bit const. +|.if X64 +| mov64 tmp, U64x(val,00000000); movd reg, tmp +|.else +| mov tmp, 0x .. val; movd reg, tmp; pshufd reg, reg, 0x51 +|.endif +|.endmacro +| +|.macro sseconst_sign, reg, tmp // Synthesize sign mask. +| sseconst_hi reg, tmp, 80000000 +|.endmacro +|.macro sseconst_1, reg, tmp // Synthesize 1.0. +| sseconst_hi reg, tmp, 3ff00000 +|.endmacro +|.macro sseconst_m1, reg, tmp // Synthesize -1.0. +| sseconst_hi reg, tmp, bff00000 +|.endmacro +|.macro sseconst_2p52, reg, tmp // Synthesize 2^52. +| sseconst_hi reg, tmp, 43300000 +|.endmacro +|.macro sseconst_tobit, reg, tmp // Synthesize 2^52 + 2^51. +| sseconst_hi reg, tmp, 43380000 +|.endmacro +| +|// Move table write barrier back. Overwrites reg. +|.macro barrierback, tab, reg +| and byte tab->marked, (uint8_t)~LJ_GC_BLACK // black2gray(tab) +| mov reg, [DISPATCH+DISPATCH_GL(gc.grayagain)] +| mov [DISPATCH+DISPATCH_GL(gc.grayagain)], tab +| mov tab->gclist, reg +|.endmacro +| +|//----------------------------------------------------------------------- + +/* Generate subroutines used by opcodes and other parts of the VM. */ +/* The .code_sub section should be last to help static branch prediction. */ +static void build_subroutines(BuildCtx *ctx) +{ + |.code_sub + | + |//----------------------------------------------------------------------- + |//-- Return handling ---------------------------------------------------- + |//----------------------------------------------------------------------- + | + |->vm_returnp: + | test PC, FRAME_P + | jz ->cont_dispatch + | + | // Return from pcall or xpcall fast func. + | and PC, -8 + | sub BASE, PC // Restore caller base. + | lea RAa, [RA+PC-8] // Rebase RA and prepend one result. + | mov PC, [BASE-4] // Fetch PC of previous frame. + | // Prepending may overwrite the pcall frame, so do it at the end. + | mov dword [BASE+RA+4], LJ_TTRUE // Prepend true to results. + | + |->vm_returnc: + | add RD, 1 // RD = nresults+1 + | jz ->vm_unwind_yield + | mov MULTRES, RD + | test PC, FRAME_TYPE + | jz ->BC_RET_Z // Handle regular return to Lua. + | + |->vm_return: + | // BASE = base, RA = resultofs, RD = nresults+1 (= MULTRES), PC = return + | xor PC, FRAME_C + | test PC, FRAME_TYPE + | jnz ->vm_returnp + | + | // Return to C. + | set_vmstate C + | and PC, -8 + | sub PC, BASE + | neg PC // Previous base = BASE - delta. + | + | sub RD, 1 + | jz >2 + |1: // Move results down. + |.if X64 + | mov RBa, [BASE+RA] + | mov [BASE-8], RBa + |.else + | mov RB, [BASE+RA] + | mov [BASE-8], RB + | mov RB, [BASE+RA+4] + | mov [BASE-4], RB + |.endif + | add BASE, 8 + | sub RD, 1 + | jnz <1 + |2: + | mov L:RB, SAVE_L + | mov L:RB->base, PC + |3: + | mov RD, MULTRES + | mov RA, SAVE_NRES // RA = wanted nresults+1 + |4: + | cmp RA, RD + | jne >6 // More/less results wanted? + |5: + | sub BASE, 8 + | mov L:RB->top, BASE + | + |->vm_leave_cp: + | mov RAa, SAVE_CFRAME // Restore previous C frame. + | mov L:RB->cframe, RAa + | xor eax, eax // Ok return status for vm_pcall. + | + |->vm_leave_unw: + | restoreregs + | ret + | + |6: + | jb >7 // Less results wanted? + | // More results wanted. Check stack size and fill up results with nil. + | cmp BASE, L:RB->maxstack + | ja >8 + | mov dword [BASE-4], LJ_TNIL + | add BASE, 8 + | add RD, 1 + | jmp <4 + | + |7: // Less results wanted. + | test RA, RA + | jz <5 // But check for LUA_MULTRET+1. + | sub RA, RD // Negative result! + | lea BASE, [BASE+RA*8] // Correct top. + | jmp <5 + | + |8: // Corner case: need to grow stack for filling up results. + | // This can happen if: + | // - A C function grows the stack (a lot). + | // - The GC shrinks the stack in between. + | // - A return back from a lua_call() with (high) nresults adjustment. + | mov L:RB->top, BASE // Save current top held in BASE (yes). + | mov MULTRES, RD // Need to fill only remainder with nil. + | mov FCARG2, RA + | mov FCARG1, L:RB + | call extern lj_state_growstack@8 // (lua_State *L, int n) + | mov BASE, L:RB->top // Need the (realloced) L->top in BASE. + | jmp <3 + | + |->vm_unwind_yield: + | mov al, LUA_YIELD + | jmp ->vm_unwind_c_eh + | + |->vm_unwind_c@8: // Unwind C stack, return from vm_pcall. + | // (void *cframe, int errcode) + |.if X64 + | mov eax, CARG2d // Error return status for vm_pcall. + | mov rsp, CARG1 + |.else + | mov eax, FCARG2 // Error return status for vm_pcall. + | mov esp, FCARG1 + |.if WIN + | lea FCARG1, SEH_NEXT + | fs; mov [0], FCARG1 + |.endif + |.endif + |->vm_unwind_c_eh: // Landing pad for external unwinder. + | mov L:RB, SAVE_L + | mov GL:RB, L:RB->glref + | mov dword GL:RB->vmstate, ~LJ_VMST_C + | jmp ->vm_leave_unw + | + |->vm_unwind_rethrow: + |.if X64 and not X64WIN + | mov FCARG1, SAVE_L + | mov FCARG2, eax + | restoreregs + | jmp extern lj_err_throw@8 // (lua_State *L, int errcode) + |.endif + | + |->vm_unwind_ff@4: // Unwind C stack, return from ff pcall. + | // (void *cframe) + |.if X64 + | and CARG1, CFRAME_RAWMASK + | mov rsp, CARG1 + |.else + | and FCARG1, CFRAME_RAWMASK + | mov esp, FCARG1 + |.if WIN + | lea FCARG1, SEH_NEXT + | fs; mov [0], FCARG1 + |.endif + |.endif + |->vm_unwind_ff_eh: // Landing pad for external unwinder. + | mov L:RB, SAVE_L + | mov RAa, -8 // Results start at BASE+RA = BASE-8. + | mov RD, 1+1 // Really 1+2 results, incr. later. + | mov BASE, L:RB->base + | mov DISPATCH, L:RB->glref // Setup pointer to dispatch table. + | add DISPATCH, GG_G2DISP + | mov PC, [BASE-4] // Fetch PC of previous frame. + | mov dword [BASE-4], LJ_TFALSE // Prepend false to error message. + | set_vmstate INTERP + | jmp ->vm_returnc // Increments RD/MULTRES and returns. + | + |.if WIN and not X64 + |->vm_rtlunwind@16: // Thin layer around RtlUnwind. + | // (void *cframe, void *excptrec, void *unwinder, int errcode) + | mov [esp], FCARG1 // Return value for RtlUnwind. + | push FCARG2 // Exception record for RtlUnwind. + | push 0 // Ignored by RtlUnwind. + | push dword [FCARG1+CFRAME_OFS_SEH] + | call extern RtlUnwind@16 // Violates ABI (clobbers too much). + | mov FCARG1, eax + | mov FCARG2, [esp+4] // errcode (for vm_unwind_c). + | ret // Jump to unwinder. + |.endif + | + |//----------------------------------------------------------------------- + |//-- Grow stack for calls ----------------------------------------------- + |//----------------------------------------------------------------------- + | + |->vm_growstack_c: // Grow stack for C function. + | mov FCARG2, LUA_MINSTACK + | jmp >2 + | + |->vm_growstack_v: // Grow stack for vararg Lua function. + | sub RD, 8 + | jmp >1 + | + |->vm_growstack_f: // Grow stack for fixarg Lua function. + | // BASE = new base, RD = nargs+1, RB = L, PC = first PC + | lea RD, [BASE+NARGS:RD*8-8] + |1: + | movzx RA, byte [PC-4+PC2PROTO(framesize)] + | add PC, 4 // Must point after first instruction. + | mov L:RB->base, BASE + | mov L:RB->top, RD + | mov SAVE_PC, PC + | mov FCARG2, RA + |2: + | // RB = L, L->base = new base, L->top = top + | mov FCARG1, L:RB + | call extern lj_state_growstack@8 // (lua_State *L, int n) + | mov BASE, L:RB->base + | mov RD, L:RB->top + | mov LFUNC:RB, [BASE-8] + | sub RD, BASE + | shr RD, 3 + | add NARGS:RD, 1 + | // BASE = new base, RB = LFUNC, RD = nargs+1 + | ins_callt // Just retry the call. + | + |//----------------------------------------------------------------------- + |//-- Entry points into the assembler VM --------------------------------- + |//----------------------------------------------------------------------- + | + |->vm_resume: // Setup C frame and resume thread. + | // (lua_State *L, TValue *base, int nres1 = 0, ptrdiff_t ef = 0) + | saveregs + |.if X64 + | mov L:RB, CARG1d // Caveat: CARG1d may be RA. + | mov SAVE_L, CARG1d + | mov RA, CARG2d + |.else + | mov L:RB, SAVE_L + | mov RA, INARG_BASE // Caveat: overlaps SAVE_CFRAME! + |.endif + | mov PC, FRAME_CP + | xor RD, RD + | lea KBASEa, [esp+CFRAME_RESUME] + | mov DISPATCH, L:RB->glref // Setup pointer to dispatch table. + | add DISPATCH, GG_G2DISP + | mov SAVE_PC, RD // Any value outside of bytecode is ok. + | mov SAVE_CFRAME, RDa + |.if X64 + | mov SAVE_NRES, RD + | mov SAVE_ERRF, RD + |.endif + | mov L:RB->cframe, KBASEa + | cmp byte L:RB->status, RDL + | je >2 // Initial resume (like a call). + | + | // Resume after yield (like a return). + | mov [DISPATCH+DISPATCH_GL(cur_L)], L:RB + | set_vmstate INTERP + | mov byte L:RB->status, RDL + | mov BASE, L:RB->base + | mov RD, L:RB->top + | sub RD, RA + | shr RD, 3 + | add RD, 1 // RD = nresults+1 + | sub RA, BASE // RA = resultofs + | mov PC, [BASE-4] + | mov MULTRES, RD + | test PC, FRAME_TYPE + | jz ->BC_RET_Z + | jmp ->vm_return + | + |->vm_pcall: // Setup protected C frame and enter VM. + | // (lua_State *L, TValue *base, int nres1, ptrdiff_t ef) + | saveregs + | mov PC, FRAME_CP + |.if X64 + | mov SAVE_ERRF, CARG4d + |.endif + | jmp >1 + | + |->vm_call: // Setup C frame and enter VM. + | // (lua_State *L, TValue *base, int nres1) + | saveregs + | mov PC, FRAME_C + | + |1: // Entry point for vm_pcall above (PC = ftype). + |.if X64 + | mov SAVE_NRES, CARG3d + | mov L:RB, CARG1d // Caveat: CARG1d may be RA. + | mov SAVE_L, CARG1d + | mov RA, CARG2d + |.else + | mov L:RB, SAVE_L + | mov RA, INARG_BASE // Caveat: overlaps SAVE_CFRAME! + |.endif + | + | mov DISPATCH, L:RB->glref // Setup pointer to dispatch table. + | mov KBASEa, L:RB->cframe // Add our C frame to cframe chain. + | mov SAVE_CFRAME, KBASEa + | mov SAVE_PC, L:RB // Any value outside of bytecode is ok. + | add DISPATCH, GG_G2DISP + |.if X64 + | mov L:RB->cframe, rsp + |.else + | mov L:RB->cframe, esp + |.endif + | + |2: // Entry point for vm_resume/vm_cpcall (RA = base, RB = L, PC = ftype). + | mov [DISPATCH+DISPATCH_GL(cur_L)], L:RB + | set_vmstate INTERP + | mov BASE, L:RB->base // BASE = old base (used in vmeta_call). + | add PC, RA + | sub PC, BASE // PC = frame delta + frame type + | + | mov RD, L:RB->top + | sub RD, RA + | shr NARGS:RD, 3 + | add NARGS:RD, 1 // RD = nargs+1 + | + |->vm_call_dispatch: + | mov LFUNC:RB, [RA-8] + | cmp dword [RA-4], LJ_TFUNC + | jne ->vmeta_call // Ensure KBASE defined and != BASE. + | + |->vm_call_dispatch_f: + | mov BASE, RA + | ins_call + | // BASE = new base, RB = func, RD = nargs+1, PC = caller PC + | + |->vm_cpcall: // Setup protected C frame, call C. + | // (lua_State *L, lua_CFunction func, void *ud, lua_CPFunction cp) + | saveregs + |.if X64 + | mov L:RB, CARG1d // Caveat: CARG1d may be RA. + | mov SAVE_L, CARG1d + |.else + | mov L:RB, SAVE_L + | // Caveat: INARG_CP_* and SAVE_CFRAME/SAVE_NRES/SAVE_ERRF overlap! + | mov RC, INARG_CP_UD // Get args before they are overwritten. + | mov RA, INARG_CP_FUNC + | mov BASE, INARG_CP_CALL + |.endif + | mov SAVE_PC, L:RB // Any value outside of bytecode is ok. + | + | mov KBASE, L:RB->stack // Compute -savestack(L, L->top). + | sub KBASE, L:RB->top + | mov DISPATCH, L:RB->glref // Setup pointer to dispatch table. + | mov SAVE_ERRF, 0 // No error function. + | mov SAVE_NRES, KBASE // Neg. delta means cframe w/o frame. + | add DISPATCH, GG_G2DISP + | // Handler may change cframe_nres(L->cframe) or cframe_errfunc(L->cframe). + | + |.if X64 + | mov KBASEa, L:RB->cframe // Add our C frame to cframe chain. + | mov SAVE_CFRAME, KBASEa + | mov L:RB->cframe, rsp + | mov [DISPATCH+DISPATCH_GL(cur_L)], L:RB + | + | call CARG4 // (lua_State *L, lua_CFunction func, void *ud) + |.else + | mov ARG3, RC // Have to copy args downwards. + | mov ARG2, RA + | mov ARG1, L:RB + | + | mov KBASE, L:RB->cframe // Add our C frame to cframe chain. + | mov SAVE_CFRAME, KBASE + | mov L:RB->cframe, esp + | mov [DISPATCH+DISPATCH_GL(cur_L)], L:RB + | + | call BASE // (lua_State *L, lua_CFunction func, void *ud) + |.endif + | // TValue * (new base) or NULL returned in eax (RC). + | test RC, RC + | jz ->vm_leave_cp // No base? Just remove C frame. + | mov RA, RC + | mov PC, FRAME_CP + | jmp <2 // Else continue with the call. + | + |//----------------------------------------------------------------------- + |//-- Metamethod handling ------------------------------------------------ + |//----------------------------------------------------------------------- + | + |//-- Continuation dispatch ---------------------------------------------- + | + |->cont_dispatch: + | // BASE = meta base, RA = resultofs, RD = nresults+1 (also in MULTRES) + | add RA, BASE + | and PC, -8 + | mov RB, BASE + | sub BASE, PC // Restore caller BASE. + | mov dword [RA+RD*8-4], LJ_TNIL // Ensure one valid arg. + | mov RC, RA // ... in [RC] + | mov PC, [RB-12] // Restore PC from [cont|PC]. + |.if X64 + | movsxd RAa, dword [RB-16] // May be negative on WIN64 with debug. + |.if FFI + | cmp RA, 1 + | jbe >1 + |.endif + | lea KBASEa, qword [=>0] + | add RAa, KBASEa + |.else + | mov RA, dword [RB-16] + |.if FFI + | cmp RA, 1 + | jbe >1 + |.endif + |.endif + | mov LFUNC:KBASE, [BASE-8] + | mov KBASE, LFUNC:KBASE->pc + | mov KBASE, [KBASE+PC2PROTO(k)] + | // BASE = base, RC = result, RB = meta base + | jmp RAa // Jump to continuation. + | + |.if FFI + |1: + | je ->cont_ffi_callback // cont = 1: return from FFI callback. + | // cont = 0: Tail call from C function. + | sub RB, BASE + | shr RB, 3 + | lea RD, [RB-1] + | jmp ->vm_call_tail + |.endif + | + |->cont_cat: // BASE = base, RC = result, RB = mbase + | movzx RA, PC_RB + | sub RB, 16 + | lea RA, [BASE+RA*8] + | sub RA, RB + | je ->cont_ra + | neg RA + | shr RA, 3 + |.if X64WIN + | mov CARG3d, RA + | mov L:CARG1d, SAVE_L + | mov L:CARG1d->base, BASE + | mov RCa, [RC] + | mov [RB], RCa + | mov CARG2d, RB + |.elif X64 + | mov L:CARG1d, SAVE_L + | mov L:CARG1d->base, BASE + | mov CARG3d, RA + | mov RAa, [RC] + | mov [RB], RAa + | mov CARG2d, RB + |.else + | mov ARG3, RA + | mov RA, [RC+4] + | mov RC, [RC] + | mov [RB+4], RA + | mov [RB], RC + | mov ARG2, RB + |.endif + | jmp ->BC_CAT_Z + | + |//-- Table indexing metamethods ----------------------------------------- + | + |->vmeta_tgets: + | mov TMP1, RC // RC = GCstr * + | mov TMP2, LJ_TSTR + | lea RCa, TMP1 // Store temp. TValue in TMP1/TMP2. + | cmp PC_OP, BC_GGET + | jne >1 + | lea RA, [DISPATCH+DISPATCH_GL(tmptv)] // Store fn->l.env in g->tmptv. + | mov [RA], TAB:RB // RB = GCtab * + | mov dword [RA+4], LJ_TTAB + | mov RB, RA + | jmp >2 + | + |->vmeta_tgetb: + | movzx RC, PC_RC + |.if DUALNUM + | mov TMP2, LJ_TISNUM + | mov TMP1, RC + |.else + | cvtsi2sd xmm0, RC + | movsd TMPQ, xmm0 + |.endif + | lea RCa, TMPQ // Store temp. TValue in TMPQ. + | jmp >1 + | + |->vmeta_tgetv: + | movzx RC, PC_RC // Reload TValue *k from RC. + | lea RC, [BASE+RC*8] + |1: + | movzx RB, PC_RB // Reload TValue *t from RB. + | lea RB, [BASE+RB*8] + |2: + |.if X64 + | mov L:CARG1d, SAVE_L + | mov L:CARG1d->base, BASE // Caveat: CARG2d/CARG3d may be BASE. + | mov CARG2d, RB + | mov CARG3, RCa // May be 64 bit ptr to stack. + | mov L:RB, L:CARG1d + |.else + | mov ARG2, RB + | mov L:RB, SAVE_L + | mov ARG3, RC + | mov ARG1, L:RB + | mov L:RB->base, BASE + |.endif + | mov SAVE_PC, PC + | call extern lj_meta_tget // (lua_State *L, TValue *o, TValue *k) + | // TValue * (finished) or NULL (metamethod) returned in eax (RC). + | mov BASE, L:RB->base + | test RC, RC + | jz >3 + |->cont_ra: // BASE = base, RC = result + | movzx RA, PC_RA + |.if X64 + | mov RBa, [RC] + | mov [BASE+RA*8], RBa + |.else + | mov RB, [RC+4] + | mov RC, [RC] + | mov [BASE+RA*8+4], RB + | mov [BASE+RA*8], RC + |.endif + | ins_next + | + |3: // Call __index metamethod. + | // BASE = base, L->top = new base, stack = cont/func/t/k + | mov RA, L:RB->top + | mov [RA-12], PC // [cont|PC] + | lea PC, [RA+FRAME_CONT] + | sub PC, BASE + | mov LFUNC:RB, [RA-8] // Guaranteed to be a function here. + | mov NARGS:RD, 2+1 // 2 args for func(t, k). + | jmp ->vm_call_dispatch_f + | + |->vmeta_tgetr: + | mov FCARG1, TAB:RB + | mov RB, BASE // Save BASE. + | mov FCARG2, RC // Caveat: FCARG2 == BASE + | call extern lj_tab_getinth@8 // (GCtab *t, int32_t key) + | // cTValue * or NULL returned in eax (RC). + | movzx RA, PC_RA + | mov BASE, RB // Restore BASE. + | test RC, RC + | jnz ->BC_TGETR_Z + | mov dword [BASE+RA*8+4], LJ_TNIL + | jmp ->BC_TGETR2_Z + | + |//----------------------------------------------------------------------- + | + |->vmeta_tsets: + | mov TMP1, RC // RC = GCstr * + | mov TMP2, LJ_TSTR + | lea RCa, TMP1 // Store temp. TValue in TMP1/TMP2. + | cmp PC_OP, BC_GSET + | jne >1 + | lea RA, [DISPATCH+DISPATCH_GL(tmptv)] // Store fn->l.env in g->tmptv. + | mov [RA], TAB:RB // RB = GCtab * + | mov dword [RA+4], LJ_TTAB + | mov RB, RA + | jmp >2 + | + |->vmeta_tsetb: + | movzx RC, PC_RC + |.if DUALNUM + | mov TMP2, LJ_TISNUM + | mov TMP1, RC + |.else + | cvtsi2sd xmm0, RC + | movsd TMPQ, xmm0 + |.endif + | lea RCa, TMPQ // Store temp. TValue in TMPQ. + | jmp >1 + | + |->vmeta_tsetv: + | movzx RC, PC_RC // Reload TValue *k from RC. + | lea RC, [BASE+RC*8] + |1: + | movzx RB, PC_RB // Reload TValue *t from RB. + | lea RB, [BASE+RB*8] + |2: + |.if X64 + | mov L:CARG1d, SAVE_L + | mov L:CARG1d->base, BASE // Caveat: CARG2d/CARG3d may be BASE. + | mov CARG2d, RB + | mov CARG3, RCa // May be 64 bit ptr to stack. + | mov L:RB, L:CARG1d + |.else + | mov ARG2, RB + | mov L:RB, SAVE_L + | mov ARG3, RC + | mov ARG1, L:RB + | mov L:RB->base, BASE + |.endif + | mov SAVE_PC, PC + | call extern lj_meta_tset // (lua_State *L, TValue *o, TValue *k) + | // TValue * (finished) or NULL (metamethod) returned in eax (RC). + | mov BASE, L:RB->base + | test RC, RC + | jz >3 + | // NOBARRIER: lj_meta_tset ensures the table is not black. + | movzx RA, PC_RA + |.if X64 + | mov RBa, [BASE+RA*8] + | mov [RC], RBa + |.else + | mov RB, [BASE+RA*8+4] + | mov RA, [BASE+RA*8] + | mov [RC+4], RB + | mov [RC], RA + |.endif + |->cont_nop: // BASE = base, (RC = result) + | ins_next + | + |3: // Call __newindex metamethod. + | // BASE = base, L->top = new base, stack = cont/func/t/k/(v) + | mov RA, L:RB->top + | mov [RA-12], PC // [cont|PC] + | movzx RC, PC_RA + | // Copy value to third argument. + |.if X64 + | mov RBa, [BASE+RC*8] + | mov [RA+16], RBa + |.else + | mov RB, [BASE+RC*8+4] + | mov RC, [BASE+RC*8] + | mov [RA+20], RB + | mov [RA+16], RC + |.endif + | lea PC, [RA+FRAME_CONT] + | sub PC, BASE + | mov LFUNC:RB, [RA-8] // Guaranteed to be a function here. + | mov NARGS:RD, 3+1 // 3 args for func(t, k, v). + | jmp ->vm_call_dispatch_f + | + |->vmeta_tsetr: + |.if X64WIN + | mov L:CARG1d, SAVE_L + | mov CARG3d, RC + | mov L:CARG1d->base, BASE + | xchg CARG2d, TAB:RB // Caveat: CARG2d == BASE. + |.elif X64 + | mov L:CARG1d, SAVE_L + | mov CARG2d, TAB:RB + | mov L:CARG1d->base, BASE + | mov RB, BASE // Save BASE. + | mov CARG3d, RC // Caveat: CARG3d == BASE. + |.else + | mov L:RA, SAVE_L + | mov ARG2, TAB:RB + | mov RB, BASE // Save BASE. + | mov ARG3, RC + | mov ARG1, L:RA + | mov L:RA->base, BASE + |.endif + | mov SAVE_PC, PC + | call extern lj_tab_setinth // (lua_State *L, GCtab *t, int32_t key) + | // TValue * returned in eax (RC). + | movzx RA, PC_RA + | mov BASE, RB // Restore BASE. + | jmp ->BC_TSETR_Z + | + |//-- Comparison metamethods --------------------------------------------- + | + |->vmeta_comp: + |.if X64 + | mov L:RB, SAVE_L + | mov L:RB->base, BASE // Caveat: CARG2d/CARG3d == BASE. + |.if X64WIN + | lea CARG3d, [BASE+RD*8] + | lea CARG2d, [BASE+RA*8] + |.else + | lea CARG2d, [BASE+RA*8] + | lea CARG3d, [BASE+RD*8] + |.endif + | mov CARG1d, L:RB // Caveat: CARG1d/CARG4d == RA. + | movzx CARG4d, PC_OP + |.else + | movzx RB, PC_OP + | lea RD, [BASE+RD*8] + | lea RA, [BASE+RA*8] + | mov ARG4, RB + | mov L:RB, SAVE_L + | mov ARG3, RD + | mov ARG2, RA + | mov ARG1, L:RB + | mov L:RB->base, BASE + |.endif + | mov SAVE_PC, PC + | call extern lj_meta_comp // (lua_State *L, TValue *o1, *o2, int op) + | // 0/1 or TValue * (metamethod) returned in eax (RC). + |3: + | mov BASE, L:RB->base + | cmp RC, 1 + | ja ->vmeta_binop + |4: + | lea PC, [PC+4] + | jb >6 + |5: + | movzx RD, PC_RD + | branchPC RD + |6: + | ins_next + | + |->cont_condt: // BASE = base, RC = result + | add PC, 4 + | cmp dword [RC+4], LJ_TISTRUECOND // Branch if result is true. + | jb <5 + | jmp <6 + | + |->cont_condf: // BASE = base, RC = result + | cmp dword [RC+4], LJ_TISTRUECOND // Branch if result is false. + | jmp <4 + | + |->vmeta_equal: + | sub PC, 4 + |.if X64WIN + | mov CARG3d, RD + | mov CARG4d, RB + | mov L:RB, SAVE_L + | mov L:RB->base, BASE // Caveat: CARG2d == BASE. + | mov CARG2d, RA + | mov CARG1d, L:RB // Caveat: CARG1d == RA. + |.elif X64 + | mov CARG2d, RA + | mov CARG4d, RB // Caveat: CARG4d == RA. + | mov L:RB, SAVE_L + | mov L:RB->base, BASE // Caveat: CARG3d == BASE. + | mov CARG3d, RD + | mov CARG1d, L:RB + |.else + | mov ARG4, RB + | mov L:RB, SAVE_L + | mov ARG3, RD + | mov ARG2, RA + | mov ARG1, L:RB + | mov L:RB->base, BASE + |.endif + | mov SAVE_PC, PC + | call extern lj_meta_equal // (lua_State *L, GCobj *o1, *o2, int ne) + | // 0/1 or TValue * (metamethod) returned in eax (RC). + | jmp <3 + | + |->vmeta_equal_cd: + |.if FFI + | sub PC, 4 + | mov L:RB, SAVE_L + | mov L:RB->base, BASE + | mov FCARG1, L:RB + | mov FCARG2, dword [PC-4] + | mov SAVE_PC, PC + | call extern lj_meta_equal_cd@8 // (lua_State *L, BCIns ins) + | // 0/1 or TValue * (metamethod) returned in eax (RC). + | jmp <3 + |.endif + | + |->vmeta_istype: + |.if X64 + | mov L:RB, SAVE_L + | mov L:RB->base, BASE // Caveat: CARG2d/CARG3d may be BASE. + | mov CARG2d, RA + | movzx CARG3d, PC_RD + | mov L:CARG1d, L:RB + |.else + | movzx RD, PC_RD + | mov ARG2, RA + | mov L:RB, SAVE_L + | mov ARG3, RD + | mov ARG1, L:RB + | mov L:RB->base, BASE + |.endif + | mov SAVE_PC, PC + | call extern lj_meta_istype // (lua_State *L, BCReg ra, BCReg tp) + | mov BASE, L:RB->base + | jmp <6 + | + |//-- Arithmetic metamethods --------------------------------------------- + | + |->vmeta_arith_vno: + |.if DUALNUM + | movzx RB, PC_RB + |.endif + |->vmeta_arith_vn: + | lea RC, [KBASE+RC*8] + | jmp >1 + | + |->vmeta_arith_nvo: + |.if DUALNUM + | movzx RC, PC_RC + |.endif + |->vmeta_arith_nv: + | lea RC, [KBASE+RC*8] + | lea RB, [BASE+RB*8] + | xchg RB, RC + | jmp >2 + | + |->vmeta_unm: + | lea RC, [BASE+RD*8] + | mov RB, RC + | jmp >2 + | + |->vmeta_arith_vvo: + |.if DUALNUM + | movzx RB, PC_RB + |.endif + |->vmeta_arith_vv: + | lea RC, [BASE+RC*8] + |1: + | lea RB, [BASE+RB*8] + |2: + | lea RA, [BASE+RA*8] + |.if X64WIN + | mov CARG3d, RB + | mov CARG4d, RC + | movzx RC, PC_OP + | mov ARG5d, RC + | mov L:RB, SAVE_L + | mov L:RB->base, BASE // Caveat: CARG2d == BASE. + | mov CARG2d, RA + | mov CARG1d, L:RB // Caveat: CARG1d == RA. + |.elif X64 + | movzx CARG5d, PC_OP + | mov CARG2d, RA + | mov CARG4d, RC // Caveat: CARG4d == RA. + | mov L:CARG1d, SAVE_L + | mov L:CARG1d->base, BASE // Caveat: CARG3d == BASE. + | mov CARG3d, RB + | mov L:RB, L:CARG1d + |.else + | mov ARG3, RB + | mov L:RB, SAVE_L + | mov ARG4, RC + | movzx RC, PC_OP + | mov ARG2, RA + | mov ARG5, RC + | mov ARG1, L:RB + | mov L:RB->base, BASE + |.endif + | mov SAVE_PC, PC + | call extern lj_meta_arith // (lua_State *L, TValue *ra,*rb,*rc, BCReg op) + | // NULL (finished) or TValue * (metamethod) returned in eax (RC). + | mov BASE, L:RB->base + | test RC, RC + | jz ->cont_nop + | + | // Call metamethod for binary op. + |->vmeta_binop: + | // BASE = base, RC = new base, stack = cont/func/o1/o2 + | mov RA, RC + | sub RC, BASE + | mov [RA-12], PC // [cont|PC] + | lea PC, [RC+FRAME_CONT] + | mov NARGS:RD, 2+1 // 2 args for func(o1, o2). + | jmp ->vm_call_dispatch + | + |->vmeta_len: + | mov L:RB, SAVE_L + | mov L:RB->base, BASE + | lea FCARG2, [BASE+RD*8] // Caveat: FCARG2 == BASE + | mov L:FCARG1, L:RB + | mov SAVE_PC, PC + | call extern lj_meta_len@8 // (lua_State *L, TValue *o) + | // NULL (retry) or TValue * (metamethod) returned in eax (RC). + | mov BASE, L:RB->base +#if LJ_52 + | test RC, RC + | jne ->vmeta_binop // Binop call for compatibility. + | movzx RD, PC_RD + | mov TAB:FCARG1, [BASE+RD*8] + | jmp ->BC_LEN_Z +#else + | jmp ->vmeta_binop // Binop call for compatibility. +#endif + | + |//-- Call metamethod ---------------------------------------------------- + | + |->vmeta_call_ra: + | lea RA, [BASE+RA*8+8] + |->vmeta_call: // Resolve and call __call metamethod. + | // BASE = old base, RA = new base, RC = nargs+1, PC = return + | mov TMP2, RA // Save RA, RC for us. + | mov TMP1, NARGS:RD + | sub RA, 8 + |.if X64 + | mov L:RB, SAVE_L + | mov L:RB->base, BASE // Caveat: CARG2d/CARG3d may be BASE. + | mov CARG2d, RA + | lea CARG3d, [RA+NARGS:RD*8] + | mov CARG1d, L:RB // Caveat: CARG1d may be RA. + |.else + | lea RC, [RA+NARGS:RD*8] + | mov L:RB, SAVE_L + | mov ARG2, RA + | mov ARG3, RC + | mov ARG1, L:RB + | mov L:RB->base, BASE // This is the callers base! + |.endif + | mov SAVE_PC, PC + | call extern lj_meta_call // (lua_State *L, TValue *func, TValue *top) + | mov BASE, L:RB->base + | mov RA, TMP2 + | mov NARGS:RD, TMP1 + | mov LFUNC:RB, [RA-8] + | add NARGS:RD, 1 + | // This is fragile. L->base must not move, KBASE must always be defined. + | cmp KBASE, BASE // Continue with CALLT if flag set. + | je ->BC_CALLT_Z + | mov BASE, RA + | ins_call // Otherwise call resolved metamethod. + | + |//-- Argument coercion for 'for' statement ------------------------------ + | + |->vmeta_for: + | mov L:RB, SAVE_L + | mov L:RB->base, BASE + | mov FCARG2, RA // Caveat: FCARG2 == BASE + | mov L:FCARG1, L:RB // Caveat: FCARG1 == RA + | mov SAVE_PC, PC + | call extern lj_meta_for@8 // (lua_State *L, TValue *base) + | mov BASE, L:RB->base + | mov RC, [PC-4] + | movzx RA, RCH + | movzx OP, RCL + | shr RC, 16 + |.if X64 + | jmp aword [DISPATCH+OP*8+GG_DISP2STATIC] // Retry FORI or JFORI. + |.else + | jmp aword [DISPATCH+OP*4+GG_DISP2STATIC] // Retry FORI or JFORI. + |.endif + | + |//----------------------------------------------------------------------- + |//-- Fast functions ----------------------------------------------------- + |//----------------------------------------------------------------------- + | + |.macro .ffunc, name + |->ff_ .. name: + |.endmacro + | + |.macro .ffunc_1, name + |->ff_ .. name: + | cmp NARGS:RD, 1+1; jb ->fff_fallback + |.endmacro + | + |.macro .ffunc_2, name + |->ff_ .. name: + | cmp NARGS:RD, 2+1; jb ->fff_fallback + |.endmacro + | + |.macro .ffunc_nsse, name, op + | .ffunc_1 name + | cmp dword [BASE+4], LJ_TISNUM; jae ->fff_fallback + | op xmm0, qword [BASE] + |.endmacro + | + |.macro .ffunc_nsse, name + | .ffunc_nsse name, movsd + |.endmacro + | + |.macro .ffunc_nnsse, name + | .ffunc_2 name + | cmp dword [BASE+4], LJ_TISNUM; jae ->fff_fallback + | cmp dword [BASE+12], LJ_TISNUM; jae ->fff_fallback + | movsd xmm0, qword [BASE] + | movsd xmm1, qword [BASE+8] + |.endmacro + | + |.macro .ffunc_nnr, name + | .ffunc_2 name + | cmp dword [BASE+4], LJ_TISNUM; jae ->fff_fallback + | cmp dword [BASE+12], LJ_TISNUM; jae ->fff_fallback + | fld qword [BASE+8] + | fld qword [BASE] + |.endmacro + | + |// Inlined GC threshold check. Caveat: uses label 1. + |.macro ffgccheck + | mov RB, [DISPATCH+DISPATCH_GL(gc.total)] + | cmp RB, [DISPATCH+DISPATCH_GL(gc.threshold)] + | jb >1 + | call ->fff_gcstep + |1: + |.endmacro + | + |//-- Base library: checks ----------------------------------------------- + | + |.ffunc_1 assert + | mov RB, [BASE+4] + | cmp RB, LJ_TISTRUECOND; jae ->fff_fallback + | mov PC, [BASE-4] + | mov MULTRES, RD + | mov [BASE-4], RB + | mov RB, [BASE] + | mov [BASE-8], RB + | sub RD, 2 + | jz >2 + | mov RA, BASE + |1: + | add RA, 8 + |.if X64 + | mov RBa, [RA] + | mov [RA-8], RBa + |.else + | mov RB, [RA+4] + | mov [RA-4], RB + | mov RB, [RA] + | mov [RA-8], RB + |.endif + | sub RD, 1 + | jnz <1 + |2: + | mov RD, MULTRES + | jmp ->fff_res_ + | + |.ffunc_1 type + | mov RB, [BASE+4] + |.if X64 + | mov RA, RB + | sar RA, 15 + | cmp RA, -2 + | je >3 + |.endif + | mov RC, ~LJ_TNUMX + | not RB + | cmp RC, RB + | cmova RC, RB + |2: + | mov CFUNC:RB, [BASE-8] + | mov STR:RC, [CFUNC:RB+RC*8+((char *)(&((GCfuncC *)0)->upvalue))] + | mov PC, [BASE-4] + | mov dword [BASE-4], LJ_TSTR + | mov [BASE-8], STR:RC + | jmp ->fff_res1 + |.if X64 + |3: + | mov RC, ~LJ_TLIGHTUD + | jmp <2 + |.endif + | + |//-- Base library: getters and setters --------------------------------- + | + |.ffunc_1 getmetatable + | mov RB, [BASE+4] + | mov PC, [BASE-4] + | cmp RB, LJ_TTAB; jne >6 + |1: // Field metatable must be at same offset for GCtab and GCudata! + | mov TAB:RB, [BASE] + | mov TAB:RB, TAB:RB->metatable + |2: + | test TAB:RB, TAB:RB + | mov dword [BASE-4], LJ_TNIL + | jz ->fff_res1 + | mov STR:RC, [DISPATCH+DISPATCH_GL(gcroot)+4*(GCROOT_MMNAME+MM_metatable)] + | mov dword [BASE-4], LJ_TTAB // Store metatable as default result. + | mov [BASE-8], TAB:RB + | mov RA, TAB:RB->hmask + | and RA, STR:RC->hash + | imul RA, #NODE + | add NODE:RA, TAB:RB->node + |3: // Rearranged logic, because we expect _not_ to find the key. + | cmp dword NODE:RA->key.it, LJ_TSTR + | jne >4 + | cmp dword NODE:RA->key.gcr, STR:RC + | je >5 + |4: + | mov NODE:RA, NODE:RA->next + | test NODE:RA, NODE:RA + | jnz <3 + | jmp ->fff_res1 // Not found, keep default result. + |5: + | mov RB, [RA+4] + | cmp RB, LJ_TNIL; je ->fff_res1 // Ditto for nil value. + | mov RC, [RA] + | mov [BASE-4], RB // Return value of mt.__metatable. + | mov [BASE-8], RC + | jmp ->fff_res1 + | + |6: + | cmp RB, LJ_TUDATA; je <1 + |.if X64 + | cmp RB, LJ_TNUMX; ja >8 + | cmp RB, LJ_TISNUM; jbe >7 + | mov RB, LJ_TLIGHTUD + | jmp >8 + |7: + |.else + | cmp RB, LJ_TISNUM; ja >8 + |.endif + | mov RB, LJ_TNUMX + |8: + | not RB + | mov TAB:RB, [DISPATCH+RB*4+DISPATCH_GL(gcroot[GCROOT_BASEMT])] + | jmp <2 + | + |.ffunc_2 setmetatable + | cmp dword [BASE+4], LJ_TTAB; jne ->fff_fallback + | // Fast path: no mt for table yet and not clearing the mt. + | mov TAB:RB, [BASE] + | cmp dword TAB:RB->metatable, 0; jne ->fff_fallback + | cmp dword [BASE+12], LJ_TTAB; jne ->fff_fallback + | mov TAB:RC, [BASE+8] + | mov TAB:RB->metatable, TAB:RC + | mov PC, [BASE-4] + | mov dword [BASE-4], LJ_TTAB // Return original table. + | mov [BASE-8], TAB:RB + | test byte TAB:RB->marked, LJ_GC_BLACK // isblack(table) + | jz >1 + | // Possible write barrier. Table is black, but skip iswhite(mt) check. + | barrierback TAB:RB, RC + |1: + | jmp ->fff_res1 + | + |.ffunc_2 rawget + | cmp dword [BASE+4], LJ_TTAB; jne ->fff_fallback + |.if X64WIN + | mov RB, BASE // Save BASE. + | lea CARG3d, [BASE+8] + | mov CARG2d, [BASE] // Caveat: CARG2d == BASE. + | mov CARG1d, SAVE_L + |.elif X64 + | mov RB, BASE // Save BASE. + | mov CARG2d, [BASE] + | lea CARG3d, [BASE+8] // Caveat: CARG3d == BASE. + | mov CARG1d, SAVE_L + |.else + | mov TAB:RD, [BASE] + | mov L:RB, SAVE_L + | mov ARG2, TAB:RD + | mov ARG1, L:RB + | mov RB, BASE // Save BASE. + | add BASE, 8 + | mov ARG3, BASE + |.endif + | call extern lj_tab_get // (lua_State *L, GCtab *t, cTValue *key) + | // cTValue * returned in eax (RD). + | mov BASE, RB // Restore BASE. + | // Copy table slot. + |.if X64 + | mov RBa, [RD] + | mov PC, [BASE-4] + | mov [BASE-8], RBa + |.else + | mov RB, [RD] + | mov RD, [RD+4] + | mov PC, [BASE-4] + | mov [BASE-8], RB + | mov [BASE-4], RD + |.endif + | jmp ->fff_res1 + | + |//-- Base library: conversions ------------------------------------------ + | + |.ffunc tonumber + | // Only handles the number case inline (without a base argument). + | cmp NARGS:RD, 1+1; jne ->fff_fallback // Exactly one argument. + | cmp dword [BASE+4], LJ_TISNUM + |.if DUALNUM + | jne >1 + | mov RB, dword [BASE]; jmp ->fff_resi + |1: + | ja ->fff_fallback + |.else + | jae ->fff_fallback + |.endif + | movsd xmm0, qword [BASE]; jmp ->fff_resxmm0 + | + |.ffunc_1 tostring + | // Only handles the string or number case inline. + | mov PC, [BASE-4] + | cmp dword [BASE+4], LJ_TSTR; jne >3 + | // A __tostring method in the string base metatable is ignored. + | mov STR:RD, [BASE] + |2: + | mov dword [BASE-4], LJ_TSTR + | mov [BASE-8], STR:RD + | jmp ->fff_res1 + |3: // Handle numbers inline, unless a number base metatable is present. + | cmp dword [BASE+4], LJ_TISNUM; ja ->fff_fallback + | cmp dword [DISPATCH+DISPATCH_GL(gcroot[GCROOT_BASEMT_NUM])], 0 + | jne ->fff_fallback + | ffgccheck // Caveat: uses label 1. + | mov L:RB, SAVE_L + | mov L:RB->base, BASE // Add frame since C call can throw. + | mov SAVE_PC, PC // Redundant (but a defined value). + |.if X64 and not X64WIN + | mov FCARG2, BASE // Otherwise: FCARG2 == BASE + |.endif + | mov L:FCARG1, L:RB + |.if DUALNUM + | call extern lj_strfmt_number@8 // (lua_State *L, cTValue *o) + |.else + | call extern lj_strfmt_num@8 // (lua_State *L, lua_Number *np) + |.endif + | // GCstr returned in eax (RD). + | mov BASE, L:RB->base + | jmp <2 + | + |//-- Base library: iterators ------------------------------------------- + | + |.ffunc_1 next + | je >2 // Missing 2nd arg? + |1: + | cmp dword [BASE+4], LJ_TTAB; jne ->fff_fallback + | mov L:RB, SAVE_L + | mov L:RB->base, BASE // Add frame since C call can throw. + | mov L:RB->top, BASE // Dummy frame length is ok. + | mov PC, [BASE-4] + |.if X64WIN + | lea CARG3d, [BASE+8] + | mov CARG2d, [BASE] // Caveat: CARG2d == BASE. + | mov CARG1d, L:RB + |.elif X64 + | mov CARG2d, [BASE] + | lea CARG3d, [BASE+8] // Caveat: CARG3d == BASE. + | mov CARG1d, L:RB + |.else + | mov TAB:RD, [BASE] + | mov ARG2, TAB:RD + | mov ARG1, L:RB + | add BASE, 8 + | mov ARG3, BASE + |.endif + | mov SAVE_PC, PC // Needed for ITERN fallback. + | call extern lj_tab_next // (lua_State *L, GCtab *t, TValue *key) + | // Flag returned in eax (RD). + | mov BASE, L:RB->base + | test RD, RD; jz >3 // End of traversal? + | // Copy key and value to results. + |.if X64 + | mov RBa, [BASE+8] + | mov RDa, [BASE+16] + | mov [BASE-8], RBa + | mov [BASE], RDa + |.else + | mov RB, [BASE+8] + | mov RD, [BASE+12] + | mov [BASE-8], RB + | mov [BASE-4], RD + | mov RB, [BASE+16] + | mov RD, [BASE+20] + | mov [BASE], RB + | mov [BASE+4], RD + |.endif + |->fff_res2: + | mov RD, 1+2 + | jmp ->fff_res + |2: // Set missing 2nd arg to nil. + | mov dword [BASE+12], LJ_TNIL + | jmp <1 + |3: // End of traversal: return nil. + | mov dword [BASE-4], LJ_TNIL + | jmp ->fff_res1 + | + |.ffunc_1 pairs + | mov TAB:RB, [BASE] + | cmp dword [BASE+4], LJ_TTAB; jne ->fff_fallback +#if LJ_52 + | cmp dword TAB:RB->metatable, 0; jne ->fff_fallback +#endif + | mov CFUNC:RB, [BASE-8] + | mov CFUNC:RD, CFUNC:RB->upvalue[0] + | mov PC, [BASE-4] + | mov dword [BASE-4], LJ_TFUNC + | mov [BASE-8], CFUNC:RD + | mov dword [BASE+12], LJ_TNIL + | mov RD, 1+3 + | jmp ->fff_res + | + |.ffunc_2 ipairs_aux + | cmp dword [BASE+4], LJ_TTAB; jne ->fff_fallback + | cmp dword [BASE+12], LJ_TISNUM + |.if DUALNUM + | jne ->fff_fallback + |.else + | jae ->fff_fallback + |.endif + | mov PC, [BASE-4] + |.if DUALNUM + | mov RD, dword [BASE+8] + | add RD, 1 + | mov dword [BASE-4], LJ_TISNUM + | mov dword [BASE-8], RD + |.else + | movsd xmm0, qword [BASE+8] + | sseconst_1 xmm1, RBa + | addsd xmm0, xmm1 + | cvttsd2si RD, xmm0 + | movsd qword [BASE-8], xmm0 + |.endif + | mov TAB:RB, [BASE] + | cmp RD, TAB:RB->asize; jae >2 // Not in array part? + | shl RD, 3 + | add RD, TAB:RB->array + |1: + | cmp dword [RD+4], LJ_TNIL; je ->fff_res0 + | // Copy array slot. + |.if X64 + | mov RBa, [RD] + | mov [BASE], RBa + |.else + | mov RB, [RD] + | mov RD, [RD+4] + | mov [BASE], RB + | mov [BASE+4], RD + |.endif + | jmp ->fff_res2 + |2: // Check for empty hash part first. Otherwise call C function. + | cmp dword TAB:RB->hmask, 0; je ->fff_res0 + | mov FCARG1, TAB:RB + | mov RB, BASE // Save BASE. + | mov FCARG2, RD // Caveat: FCARG2 == BASE + | call extern lj_tab_getinth@8 // (GCtab *t, int32_t key) + | // cTValue * or NULL returned in eax (RD). + | mov BASE, RB + | test RD, RD + | jnz <1 + |->fff_res0: + | mov RD, 1+0 + | jmp ->fff_res + | + |.ffunc_1 ipairs + | mov TAB:RB, [BASE] + | cmp dword [BASE+4], LJ_TTAB; jne ->fff_fallback +#if LJ_52 + | cmp dword TAB:RB->metatable, 0; jne ->fff_fallback +#endif + | mov CFUNC:RB, [BASE-8] + | mov CFUNC:RD, CFUNC:RB->upvalue[0] + | mov PC, [BASE-4] + | mov dword [BASE-4], LJ_TFUNC + | mov [BASE-8], CFUNC:RD + |.if DUALNUM + | mov dword [BASE+12], LJ_TISNUM + | mov dword [BASE+8], 0 + |.else + | xorps xmm0, xmm0 + | movsd qword [BASE+8], xmm0 + |.endif + | mov RD, 1+3 + | jmp ->fff_res + | + |//-- Base library: catch errors ---------------------------------------- + | + |.ffunc_1 pcall + | lea RA, [BASE+8] + | sub NARGS:RD, 1 + | mov PC, 8+FRAME_PCALL + |1: + | movzx RB, byte [DISPATCH+DISPATCH_GL(hookmask)] + | shr RB, HOOK_ACTIVE_SHIFT + | and RB, 1 + | add PC, RB // Remember active hook before pcall. + | jmp ->vm_call_dispatch + | + |.ffunc_2 xpcall + | cmp dword [BASE+12], LJ_TFUNC; jne ->fff_fallback + | mov RB, [BASE+4] // Swap function and traceback. + | mov [BASE+12], RB + | mov dword [BASE+4], LJ_TFUNC + | mov LFUNC:RB, [BASE] + | mov PC, [BASE+8] + | mov [BASE+8], LFUNC:RB + | mov [BASE], PC + | lea RA, [BASE+16] + | sub NARGS:RD, 2 + | mov PC, 16+FRAME_PCALL + | jmp <1 + | + |//-- Coroutine library -------------------------------------------------- + | + |.macro coroutine_resume_wrap, resume + |.if resume + |.ffunc_1 coroutine_resume + | mov L:RB, [BASE] + |.else + |.ffunc coroutine_wrap_aux + | mov CFUNC:RB, [BASE-8] + | mov L:RB, CFUNC:RB->upvalue[0].gcr + |.endif + | mov PC, [BASE-4] + | mov SAVE_PC, PC + |.if X64 + | mov TMP1, L:RB + |.else + | mov ARG1, L:RB + |.endif + |.if resume + | cmp dword [BASE+4], LJ_TTHREAD; jne ->fff_fallback + |.endif + | cmp aword L:RB->cframe, 0; jne ->fff_fallback + | cmp byte L:RB->status, LUA_YIELD; ja ->fff_fallback + | mov RA, L:RB->top + | je >1 // Status != LUA_YIELD (i.e. 0)? + | cmp RA, L:RB->base // Check for presence of initial func. + | je ->fff_fallback + |1: + |.if resume + | lea PC, [RA+NARGS:RD*8-16] // Check stack space (-1-thread). + |.else + | lea PC, [RA+NARGS:RD*8-8] // Check stack space (-1). + |.endif + | cmp PC, L:RB->maxstack; ja ->fff_fallback + | mov L:RB->top, PC + | + | mov L:RB, SAVE_L + | mov L:RB->base, BASE + |.if resume + | add BASE, 8 // Keep resumed thread in stack for GC. + |.endif + | mov L:RB->top, BASE + |.if resume + | lea RB, [BASE+NARGS:RD*8-24] // RB = end of source for stack move. + |.else + | lea RB, [BASE+NARGS:RD*8-16] // RB = end of source for stack move. + |.endif + | sub RBa, PCa // Relative to PC. + | + | cmp PC, RA + | je >3 + |2: // Move args to coroutine. + |.if X64 + | mov RCa, [PC+RB] + | mov [PC-8], RCa + |.else + | mov RC, [PC+RB+4] + | mov [PC-4], RC + | mov RC, [PC+RB] + | mov [PC-8], RC + |.endif + | sub PC, 8 + | cmp PC, RA + | jne <2 + |3: + |.if X64 + | mov CARG2d, RA + | mov CARG1d, TMP1 + |.else + | mov ARG2, RA + | xor RA, RA + | mov ARG4, RA + | mov ARG3, RA + |.endif + | call ->vm_resume // (lua_State *L, TValue *base, 0, 0) + | + | mov L:RB, SAVE_L + |.if X64 + | mov L:PC, TMP1 + |.else + | mov L:PC, ARG1 // The callee doesn't modify SAVE_L. + |.endif + | mov BASE, L:RB->base + | mov [DISPATCH+DISPATCH_GL(cur_L)], L:RB + | set_vmstate INTERP + | + | cmp eax, LUA_YIELD + | ja >8 + |4: + | mov RA, L:PC->base + | mov KBASE, L:PC->top + | mov L:PC->top, RA // Clear coroutine stack. + | mov PC, KBASE + | sub PC, RA + | je >6 // No results? + | lea RD, [BASE+PC] + | shr PC, 3 + | cmp RD, L:RB->maxstack + | ja >9 // Need to grow stack? + | + | mov RB, BASE + | sub RBa, RAa + |5: // Move results from coroutine. + |.if X64 + | mov RDa, [RA] + | mov [RA+RB], RDa + |.else + | mov RD, [RA] + | mov [RA+RB], RD + | mov RD, [RA+4] + | mov [RA+RB+4], RD + |.endif + | add RA, 8 + | cmp RA, KBASE + | jne <5 + |6: + |.if resume + | lea RD, [PC+2] // nresults+1 = 1 + true + results. + | mov dword [BASE-4], LJ_TTRUE // Prepend true to results. + |.else + | lea RD, [PC+1] // nresults+1 = 1 + results. + |.endif + |7: + | mov PC, SAVE_PC + | mov MULTRES, RD + |.if resume + | mov RAa, -8 + |.else + | xor RA, RA + |.endif + | test PC, FRAME_TYPE + | jz ->BC_RET_Z + | jmp ->vm_return + | + |8: // Coroutine returned with error (at co->top-1). + |.if resume + | mov dword [BASE-4], LJ_TFALSE // Prepend false to results. + | mov RA, L:PC->top + | sub RA, 8 + | mov L:PC->top, RA // Clear error from coroutine stack. + | // Copy error message. + |.if X64 + | mov RDa, [RA] + | mov [BASE], RDa + |.else + | mov RD, [RA] + | mov [BASE], RD + | mov RD, [RA+4] + | mov [BASE+4], RD + |.endif + | mov RD, 1+2 // nresults+1 = 1 + false + error. + | jmp <7 + |.else + | mov FCARG2, L:PC + | mov FCARG1, L:RB + | call extern lj_ffh_coroutine_wrap_err@8 // (lua_State *L, lua_State *co) + | // Error function does not return. + |.endif + | + |9: // Handle stack expansion on return from yield. + |.if X64 + | mov L:RA, TMP1 + |.else + | mov L:RA, ARG1 // The callee doesn't modify SAVE_L. + |.endif + | mov L:RA->top, KBASE // Undo coroutine stack clearing. + | mov FCARG2, PC + | mov FCARG1, L:RB + | call extern lj_state_growstack@8 // (lua_State *L, int n) + |.if X64 + | mov L:PC, TMP1 + |.else + | mov L:PC, ARG1 + |.endif + | mov BASE, L:RB->base + | jmp <4 // Retry the stack move. + |.endmacro + | + | coroutine_resume_wrap 1 // coroutine.resume + | coroutine_resume_wrap 0 // coroutine.wrap + | + |.ffunc coroutine_yield + | mov L:RB, SAVE_L + | test aword L:RB->cframe, CFRAME_RESUME + | jz ->fff_fallback + | mov L:RB->base, BASE + | lea RD, [BASE+NARGS:RD*8-8] + | mov L:RB->top, RD + | xor RD, RD + | mov aword L:RB->cframe, RDa + | mov al, LUA_YIELD + | mov byte L:RB->status, al + | jmp ->vm_leave_unw + | + |//-- Math library ------------------------------------------------------- + | + |.if not DUALNUM + |->fff_resi: // Dummy. + |.endif + | + |->fff_resn: + | mov PC, [BASE-4] + | fstp qword [BASE-8] + | jmp ->fff_res1 + | + | .ffunc_1 math_abs + |.if DUALNUM + | cmp dword [BASE+4], LJ_TISNUM; jne >2 + | mov RB, dword [BASE] + | cmp RB, 0; jns ->fff_resi + | neg RB; js >1 + |->fff_resbit: + |->fff_resi: + | mov PC, [BASE-4] + | mov dword [BASE-4], LJ_TISNUM + | mov dword [BASE-8], RB + | jmp ->fff_res1 + |1: + | mov PC, [BASE-4] + | mov dword [BASE-4], 0x41e00000 // 2^31. + | mov dword [BASE-8], 0 + | jmp ->fff_res1 + |2: + | ja ->fff_fallback + |.else + | cmp dword [BASE+4], LJ_TISNUM; jae ->fff_fallback + |.endif + | movsd xmm0, qword [BASE] + | sseconst_abs xmm1, RDa + | andps xmm0, xmm1 + |->fff_resxmm0: + | mov PC, [BASE-4] + | movsd qword [BASE-8], xmm0 + | // fallthrough + | + |->fff_res1: + | mov RD, 1+1 + |->fff_res: + | mov MULTRES, RD + |->fff_res_: + | test PC, FRAME_TYPE + | jnz >7 + |5: + | cmp PC_RB, RDL // More results expected? + | ja >6 + | // Adjust BASE. KBASE is assumed to be set for the calling frame. + | movzx RA, PC_RA + | not RAa // Note: ~RA = -(RA+1) + | lea BASE, [BASE+RA*8] // base = base - (RA+1)*8 + | ins_next + | + |6: // Fill up results with nil. + | mov dword [BASE+RD*8-12], LJ_TNIL + | add RD, 1 + | jmp <5 + | + |7: // Non-standard return case. + | mov RAa, -8 // Results start at BASE+RA = BASE-8. + | jmp ->vm_return + | + |.if X64 + |.define fff_resfp, fff_resxmm0 + |.else + |.define fff_resfp, fff_resn + |.endif + | + |.macro math_round, func + | .ffunc math_ .. func + |.if DUALNUM + | cmp dword [BASE+4], LJ_TISNUM; jne >1 + | mov RB, dword [BASE]; jmp ->fff_resi + |1: + | ja ->fff_fallback + |.else + | cmp dword [BASE+4], LJ_TISNUM; jae ->fff_fallback + |.endif + | movsd xmm0, qword [BASE] + | call ->vm_ .. func .. _sse + |.if DUALNUM + | cvttsd2si RB, xmm0 + | cmp RB, 0x80000000 + | jne ->fff_resi + | cvtsi2sd xmm1, RB + | ucomisd xmm0, xmm1 + | jp ->fff_resxmm0 + | je ->fff_resi + |.endif + | jmp ->fff_resxmm0 + |.endmacro + | + | math_round floor + | math_round ceil + | + |.ffunc_nsse math_sqrt, sqrtsd; jmp ->fff_resxmm0 + | + |.ffunc math_log + | cmp NARGS:RD, 1+1; jne ->fff_fallback // Exactly one argument. + | cmp dword [BASE+4], LJ_TISNUM; jae ->fff_fallback + | movsd xmm0, qword [BASE] + |.if not X64 + | movsd FPARG1, xmm0 + |.endif + | mov RB, BASE + | call extern log + | mov BASE, RB + | jmp ->fff_resfp + | + |.macro math_extern, func + | .ffunc_nsse math_ .. func + |.if not X64 + | movsd FPARG1, xmm0 + |.endif + | mov RB, BASE + | call extern func + | mov BASE, RB + | jmp ->fff_resfp + |.endmacro + | + |.macro math_extern2, func + | .ffunc_nnsse math_ .. func + |.if not X64 + | movsd FPARG1, xmm0 + | movsd FPARG3, xmm1 + |.endif + | mov RB, BASE + | call extern func + | mov BASE, RB + | jmp ->fff_resfp + |.endmacro + | + | math_extern log10 + | math_extern exp + | math_extern sin + | math_extern cos + | math_extern tan + | math_extern asin + | math_extern acos + | math_extern atan + | math_extern sinh + | math_extern cosh + | math_extern tanh + | math_extern2 pow + | math_extern2 atan2 + | math_extern2 fmod + | + |.ffunc_nnr math_ldexp; fscale; fpop1; jmp ->fff_resn + | + |.ffunc_1 math_frexp + | mov RB, [BASE+4] + | cmp RB, LJ_TISNUM; jae ->fff_fallback + | mov PC, [BASE-4] + | mov RC, [BASE] + | mov [BASE-4], RB; mov [BASE-8], RC + | shl RB, 1; cmp RB, 0xffe00000; jae >3 + | or RC, RB; jz >3 + | mov RC, 1022 + | cmp RB, 0x00200000; jb >4 + |1: + | shr RB, 21; sub RB, RC // Extract and unbias exponent. + | cvtsi2sd xmm0, RB + | mov RB, [BASE-4] + | and RB, 0x800fffff // Mask off exponent. + | or RB, 0x3fe00000 // Put mantissa in range [0.5,1) or 0. + | mov [BASE-4], RB + |2: + | movsd qword [BASE], xmm0 + | mov RD, 1+2 + | jmp ->fff_res + |3: // Return +-0, +-Inf, NaN unmodified and an exponent of 0. + | xorps xmm0, xmm0; jmp <2 + |4: // Handle denormals by multiplying with 2^54 and adjusting the bias. + | movsd xmm0, qword [BASE] + | sseconst_hi xmm1, RBa, 43500000 // 2^54. + | mulsd xmm0, xmm1 + | movsd qword [BASE-8], xmm0 + | mov RB, [BASE-4]; mov RC, 1076; shl RB, 1; jmp <1 + | + |.ffunc_nsse math_modf + | mov RB, [BASE+4] + | mov PC, [BASE-4] + | shl RB, 1; cmp RB, 0xffe00000; je >4 // +-Inf? + | movaps xmm4, xmm0 + | call ->vm_trunc_sse + | subsd xmm4, xmm0 + |1: + | movsd qword [BASE-8], xmm0 + | movsd qword [BASE], xmm4 + | mov RC, [BASE-4]; mov RB, [BASE+4] + | xor RC, RB; js >3 // Need to adjust sign? + |2: + | mov RD, 1+2 + | jmp ->fff_res + |3: + | xor RB, 0x80000000; mov [BASE+4], RB // Flip sign of fraction. + | jmp <2 + |4: + | xorps xmm4, xmm4; jmp <1 // Return +-Inf and +-0. + | + |.macro math_minmax, name, cmovop, sseop + | .ffunc name + | mov RA, 2 + | cmp dword [BASE+4], LJ_TISNUM + |.if DUALNUM + | jne >4 + | mov RB, dword [BASE] + |1: // Handle integers. + | cmp RA, RD; jae ->fff_resi + | cmp dword [BASE+RA*8-4], LJ_TISNUM; jne >3 + | cmp RB, dword [BASE+RA*8-8] + | cmovop RB, dword [BASE+RA*8-8] + | add RA, 1 + | jmp <1 + |3: + | ja ->fff_fallback + | // Convert intermediate result to number and continue below. + | cvtsi2sd xmm0, RB + | jmp >6 + |4: + | ja ->fff_fallback + |.else + | jae ->fff_fallback + |.endif + | + | movsd xmm0, qword [BASE] + |5: // Handle numbers or integers. + | cmp RA, RD; jae ->fff_resxmm0 + | cmp dword [BASE+RA*8-4], LJ_TISNUM + |.if DUALNUM + | jb >6 + | ja ->fff_fallback + | cvtsi2sd xmm1, dword [BASE+RA*8-8] + | jmp >7 + |.else + | jae ->fff_fallback + |.endif + |6: + | movsd xmm1, qword [BASE+RA*8-8] + |7: + | sseop xmm0, xmm1 + | add RA, 1 + | jmp <5 + |.endmacro + | + | math_minmax math_min, cmovg, minsd + | math_minmax math_max, cmovl, maxsd + | + |//-- String library ----------------------------------------------------- + | + |.ffunc string_byte // Only handle the 1-arg case here. + | cmp NARGS:RD, 1+1; jne ->fff_fallback + | cmp dword [BASE+4], LJ_TSTR; jne ->fff_fallback + | mov STR:RB, [BASE] + | mov PC, [BASE-4] + | cmp dword STR:RB->len, 1 + | jb ->fff_res0 // Return no results for empty string. + | movzx RB, byte STR:RB[1] + |.if DUALNUM + | jmp ->fff_resi + |.else + | cvtsi2sd xmm0, RB; jmp ->fff_resxmm0 + |.endif + | + |.ffunc string_char // Only handle the 1-arg case here. + | ffgccheck + | cmp NARGS:RD, 1+1; jne ->fff_fallback // *Exactly* 1 arg. + | cmp dword [BASE+4], LJ_TISNUM + |.if DUALNUM + | jne ->fff_fallback + | mov RB, dword [BASE] + | cmp RB, 255; ja ->fff_fallback + | mov TMP2, RB + |.else + | jae ->fff_fallback + | cvttsd2si RB, qword [BASE] + | cmp RB, 255; ja ->fff_fallback + | mov TMP2, RB + |.endif + |.if X64 + | mov TMP3, 1 + |.else + | mov ARG3, 1 + |.endif + | lea RDa, TMP2 // Points to stack. Little-endian. + |->fff_newstr: + | mov L:RB, SAVE_L + | mov L:RB->base, BASE + |.if X64 + | mov CARG3d, TMP3 // Zero-extended to size_t. + | mov CARG2, RDa // May be 64 bit ptr to stack. + | mov CARG1d, L:RB + |.else + | mov ARG2, RD + | mov ARG1, L:RB + |.endif + | mov SAVE_PC, PC + | call extern lj_str_new // (lua_State *L, char *str, size_t l) + |->fff_resstr: + | // GCstr * returned in eax (RD). + | mov BASE, L:RB->base + | mov PC, [BASE-4] + | mov dword [BASE-4], LJ_TSTR + | mov [BASE-8], STR:RD + | jmp ->fff_res1 + | + |.ffunc string_sub + | ffgccheck + | mov TMP2, -1 + | cmp NARGS:RD, 1+2; jb ->fff_fallback + | jna >1 + | cmp dword [BASE+20], LJ_TISNUM + |.if DUALNUM + | jne ->fff_fallback + | mov RB, dword [BASE+16] + | mov TMP2, RB + |.else + | jae ->fff_fallback + | cvttsd2si RB, qword [BASE+16] + | mov TMP2, RB + |.endif + |1: + | cmp dword [BASE+4], LJ_TSTR; jne ->fff_fallback + | cmp dword [BASE+12], LJ_TISNUM + |.if DUALNUM + | jne ->fff_fallback + |.else + | jae ->fff_fallback + |.endif + | mov STR:RB, [BASE] + | mov TMP3, STR:RB + | mov RB, STR:RB->len + |.if DUALNUM + | mov RA, dword [BASE+8] + |.else + | cvttsd2si RA, qword [BASE+8] + |.endif + | mov RC, TMP2 + | cmp RB, RC // len < end? (unsigned compare) + | jb >5 + |2: + | test RA, RA // start <= 0? + | jle >7 + |3: + | mov STR:RB, TMP3 + | sub RC, RA // start > end? + | jl ->fff_emptystr + | lea RB, [STR:RB+RA+#STR-1] + | add RC, 1 + |4: + |.if X64 + | mov TMP3, RC + |.else + | mov ARG3, RC + |.endif + | mov RD, RB + | jmp ->fff_newstr + | + |5: // Negative end or overflow. + | jl >6 + | lea RC, [RC+RB+1] // end = end+(len+1) + | jmp <2 + |6: // Overflow. + | mov RC, RB // end = len + | jmp <2 + | + |7: // Negative start or underflow. + | je >8 + | add RA, RB // start = start+(len+1) + | add RA, 1 + | jg <3 // start > 0? + |8: // Underflow. + | mov RA, 1 // start = 1 + | jmp <3 + | + |->fff_emptystr: // Range underflow. + | xor RC, RC // Zero length. Any ptr in RB is ok. + | jmp <4 + | + |.macro ffstring_op, name + | .ffunc_1 string_ .. name + | ffgccheck + | cmp dword [BASE+4], LJ_TSTR; jne ->fff_fallback + | mov L:RB, SAVE_L + | lea SBUF:FCARG1, [DISPATCH+DISPATCH_GL(tmpbuf)] + | mov L:RB->base, BASE + | mov STR:FCARG2, [BASE] // Caveat: FCARG2 == BASE + | mov RC, SBUF:FCARG1->b + | mov SBUF:FCARG1->L, L:RB + | mov SBUF:FCARG1->p, RC + | mov SAVE_PC, PC + | call extern lj_buf_putstr_ .. name .. @8 + | mov FCARG1, eax + | call extern lj_buf_tostr@4 + | jmp ->fff_resstr + |.endmacro + | + |ffstring_op reverse + |ffstring_op lower + |ffstring_op upper + | + |//-- Bit library -------------------------------------------------------- + | + |.macro .ffunc_bit, name, kind, fdef + | fdef name + |.if kind == 2 + | sseconst_tobit xmm1, RBa + |.endif + | cmp dword [BASE+4], LJ_TISNUM + |.if DUALNUM + | jne >1 + | mov RB, dword [BASE] + |.if kind > 0 + | jmp >2 + |.else + | jmp ->fff_resbit + |.endif + |1: + | ja ->fff_fallback + |.else + | jae ->fff_fallback + |.endif + | movsd xmm0, qword [BASE] + |.if kind < 2 + | sseconst_tobit xmm1, RBa + |.endif + | addsd xmm0, xmm1 + | movd RB, xmm0 + |2: + |.endmacro + | + |.macro .ffunc_bit, name, kind + | .ffunc_bit name, kind, .ffunc_1 + |.endmacro + | + |.ffunc_bit bit_tobit, 0 + | jmp ->fff_resbit + | + |.macro .ffunc_bit_op, name, ins + | .ffunc_bit name, 2 + | mov TMP2, NARGS:RD // Save for fallback. + | lea RD, [BASE+NARGS:RD*8-16] + |1: + | cmp RD, BASE + | jbe ->fff_resbit + | cmp dword [RD+4], LJ_TISNUM + |.if DUALNUM + | jne >2 + | ins RB, dword [RD] + | sub RD, 8 + | jmp <1 + |2: + | ja ->fff_fallback_bit_op + |.else + | jae ->fff_fallback_bit_op + |.endif + | movsd xmm0, qword [RD] + | addsd xmm0, xmm1 + | movd RA, xmm0 + | ins RB, RA + | sub RD, 8 + | jmp <1 + |.endmacro + | + |.ffunc_bit_op bit_band, and + |.ffunc_bit_op bit_bor, or + |.ffunc_bit_op bit_bxor, xor + | + |.ffunc_bit bit_bswap, 1 + | bswap RB + | jmp ->fff_resbit + | + |.ffunc_bit bit_bnot, 1 + | not RB + |.if DUALNUM + | jmp ->fff_resbit + |.else + |->fff_resbit: + | cvtsi2sd xmm0, RB + | jmp ->fff_resxmm0 + |.endif + | + |->fff_fallback_bit_op: + | mov NARGS:RD, TMP2 // Restore for fallback + | jmp ->fff_fallback + | + |.macro .ffunc_bit_sh, name, ins + |.if DUALNUM + | .ffunc_bit name, 1, .ffunc_2 + | // Note: no inline conversion from number for 2nd argument! + | cmp dword [BASE+12], LJ_TISNUM; jne ->fff_fallback + | mov RA, dword [BASE+8] + |.else + | .ffunc_nnsse name + | sseconst_tobit xmm2, RBa + | addsd xmm0, xmm2 + | addsd xmm1, xmm2 + | movd RB, xmm0 + | movd RA, xmm1 + |.endif + | ins RB, cl // Assumes RA is ecx. + | jmp ->fff_resbit + |.endmacro + | + |.ffunc_bit_sh bit_lshift, shl + |.ffunc_bit_sh bit_rshift, shr + |.ffunc_bit_sh bit_arshift, sar + |.ffunc_bit_sh bit_rol, rol + |.ffunc_bit_sh bit_ror, ror + | + |//----------------------------------------------------------------------- + | + |->fff_fallback_2: + | mov NARGS:RD, 1+2 // Other args are ignored, anyway. + | jmp ->fff_fallback + |->fff_fallback_1: + | mov NARGS:RD, 1+1 // Other args are ignored, anyway. + |->fff_fallback: // Call fast function fallback handler. + | // BASE = new base, RD = nargs+1 + | mov L:RB, SAVE_L + | mov PC, [BASE-4] // Fallback may overwrite PC. + | mov SAVE_PC, PC // Redundant (but a defined value). + | mov L:RB->base, BASE + | lea RD, [BASE+NARGS:RD*8-8] + | lea RA, [RD+8*LUA_MINSTACK] // Ensure enough space for handler. + | mov L:RB->top, RD + | mov CFUNC:RD, [BASE-8] + | cmp RA, L:RB->maxstack + | ja >5 // Need to grow stack. + |.if X64 + | mov CARG1d, L:RB + |.else + | mov ARG1, L:RB + |.endif + | call aword CFUNC:RD->f // (lua_State *L) + | mov BASE, L:RB->base + | // Either throws an error, or recovers and returns -1, 0 or nresults+1. + | test RD, RD; jg ->fff_res // Returned nresults+1? + |1: + | mov RA, L:RB->top + | sub RA, BASE + | shr RA, 3 + | test RD, RD + | lea NARGS:RD, [RA+1] + | mov LFUNC:RB, [BASE-8] + | jne ->vm_call_tail // Returned -1? + | ins_callt // Returned 0: retry fast path. + | + |// Reconstruct previous base for vmeta_call during tailcall. + |->vm_call_tail: + | mov RA, BASE + | test PC, FRAME_TYPE + | jnz >3 + | movzx RB, PC_RA + | not RBa // Note: ~RB = -(RB+1) + | lea BASE, [BASE+RB*8] // base = base - (RB+1)*8 + | jmp ->vm_call_dispatch // Resolve again for tailcall. + |3: + | mov RB, PC + | and RB, -8 + | sub BASE, RB + | jmp ->vm_call_dispatch // Resolve again for tailcall. + | + |5: // Grow stack for fallback handler. + | mov FCARG2, LUA_MINSTACK + | mov FCARG1, L:RB + | call extern lj_state_growstack@8 // (lua_State *L, int n) + | mov BASE, L:RB->base + | xor RD, RD // Simulate a return 0. + | jmp <1 // Dumb retry (goes through ff first). + | + |->fff_gcstep: // Call GC step function. + | // BASE = new base, RD = nargs+1 + | pop RBa // Must keep stack at same level. + | mov TMPa, RBa // Save return address + | mov L:RB, SAVE_L + | mov SAVE_PC, PC // Redundant (but a defined value). + | mov L:RB->base, BASE + | lea RD, [BASE+NARGS:RD*8-8] + | mov FCARG1, L:RB + | mov L:RB->top, RD + | call extern lj_gc_step@4 // (lua_State *L) + | mov BASE, L:RB->base + | mov RD, L:RB->top + | sub RD, BASE + | shr RD, 3 + | add NARGS:RD, 1 + | mov RBa, TMPa + | push RBa // Restore return address. + | ret + | + |//----------------------------------------------------------------------- + |//-- Special dispatch targets ------------------------------------------- + |//----------------------------------------------------------------------- + | + |->vm_record: // Dispatch target for recording phase. + |.if JIT + | movzx RD, byte [DISPATCH+DISPATCH_GL(hookmask)] + | test RDL, HOOK_VMEVENT // No recording while in vmevent. + | jnz >5 + | // Decrement the hookcount for consistency, but always do the call. + | test RDL, HOOK_ACTIVE + | jnz >1 + | test RDL, LUA_MASKLINE|LUA_MASKCOUNT + | jz >1 + | dec dword [DISPATCH+DISPATCH_GL(hookcount)] + | jmp >1 + |.endif + | + |->vm_rethook: // Dispatch target for return hooks. + | movzx RD, byte [DISPATCH+DISPATCH_GL(hookmask)] + | test RDL, HOOK_ACTIVE // Hook already active? + | jnz >5 + | jmp >1 + | + |->vm_inshook: // Dispatch target for instr/line hooks. + | movzx RD, byte [DISPATCH+DISPATCH_GL(hookmask)] + | test RDL, HOOK_ACTIVE // Hook already active? + | jnz >5 + | + | test RDL, LUA_MASKLINE|LUA_MASKCOUNT + | jz >5 + | dec dword [DISPATCH+DISPATCH_GL(hookcount)] + | jz >1 + | test RDL, LUA_MASKLINE + | jz >5 + |1: + | mov L:RB, SAVE_L + | mov L:RB->base, BASE + | mov FCARG2, PC // Caveat: FCARG2 == BASE + | mov FCARG1, L:RB + | // SAVE_PC must hold the _previous_ PC. The callee updates it with PC. + | call extern lj_dispatch_ins@8 // (lua_State *L, const BCIns *pc) + |3: + | mov BASE, L:RB->base + |4: + | movzx RA, PC_RA + |5: + | movzx OP, PC_OP + | movzx RD, PC_RD + |.if X64 + | jmp aword [DISPATCH+OP*8+GG_DISP2STATIC] // Re-dispatch to static ins. + |.else + | jmp aword [DISPATCH+OP*4+GG_DISP2STATIC] // Re-dispatch to static ins. + |.endif + | + |->cont_hook: // Continue from hook yield. + | add PC, 4 + | mov RA, [RB-24] + | mov MULTRES, RA // Restore MULTRES for *M ins. + | jmp <4 + | + |->vm_hotloop: // Hot loop counter underflow. + |.if JIT + | mov LFUNC:RB, [BASE-8] // Same as curr_topL(L). + | mov RB, LFUNC:RB->pc + | movzx RD, byte [RB+PC2PROTO(framesize)] + | lea RD, [BASE+RD*8] + | mov L:RB, SAVE_L + | mov L:RB->base, BASE + | mov L:RB->top, RD + | mov FCARG2, PC + | lea FCARG1, [DISPATCH+GG_DISP2J] + | mov aword [DISPATCH+DISPATCH_J(L)], L:RBa + | mov SAVE_PC, PC + | call extern lj_trace_hot@8 // (jit_State *J, const BCIns *pc) + | jmp <3 + |.endif + | + |->vm_callhook: // Dispatch target for call hooks. + | mov SAVE_PC, PC + |.if JIT + | jmp >1 + |.endif + | + |->vm_hotcall: // Hot call counter underflow. + |.if JIT + | mov SAVE_PC, PC + | or PC, 1 // Marker for hot call. + |1: + |.endif + | lea RD, [BASE+NARGS:RD*8-8] + | mov L:RB, SAVE_L + | mov L:RB->base, BASE + | mov L:RB->top, RD + | mov FCARG2, PC + | mov FCARG1, L:RB + | call extern lj_dispatch_call@8 // (lua_State *L, const BCIns *pc) + | // ASMFunction returned in eax/rax (RDa). + | mov SAVE_PC, 0 // Invalidate for subsequent line hook. + |.if JIT + | and PC, -2 + |.endif + | mov BASE, L:RB->base + | mov RAa, RDa + | mov RD, L:RB->top + | sub RD, BASE + | mov RBa, RAa + | movzx RA, PC_RA + | shr RD, 3 + | add NARGS:RD, 1 + | jmp RBa + | + |->cont_stitch: // Trace stitching. + |.if JIT + | // BASE = base, RC = result, RB = mbase + | mov TRACE:RA, [RB-24] // Save previous trace. + | mov TMP1, TRACE:RA + | mov TMP3, DISPATCH // Need one more register. + | mov DISPATCH, MULTRES + | movzx RA, PC_RA + | lea RA, [BASE+RA*8] // Call base. + | sub DISPATCH, 1 + | jz >2 + |1: // Move results down. + |.if X64 + | mov RBa, [RC] + | mov [RA], RBa + |.else + | mov RB, [RC] + | mov [RA], RB + | mov RB, [RC+4] + | mov [RA+4], RB + |.endif + | add RC, 8 + | add RA, 8 + | sub DISPATCH, 1 + | jnz <1 + |2: + | movzx RC, PC_RA + | movzx RB, PC_RB + | add RC, RB + | lea RC, [BASE+RC*8-8] + |3: + | cmp RC, RA + | ja >9 // More results wanted? + | + | mov DISPATCH, TMP3 + | mov TRACE:RD, TMP1 // Get previous trace. + | movzx RB, word TRACE:RD->traceno + | movzx RD, word TRACE:RD->link + | cmp RD, RB + | je ->cont_nop // Blacklisted. + | test RD, RD + | jne =>BC_JLOOP // Jump to stitched trace. + | + | // Stitch a new trace to the previous trace. + | mov [DISPATCH+DISPATCH_J(exitno)], RB + | mov L:RB, SAVE_L + | mov L:RB->base, BASE + | mov FCARG2, PC + | lea FCARG1, [DISPATCH+GG_DISP2J] + | mov aword [DISPATCH+DISPATCH_J(L)], L:RBa + | call extern lj_dispatch_stitch@8 // (jit_State *J, const BCIns *pc) + | mov BASE, L:RB->base + | jmp ->cont_nop + | + |9: // Fill up results with nil. + | mov dword [RA+4], LJ_TNIL + | add RA, 8 + | jmp <3 + |.endif + | + |->vm_profhook: // Dispatch target for profiler hook. +#if LJ_HASPROFILE + | mov L:RB, SAVE_L + | mov L:RB->base, BASE + | mov FCARG2, PC // Caveat: FCARG2 == BASE + | mov FCARG1, L:RB + | call extern lj_dispatch_profile@8 // (lua_State *L, const BCIns *pc) + | mov BASE, L:RB->base + | // HOOK_PROFILE is off again, so re-dispatch to dynamic instruction. + | sub PC, 4 + | jmp ->cont_nop +#endif + | + |//----------------------------------------------------------------------- + |//-- Trace exit handler ------------------------------------------------- + |//----------------------------------------------------------------------- + | + |// Called from an exit stub with the exit number on the stack. + |// The 16 bit exit number is stored with two (sign-extended) push imm8. + |->vm_exit_handler: + |.if JIT + |.if X64 + | push r13; push r12 + | push r11; push r10; push r9; push r8 + | push rdi; push rsi; push rbp; lea rbp, [rsp+88]; push rbp + | push rbx; push rdx; push rcx; push rax + | movzx RC, byte [rbp-8] // Reconstruct exit number. + | mov RCH, byte [rbp-16] + | mov [rbp-8], r15; mov [rbp-16], r14 + |.else + | push ebp; lea ebp, [esp+12]; push ebp + | push ebx; push edx; push ecx; push eax + | movzx RC, byte [ebp-4] // Reconstruct exit number. + | mov RCH, byte [ebp-8] + | mov [ebp-4], edi; mov [ebp-8], esi + |.endif + | // Caveat: DISPATCH is ebx. + | mov DISPATCH, [ebp] + | mov RA, [DISPATCH+DISPATCH_GL(vmstate)] // Get trace number. + | set_vmstate EXIT + | mov [DISPATCH+DISPATCH_J(exitno)], RC + | mov [DISPATCH+DISPATCH_J(parent)], RA + |.if X64 + |.if X64WIN + | sub rsp, 16*8+4*8 // Room for SSE regs + save area. + |.else + | sub rsp, 16*8 // Room for SSE regs. + |.endif + | add rbp, -128 + | movsd qword [rbp-8], xmm15; movsd qword [rbp-16], xmm14 + | movsd qword [rbp-24], xmm13; movsd qword [rbp-32], xmm12 + | movsd qword [rbp-40], xmm11; movsd qword [rbp-48], xmm10 + | movsd qword [rbp-56], xmm9; movsd qword [rbp-64], xmm8 + | movsd qword [rbp-72], xmm7; movsd qword [rbp-80], xmm6 + | movsd qword [rbp-88], xmm5; movsd qword [rbp-96], xmm4 + | movsd qword [rbp-104], xmm3; movsd qword [rbp-112], xmm2 + | movsd qword [rbp-120], xmm1; movsd qword [rbp-128], xmm0 + |.else + | sub esp, 8*8+16 // Room for SSE regs + args. + | movsd qword [ebp-40], xmm7; movsd qword [ebp-48], xmm6 + | movsd qword [ebp-56], xmm5; movsd qword [ebp-64], xmm4 + | movsd qword [ebp-72], xmm3; movsd qword [ebp-80], xmm2 + | movsd qword [ebp-88], xmm1; movsd qword [ebp-96], xmm0 + |.endif + | // Caveat: RB is ebp. + | mov L:RB, [DISPATCH+DISPATCH_GL(cur_L)] + | mov BASE, [DISPATCH+DISPATCH_GL(jit_base)] + | mov aword [DISPATCH+DISPATCH_J(L)], L:RBa + | mov L:RB->base, BASE + |.if X64WIN + | lea CARG2, [rsp+4*8] + |.elif X64 + | mov CARG2, rsp + |.else + | lea FCARG2, [esp+16] + |.endif + | lea FCARG1, [DISPATCH+GG_DISP2J] + | mov dword [DISPATCH+DISPATCH_GL(jit_base)], 0 + | call extern lj_trace_exit@8 // (jit_State *J, ExitState *ex) + | // MULTRES or negated error code returned in eax (RD). + | mov RAa, L:RB->cframe + | and RAa, CFRAME_RAWMASK + |.if X64WIN + | // Reposition stack later. + |.elif X64 + | mov rsp, RAa // Reposition stack to C frame. + |.else + | mov esp, RAa // Reposition stack to C frame. + |.endif + | mov [RAa+CFRAME_OFS_L], L:RB // Set SAVE_L (on-trace resume/yield). + | mov BASE, L:RB->base + | mov PC, [RAa+CFRAME_OFS_PC] // Get SAVE_PC. + |.if X64 + | jmp >1 + |.endif + |.endif + |->vm_exit_interp: + | // RD = MULTRES or negated error code, BASE, PC and DISPATCH set. + |.if JIT + |.if X64 + | // Restore additional callee-save registers only used in compiled code. + |.if X64WIN + | lea RAa, [rsp+9*16+4*8] + |1: + | movdqa xmm15, [RAa-9*16] + | movdqa xmm14, [RAa-8*16] + | movdqa xmm13, [RAa-7*16] + | movdqa xmm12, [RAa-6*16] + | movdqa xmm11, [RAa-5*16] + | movdqa xmm10, [RAa-4*16] + | movdqa xmm9, [RAa-3*16] + | movdqa xmm8, [RAa-2*16] + | movdqa xmm7, [RAa-1*16] + | mov rsp, RAa // Reposition stack to C frame. + | movdqa xmm6, [RAa] + | mov r15, CSAVE_3 + | mov r14, CSAVE_4 + |.else + | add rsp, 16 // Reposition stack to C frame. + |1: + |.endif + | mov r13, TMPa + | mov r12, TMPQ + |.endif + | test RD, RD; js >9 // Check for error from exit. + | mov L:RB, SAVE_L + | mov MULTRES, RD + | mov LFUNC:KBASE, [BASE-8] + | mov KBASE, LFUNC:KBASE->pc + | mov KBASE, [KBASE+PC2PROTO(k)] + | mov L:RB->base, BASE + | mov dword [DISPATCH+DISPATCH_GL(jit_base)], 0 + | set_vmstate INTERP + | // Modified copy of ins_next which handles function header dispatch, too. + | mov RC, [PC] + | movzx RA, RCH + | movzx OP, RCL + | add PC, 4 + | shr RC, 16 + | cmp OP, BC_FUNCF // Function header? + | jb >3 + | cmp OP, BC_FUNCC+2 // Fast function? + | jae >4 + |2: + | mov RC, MULTRES // RC/RD holds nres+1. + |3: + |.if X64 + | jmp aword [DISPATCH+OP*8] + |.else + | jmp aword [DISPATCH+OP*4] + |.endif + | + |4: // Check frame below fast function. + | mov RC, [BASE-4] + | test RC, FRAME_TYPE + | jnz <2 // Trace stitching continuation? + | // Otherwise set KBASE for Lua function below fast function. + | movzx RC, byte [RC-3] + | not RCa + | mov LFUNC:KBASE, [BASE+RC*8-8] + | mov KBASE, LFUNC:KBASE->pc + | mov KBASE, [KBASE+PC2PROTO(k)] + | jmp <2 + | + |9: // Rethrow error from the right C frame. + | neg RD + | mov FCARG1, L:RB + | mov FCARG2, RD + | call extern lj_err_throw@8 // (lua_State *L, int errcode) + |.endif + | + |//----------------------------------------------------------------------- + |//-- Math helper functions ---------------------------------------------- + |//----------------------------------------------------------------------- + | + |// FP value rounding. Called by math.floor/math.ceil fast functions + |// and from JIT code. arg/ret is xmm0. xmm0-xmm3 and RD (eax) modified. + |.macro vm_round, name, mode, cond + |->name: + |.if not X64 and cond + | movsd xmm0, qword [esp+4] + | call ->name .. _sse + | movsd qword [esp+4], xmm0 // Overwrite callee-owned arg. + | fld qword [esp+4] + | ret + |.endif + | + |->name .. _sse: + | sseconst_abs xmm2, RDa + | sseconst_2p52 xmm3, RDa + | movaps xmm1, xmm0 + | andpd xmm1, xmm2 // |x| + | ucomisd xmm3, xmm1 // No truncation if 2^52 <= |x|. + | jbe >1 + | andnpd xmm2, xmm0 // Isolate sign bit. + |.if mode == 2 // trunc(x)? + | movaps xmm0, xmm1 + | addsd xmm1, xmm3 // (|x| + 2^52) - 2^52 + | subsd xmm1, xmm3 + | sseconst_1 xmm3, RDa + | cmpsd xmm0, xmm1, 1 // |x| < result? + | andpd xmm0, xmm3 + | subsd xmm1, xmm0 // If yes, subtract -1. + | orpd xmm1, xmm2 // Merge sign bit back in. + |.else + | addsd xmm1, xmm3 // (|x| + 2^52) - 2^52 + | subsd xmm1, xmm3 + | orpd xmm1, xmm2 // Merge sign bit back in. + | .if mode == 1 // ceil(x)? + | sseconst_m1 xmm2, RDa // Must subtract -1 to preserve -0. + | cmpsd xmm0, xmm1, 6 // x > result? + | .else // floor(x)? + | sseconst_1 xmm2, RDa + | cmpsd xmm0, xmm1, 1 // x < result? + | .endif + | andpd xmm0, xmm2 + | subsd xmm1, xmm0 // If yes, subtract +-1. + |.endif + | movaps xmm0, xmm1 + |1: + | ret + |.endmacro + | + | vm_round vm_floor, 0, 1 + | vm_round vm_ceil, 1, JIT + | vm_round vm_trunc, 2, JIT + | + |// FP modulo x%y. Called by BC_MOD* and vm_arith. + |->vm_mod: + |// Args in xmm0/xmm1, return value in xmm0. + |// Caveat: xmm0-xmm5 and RC (eax) modified! + | movaps xmm5, xmm0 + | divsd xmm0, xmm1 + | sseconst_abs xmm2, RDa + | sseconst_2p52 xmm3, RDa + | movaps xmm4, xmm0 + | andpd xmm4, xmm2 // |x/y| + | ucomisd xmm3, xmm4 // No truncation if 2^52 <= |x/y|. + | jbe >1 + | andnpd xmm2, xmm0 // Isolate sign bit. + | addsd xmm4, xmm3 // (|x/y| + 2^52) - 2^52 + | subsd xmm4, xmm3 + | orpd xmm4, xmm2 // Merge sign bit back in. + | sseconst_1 xmm2, RDa + | cmpsd xmm0, xmm4, 1 // x/y < result? + | andpd xmm0, xmm2 + | subsd xmm4, xmm0 // If yes, subtract 1.0. + | movaps xmm0, xmm5 + | mulsd xmm1, xmm4 + | subsd xmm0, xmm1 + | ret + |1: + | mulsd xmm1, xmm0 + | movaps xmm0, xmm5 + | subsd xmm0, xmm1 + | ret + | + |// Args in xmm0/eax. Ret in xmm0. xmm0-xmm1 and eax modified. + |->vm_powi_sse: + | cmp eax, 1; jle >6 // i<=1? + | // Now 1 < (unsigned)i <= 0x80000000. + |1: // Handle leading zeros. + | test eax, 1; jnz >2 + | mulsd xmm0, xmm0 + | shr eax, 1 + | jmp <1 + |2: + | shr eax, 1; jz >5 + | movaps xmm1, xmm0 + |3: // Handle trailing bits. + | mulsd xmm0, xmm0 + | shr eax, 1; jz >4 + | jnc <3 + | mulsd xmm1, xmm0 + | jmp <3 + |4: + | mulsd xmm0, xmm1 + |5: + | ret + |6: + | je <5 // x^1 ==> x + | jb >7 // x^0 ==> 1 + | neg eax + | call <1 + | sseconst_1 xmm1, RDa + | divsd xmm1, xmm0 + | movaps xmm0, xmm1 + | ret + |7: + | sseconst_1 xmm0, RDa + | ret + | + |//----------------------------------------------------------------------- + |//-- Miscellaneous functions -------------------------------------------- + |//----------------------------------------------------------------------- + | + |// int lj_vm_cpuid(uint32_t f, uint32_t res[4]) + |->vm_cpuid: + |.if X64 + | mov eax, CARG1d + | .if X64WIN; push rsi; mov rsi, CARG2; .endif + | push rbx + | xor ecx, ecx + | cpuid + | mov [rsi], eax + | mov [rsi+4], ebx + | mov [rsi+8], ecx + | mov [rsi+12], edx + | pop rbx + | .if X64WIN; pop rsi; .endif + | ret + |.else + | pushfd + | pop edx + | mov ecx, edx + | xor edx, 0x00200000 // Toggle ID bit in flags. + | push edx + | popfd + | pushfd + | pop edx + | xor eax, eax // Zero means no features supported. + | cmp ecx, edx + | jz >1 // No ID toggle means no CPUID support. + | mov eax, [esp+4] // Argument 1 is function number. + | push edi + | push ebx + | xor ecx, ecx + | cpuid + | mov edi, [esp+16] // Argument 2 is result area. + | mov [edi], eax + | mov [edi+4], ebx + | mov [edi+8], ecx + | mov [edi+12], edx + | pop ebx + | pop edi + |1: + | ret + |.endif + | + |//----------------------------------------------------------------------- + |//-- Assertions --------------------------------------------------------- + |//----------------------------------------------------------------------- + | + |->assert_bad_for_arg_type: +#ifdef LUA_USE_ASSERT + | int3 +#endif + | int3 + | + |//----------------------------------------------------------------------- + |//-- FFI helper functions ----------------------------------------------- + |//----------------------------------------------------------------------- + | + |// Handler for callback functions. Callback slot number in ah/al. + |->vm_ffi_callback: + |.if FFI + |.type CTSTATE, CTState, PC + |.if not X64 + | sub esp, 16 // Leave room for SAVE_ERRF etc. + |.endif + | saveregs_ // ebp/rbp already saved. ebp now holds global_State *. + | lea DISPATCH, [ebp+GG_G2DISP] + | mov CTSTATE, GL:ebp->ctype_state + | movzx eax, ax + | mov CTSTATE->cb.slot, eax + |.if X64 + | mov CTSTATE->cb.gpr[0], CARG1 + | mov CTSTATE->cb.gpr[1], CARG2 + | mov CTSTATE->cb.gpr[2], CARG3 + | mov CTSTATE->cb.gpr[3], CARG4 + | movsd qword CTSTATE->cb.fpr[0], xmm0 + | movsd qword CTSTATE->cb.fpr[1], xmm1 + | movsd qword CTSTATE->cb.fpr[2], xmm2 + | movsd qword CTSTATE->cb.fpr[3], xmm3 + |.if X64WIN + | lea rax, [rsp+CFRAME_SIZE+4*8] + |.else + | lea rax, [rsp+CFRAME_SIZE] + | mov CTSTATE->cb.gpr[4], CARG5 + | mov CTSTATE->cb.gpr[5], CARG6 + | movsd qword CTSTATE->cb.fpr[4], xmm4 + | movsd qword CTSTATE->cb.fpr[5], xmm5 + | movsd qword CTSTATE->cb.fpr[6], xmm6 + | movsd qword CTSTATE->cb.fpr[7], xmm7 + |.endif + | mov CTSTATE->cb.stack, rax + | mov CARG2, rsp + |.else + | lea eax, [esp+CFRAME_SIZE+16] + | mov CTSTATE->cb.gpr[0], FCARG1 + | mov CTSTATE->cb.gpr[1], FCARG2 + | mov CTSTATE->cb.stack, eax + | mov FCARG1, [esp+CFRAME_SIZE+12] // Move around misplaced retaddr/ebp. + | mov FCARG2, [esp+CFRAME_SIZE+8] + | mov SAVE_RET, FCARG1 + | mov SAVE_R4, FCARG2 + | mov FCARG2, esp + |.endif + | mov SAVE_PC, CTSTATE // Any value outside of bytecode is ok. + | mov FCARG1, CTSTATE + | call extern lj_ccallback_enter@8 // (CTState *cts, void *cf) + | // lua_State * returned in eax (RD). + | set_vmstate INTERP + | mov BASE, L:RD->base + | mov RD, L:RD->top + | sub RD, BASE + | mov LFUNC:RB, [BASE-8] + | shr RD, 3 + | add RD, 1 + | ins_callt + |.endif + | + |->cont_ffi_callback: // Return from FFI callback. + |.if FFI + | mov L:RA, SAVE_L + | mov CTSTATE, [DISPATCH+DISPATCH_GL(ctype_state)] + | mov aword CTSTATE->L, L:RAa + | mov L:RA->base, BASE + | mov L:RA->top, RB + | mov FCARG1, CTSTATE + | mov FCARG2, RC + | call extern lj_ccallback_leave@8 // (CTState *cts, TValue *o) + |.if X64 + | mov rax, CTSTATE->cb.gpr[0] + | movsd xmm0, qword CTSTATE->cb.fpr[0] + | jmp ->vm_leave_unw + |.else + | mov L:RB, SAVE_L + | mov eax, CTSTATE->cb.gpr[0] + | mov edx, CTSTATE->cb.gpr[1] + | cmp dword CTSTATE->cb.gpr[2], 1 + | jb >7 + | je >6 + | fld qword CTSTATE->cb.fpr[0].d + | jmp >7 + |6: + | fld dword CTSTATE->cb.fpr[0].f + |7: + | mov ecx, L:RB->top + | movzx ecx, word [ecx+6] // Get stack adjustment and copy up. + | mov SAVE_L, ecx // Must be one slot above SAVE_RET + | restoreregs + | pop ecx // Move return addr from SAVE_RET. + | add esp, [esp] // Adjust stack. + | add esp, 16 + | push ecx + | ret + |.endif + |.endif + | + |->vm_ffi_call@4: // Call C function via FFI. + | // Caveat: needs special frame unwinding, see below. + |.if FFI + |.if X64 + | .type CCSTATE, CCallState, rbx + | push rbp; mov rbp, rsp; push rbx; mov CCSTATE, CARG1 + |.else + | .type CCSTATE, CCallState, ebx + | push ebp; mov ebp, esp; push ebx; mov CCSTATE, FCARG1 + |.endif + | + | // Readjust stack. + |.if X64 + | mov eax, CCSTATE->spadj + | sub rsp, rax + |.else + | sub esp, CCSTATE->spadj + |.if WIN + | mov CCSTATE->spadj, esp + |.endif + |.endif + | + | // Copy stack slots. + | movzx ecx, byte CCSTATE->nsp + | sub ecx, 1 + | js >2 + |1: + |.if X64 + | mov rax, [CCSTATE+rcx*8+offsetof(CCallState, stack)] + | mov [rsp+rcx*8+CCALL_SPS_EXTRA*8], rax + |.else + | mov eax, [CCSTATE+ecx*4+offsetof(CCallState, stack)] + | mov [esp+ecx*4], eax + |.endif + | sub ecx, 1 + | jns <1 + |2: + | + |.if X64 + | movzx eax, byte CCSTATE->nfpr + | mov CARG1, CCSTATE->gpr[0] + | mov CARG2, CCSTATE->gpr[1] + | mov CARG3, CCSTATE->gpr[2] + | mov CARG4, CCSTATE->gpr[3] + |.if not X64WIN + | mov CARG5, CCSTATE->gpr[4] + | mov CARG6, CCSTATE->gpr[5] + |.endif + | test eax, eax; jz >5 + | movaps xmm0, CCSTATE->fpr[0] + | movaps xmm1, CCSTATE->fpr[1] + | movaps xmm2, CCSTATE->fpr[2] + | movaps xmm3, CCSTATE->fpr[3] + |.if not X64WIN + | cmp eax, 4; jbe >5 + | movaps xmm4, CCSTATE->fpr[4] + | movaps xmm5, CCSTATE->fpr[5] + | movaps xmm6, CCSTATE->fpr[6] + | movaps xmm7, CCSTATE->fpr[7] + |.endif + |5: + |.else + | mov FCARG1, CCSTATE->gpr[0] + | mov FCARG2, CCSTATE->gpr[1] + |.endif + | + | call aword CCSTATE->func + | + |.if X64 + | mov CCSTATE->gpr[0], rax + | movaps CCSTATE->fpr[0], xmm0 + |.if not X64WIN + | mov CCSTATE->gpr[1], rdx + | movaps CCSTATE->fpr[1], xmm1 + |.endif + |.else + | mov CCSTATE->gpr[0], eax + | mov CCSTATE->gpr[1], edx + | cmp byte CCSTATE->resx87, 1 + | jb >7 + | je >6 + | fstp qword CCSTATE->fpr[0].d[0] + | jmp >7 + |6: + | fstp dword CCSTATE->fpr[0].f[0] + |7: + |.if WIN + | sub CCSTATE->spadj, esp + |.endif + |.endif + | + |.if X64 + | mov rbx, [rbp-8]; leave; ret + |.else + | mov ebx, [ebp-4]; leave; ret + |.endif + |.endif + |// Note: vm_ffi_call must be the last function in this object file! + | + |//----------------------------------------------------------------------- +} + +/* Generate the code for a single instruction. */ +static void build_ins(BuildCtx *ctx, BCOp op, int defop) +{ + int vk = 0; + |// Note: aligning all instructions does not pay off. + |=>defop: + + switch (op) { + + /* -- Comparison ops ---------------------------------------------------- */ + + /* Remember: all ops branch for a true comparison, fall through otherwise. */ + + |.macro jmp_comp, lt, ge, le, gt, target + ||switch (op) { + ||case BC_ISLT: + | lt target + ||break; + ||case BC_ISGE: + | ge target + ||break; + ||case BC_ISLE: + | le target + ||break; + ||case BC_ISGT: + | gt target + ||break; + ||default: break; /* Shut up GCC. */ + ||} + |.endmacro + + case BC_ISLT: case BC_ISGE: case BC_ISLE: case BC_ISGT: + | // RA = src1, RD = src2, JMP with RD = target + | ins_AD + |.if DUALNUM + | checkint RA, >7 + | checkint RD, >8 + | mov RB, dword [BASE+RA*8] + | add PC, 4 + | cmp RB, dword [BASE+RD*8] + | jmp_comp jge, jl, jg, jle, >9 + |6: + | movzx RD, PC_RD + | branchPC RD + |9: + | ins_next + | + |7: // RA is not an integer. + | ja ->vmeta_comp + | // RA is a number. + | cmp dword [BASE+RD*8+4], LJ_TISNUM; jb >1; jne ->vmeta_comp + | // RA is a number, RD is an integer. + | cvtsi2sd xmm0, dword [BASE+RD*8] + | jmp >2 + | + |8: // RA is an integer, RD is not an integer. + | ja ->vmeta_comp + | // RA is an integer, RD is a number. + | cvtsi2sd xmm1, dword [BASE+RA*8] + | movsd xmm0, qword [BASE+RD*8] + | add PC, 4 + | ucomisd xmm0, xmm1 + | jmp_comp jbe, ja, jb, jae, <9 + | jmp <6 + |.else + | checknum RA, ->vmeta_comp + | checknum RD, ->vmeta_comp + |.endif + |1: + | movsd xmm0, qword [BASE+RD*8] + |2: + | add PC, 4 + | ucomisd xmm0, qword [BASE+RA*8] + |3: + | // Unordered: all of ZF CF PF set, ordered: PF clear. + | // To preserve NaN semantics GE/GT branch on unordered, but LT/LE don't. + |.if DUALNUM + | jmp_comp jbe, ja, jb, jae, <9 + | jmp <6 + |.else + | jmp_comp jbe, ja, jb, jae, >1 + | movzx RD, PC_RD + | branchPC RD + |1: + | ins_next + |.endif + break; + + case BC_ISEQV: case BC_ISNEV: + vk = op == BC_ISEQV; + | ins_AD // RA = src1, RD = src2, JMP with RD = target + | mov RB, [BASE+RD*8+4] + | add PC, 4 + |.if DUALNUM + | cmp RB, LJ_TISNUM; jne >7 + | checkint RA, >8 + | mov RB, dword [BASE+RD*8] + | cmp RB, dword [BASE+RA*8] + if (vk) { + | jne >9 + } else { + | je >9 + } + | movzx RD, PC_RD + | branchPC RD + |9: + | ins_next + | + |7: // RD is not an integer. + | ja >5 + | // RD is a number. + | cmp dword [BASE+RA*8+4], LJ_TISNUM; jb >1; jne >5 + | // RD is a number, RA is an integer. + | cvtsi2sd xmm0, dword [BASE+RA*8] + | jmp >2 + | + |8: // RD is an integer, RA is not an integer. + | ja >5 + | // RD is an integer, RA is a number. + | cvtsi2sd xmm0, dword [BASE+RD*8] + | ucomisd xmm0, qword [BASE+RA*8] + | jmp >4 + | + |.else + | cmp RB, LJ_TISNUM; jae >5 + | checknum RA, >5 + |.endif + |1: + | movsd xmm0, qword [BASE+RA*8] + |2: + | ucomisd xmm0, qword [BASE+RD*8] + |4: + iseqne_fp: + if (vk) { + | jp >2 // Unordered means not equal. + | jne >2 + } else { + | jp >2 // Unordered means not equal. + | je >1 + } + iseqne_end: + if (vk) { + |1: // EQ: Branch to the target. + | movzx RD, PC_RD + | branchPC RD + |2: // NE: Fallthrough to next instruction. + |.if not FFI + |3: + |.endif + } else { + |.if not FFI + |3: + |.endif + |2: // NE: Branch to the target. + | movzx RD, PC_RD + | branchPC RD + |1: // EQ: Fallthrough to next instruction. + } + if (LJ_DUALNUM && (op == BC_ISEQV || op == BC_ISNEV || + op == BC_ISEQN || op == BC_ISNEN)) { + | jmp <9 + } else { + | ins_next + } + | + if (op == BC_ISEQV || op == BC_ISNEV) { + |5: // Either or both types are not numbers. + |.if FFI + | cmp RB, LJ_TCDATA; je ->vmeta_equal_cd + | checktp RA, LJ_TCDATA; je ->vmeta_equal_cd + |.endif + | checktp RA, RB // Compare types. + | jne <2 // Not the same type? + | cmp RB, LJ_TISPRI + | jae <1 // Same type and primitive type? + | + | // Same types and not a primitive type. Compare GCobj or pvalue. + | mov RA, [BASE+RA*8] + | mov RD, [BASE+RD*8] + | cmp RA, RD + | je <1 // Same GCobjs or pvalues? + | cmp RB, LJ_TISTABUD + | ja <2 // Different objects and not table/ud? + |.if X64 + | cmp RB, LJ_TUDATA // And not 64 bit lightuserdata. + | jb <2 + |.endif + | + | // Different tables or userdatas. Need to check __eq metamethod. + | // Field metatable must be at same offset for GCtab and GCudata! + | mov TAB:RB, TAB:RA->metatable + | test TAB:RB, TAB:RB + | jz <2 // No metatable? + | test byte TAB:RB->nomm, 1<vmeta_equal // Handle __eq metamethod. + } else { + |.if FFI + |3: + | cmp RB, LJ_TCDATA + if (LJ_DUALNUM && vk) { + | jne <9 + } else { + | jne <2 + } + | jmp ->vmeta_equal_cd + |.endif + } + break; + case BC_ISEQS: case BC_ISNES: + vk = op == BC_ISEQS; + | ins_AND // RA = src, RD = str const, JMP with RD = target + | mov RB, [BASE+RA*8+4] + | add PC, 4 + | cmp RB, LJ_TSTR; jne >3 + | mov RA, [BASE+RA*8] + | cmp RA, [KBASE+RD*4] + iseqne_test: + if (vk) { + | jne >2 + } else { + | je >1 + } + goto iseqne_end; + case BC_ISEQN: case BC_ISNEN: + vk = op == BC_ISEQN; + | ins_AD // RA = src, RD = num const, JMP with RD = target + | mov RB, [BASE+RA*8+4] + | add PC, 4 + |.if DUALNUM + | cmp RB, LJ_TISNUM; jne >7 + | cmp dword [KBASE+RD*8+4], LJ_TISNUM; jne >8 + | mov RB, dword [KBASE+RD*8] + | cmp RB, dword [BASE+RA*8] + if (vk) { + | jne >9 + } else { + | je >9 + } + | movzx RD, PC_RD + | branchPC RD + |9: + | ins_next + | + |7: // RA is not an integer. + | ja >3 + | // RA is a number. + | cmp dword [KBASE+RD*8+4], LJ_TISNUM; jb >1 + | // RA is a number, RD is an integer. + | cvtsi2sd xmm0, dword [KBASE+RD*8] + | jmp >2 + | + |8: // RA is an integer, RD is a number. + | cvtsi2sd xmm0, dword [BASE+RA*8] + | ucomisd xmm0, qword [KBASE+RD*8] + | jmp >4 + |.else + | cmp RB, LJ_TISNUM; jae >3 + |.endif + |1: + | movsd xmm0, qword [KBASE+RD*8] + |2: + | ucomisd xmm0, qword [BASE+RA*8] + |4: + goto iseqne_fp; + case BC_ISEQP: case BC_ISNEP: + vk = op == BC_ISEQP; + | ins_AND // RA = src, RD = primitive type (~), JMP with RD = target + | mov RB, [BASE+RA*8+4] + | add PC, 4 + | cmp RB, RD + if (!LJ_HASFFI) goto iseqne_test; + if (vk) { + | jne >3 + | movzx RD, PC_RD + | branchPC RD + |2: + | ins_next + |3: + | cmp RB, LJ_TCDATA; jne <2 + | jmp ->vmeta_equal_cd + } else { + | je >2 + | cmp RB, LJ_TCDATA; je ->vmeta_equal_cd + | movzx RD, PC_RD + | branchPC RD + |2: + | ins_next + } + break; + + /* -- Unary test and copy ops ------------------------------------------- */ + + case BC_ISTC: case BC_ISFC: case BC_IST: case BC_ISF: + | ins_AD // RA = dst or unused, RD = src, JMP with RD = target + | mov RB, [BASE+RD*8+4] + | add PC, 4 + | cmp RB, LJ_TISTRUECOND + if (op == BC_IST || op == BC_ISTC) { + | jae >1 + } else { + | jb >1 + } + if (op == BC_ISTC || op == BC_ISFC) { + | mov [BASE+RA*8+4], RB + | mov RB, [BASE+RD*8] + | mov [BASE+RA*8], RB + } + | movzx RD, PC_RD + | branchPC RD + |1: // Fallthrough to the next instruction. + | ins_next + break; + + case BC_ISTYPE: + | ins_AD // RA = src, RD = -type + | add RD, [BASE+RA*8+4] + | jne ->vmeta_istype + | ins_next + break; + case BC_ISNUM: + | ins_AD // RA = src, RD = -(TISNUM-1) + | checknum RA, ->vmeta_istype + | ins_next + break; + + /* -- Unary ops --------------------------------------------------------- */ + + case BC_MOV: + | ins_AD // RA = dst, RD = src + |.if X64 + | mov RBa, [BASE+RD*8] + | mov [BASE+RA*8], RBa + |.else + | mov RB, [BASE+RD*8+4] + | mov RD, [BASE+RD*8] + | mov [BASE+RA*8+4], RB + | mov [BASE+RA*8], RD + |.endif + | ins_next_ + break; + case BC_NOT: + | ins_AD // RA = dst, RD = src + | xor RB, RB + | checktp RD, LJ_TISTRUECOND + | adc RB, LJ_TTRUE + | mov [BASE+RA*8+4], RB + | ins_next + break; + case BC_UNM: + | ins_AD // RA = dst, RD = src + |.if DUALNUM + | checkint RD, >5 + | mov RB, [BASE+RD*8] + | neg RB + | jo >4 + | mov dword [BASE+RA*8+4], LJ_TISNUM + | mov dword [BASE+RA*8], RB + |9: + | ins_next + |4: + | mov dword [BASE+RA*8+4], 0x41e00000 // 2^31. + | mov dword [BASE+RA*8], 0 + | jmp <9 + |5: + | ja ->vmeta_unm + |.else + | checknum RD, ->vmeta_unm + |.endif + | movsd xmm0, qword [BASE+RD*8] + | sseconst_sign xmm1, RDa + | xorps xmm0, xmm1 + | movsd qword [BASE+RA*8], xmm0 + |.if DUALNUM + | jmp <9 + |.else + | ins_next + |.endif + break; + case BC_LEN: + | ins_AD // RA = dst, RD = src + | checkstr RD, >2 + | mov STR:RD, [BASE+RD*8] + |.if DUALNUM + | mov RD, dword STR:RD->len + |1: + | mov dword [BASE+RA*8+4], LJ_TISNUM + | mov dword [BASE+RA*8], RD + |.else + | xorps xmm0, xmm0 + | cvtsi2sd xmm0, dword STR:RD->len + |1: + | movsd qword [BASE+RA*8], xmm0 + |.endif + | ins_next + |2: + | checktab RD, ->vmeta_len + | mov TAB:FCARG1, [BASE+RD*8] +#if LJ_52 + | mov TAB:RB, TAB:FCARG1->metatable + | cmp TAB:RB, 0 + | jnz >9 + |3: +#endif + |->BC_LEN_Z: + | mov RB, BASE // Save BASE. + | call extern lj_tab_len@4 // (GCtab *t) + | // Length of table returned in eax (RD). + |.if DUALNUM + | // Nothing to do. + |.else + | cvtsi2sd xmm0, RD + |.endif + | mov BASE, RB // Restore BASE. + | movzx RA, PC_RA + | jmp <1 +#if LJ_52 + |9: // Check for __len. + | test byte TAB:RB->nomm, 1<vmeta_len // 'no __len' flag NOT set: check. +#endif + break; + + /* -- Binary ops -------------------------------------------------------- */ + + |.macro ins_arithpre, sseins, ssereg + | ins_ABC + ||vk = ((int)op - BC_ADDVN) / (BC_ADDNV-BC_ADDVN); + ||switch (vk) { + ||case 0: + | checknum RB, ->vmeta_arith_vn + | .if DUALNUM + | cmp dword [KBASE+RC*8+4], LJ_TISNUM; jae ->vmeta_arith_vn + | .endif + | movsd xmm0, qword [BASE+RB*8] + | sseins ssereg, qword [KBASE+RC*8] + || break; + ||case 1: + | checknum RB, ->vmeta_arith_nv + | .if DUALNUM + | cmp dword [KBASE+RC*8+4], LJ_TISNUM; jae ->vmeta_arith_nv + | .endif + | movsd xmm0, qword [KBASE+RC*8] + | sseins ssereg, qword [BASE+RB*8] + || break; + ||default: + | checknum RB, ->vmeta_arith_vv + | checknum RC, ->vmeta_arith_vv + | movsd xmm0, qword [BASE+RB*8] + | sseins ssereg, qword [BASE+RC*8] + || break; + ||} + |.endmacro + | + |.macro ins_arithdn, intins + | ins_ABC + ||vk = ((int)op - BC_ADDVN) / (BC_ADDNV-BC_ADDVN); + ||switch (vk) { + ||case 0: + | checkint RB, ->vmeta_arith_vn + | cmp dword [KBASE+RC*8+4], LJ_TISNUM; jne ->vmeta_arith_vn + | mov RB, [BASE+RB*8] + | intins RB, [KBASE+RC*8]; jo ->vmeta_arith_vno + || break; + ||case 1: + | checkint RB, ->vmeta_arith_nv + | cmp dword [KBASE+RC*8+4], LJ_TISNUM; jne ->vmeta_arith_nv + | mov RC, [KBASE+RC*8] + | intins RC, [BASE+RB*8]; jo ->vmeta_arith_nvo + || break; + ||default: + | checkint RB, ->vmeta_arith_vv + | checkint RC, ->vmeta_arith_vv + | mov RB, [BASE+RB*8] + | intins RB, [BASE+RC*8]; jo ->vmeta_arith_vvo + || break; + ||} + | mov dword [BASE+RA*8+4], LJ_TISNUM + ||if (vk == 1) { + | mov dword [BASE+RA*8], RC + ||} else { + | mov dword [BASE+RA*8], RB + ||} + | ins_next + |.endmacro + | + |.macro ins_arithpost + | movsd qword [BASE+RA*8], xmm0 + |.endmacro + | + |.macro ins_arith, sseins + | ins_arithpre sseins, xmm0 + | ins_arithpost + | ins_next + |.endmacro + | + |.macro ins_arith, intins, sseins + |.if DUALNUM + | ins_arithdn intins + |.else + | ins_arith, sseins + |.endif + |.endmacro + + | // RA = dst, RB = src1 or num const, RC = src2 or num const + case BC_ADDVN: case BC_ADDNV: case BC_ADDVV: + | ins_arith add, addsd + break; + case BC_SUBVN: case BC_SUBNV: case BC_SUBVV: + | ins_arith sub, subsd + break; + case BC_MULVN: case BC_MULNV: case BC_MULVV: + | ins_arith imul, mulsd + break; + case BC_DIVVN: case BC_DIVNV: case BC_DIVVV: + | ins_arith divsd + break; + case BC_MODVN: + | ins_arithpre movsd, xmm1 + |->BC_MODVN_Z: + | call ->vm_mod + | ins_arithpost + | ins_next + break; + case BC_MODNV: case BC_MODVV: + | ins_arithpre movsd, xmm1 + | jmp ->BC_MODVN_Z // Avoid 3 copies. It's slow anyway. + break; + case BC_POW: + | ins_arithpre movsd, xmm1 + | mov RB, BASE + |.if not X64 + | movsd FPARG1, xmm0 + | movsd FPARG3, xmm1 + |.endif + | call extern pow + | movzx RA, PC_RA + | mov BASE, RB + |.if X64 + | ins_arithpost + |.else + | fstp qword [BASE+RA*8] + |.endif + | ins_next + break; + + case BC_CAT: + | ins_ABC // RA = dst, RB = src_start, RC = src_end + |.if X64 + | mov L:CARG1d, SAVE_L + | mov L:CARG1d->base, BASE + | lea CARG2d, [BASE+RC*8] + | mov CARG3d, RC + | sub CARG3d, RB + |->BC_CAT_Z: + | mov L:RB, L:CARG1d + |.else + | lea RA, [BASE+RC*8] + | sub RC, RB + | mov ARG2, RA + | mov ARG3, RC + |->BC_CAT_Z: + | mov L:RB, SAVE_L + | mov ARG1, L:RB + | mov L:RB->base, BASE + |.endif + | mov SAVE_PC, PC + | call extern lj_meta_cat // (lua_State *L, TValue *top, int left) + | // NULL (finished) or TValue * (metamethod) returned in eax (RC). + | mov BASE, L:RB->base + | test RC, RC + | jnz ->vmeta_binop + | movzx RB, PC_RB // Copy result to Stk[RA] from Stk[RB]. + | movzx RA, PC_RA + |.if X64 + | mov RCa, [BASE+RB*8] + | mov [BASE+RA*8], RCa + |.else + | mov RC, [BASE+RB*8+4] + | mov RB, [BASE+RB*8] + | mov [BASE+RA*8+4], RC + | mov [BASE+RA*8], RB + |.endif + | ins_next + break; + + /* -- Constant ops ------------------------------------------------------ */ + + case BC_KSTR: + | ins_AND // RA = dst, RD = str const (~) + | mov RD, [KBASE+RD*4] + | mov dword [BASE+RA*8+4], LJ_TSTR + | mov [BASE+RA*8], RD + | ins_next + break; + case BC_KCDATA: + |.if FFI + | ins_AND // RA = dst, RD = cdata const (~) + | mov RD, [KBASE+RD*4] + | mov dword [BASE+RA*8+4], LJ_TCDATA + | mov [BASE+RA*8], RD + | ins_next + |.endif + break; + case BC_KSHORT: + | ins_AD // RA = dst, RD = signed int16 literal + |.if DUALNUM + | movsx RD, RDW + | mov dword [BASE+RA*8+4], LJ_TISNUM + | mov dword [BASE+RA*8], RD + |.else + | movsx RD, RDW // Sign-extend literal. + | cvtsi2sd xmm0, RD + | movsd qword [BASE+RA*8], xmm0 + |.endif + | ins_next + break; + case BC_KNUM: + | ins_AD // RA = dst, RD = num const + | movsd xmm0, qword [KBASE+RD*8] + | movsd qword [BASE+RA*8], xmm0 + | ins_next + break; + case BC_KPRI: + | ins_AND // RA = dst, RD = primitive type (~) + | mov [BASE+RA*8+4], RD + | ins_next + break; + case BC_KNIL: + | ins_AD // RA = dst_start, RD = dst_end + | lea RA, [BASE+RA*8+12] + | lea RD, [BASE+RD*8+4] + | mov RB, LJ_TNIL + | mov [RA-8], RB // Sets minimum 2 slots. + |1: + | mov [RA], RB + | add RA, 8 + | cmp RA, RD + | jbe <1 + | ins_next + break; + + /* -- Upvalue and function ops ------------------------------------------ */ + + case BC_UGET: + | ins_AD // RA = dst, RD = upvalue # + | mov LFUNC:RB, [BASE-8] + | mov UPVAL:RB, [LFUNC:RB+RD*4+offsetof(GCfuncL, uvptr)] + | mov RB, UPVAL:RB->v + |.if X64 + | mov RDa, [RB] + | mov [BASE+RA*8], RDa + |.else + | mov RD, [RB+4] + | mov RB, [RB] + | mov [BASE+RA*8+4], RD + | mov [BASE+RA*8], RB + |.endif + | ins_next + break; + case BC_USETV: +#define TV2MARKOFS \ + ((int32_t)offsetof(GCupval, marked)-(int32_t)offsetof(GCupval, tv)) + | ins_AD // RA = upvalue #, RD = src + | mov LFUNC:RB, [BASE-8] + | mov UPVAL:RB, [LFUNC:RB+RA*4+offsetof(GCfuncL, uvptr)] + | cmp byte UPVAL:RB->closed, 0 + | mov RB, UPVAL:RB->v + | mov RA, [BASE+RD*8] + | mov RD, [BASE+RD*8+4] + | mov [RB], RA + | mov [RB+4], RD + | jz >1 + | // Check barrier for closed upvalue. + | test byte [RB+TV2MARKOFS], LJ_GC_BLACK // isblack(uv) + | jnz >2 + |1: + | ins_next + | + |2: // Upvalue is black. Check if new value is collectable and white. + | sub RD, LJ_TISGCV + | cmp RD, LJ_TNUMX - LJ_TISGCV // tvisgcv(v) + | jbe <1 + | test byte GCOBJ:RA->gch.marked, LJ_GC_WHITES // iswhite(v) + | jz <1 + | // Crossed a write barrier. Move the barrier forward. + |.if X64 and not X64WIN + | mov FCARG2, RB + | mov RB, BASE // Save BASE. + |.else + | xchg FCARG2, RB // Save BASE (FCARG2 == BASE). + |.endif + | lea GL:FCARG1, [DISPATCH+GG_DISP2G] + | call extern lj_gc_barrieruv@8 // (global_State *g, TValue *tv) + | mov BASE, RB // Restore BASE. + | jmp <1 + break; +#undef TV2MARKOFS + case BC_USETS: + | ins_AND // RA = upvalue #, RD = str const (~) + | mov LFUNC:RB, [BASE-8] + | mov UPVAL:RB, [LFUNC:RB+RA*4+offsetof(GCfuncL, uvptr)] + | mov GCOBJ:RA, [KBASE+RD*4] + | mov RD, UPVAL:RB->v + | mov [RD], GCOBJ:RA + | mov dword [RD+4], LJ_TSTR + | test byte UPVAL:RB->marked, LJ_GC_BLACK // isblack(uv) + | jnz >2 + |1: + | ins_next + | + |2: // Check if string is white and ensure upvalue is closed. + | test byte GCOBJ:RA->gch.marked, LJ_GC_WHITES // iswhite(str) + | jz <1 + | cmp byte UPVAL:RB->closed, 0 + | jz <1 + | // Crossed a write barrier. Move the barrier forward. + | mov RB, BASE // Save BASE (FCARG2 == BASE). + | mov FCARG2, RD + | lea GL:FCARG1, [DISPATCH+GG_DISP2G] + | call extern lj_gc_barrieruv@8 // (global_State *g, TValue *tv) + | mov BASE, RB // Restore BASE. + | jmp <1 + break; + case BC_USETN: + | ins_AD // RA = upvalue #, RD = num const + | mov LFUNC:RB, [BASE-8] + | movsd xmm0, qword [KBASE+RD*8] + | mov UPVAL:RB, [LFUNC:RB+RA*4+offsetof(GCfuncL, uvptr)] + | mov RA, UPVAL:RB->v + | movsd qword [RA], xmm0 + | ins_next + break; + case BC_USETP: + | ins_AND // RA = upvalue #, RD = primitive type (~) + | mov LFUNC:RB, [BASE-8] + | mov UPVAL:RB, [LFUNC:RB+RA*4+offsetof(GCfuncL, uvptr)] + | mov RA, UPVAL:RB->v + | mov [RA+4], RD + | ins_next + break; + case BC_UCLO: + | ins_AD // RA = level, RD = target + | branchPC RD // Do this first to free RD. + | mov L:RB, SAVE_L + | cmp dword L:RB->openupval, 0 + | je >1 + | mov L:RB->base, BASE + | lea FCARG2, [BASE+RA*8] // Caveat: FCARG2 == BASE + | mov L:FCARG1, L:RB // Caveat: FCARG1 == RA + | call extern lj_func_closeuv@8 // (lua_State *L, TValue *level) + | mov BASE, L:RB->base + |1: + | ins_next + break; + + case BC_FNEW: + | ins_AND // RA = dst, RD = proto const (~) (holding function prototype) + |.if X64 + | mov L:RB, SAVE_L + | mov L:RB->base, BASE // Caveat: CARG2d/CARG3d may be BASE. + | mov CARG3d, [BASE-8] + | mov CARG2d, [KBASE+RD*4] // Fetch GCproto *. + | mov CARG1d, L:RB + |.else + | mov LFUNC:RA, [BASE-8] + | mov PROTO:RD, [KBASE+RD*4] // Fetch GCproto *. + | mov L:RB, SAVE_L + | mov ARG3, LFUNC:RA + | mov ARG2, PROTO:RD + | mov ARG1, L:RB + | mov L:RB->base, BASE + |.endif + | mov SAVE_PC, PC + | // (lua_State *L, GCproto *pt, GCfuncL *parent) + | call extern lj_func_newL_gc + | // GCfuncL * returned in eax (RC). + | mov BASE, L:RB->base + | movzx RA, PC_RA + | mov [BASE+RA*8], LFUNC:RC + | mov dword [BASE+RA*8+4], LJ_TFUNC + | ins_next + break; + + /* -- Table ops --------------------------------------------------------- */ + + case BC_TNEW: + | ins_AD // RA = dst, RD = hbits|asize + | mov L:RB, SAVE_L + | mov L:RB->base, BASE + | mov RA, [DISPATCH+DISPATCH_GL(gc.total)] + | cmp RA, [DISPATCH+DISPATCH_GL(gc.threshold)] + | mov SAVE_PC, PC + | jae >5 + |1: + |.if X64 + | mov CARG3d, RD + | and RD, 0x7ff + | shr CARG3d, 11 + |.else + | mov RA, RD + | and RD, 0x7ff + | shr RA, 11 + | mov ARG3, RA + |.endif + | cmp RD, 0x7ff + | je >3 + |2: + |.if X64 + | mov L:CARG1d, L:RB + | mov CARG2d, RD + |.else + | mov ARG1, L:RB + | mov ARG2, RD + |.endif + | call extern lj_tab_new // (lua_State *L, int32_t asize, uint32_t hbits) + | // Table * returned in eax (RC). + | mov BASE, L:RB->base + | movzx RA, PC_RA + | mov [BASE+RA*8], TAB:RC + | mov dword [BASE+RA*8+4], LJ_TTAB + | ins_next + |3: // Turn 0x7ff into 0x801. + | mov RD, 0x801 + | jmp <2 + |5: + | mov L:FCARG1, L:RB + | call extern lj_gc_step_fixtop@4 // (lua_State *L) + | movzx RD, PC_RD + | jmp <1 + break; + case BC_TDUP: + | ins_AND // RA = dst, RD = table const (~) (holding template table) + | mov L:RB, SAVE_L + | mov RA, [DISPATCH+DISPATCH_GL(gc.total)] + | mov SAVE_PC, PC + | cmp RA, [DISPATCH+DISPATCH_GL(gc.threshold)] + | mov L:RB->base, BASE + | jae >3 + |2: + | mov TAB:FCARG2, [KBASE+RD*4] // Caveat: FCARG2 == BASE + | mov L:FCARG1, L:RB // Caveat: FCARG1 == RA + | call extern lj_tab_dup@8 // (lua_State *L, Table *kt) + | // Table * returned in eax (RC). + | mov BASE, L:RB->base + | movzx RA, PC_RA + | mov [BASE+RA*8], TAB:RC + | mov dword [BASE+RA*8+4], LJ_TTAB + | ins_next + |3: + | mov L:FCARG1, L:RB + | call extern lj_gc_step_fixtop@4 // (lua_State *L) + | movzx RD, PC_RD // Need to reload RD. + | not RDa + | jmp <2 + break; + + case BC_GGET: + | ins_AND // RA = dst, RD = str const (~) + | mov LFUNC:RB, [BASE-8] + | mov TAB:RB, LFUNC:RB->env + | mov STR:RC, [KBASE+RD*4] + | jmp ->BC_TGETS_Z + break; + case BC_GSET: + | ins_AND // RA = src, RD = str const (~) + | mov LFUNC:RB, [BASE-8] + | mov TAB:RB, LFUNC:RB->env + | mov STR:RC, [KBASE+RD*4] + | jmp ->BC_TSETS_Z + break; + + case BC_TGETV: + | ins_ABC // RA = dst, RB = table, RC = key + | checktab RB, ->vmeta_tgetv + | mov TAB:RB, [BASE+RB*8] + | + | // Integer key? + |.if DUALNUM + | checkint RC, >5 + | mov RC, dword [BASE+RC*8] + |.else + | // Convert number to int and back and compare. + | checknum RC, >5 + | movsd xmm0, qword [BASE+RC*8] + | cvttsd2si RC, xmm0 + | cvtsi2sd xmm1, RC + | ucomisd xmm0, xmm1 + | jne ->vmeta_tgetv // Generic numeric key? Use fallback. + |.endif + | cmp RC, TAB:RB->asize // Takes care of unordered, too. + | jae ->vmeta_tgetv // Not in array part? Use fallback. + | shl RC, 3 + | add RC, TAB:RB->array + | cmp dword [RC+4], LJ_TNIL // Avoid overwriting RB in fastpath. + | je >2 + | // Get array slot. + |.if X64 + | mov RBa, [RC] + | mov [BASE+RA*8], RBa + |.else + | mov RB, [RC] + | mov RC, [RC+4] + | mov [BASE+RA*8], RB + | mov [BASE+RA*8+4], RC + |.endif + |1: + | ins_next + | + |2: // Check for __index if table value is nil. + | cmp dword TAB:RB->metatable, 0 // Shouldn't overwrite RA for fastpath. + | jz >3 + | mov TAB:RA, TAB:RB->metatable + | test byte TAB:RA->nomm, 1<vmeta_tgetv // 'no __index' flag NOT set: check. + | movzx RA, PC_RA // Restore RA. + |3: + | mov dword [BASE+RA*8+4], LJ_TNIL + | jmp <1 + | + |5: // String key? + | checkstr RC, ->vmeta_tgetv + | mov STR:RC, [BASE+RC*8] + | jmp ->BC_TGETS_Z + break; + case BC_TGETS: + | ins_ABC // RA = dst, RB = table, RC = str const (~) + | not RCa + | mov STR:RC, [KBASE+RC*4] + | checktab RB, ->vmeta_tgets + | mov TAB:RB, [BASE+RB*8] + |->BC_TGETS_Z: // RB = GCtab *, RC = GCstr *, refetches PC_RA. + | mov RA, TAB:RB->hmask + | and RA, STR:RC->hash + | imul RA, #NODE + | add NODE:RA, TAB:RB->node + |1: + | cmp dword NODE:RA->key.it, LJ_TSTR + | jne >4 + | cmp dword NODE:RA->key.gcr, STR:RC + | jne >4 + | // Ok, key found. Assumes: offsetof(Node, val) == 0 + | cmp dword [RA+4], LJ_TNIL // Avoid overwriting RB in fastpath. + | je >5 // Key found, but nil value? + | movzx RC, PC_RA + | // Get node value. + |.if X64 + | mov RBa, [RA] + | mov [BASE+RC*8], RBa + |.else + | mov RB, [RA] + | mov RA, [RA+4] + | mov [BASE+RC*8], RB + | mov [BASE+RC*8+4], RA + |.endif + |2: + | ins_next + | + |3: + | movzx RC, PC_RA + | mov dword [BASE+RC*8+4], LJ_TNIL + | jmp <2 + | + |4: // Follow hash chain. + | mov NODE:RA, NODE:RA->next + | test NODE:RA, NODE:RA + | jnz <1 + | // End of hash chain: key not found, nil result. + | + |5: // Check for __index if table value is nil. + | mov TAB:RA, TAB:RB->metatable + | test TAB:RA, TAB:RA + | jz <3 // No metatable: done. + | test byte TAB:RA->nomm, 1<vmeta_tgets // Caveat: preserve STR:RC. + break; + case BC_TGETB: + | ins_ABC // RA = dst, RB = table, RC = byte literal + | checktab RB, ->vmeta_tgetb + | mov TAB:RB, [BASE+RB*8] + | cmp RC, TAB:RB->asize + | jae ->vmeta_tgetb + | shl RC, 3 + | add RC, TAB:RB->array + | cmp dword [RC+4], LJ_TNIL // Avoid overwriting RB in fastpath. + | je >2 + | // Get array slot. + |.if X64 + | mov RBa, [RC] + | mov [BASE+RA*8], RBa + |.else + | mov RB, [RC] + | mov RC, [RC+4] + | mov [BASE+RA*8], RB + | mov [BASE+RA*8+4], RC + |.endif + |1: + | ins_next + | + |2: // Check for __index if table value is nil. + | cmp dword TAB:RB->metatable, 0 // Shouldn't overwrite RA for fastpath. + | jz >3 + | mov TAB:RA, TAB:RB->metatable + | test byte TAB:RA->nomm, 1<vmeta_tgetb // 'no __index' flag NOT set: check. + | movzx RA, PC_RA // Restore RA. + |3: + | mov dword [BASE+RA*8+4], LJ_TNIL + | jmp <1 + break; + case BC_TGETR: + | ins_ABC // RA = dst, RB = table, RC = key + | mov TAB:RB, [BASE+RB*8] + |.if DUALNUM + | mov RC, dword [BASE+RC*8] + |.else + | cvttsd2si RC, qword [BASE+RC*8] + |.endif + | cmp RC, TAB:RB->asize + | jae ->vmeta_tgetr // Not in array part? Use fallback. + | shl RC, 3 + | add RC, TAB:RB->array + | // Get array slot. + |->BC_TGETR_Z: + |.if X64 + | mov RBa, [RC] + | mov [BASE+RA*8], RBa + |.else + | mov RB, [RC] + | mov RC, [RC+4] + | mov [BASE+RA*8], RB + | mov [BASE+RA*8+4], RC + |.endif + |->BC_TGETR2_Z: + | ins_next + break; + + case BC_TSETV: + | ins_ABC // RA = src, RB = table, RC = key + | checktab RB, ->vmeta_tsetv + | mov TAB:RB, [BASE+RB*8] + | + | // Integer key? + |.if DUALNUM + | checkint RC, >5 + | mov RC, dword [BASE+RC*8] + |.else + | // Convert number to int and back and compare. + | checknum RC, >5 + | movsd xmm0, qword [BASE+RC*8] + | cvttsd2si RC, xmm0 + | cvtsi2sd xmm1, RC + | ucomisd xmm0, xmm1 + | jne ->vmeta_tsetv // Generic numeric key? Use fallback. + |.endif + | cmp RC, TAB:RB->asize // Takes care of unordered, too. + | jae ->vmeta_tsetv + | shl RC, 3 + | add RC, TAB:RB->array + | cmp dword [RC+4], LJ_TNIL + | je >3 // Previous value is nil? + |1: + | test byte TAB:RB->marked, LJ_GC_BLACK // isblack(table) + | jnz >7 + |2: // Set array slot. + |.if X64 + | mov RBa, [BASE+RA*8] + | mov [RC], RBa + |.else + | mov RB, [BASE+RA*8+4] + | mov RA, [BASE+RA*8] + | mov [RC+4], RB + | mov [RC], RA + |.endif + | ins_next + | + |3: // Check for __newindex if previous value is nil. + | cmp dword TAB:RB->metatable, 0 // Shouldn't overwrite RA for fastpath. + | jz <1 + | mov TAB:RA, TAB:RB->metatable + | test byte TAB:RA->nomm, 1<vmeta_tsetv // 'no __newindex' flag NOT set: check. + | movzx RA, PC_RA // Restore RA. + | jmp <1 + | + |5: // String key? + | checkstr RC, ->vmeta_tsetv + | mov STR:RC, [BASE+RC*8] + | jmp ->BC_TSETS_Z + | + |7: // Possible table write barrier for the value. Skip valiswhite check. + | barrierback TAB:RB, RA + | movzx RA, PC_RA // Restore RA. + | jmp <2 + break; + case BC_TSETS: + | ins_ABC // RA = src, RB = table, RC = str const (~) + | not RCa + | mov STR:RC, [KBASE+RC*4] + | checktab RB, ->vmeta_tsets + | mov TAB:RB, [BASE+RB*8] + |->BC_TSETS_Z: // RB = GCtab *, RC = GCstr *, refetches PC_RA. + | mov RA, TAB:RB->hmask + | and RA, STR:RC->hash + | imul RA, #NODE + | mov byte TAB:RB->nomm, 0 // Clear metamethod cache. + | add NODE:RA, TAB:RB->node + |1: + | cmp dword NODE:RA->key.it, LJ_TSTR + | jne >5 + | cmp dword NODE:RA->key.gcr, STR:RC + | jne >5 + | // Ok, key found. Assumes: offsetof(Node, val) == 0 + | cmp dword [RA+4], LJ_TNIL + | je >4 // Previous value is nil? + |2: + | test byte TAB:RB->marked, LJ_GC_BLACK // isblack(table) + | jnz >7 + |3: // Set node value. + | movzx RC, PC_RA + |.if X64 + | mov RBa, [BASE+RC*8] + | mov [RA], RBa + |.else + | mov RB, [BASE+RC*8+4] + | mov RC, [BASE+RC*8] + | mov [RA+4], RB + | mov [RA], RC + |.endif + | ins_next + | + |4: // Check for __newindex if previous value is nil. + | cmp dword TAB:RB->metatable, 0 // Shouldn't overwrite RA for fastpath. + | jz <2 + | mov TMP1, RA // Save RA. + | mov TAB:RA, TAB:RB->metatable + | test byte TAB:RA->nomm, 1<vmeta_tsets // 'no __newindex' flag NOT set: check. + | mov RA, TMP1 // Restore RA. + | jmp <2 + | + |5: // Follow hash chain. + | mov NODE:RA, NODE:RA->next + | test NODE:RA, NODE:RA + | jnz <1 + | // End of hash chain: key not found, add a new one. + | + | // But check for __newindex first. + | mov TAB:RA, TAB:RB->metatable + | test TAB:RA, TAB:RA + | jz >6 // No metatable: continue. + | test byte TAB:RA->nomm, 1<vmeta_tsets // 'no __newindex' flag NOT set: check. + |6: + | mov TMP1, STR:RC + | mov TMP2, LJ_TSTR + | mov TMP3, TAB:RB // Save TAB:RB for us. + |.if X64 + | mov L:CARG1d, SAVE_L + | mov L:CARG1d->base, BASE + | lea CARG3, TMP1 + | mov CARG2d, TAB:RB + | mov L:RB, L:CARG1d + |.else + | lea RC, TMP1 // Store temp. TValue in TMP1/TMP2. + | mov ARG2, TAB:RB + | mov L:RB, SAVE_L + | mov ARG3, RC + | mov ARG1, L:RB + | mov L:RB->base, BASE + |.endif + | mov SAVE_PC, PC + | call extern lj_tab_newkey // (lua_State *L, GCtab *t, TValue *k) + | // Handles write barrier for the new key. TValue * returned in eax (RC). + | mov BASE, L:RB->base + | mov TAB:RB, TMP3 // Need TAB:RB for barrier. + | mov RA, eax + | jmp <2 // Must check write barrier for value. + | + |7: // Possible table write barrier for the value. Skip valiswhite check. + | barrierback TAB:RB, RC // Destroys STR:RC. + | jmp <3 + break; + case BC_TSETB: + | ins_ABC // RA = src, RB = table, RC = byte literal + | checktab RB, ->vmeta_tsetb + | mov TAB:RB, [BASE+RB*8] + | cmp RC, TAB:RB->asize + | jae ->vmeta_tsetb + | shl RC, 3 + | add RC, TAB:RB->array + | cmp dword [RC+4], LJ_TNIL + | je >3 // Previous value is nil? + |1: + | test byte TAB:RB->marked, LJ_GC_BLACK // isblack(table) + | jnz >7 + |2: // Set array slot. + |.if X64 + | mov RAa, [BASE+RA*8] + | mov [RC], RAa + |.else + | mov RB, [BASE+RA*8+4] + | mov RA, [BASE+RA*8] + | mov [RC+4], RB + | mov [RC], RA + |.endif + | ins_next + | + |3: // Check for __newindex if previous value is nil. + | cmp dword TAB:RB->metatable, 0 // Shouldn't overwrite RA for fastpath. + | jz <1 + | mov TAB:RA, TAB:RB->metatable + | test byte TAB:RA->nomm, 1<vmeta_tsetb // 'no __newindex' flag NOT set: check. + | movzx RA, PC_RA // Restore RA. + | jmp <1 + | + |7: // Possible table write barrier for the value. Skip valiswhite check. + | barrierback TAB:RB, RA + | movzx RA, PC_RA // Restore RA. + | jmp <2 + break; + case BC_TSETR: + | ins_ABC // RA = src, RB = table, RC = key + | mov TAB:RB, [BASE+RB*8] + |.if DUALNUM + | mov RC, dword [BASE+RC*8] + |.else + | cvttsd2si RC, qword [BASE+RC*8] + |.endif + | test byte TAB:RB->marked, LJ_GC_BLACK // isblack(table) + | jnz >7 + |2: + | cmp RC, TAB:RB->asize + | jae ->vmeta_tsetr + | shl RC, 3 + | add RC, TAB:RB->array + | // Set array slot. + |->BC_TSETR_Z: + |.if X64 + | mov RBa, [BASE+RA*8] + | mov [RC], RBa + |.else + | mov RB, [BASE+RA*8+4] + | mov RA, [BASE+RA*8] + | mov [RC+4], RB + | mov [RC], RA + |.endif + | ins_next + | + |7: // Possible table write barrier for the value. Skip valiswhite check. + | barrierback TAB:RB, RA + | movzx RA, PC_RA // Restore RA. + | jmp <2 + break; + + case BC_TSETM: + | ins_AD // RA = base (table at base-1), RD = num const (start index) + | mov TMP1, KBASE // Need one more free register. + | mov KBASE, dword [KBASE+RD*8] // Integer constant is in lo-word. + |1: + | lea RA, [BASE+RA*8] + | mov TAB:RB, [RA-8] // Guaranteed to be a table. + | test byte TAB:RB->marked, LJ_GC_BLACK // isblack(table) + | jnz >7 + |2: + | mov RD, MULTRES + | sub RD, 1 + | jz >4 // Nothing to copy? + | add RD, KBASE // Compute needed size. + | cmp RD, TAB:RB->asize + | ja >5 // Doesn't fit into array part? + | sub RD, KBASE + | shl KBASE, 3 + | add KBASE, TAB:RB->array + |3: // Copy result slots to table. + |.if X64 + | mov RBa, [RA] + | add RA, 8 + | mov [KBASE], RBa + |.else + | mov RB, [RA] + | mov [KBASE], RB + | mov RB, [RA+4] + | add RA, 8 + | mov [KBASE+4], RB + |.endif + | add KBASE, 8 + | sub RD, 1 + | jnz <3 + |4: + | mov KBASE, TMP1 + | ins_next + | + |5: // Need to resize array part. + |.if X64 + | mov L:CARG1d, SAVE_L + | mov L:CARG1d->base, BASE // Caveat: CARG2d/CARG3d may be BASE. + | mov CARG2d, TAB:RB + | mov CARG3d, RD + | mov L:RB, L:CARG1d + |.else + | mov ARG2, TAB:RB + | mov L:RB, SAVE_L + | mov L:RB->base, BASE + | mov ARG3, RD + | mov ARG1, L:RB + |.endif + | mov SAVE_PC, PC + | call extern lj_tab_reasize // (lua_State *L, GCtab *t, int nasize) + | mov BASE, L:RB->base + | movzx RA, PC_RA // Restore RA. + | jmp <1 // Retry. + | + |7: // Possible table write barrier for any value. Skip valiswhite check. + | barrierback TAB:RB, RD + | jmp <2 + break; + + /* -- Calls and vararg handling ----------------------------------------- */ + + case BC_CALL: case BC_CALLM: + | ins_A_C // RA = base, (RB = nresults+1,) RC = nargs+1 | extra_nargs + if (op == BC_CALLM) { + | add NARGS:RD, MULTRES + } + | cmp dword [BASE+RA*8+4], LJ_TFUNC + | mov LFUNC:RB, [BASE+RA*8] + | jne ->vmeta_call_ra + | lea BASE, [BASE+RA*8+8] + | ins_call + break; + + case BC_CALLMT: + | ins_AD // RA = base, RD = extra_nargs + | add NARGS:RD, MULTRES + | // Fall through. Assumes BC_CALLT follows and ins_AD is a no-op. + break; + case BC_CALLT: + | ins_AD // RA = base, RD = nargs+1 + | lea RA, [BASE+RA*8+8] + | mov KBASE, BASE // Use KBASE for move + vmeta_call hint. + | mov LFUNC:RB, [RA-8] + | cmp dword [RA-4], LJ_TFUNC + | jne ->vmeta_call + |->BC_CALLT_Z: + | mov PC, [BASE-4] + | test PC, FRAME_TYPE + | jnz >7 + |1: + | mov [BASE-8], LFUNC:RB // Copy function down, reloaded below. + | mov MULTRES, NARGS:RD + | sub NARGS:RD, 1 + | jz >3 + |2: // Move args down. + |.if X64 + | mov RBa, [RA] + | add RA, 8 + | mov [KBASE], RBa + |.else + | mov RB, [RA] + | mov [KBASE], RB + | mov RB, [RA+4] + | add RA, 8 + | mov [KBASE+4], RB + |.endif + | add KBASE, 8 + | sub NARGS:RD, 1 + | jnz <2 + | + | mov LFUNC:RB, [BASE-8] + |3: + | mov NARGS:RD, MULTRES + | cmp byte LFUNC:RB->ffid, 1 // (> FF_C) Calling a fast function? + | ja >5 + |4: + | ins_callt + | + |5: // Tailcall to a fast function. + | test PC, FRAME_TYPE // Lua frame below? + | jnz <4 + | movzx RA, PC_RA + | not RAa + | mov LFUNC:KBASE, [BASE+RA*8-8] // Need to prepare KBASE. + | mov KBASE, LFUNC:KBASE->pc + | mov KBASE, [KBASE+PC2PROTO(k)] + | jmp <4 + | + |7: // Tailcall from a vararg function. + | sub PC, FRAME_VARG + | test PC, FRAME_TYPEP + | jnz >8 // Vararg frame below? + | sub BASE, PC // Need to relocate BASE/KBASE down. + | mov KBASE, BASE + | mov PC, [BASE-4] + | jmp <1 + |8: + | add PC, FRAME_VARG + | jmp <1 + break; + + case BC_ITERC: + | ins_A // RA = base, (RB = nresults+1,) RC = nargs+1 (2+1) + | lea RA, [BASE+RA*8+8] // fb = base+1 + |.if X64 + | mov RBa, [RA-24] // Copy state. fb[0] = fb[-3]. + | mov RCa, [RA-16] // Copy control var. fb[1] = fb[-2]. + | mov [RA], RBa + | mov [RA+8], RCa + |.else + | mov RB, [RA-24] // Copy state. fb[0] = fb[-3]. + | mov RC, [RA-20] + | mov [RA], RB + | mov [RA+4], RC + | mov RB, [RA-16] // Copy control var. fb[1] = fb[-2]. + | mov RC, [RA-12] + | mov [RA+8], RB + | mov [RA+12], RC + |.endif + | mov LFUNC:RB, [RA-32] // Copy callable. fb[-1] = fb[-4] + | mov RC, [RA-28] + | mov [RA-8], LFUNC:RB + | mov [RA-4], RC + | cmp RC, LJ_TFUNC // Handle like a regular 2-arg call. + | mov NARGS:RD, 2+1 + | jne ->vmeta_call + | mov BASE, RA + | ins_call + break; + + case BC_ITERN: + | ins_A // RA = base, (RB = nresults+1, RC = nargs+1 (2+1)) + |.if JIT + | // NYI: add hotloop, record BC_ITERN. + |.endif + | mov TMP1, KBASE // Need two more free registers. + | mov TMP2, DISPATCH + | mov TAB:RB, [BASE+RA*8-16] + | mov RC, [BASE+RA*8-8] // Get index from control var. + | mov DISPATCH, TAB:RB->asize + | add PC, 4 + | mov KBASE, TAB:RB->array + |1: // Traverse array part. + | cmp RC, DISPATCH; jae >5 // Index points after array part? + | cmp dword [KBASE+RC*8+4], LJ_TNIL; je >4 + |.if DUALNUM + | mov dword [BASE+RA*8+4], LJ_TISNUM + | mov dword [BASE+RA*8], RC + |.else + | cvtsi2sd xmm0, RC + |.endif + | // Copy array slot to returned value. + |.if X64 + | mov RBa, [KBASE+RC*8] + | mov [BASE+RA*8+8], RBa + |.else + | mov RB, [KBASE+RC*8+4] + | mov [BASE+RA*8+12], RB + | mov RB, [KBASE+RC*8] + | mov [BASE+RA*8+8], RB + |.endif + | add RC, 1 + | // Return array index as a numeric key. + |.if DUALNUM + | // See above. + |.else + | movsd qword [BASE+RA*8], xmm0 + |.endif + | mov [BASE+RA*8-8], RC // Update control var. + |2: + | movzx RD, PC_RD // Get target from ITERL. + | branchPC RD + |3: + | mov DISPATCH, TMP2 + | mov KBASE, TMP1 + | ins_next + | + |4: // Skip holes in array part. + | add RC, 1 + | jmp <1 + | + |5: // Traverse hash part. + | sub RC, DISPATCH + |6: + | cmp RC, TAB:RB->hmask; ja <3 // End of iteration? Branch to ITERL+1. + | imul KBASE, RC, #NODE + | add NODE:KBASE, TAB:RB->node + | cmp dword NODE:KBASE->val.it, LJ_TNIL; je >7 + | lea DISPATCH, [RC+DISPATCH+1] + | // Copy key and value from hash slot. + |.if X64 + | mov RBa, NODE:KBASE->key + | mov RCa, NODE:KBASE->val + | mov [BASE+RA*8], RBa + | mov [BASE+RA*8+8], RCa + |.else + | mov RB, NODE:KBASE->key.gcr + | mov RC, NODE:KBASE->key.it + | mov [BASE+RA*8], RB + | mov [BASE+RA*8+4], RC + | mov RB, NODE:KBASE->val.gcr + | mov RC, NODE:KBASE->val.it + | mov [BASE+RA*8+8], RB + | mov [BASE+RA*8+12], RC + |.endif + | mov [BASE+RA*8-8], DISPATCH + | jmp <2 + | + |7: // Skip holes in hash part. + | add RC, 1 + | jmp <6 + break; + + case BC_ISNEXT: + | ins_AD // RA = base, RD = target (points to ITERN) + | cmp dword [BASE+RA*8-20], LJ_TFUNC; jne >5 + | mov CFUNC:RB, [BASE+RA*8-24] + | cmp dword [BASE+RA*8-12], LJ_TTAB; jne >5 + | cmp dword [BASE+RA*8-4], LJ_TNIL; jne >5 + | cmp byte CFUNC:RB->ffid, FF_next_N; jne >5 + | branchPC RD + | mov dword [BASE+RA*8-8], 0 // Initialize control var. + | mov dword [BASE+RA*8-4], 0xfffe7fff + |1: + | ins_next + |5: // Despecialize bytecode if any of the checks fail. + | mov PC_OP, BC_JMP + | branchPC RD + | mov byte [PC], BC_ITERC + | jmp <1 + break; + + case BC_VARG: + | ins_ABC // RA = base, RB = nresults+1, RC = numparams + | mov TMP1, KBASE // Need one more free register. + | lea KBASE, [BASE+RC*8+(8+FRAME_VARG)] + | lea RA, [BASE+RA*8] + | sub KBASE, [BASE-4] + | // Note: KBASE may now be even _above_ BASE if nargs was < numparams. + | test RB, RB + | jz >5 // Copy all varargs? + | lea RB, [RA+RB*8-8] + | cmp KBASE, BASE // No vararg slots? + | jnb >2 + |1: // Copy vararg slots to destination slots. + |.if X64 + | mov RCa, [KBASE-8] + | add KBASE, 8 + | mov [RA], RCa + |.else + | mov RC, [KBASE-8] + | mov [RA], RC + | mov RC, [KBASE-4] + | add KBASE, 8 + | mov [RA+4], RC + |.endif + | add RA, 8 + | cmp RA, RB // All destination slots filled? + | jnb >3 + | cmp KBASE, BASE // No more vararg slots? + | jb <1 + |2: // Fill up remainder with nil. + | mov dword [RA+4], LJ_TNIL + | add RA, 8 + | cmp RA, RB + | jb <2 + |3: + | mov KBASE, TMP1 + | ins_next + | + |5: // Copy all varargs. + | mov MULTRES, 1 // MULTRES = 0+1 + | mov RC, BASE + | sub RC, KBASE + | jbe <3 // No vararg slots? + | mov RB, RC + | shr RB, 3 + | add RB, 1 + | mov MULTRES, RB // MULTRES = #varargs+1 + | mov L:RB, SAVE_L + | add RC, RA + | cmp RC, L:RB->maxstack + | ja >7 // Need to grow stack? + |6: // Copy all vararg slots. + |.if X64 + | mov RCa, [KBASE-8] + | add KBASE, 8 + | mov [RA], RCa + |.else + | mov RC, [KBASE-8] + | mov [RA], RC + | mov RC, [KBASE-4] + | add KBASE, 8 + | mov [RA+4], RC + |.endif + | add RA, 8 + | cmp KBASE, BASE // No more vararg slots? + | jb <6 + | jmp <3 + | + |7: // Grow stack for varargs. + | mov L:RB->base, BASE + | mov L:RB->top, RA + | mov SAVE_PC, PC + | sub KBASE, BASE // Need delta, because BASE may change. + | mov FCARG2, MULTRES + | sub FCARG2, 1 + | mov FCARG1, L:RB + | call extern lj_state_growstack@8 // (lua_State *L, int n) + | mov BASE, L:RB->base + | mov RA, L:RB->top + | add KBASE, BASE + | jmp <6 + break; + + /* -- Returns ----------------------------------------------------------- */ + + case BC_RETM: + | ins_AD // RA = results, RD = extra_nresults + | add RD, MULTRES // MULTRES >=1, so RD >=1. + | // Fall through. Assumes BC_RET follows and ins_AD is a no-op. + break; + + case BC_RET: case BC_RET0: case BC_RET1: + | ins_AD // RA = results, RD = nresults+1 + if (op != BC_RET0) { + | shl RA, 3 + } + |1: + | mov PC, [BASE-4] + | mov MULTRES, RD // Save nresults+1. + | test PC, FRAME_TYPE // Check frame type marker. + | jnz >7 // Not returning to a fixarg Lua func? + switch (op) { + case BC_RET: + |->BC_RET_Z: + | mov KBASE, BASE // Use KBASE for result move. + | sub RD, 1 + | jz >3 + |2: // Move results down. + |.if X64 + | mov RBa, [KBASE+RA] + | mov [KBASE-8], RBa + |.else + | mov RB, [KBASE+RA] + | mov [KBASE-8], RB + | mov RB, [KBASE+RA+4] + | mov [KBASE-4], RB + |.endif + | add KBASE, 8 + | sub RD, 1 + | jnz <2 + |3: + | mov RD, MULTRES // Note: MULTRES may be >255. + | movzx RB, PC_RB // So cannot compare with RDL! + |5: + | cmp RB, RD // More results expected? + | ja >6 + break; + case BC_RET1: + |.if X64 + | mov RBa, [BASE+RA] + | mov [BASE-8], RBa + |.else + | mov RB, [BASE+RA+4] + | mov [BASE-4], RB + | mov RB, [BASE+RA] + | mov [BASE-8], RB + |.endif + /* fallthrough */ + case BC_RET0: + |5: + | cmp PC_RB, RDL // More results expected? + | ja >6 + default: + break; + } + | movzx RA, PC_RA + | not RAa // Note: ~RA = -(RA+1) + | lea BASE, [BASE+RA*8] // base = base - (RA+1)*8 + | mov LFUNC:KBASE, [BASE-8] + | mov KBASE, LFUNC:KBASE->pc + | mov KBASE, [KBASE+PC2PROTO(k)] + | ins_next + | + |6: // Fill up results with nil. + if (op == BC_RET) { + | mov dword [KBASE-4], LJ_TNIL // Note: relies on shifted base. + | add KBASE, 8 + } else { + | mov dword [BASE+RD*8-12], LJ_TNIL + } + | add RD, 1 + | jmp <5 + | + |7: // Non-standard return case. + | lea RB, [PC-FRAME_VARG] + | test RB, FRAME_TYPEP + | jnz ->vm_return + | // Return from vararg function: relocate BASE down and RA up. + | sub BASE, RB + if (op != BC_RET0) { + | add RA, RB + } + | jmp <1 + break; + + /* -- Loops and branches ------------------------------------------------ */ + + |.define FOR_IDX, [RA]; .define FOR_TIDX, dword [RA+4] + |.define FOR_STOP, [RA+8]; .define FOR_TSTOP, dword [RA+12] + |.define FOR_STEP, [RA+16]; .define FOR_TSTEP, dword [RA+20] + |.define FOR_EXT, [RA+24]; .define FOR_TEXT, dword [RA+28] + + case BC_FORL: + |.if JIT + | hotloop RB + |.endif + | // Fall through. Assumes BC_IFORL follows and ins_AJ is a no-op. + break; + + case BC_JFORI: + case BC_JFORL: +#if !LJ_HASJIT + break; +#endif + case BC_FORI: + case BC_IFORL: + vk = (op == BC_IFORL || op == BC_JFORL); + | ins_AJ // RA = base, RD = target (after end of loop or start of loop) + | lea RA, [BASE+RA*8] + if (LJ_DUALNUM) { + | cmp FOR_TIDX, LJ_TISNUM; jne >9 + if (!vk) { + | cmp FOR_TSTOP, LJ_TISNUM; jne ->vmeta_for + | cmp FOR_TSTEP, LJ_TISNUM; jne ->vmeta_for + | mov RB, dword FOR_IDX + | cmp dword FOR_STEP, 0; jl >5 + } else { +#ifdef LUA_USE_ASSERT + | cmp FOR_TSTOP, LJ_TISNUM; jne ->assert_bad_for_arg_type + | cmp FOR_TSTEP, LJ_TISNUM; jne ->assert_bad_for_arg_type +#endif + | mov RB, dword FOR_STEP + | test RB, RB; js >5 + | add RB, dword FOR_IDX; jo >1 + | mov dword FOR_IDX, RB + } + | cmp RB, dword FOR_STOP + | mov FOR_TEXT, LJ_TISNUM + | mov dword FOR_EXT, RB + if (op == BC_FORI) { + | jle >7 + |1: + |6: + | branchPC RD + } else if (op == BC_JFORI) { + | branchPC RD + | movzx RD, PC_RD + | jle =>BC_JLOOP + |1: + |6: + } else if (op == BC_IFORL) { + | jg >7 + |6: + | branchPC RD + |1: + } else { + | jle =>BC_JLOOP + |1: + |6: + } + |7: + | ins_next + | + |5: // Invert check for negative step. + if (vk) { + | add RB, dword FOR_IDX; jo <1 + | mov dword FOR_IDX, RB + } + | cmp RB, dword FOR_STOP + | mov FOR_TEXT, LJ_TISNUM + | mov dword FOR_EXT, RB + if (op == BC_FORI) { + | jge <7 + } else if (op == BC_JFORI) { + | branchPC RD + | movzx RD, PC_RD + | jge =>BC_JLOOP + } else if (op == BC_IFORL) { + | jl <7 + } else { + | jge =>BC_JLOOP + } + | jmp <6 + |9: // Fallback to FP variant. + } else if (!vk) { + | cmp FOR_TIDX, LJ_TISNUM + } + if (!vk) { + | jae ->vmeta_for + | cmp FOR_TSTOP, LJ_TISNUM; jae ->vmeta_for + } else { +#ifdef LUA_USE_ASSERT + | cmp FOR_TSTOP, LJ_TISNUM; jae ->assert_bad_for_arg_type + | cmp FOR_TSTEP, LJ_TISNUM; jae ->assert_bad_for_arg_type +#endif + } + | mov RB, FOR_TSTEP // Load type/hiword of for step. + if (!vk) { + | cmp RB, LJ_TISNUM; jae ->vmeta_for + } + | movsd xmm0, qword FOR_IDX + | movsd xmm1, qword FOR_STOP + if (vk) { + | addsd xmm0, qword FOR_STEP + | movsd qword FOR_IDX, xmm0 + | test RB, RB; js >3 + } else { + | jl >3 + } + | ucomisd xmm1, xmm0 + |1: + | movsd qword FOR_EXT, xmm0 + if (op == BC_FORI) { + |.if DUALNUM + | jnb <7 + |.else + | jnb >2 + | branchPC RD + |.endif + } else if (op == BC_JFORI) { + | branchPC RD + | movzx RD, PC_RD + | jnb =>BC_JLOOP + } else if (op == BC_IFORL) { + |.if DUALNUM + | jb <7 + |.else + | jb >2 + | branchPC RD + |.endif + } else { + | jnb =>BC_JLOOP + } + |.if DUALNUM + | jmp <6 + |.else + |2: + | ins_next + |.endif + | + |3: // Invert comparison if step is negative. + | ucomisd xmm0, xmm1 + | jmp <1 + break; + + case BC_ITERL: + |.if JIT + | hotloop RB + |.endif + | // Fall through. Assumes BC_IITERL follows and ins_AJ is a no-op. + break; + + case BC_JITERL: +#if !LJ_HASJIT + break; +#endif + case BC_IITERL: + | ins_AJ // RA = base, RD = target + | lea RA, [BASE+RA*8] + | mov RB, [RA+4] + | cmp RB, LJ_TNIL; je >1 // Stop if iterator returned nil. + if (op == BC_JITERL) { + | mov [RA-4], RB + | mov RB, [RA] + | mov [RA-8], RB + | jmp =>BC_JLOOP + } else { + | branchPC RD // Otherwise save control var + branch. + | mov RD, [RA] + | mov [RA-4], RB + | mov [RA-8], RD + } + |1: + | ins_next + break; + + case BC_LOOP: + | ins_A // RA = base, RD = target (loop extent) + | // Note: RA/RD is only used by trace recorder to determine scope/extent + | // This opcode does NOT jump, it's only purpose is to detect a hot loop. + |.if JIT + | hotloop RB + |.endif + | // Fall through. Assumes BC_ILOOP follows and ins_A is a no-op. + break; + + case BC_ILOOP: + | ins_A // RA = base, RD = target (loop extent) + | ins_next + break; + + case BC_JLOOP: + |.if JIT + | ins_AD // RA = base (ignored), RD = traceno + | mov RA, [DISPATCH+DISPATCH_J(trace)] + | mov TRACE:RD, [RA+RD*4] + | mov RDa, TRACE:RD->mcode + | mov L:RB, SAVE_L + | mov [DISPATCH+DISPATCH_GL(jit_base)], BASE + | mov [DISPATCH+DISPATCH_GL(tmpbuf.L)], L:RB + | // Save additional callee-save registers only used in compiled code. + |.if X64WIN + | mov TMPQ, r12 + | mov TMPa, r13 + | mov CSAVE_4, r14 + | mov CSAVE_3, r15 + | mov RAa, rsp + | sub rsp, 9*16+4*8 + | movdqa [RAa], xmm6 + | movdqa [RAa-1*16], xmm7 + | movdqa [RAa-2*16], xmm8 + | movdqa [RAa-3*16], xmm9 + | movdqa [RAa-4*16], xmm10 + | movdqa [RAa-5*16], xmm11 + | movdqa [RAa-6*16], xmm12 + | movdqa [RAa-7*16], xmm13 + | movdqa [RAa-8*16], xmm14 + | movdqa [RAa-9*16], xmm15 + |.elif X64 + | mov TMPQ, r12 + | mov TMPa, r13 + | sub rsp, 16 + |.endif + | jmp RDa + |.endif + break; + + case BC_JMP: + | ins_AJ // RA = unused, RD = target + | branchPC RD + | ins_next + break; + + /* -- Function headers -------------------------------------------------- */ + + /* + ** Reminder: A function may be called with func/args above L->maxstack, + ** i.e. occupying EXTRA_STACK slots. And vmeta_call may add one extra slot, + ** too. This means all FUNC* ops (including fast functions) must check + ** for stack overflow _before_ adding more slots! + */ + + case BC_FUNCF: + |.if JIT + | hotcall RB + |.endif + case BC_FUNCV: /* NYI: compiled vararg functions. */ + | // Fall through. Assumes BC_IFUNCF/BC_IFUNCV follow and ins_AD is a no-op. + break; + + case BC_JFUNCF: +#if !LJ_HASJIT + break; +#endif + case BC_IFUNCF: + | ins_AD // BASE = new base, RA = framesize, RD = nargs+1 + | mov KBASE, [PC-4+PC2PROTO(k)] + | mov L:RB, SAVE_L + | lea RA, [BASE+RA*8] // Top of frame. + | cmp RA, L:RB->maxstack + | ja ->vm_growstack_f + | movzx RA, byte [PC-4+PC2PROTO(numparams)] + | cmp NARGS:RD, RA // Check for missing parameters. + | jbe >3 + |2: + if (op == BC_JFUNCF) { + | movzx RD, PC_RD + | jmp =>BC_JLOOP + } else { + | ins_next + } + | + |3: // Clear missing parameters. + | mov dword [BASE+NARGS:RD*8-4], LJ_TNIL + | add NARGS:RD, 1 + | cmp NARGS:RD, RA + | jbe <3 + | jmp <2 + break; + + case BC_JFUNCV: +#if !LJ_HASJIT + break; +#endif + | int3 // NYI: compiled vararg functions + break; /* NYI: compiled vararg functions. */ + + case BC_IFUNCV: + | ins_AD // BASE = new base, RA = framesize, RD = nargs+1 + | lea RB, [NARGS:RD*8+FRAME_VARG] + | lea RD, [BASE+NARGS:RD*8] + | mov LFUNC:KBASE, [BASE-8] + | mov [RD-4], RB // Store delta + FRAME_VARG. + | mov [RD-8], LFUNC:KBASE // Store copy of LFUNC. + | mov L:RB, SAVE_L + | lea RA, [RD+RA*8] + | cmp RA, L:RB->maxstack + | ja ->vm_growstack_v // Need to grow stack. + | mov RA, BASE + | mov BASE, RD + | movzx RB, byte [PC-4+PC2PROTO(numparams)] + | test RB, RB + | jz >2 + |1: // Copy fixarg slots up to new frame. + | add RA, 8 + | cmp RA, BASE + | jnb >3 // Less args than parameters? + | mov KBASE, [RA-8] + | mov [RD], KBASE + | mov KBASE, [RA-4] + | mov [RD+4], KBASE + | add RD, 8 + | mov dword [RA-4], LJ_TNIL // Clear old fixarg slot (help the GC). + | sub RB, 1 + | jnz <1 + |2: + if (op == BC_JFUNCV) { + | movzx RD, PC_RD + | jmp =>BC_JLOOP + } else { + | mov KBASE, [PC-4+PC2PROTO(k)] + | ins_next + } + | + |3: // Clear missing parameters. + | mov dword [RD+4], LJ_TNIL + | add RD, 8 + | sub RB, 1 + | jnz <3 + | jmp <2 + break; + + case BC_FUNCC: + case BC_FUNCCW: + | ins_AD // BASE = new base, RA = ins RA|RD (unused), RD = nargs+1 + | mov CFUNC:RB, [BASE-8] + | mov KBASEa, CFUNC:RB->f + | mov L:RB, SAVE_L + | lea RD, [BASE+NARGS:RD*8-8] + | mov L:RB->base, BASE + | lea RA, [RD+8*LUA_MINSTACK] + | cmp RA, L:RB->maxstack + | mov L:RB->top, RD + if (op == BC_FUNCC) { + |.if X64 + | mov CARG1d, L:RB // Caveat: CARG1d may be RA. + |.else + | mov ARG1, L:RB + |.endif + } else { + |.if X64 + | mov CARG2, KBASEa + | mov CARG1d, L:RB // Caveat: CARG1d may be RA. + |.else + | mov ARG2, KBASEa + | mov ARG1, L:RB + |.endif + } + | ja ->vm_growstack_c // Need to grow stack. + | set_vmstate C + if (op == BC_FUNCC) { + | call KBASEa // (lua_State *L) + } else { + | // (lua_State *L, lua_CFunction f) + | call aword [DISPATCH+DISPATCH_GL(wrapf)] + } + | // nresults returned in eax (RD). + | mov BASE, L:RB->base + | mov [DISPATCH+DISPATCH_GL(cur_L)], L:RB + | set_vmstate INTERP + | lea RA, [BASE+RD*8] + | neg RA + | add RA, L:RB->top // RA = (L->top-(L->base+nresults))*8 + | mov PC, [BASE-4] // Fetch PC of caller. + | jmp ->vm_returnc + break; + + /* ---------------------------------------------------------------------- */ + + default: + fprintf(stderr, "Error: undefined opcode BC_%s\n", bc_names[op]); + exit(2); + break; + } +} + +static int build_backend(BuildCtx *ctx) +{ + int op; + dasm_growpc(Dst, BC__MAX); + build_subroutines(ctx); + |.code_op + for (op = 0; op < BC__MAX; op++) + build_ins(ctx, (BCOp)op, op); + return BC__MAX; +} + +/* Emit pseudo frame-info for all assembler functions. */ +static void emit_asm_debug(BuildCtx *ctx) +{ + int fcofs = (int)((uint8_t *)ctx->glob[GLOB_vm_ffi_call] - ctx->code); +#if LJ_64 +#define SZPTR "8" +#define BSZPTR "3" +#define REG_SP "0x7" +#define REG_RA "0x10" +#else +#define SZPTR "4" +#define BSZPTR "2" +#define REG_SP "0x4" +#define REG_RA "0x8" +#endif + switch (ctx->mode) { + case BUILD_elfasm: + fprintf(ctx->fp, "\t.section .debug_frame,\"\",@progbits\n"); + fprintf(ctx->fp, + ".Lframe0:\n" + "\t.long .LECIE0-.LSCIE0\n" + ".LSCIE0:\n" + "\t.long 0xffffffff\n" + "\t.byte 0x1\n" + "\t.string \"\"\n" + "\t.uleb128 0x1\n" + "\t.sleb128 -" SZPTR "\n" + "\t.byte " REG_RA "\n" + "\t.byte 0xc\n\t.uleb128 " REG_SP "\n\t.uleb128 " SZPTR "\n" + "\t.byte 0x80+" REG_RA "\n\t.uleb128 0x1\n" + "\t.align " SZPTR "\n" + ".LECIE0:\n\n"); + fprintf(ctx->fp, + ".LSFDE0:\n" + "\t.long .LEFDE0-.LASFDE0\n" + ".LASFDE0:\n" + "\t.long .Lframe0\n" +#if LJ_64 + "\t.quad .Lbegin\n" + "\t.quad %d\n" + "\t.byte 0xe\n\t.uleb128 %d\n" /* def_cfa_offset */ + "\t.byte 0x86\n\t.uleb128 0x2\n" /* offset rbp */ + "\t.byte 0x83\n\t.uleb128 0x3\n" /* offset rbx */ + "\t.byte 0x8f\n\t.uleb128 0x4\n" /* offset r15 */ + "\t.byte 0x8e\n\t.uleb128 0x5\n" /* offset r14 */ +#if LJ_NO_UNWIND + "\t.byte 0x8d\n\t.uleb128 0x6\n" /* offset r13 */ + "\t.byte 0x8c\n\t.uleb128 0x7\n" /* offset r12 */ +#endif +#else + "\t.long .Lbegin\n" + "\t.long %d\n" + "\t.byte 0xe\n\t.uleb128 %d\n" /* def_cfa_offset */ + "\t.byte 0x85\n\t.uleb128 0x2\n" /* offset ebp */ + "\t.byte 0x87\n\t.uleb128 0x3\n" /* offset edi */ + "\t.byte 0x86\n\t.uleb128 0x4\n" /* offset esi */ + "\t.byte 0x83\n\t.uleb128 0x5\n" /* offset ebx */ +#endif + "\t.align " SZPTR "\n" + ".LEFDE0:\n\n", fcofs, CFRAME_SIZE); +#if LJ_HASFFI + fprintf(ctx->fp, + ".LSFDE1:\n" + "\t.long .LEFDE1-.LASFDE1\n" + ".LASFDE1:\n" + "\t.long .Lframe0\n" +#if LJ_64 + "\t.quad lj_vm_ffi_call\n" + "\t.quad %d\n" + "\t.byte 0xe\n\t.uleb128 16\n" /* def_cfa_offset */ + "\t.byte 0x86\n\t.uleb128 0x2\n" /* offset rbp */ + "\t.byte 0xd\n\t.uleb128 0x6\n" /* def_cfa_register rbp */ + "\t.byte 0x83\n\t.uleb128 0x3\n" /* offset rbx */ +#else + "\t.long lj_vm_ffi_call\n" + "\t.long %d\n" + "\t.byte 0xe\n\t.uleb128 8\n" /* def_cfa_offset */ + "\t.byte 0x85\n\t.uleb128 0x2\n" /* offset ebp */ + "\t.byte 0xd\n\t.uleb128 0x5\n" /* def_cfa_register ebp */ + "\t.byte 0x83\n\t.uleb128 0x3\n" /* offset ebx */ +#endif + "\t.align " SZPTR "\n" + ".LEFDE1:\n\n", (int)ctx->codesz - fcofs); +#endif +#if !LJ_NO_UNWIND +#if (defined(__sun__) && defined(__svr4__)) +#if LJ_64 + fprintf(ctx->fp, "\t.section .eh_frame,\"a\",@unwind\n"); +#else + fprintf(ctx->fp, "\t.section .eh_frame,\"aw\",@progbits\n"); +#endif +#else + fprintf(ctx->fp, "\t.section .eh_frame,\"a\",@progbits\n"); +#endif + fprintf(ctx->fp, + ".Lframe1:\n" + "\t.long .LECIE1-.LSCIE1\n" + ".LSCIE1:\n" + "\t.long 0\n" + "\t.byte 0x1\n" + "\t.string \"zPR\"\n" + "\t.uleb128 0x1\n" + "\t.sleb128 -" SZPTR "\n" + "\t.byte " REG_RA "\n" + "\t.uleb128 6\n" /* augmentation length */ + "\t.byte 0x1b\n" /* pcrel|sdata4 */ + "\t.long lj_err_unwind_dwarf-.\n" + "\t.byte 0x1b\n" /* pcrel|sdata4 */ + "\t.byte 0xc\n\t.uleb128 " REG_SP "\n\t.uleb128 " SZPTR "\n" + "\t.byte 0x80+" REG_RA "\n\t.uleb128 0x1\n" + "\t.align " SZPTR "\n" + ".LECIE1:\n\n"); + fprintf(ctx->fp, + ".LSFDE2:\n" + "\t.long .LEFDE2-.LASFDE2\n" + ".LASFDE2:\n" + "\t.long .LASFDE2-.Lframe1\n" + "\t.long .Lbegin-.\n" + "\t.long %d\n" + "\t.uleb128 0\n" /* augmentation length */ + "\t.byte 0xe\n\t.uleb128 %d\n" /* def_cfa_offset */ +#if LJ_64 + "\t.byte 0x86\n\t.uleb128 0x2\n" /* offset rbp */ + "\t.byte 0x83\n\t.uleb128 0x3\n" /* offset rbx */ + "\t.byte 0x8f\n\t.uleb128 0x4\n" /* offset r15 */ + "\t.byte 0x8e\n\t.uleb128 0x5\n" /* offset r14 */ +#else + "\t.byte 0x85\n\t.uleb128 0x2\n" /* offset ebp */ + "\t.byte 0x87\n\t.uleb128 0x3\n" /* offset edi */ + "\t.byte 0x86\n\t.uleb128 0x4\n" /* offset esi */ + "\t.byte 0x83\n\t.uleb128 0x5\n" /* offset ebx */ +#endif + "\t.align " SZPTR "\n" + ".LEFDE2:\n\n", fcofs, CFRAME_SIZE); +#if LJ_HASFFI + fprintf(ctx->fp, + ".Lframe2:\n" + "\t.long .LECIE2-.LSCIE2\n" + ".LSCIE2:\n" + "\t.long 0\n" + "\t.byte 0x1\n" + "\t.string \"zR\"\n" + "\t.uleb128 0x1\n" + "\t.sleb128 -" SZPTR "\n" + "\t.byte " REG_RA "\n" + "\t.uleb128 1\n" /* augmentation length */ + "\t.byte 0x1b\n" /* pcrel|sdata4 */ + "\t.byte 0xc\n\t.uleb128 " REG_SP "\n\t.uleb128 " SZPTR "\n" + "\t.byte 0x80+" REG_RA "\n\t.uleb128 0x1\n" + "\t.align " SZPTR "\n" + ".LECIE2:\n\n"); + fprintf(ctx->fp, + ".LSFDE3:\n" + "\t.long .LEFDE3-.LASFDE3\n" + ".LASFDE3:\n" + "\t.long .LASFDE3-.Lframe2\n" + "\t.long lj_vm_ffi_call-.\n" + "\t.long %d\n" + "\t.uleb128 0\n" /* augmentation length */ +#if LJ_64 + "\t.byte 0xe\n\t.uleb128 16\n" /* def_cfa_offset */ + "\t.byte 0x86\n\t.uleb128 0x2\n" /* offset rbp */ + "\t.byte 0xd\n\t.uleb128 0x6\n" /* def_cfa_register rbp */ + "\t.byte 0x83\n\t.uleb128 0x3\n" /* offset rbx */ +#else + "\t.byte 0xe\n\t.uleb128 8\n" /* def_cfa_offset */ + "\t.byte 0x85\n\t.uleb128 0x2\n" /* offset ebp */ + "\t.byte 0xd\n\t.uleb128 0x5\n" /* def_cfa_register ebp */ + "\t.byte 0x83\n\t.uleb128 0x3\n" /* offset ebx */ +#endif + "\t.align " SZPTR "\n" + ".LEFDE3:\n\n", (int)ctx->codesz - fcofs); +#endif +#endif + break; +#if !LJ_NO_UNWIND + /* Mental note: never let Apple design an assembler. + ** Or a linker. Or a plastic case. But I digress. + */ + case BUILD_machasm: { +#if LJ_HASFFI + int fcsize = 0; +#endif + int i; + fprintf(ctx->fp, "\t.section __TEXT,__eh_frame,coalesced,no_toc+strip_static_syms+live_support\n"); + fprintf(ctx->fp, + "EH_frame1:\n" + "\t.set L$set$x,LECIEX-LSCIEX\n" + "\t.long L$set$x\n" + "LSCIEX:\n" + "\t.long 0\n" + "\t.byte 0x1\n" + "\t.ascii \"zPR\\0\"\n" + "\t.byte 0x1\n" + "\t.byte 128-" SZPTR "\n" + "\t.byte " REG_RA "\n" + "\t.byte 6\n" /* augmentation length */ + "\t.byte 0x9b\n" /* indirect|pcrel|sdata4 */ +#if LJ_64 + "\t.long _lj_err_unwind_dwarf+4@GOTPCREL\n" + "\t.byte 0x1b\n" /* pcrel|sdata4 */ + "\t.byte 0xc\n\t.byte " REG_SP "\n\t.byte " SZPTR "\n" +#else + "\t.long L_lj_err_unwind_dwarf$non_lazy_ptr-.\n" + "\t.byte 0x1b\n" /* pcrel|sdata4 */ + "\t.byte 0xc\n\t.byte 0x5\n\t.byte 0x4\n" /* esp=5 on 32 bit MACH-O. */ +#endif + "\t.byte 0x80+" REG_RA "\n\t.byte 0x1\n" + "\t.align " BSZPTR "\n" + "LECIEX:\n\n"); + for (i = 0; i < ctx->nsym; i++) { + const char *name = ctx->sym[i].name; + int32_t size = ctx->sym[i+1].ofs - ctx->sym[i].ofs; + if (size == 0) continue; +#if LJ_HASFFI + if (!strcmp(name, "_lj_vm_ffi_call")) { fcsize = size; continue; } +#endif + fprintf(ctx->fp, + "%s.eh:\n" + "LSFDE%d:\n" + "\t.set L$set$%d,LEFDE%d-LASFDE%d\n" + "\t.long L$set$%d\n" + "LASFDE%d:\n" + "\t.long LASFDE%d-EH_frame1\n" + "\t.long %s-.\n" + "\t.long %d\n" + "\t.byte 0\n" /* augmentation length */ + "\t.byte 0xe\n\t.byte %d\n" /* def_cfa_offset */ +#if LJ_64 + "\t.byte 0x86\n\t.byte 0x2\n" /* offset rbp */ + "\t.byte 0x83\n\t.byte 0x3\n" /* offset rbx */ + "\t.byte 0x8f\n\t.byte 0x4\n" /* offset r15 */ + "\t.byte 0x8e\n\t.byte 0x5\n" /* offset r14 */ +#else + "\t.byte 0x84\n\t.byte 0x2\n" /* offset ebp (4 for MACH-O)*/ + "\t.byte 0x87\n\t.byte 0x3\n" /* offset edi */ + "\t.byte 0x86\n\t.byte 0x4\n" /* offset esi */ + "\t.byte 0x83\n\t.byte 0x5\n" /* offset ebx */ +#endif + "\t.align " BSZPTR "\n" + "LEFDE%d:\n\n", + name, i, i, i, i, i, i, i, name, size, CFRAME_SIZE, i); + } +#if LJ_HASFFI + if (fcsize) { + fprintf(ctx->fp, + "EH_frame2:\n" + "\t.set L$set$y,LECIEY-LSCIEY\n" + "\t.long L$set$y\n" + "LSCIEY:\n" + "\t.long 0\n" + "\t.byte 0x1\n" + "\t.ascii \"zR\\0\"\n" + "\t.byte 0x1\n" + "\t.byte 128-" SZPTR "\n" + "\t.byte " REG_RA "\n" + "\t.byte 1\n" /* augmentation length */ +#if LJ_64 + "\t.byte 0x1b\n" /* pcrel|sdata4 */ + "\t.byte 0xc\n\t.byte " REG_SP "\n\t.byte " SZPTR "\n" +#else + "\t.byte 0x1b\n" /* pcrel|sdata4 */ + "\t.byte 0xc\n\t.byte 0x5\n\t.byte 0x4\n" /* esp=5 on 32 bit MACH. */ +#endif + "\t.byte 0x80+" REG_RA "\n\t.byte 0x1\n" + "\t.align " BSZPTR "\n" + "LECIEY:\n\n"); + fprintf(ctx->fp, + "_lj_vm_ffi_call.eh:\n" + "LSFDEY:\n" + "\t.set L$set$yy,LEFDEY-LASFDEY\n" + "\t.long L$set$yy\n" + "LASFDEY:\n" + "\t.long LASFDEY-EH_frame2\n" + "\t.long _lj_vm_ffi_call-.\n" + "\t.long %d\n" + "\t.byte 0\n" /* augmentation length */ +#if LJ_64 + "\t.byte 0xe\n\t.byte 16\n" /* def_cfa_offset */ + "\t.byte 0x86\n\t.byte 0x2\n" /* offset rbp */ + "\t.byte 0xd\n\t.byte 0x6\n" /* def_cfa_register rbp */ + "\t.byte 0x83\n\t.byte 0x3\n" /* offset rbx */ +#else + "\t.byte 0xe\n\t.byte 8\n" /* def_cfa_offset */ + "\t.byte 0x84\n\t.byte 0x2\n" /* offset ebp (4 for MACH-O)*/ + "\t.byte 0xd\n\t.byte 0x4\n" /* def_cfa_register ebp */ + "\t.byte 0x83\n\t.byte 0x3\n" /* offset ebx */ +#endif + "\t.align " BSZPTR "\n" + "LEFDEY:\n\n", fcsize); + } +#endif +#if !LJ_64 + fprintf(ctx->fp, + "\t.non_lazy_symbol_pointer\n" + "L_lj_err_unwind_dwarf$non_lazy_ptr:\n" + ".indirect_symbol _lj_err_unwind_dwarf\n" + ".long 0\n\n"); + fprintf(ctx->fp, "\t.section __IMPORT,__jump_table,symbol_stubs,pure_instructions+self_modifying_code,5\n"); + { + const char *const *xn; + for (xn = ctx->extnames; *xn; xn++) + if (strncmp(*xn, LABEL_PREFIX, sizeof(LABEL_PREFIX)-1)) + fprintf(ctx->fp, "L_%s$stub:\n\t.indirect_symbol _%s\n\t.ascii \"\\364\\364\\364\\364\\364\"\n", *xn, *xn); + } +#endif + fprintf(ctx->fp, ".subsections_via_symbols\n"); + } + break; +#endif + default: /* Difficult for other modes. */ + break; + } +} + diff --git a/lib/LuaJIT/xb1build.bat b/lib/LuaJIT/xb1build.bat new file mode 100644 index 0000000..847e84a --- /dev/null +++ b/lib/LuaJIT/xb1build.bat @@ -0,0 +1,101 @@ +@rem Script to build LuaJIT with the Xbox One SDK. +@rem Donated to the public domain. +@rem +@rem Open a "Visual Studio .NET Command Prompt" (64 bit host compiler) +@rem Then cd to this directory and run this script. + +@if not defined INCLUDE goto :FAIL +@if not defined DurangoXDK goto :FAIL + +@setlocal +@echo ---- Host compiler ---- +@set LJCOMPILE=cl /nologo /c /MD /O2 /W3 /D_CRT_SECURE_NO_DEPRECATE /DLUAJIT_ENABLE_GC64 +@set LJLINK=link /nologo +@set LJMT=mt /nologo +@set DASMDIR=..\dynasm +@set DASM=%DASMDIR%\dynasm.lua +@set ALL_LIB=lib_base.c lib_math.c lib_bit.c lib_string.c lib_table.c lib_io.c lib_os.c lib_package.c lib_debug.c lib_jit.c lib_ffi.c + +%LJCOMPILE% host\minilua.c +@if errorlevel 1 goto :BAD +%LJLINK% /out:minilua.exe minilua.obj +@if errorlevel 1 goto :BAD +if exist minilua.exe.manifest^ + %LJMT% -manifest minilua.exe.manifest -outputresource:minilua.exe + +@rem Error out for 64 bit host compiler +@minilua +@if not errorlevel 8 goto :FAIL + +@set DASMFLAGS=-D WIN -D FFI -D P64 +minilua %DASM% -LN %DASMFLAGS% -o host\buildvm_arch.h vm_x64.dasc +@if errorlevel 1 goto :BAD + +%LJCOMPILE% /I "." /I %DASMDIR% /D_DURANGO host\buildvm*.c +@if errorlevel 1 goto :BAD +%LJLINK% /out:buildvm.exe buildvm*.obj +@if errorlevel 1 goto :BAD +if exist buildvm.exe.manifest^ + %LJMT% -manifest buildvm.exe.manifest -outputresource:buildvm.exe + +buildvm -m peobj -o lj_vm.obj +@if errorlevel 1 goto :BAD +buildvm -m bcdef -o lj_bcdef.h %ALL_LIB% +@if errorlevel 1 goto :BAD +buildvm -m ffdef -o lj_ffdef.h %ALL_LIB% +@if errorlevel 1 goto :BAD +buildvm -m libdef -o lj_libdef.h %ALL_LIB% +@if errorlevel 1 goto :BAD +buildvm -m recdef -o lj_recdef.h %ALL_LIB% +@if errorlevel 1 goto :BAD +buildvm -m vmdef -o jit\vmdef.lua %ALL_LIB% +@if errorlevel 1 goto :BAD +buildvm -m folddef -o lj_folddef.h lj_opt_fold.c +@if errorlevel 1 goto :BAD + +@echo ---- Cross compiler ---- + +@set CWD=%cd% +@call "%DurangoXDK%\xdk\DurangoVars.cmd" XDK +@cd /D "%CWD%" +@shift + +@set LJCOMPILE="cl" /nologo /c /W3 /GF /Gm- /GR- /GS- /Gy /openmp- /D_CRT_SECURE_NO_DEPRECATE /D_LIB /D_UNICODE /D_DURANGO +@set LJLIB="lib" /nologo + +@if "%1"=="debug" ( + @shift + @set LJCOMPILE=%LJCOMPILE% /Zi /MDd /Od + @set LJLINK=%LJLINK% /debug +) else ( + @set LJCOMPILE=%LJCOMPILE% /MD /O2 /DNDEBUG +) + +@if "%1"=="amalg" goto :AMALG +%LJCOMPILE% /DLUA_BUILD_AS_DLL lj_*.c lib_*.c +@if errorlevel 1 goto :BAD +%LJLIB% /OUT:luajit.lib lj_*.obj lib_*.obj +@if errorlevel 1 goto :BAD +@goto :NOAMALG +:AMALG +%LJCOMPILE% /DLUA_BUILD_AS_DLL ljamalg.c +@if errorlevel 1 goto :BAD +%LJLIB% /OUT:luajit.lib ljamalg.obj lj_vm.obj +@if errorlevel 1 goto :BAD +:NOAMALG + +@del *.obj *.manifest minilua.exe buildvm.exe +@echo. +@echo === Successfully built LuaJIT for Xbox One === + +@goto :END +:BAD +@echo. +@echo ******************************************************* +@echo *** Build FAILED -- Please check the error messages *** +@echo ******************************************************* +@goto :END +:FAIL +@echo To run this script you must open a "Visual Studio .NET Command Prompt" +@echo (64 bit host compiler). The Xbox One SDK must be installed, too. +:END diff --git a/lib/LuaJIT/xedkbuild.bat b/lib/LuaJIT/xedkbuild.bat new file mode 100644 index 0000000..240ec87 --- /dev/null +++ b/lib/LuaJIT/xedkbuild.bat @@ -0,0 +1,92 @@ +@rem Script to build LuaJIT with the Xbox 360 SDK. +@rem Donated to the public domain. +@rem +@rem Open a "Visual Studio .NET Command Prompt" (32 bit host compiler) +@rem Then cd to this directory and run this script. + +@if not defined INCLUDE goto :FAIL +@if not defined XEDK goto :FAIL + +@setlocal +@rem ---- Host compiler ---- +@set LJCOMPILE=cl /nologo /c /MD /O2 /W3 /D_CRT_SECURE_NO_DEPRECATE +@set LJLINK=link /nologo +@set LJMT=mt /nologo +@set DASMDIR=..\dynasm +@set DASM=%DASMDIR%\dynasm.lua +@set ALL_LIB=lib_base.c lib_math.c lib_bit.c lib_string.c lib_table.c lib_io.c lib_os.c lib_package.c lib_debug.c lib_jit.c lib_ffi.c + +%LJCOMPILE% host\minilua.c +@if errorlevel 1 goto :BAD +%LJLINK% /out:minilua.exe minilua.obj +@if errorlevel 1 goto :BAD +if exist minilua.exe.manifest^ + %LJMT% -manifest minilua.exe.manifest -outputresource:minilua.exe + +@rem Error out for 64 bit host compiler +@minilua +@if errorlevel 8 goto :FAIL + +@set DASMFLAGS=-D GPR64 -D FRAME32 -D PPE -D SQRT -D DUALNUM +minilua %DASM% -LN %DASMFLAGS% -o host\buildvm_arch.h vm_ppc.dasc +@if errorlevel 1 goto :BAD + +%LJCOMPILE% /I "." /I %DASMDIR% /D_XBOX_VER=200 /DLUAJIT_TARGET=LUAJIT_ARCH_PPC host\buildvm*.c +@if errorlevel 1 goto :BAD +%LJLINK% /out:buildvm.exe buildvm*.obj +@if errorlevel 1 goto :BAD +if exist buildvm.exe.manifest^ + %LJMT% -manifest buildvm.exe.manifest -outputresource:buildvm.exe + +buildvm -m peobj -o lj_vm.obj +@if errorlevel 1 goto :BAD +buildvm -m bcdef -o lj_bcdef.h %ALL_LIB% +@if errorlevel 1 goto :BAD +buildvm -m ffdef -o lj_ffdef.h %ALL_LIB% +@if errorlevel 1 goto :BAD +buildvm -m libdef -o lj_libdef.h %ALL_LIB% +@if errorlevel 1 goto :BAD +buildvm -m recdef -o lj_recdef.h %ALL_LIB% +@if errorlevel 1 goto :BAD +buildvm -m vmdef -o jit\vmdef.lua %ALL_LIB% +@if errorlevel 1 goto :BAD +buildvm -m folddef -o lj_folddef.h lj_opt_fold.c +@if errorlevel 1 goto :BAD + +@rem ---- Cross compiler ---- +@set LJCOMPILE="%XEDK%\bin\win32\cl" /nologo /c /MT /O2 /W3 /GF /Gm- /GR- /GS- /Gy /openmp- /D_CRT_SECURE_NO_DEPRECATE /DNDEBUG /D_XBOX /D_LIB /DLUAJIT_USE_SYSMALLOC +@set LJLIB="%XEDK%\bin\win32\lib" /nologo +@set "INCLUDE=%XEDK%\include\xbox" + +@if "%1" neq "debug" goto :NODEBUG +@shift +@set "LJCOMPILE=%LJCOMPILE% /Zi" +:NODEBUG +@if "%1"=="amalg" goto :AMALG +%LJCOMPILE% /DLUA_BUILD_AS_DLL lj_*.c lib_*.c +@if errorlevel 1 goto :BAD +%LJLIB% /OUT:luajit20.lib lj_*.obj lib_*.obj +@if errorlevel 1 goto :BAD +@goto :NOAMALG +:AMALG +%LJCOMPILE% /DLUA_BUILD_AS_DLL ljamalg.c +@if errorlevel 1 goto :BAD +%LJLIB% /OUT:luajit20.lib ljamalg.obj lj_vm.obj +@if errorlevel 1 goto :BAD +:NOAMALG + +@del *.obj *.manifest minilua.exe buildvm.exe +@echo. +@echo === Successfully built LuaJIT for Xbox 360 === + +@goto :END +:BAD +@echo. +@echo ******************************************************* +@echo *** Build FAILED -- Please check the error messages *** +@echo ******************************************************* +@goto :END +:FAIL +@echo To run this script you must open a "Visual Studio .NET Command Prompt" +@echo (32 bit host compiler). The Xbox 360 SDK must be installed, too. +:END -- 2.39.5

Mynps_vRGKZ&uK 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�Dc~>Lrwey!OfN7(RmtZae%=LOz5u}4Ac_Vu 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%0D*ZU$$>=Ux9WgI=R0N#W*N0aXx`t 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ƾ*A->f0TH>+&lu0dF%-rL4I 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 literal 0 HcmV?d00001 diff --git a/lib/LuaJIT/lj.supp b/lib/LuaJIT/lj.supp new file mode 100644 index 0000000..217f7c8 --- /dev/null +++ b/lib/LuaJIT/lj.supp @@ -0,0 +1,41 @@ +# Valgrind suppression file for LuaJIT 2.0. +{ + Optimized string compare + Memcheck:Addr4 + fun:lj_str_cmp +} +{ + Optimized string compare + Memcheck:Addr1 + fun:lj_str_cmp +} +{ + Optimized string compare + Memcheck:Addr4 + fun:lj_str_new +} +{ + Optimized string compare + Memcheck:Addr1 + fun:lj_str_new +} +{ + Optimized string compare + Memcheck:Cond + fun:lj_str_new +} +{ + Optimized string compare + Memcheck:Addr4 + fun:str_fastcmp +} +{ + Optimized string compare + Memcheck:Addr1 + fun:str_fastcmp +} +{ + Optimized string compare + Memcheck:Cond + fun:str_fastcmp +} diff --git a/lib/LuaJIT/lj_alloc.c b/lib/LuaJIT/lj_alloc.c new file mode 100644 index 0000000..33a2eb8 --- /dev/null +++ b/lib/LuaJIT/lj_alloc.c @@ -0,0 +1,1490 @@ +/* +** Bundled memory allocator. +** +** Beware: this is a HEAVILY CUSTOMIZED version of dlmalloc. +** The original bears the following remark: +** +** This is a version (aka dlmalloc) of malloc/free/realloc written by +** Doug Lea and released to the public domain, as explained at +** http://creativecommons.org/licenses/publicdomain. +** +** * Version pre-2.8.4 Wed Mar 29 19:46:29 2006 (dl at gee) +** +** No additional copyright is claimed over the customizations. +** Please do NOT bother the original author about this version here! +** +** If you want to use dlmalloc in another project, you should get +** the original from: ftp://gee.cs.oswego.edu/pub/misc/ +** For thread-safe derivatives, take a look at: +** - ptmalloc: http://www.malloc.de/ +** - nedmalloc: http://www.nedprod.com/programs/portable/nedmalloc/ +*/ + +#define lj_alloc_c +#define LUA_CORE + +/* To get the mremap prototype. Must be defined before any system includes. */ +#if defined(__linux__) && !defined(_GNU_SOURCE) +#define _GNU_SOURCE +#endif + +#include "lj_def.h" +#include "lj_arch.h" +#include "lj_alloc.h" + +#ifndef LUAJIT_USE_SYSMALLOC + +#define MAX_SIZE_T (~(size_t)0) +#define MALLOC_ALIGNMENT ((size_t)8U) + +#define DEFAULT_GRANULARITY ((size_t)128U * (size_t)1024U) +#define DEFAULT_TRIM_THRESHOLD ((size_t)2U * (size_t)1024U * (size_t)1024U) +#define DEFAULT_MMAP_THRESHOLD ((size_t)128U * (size_t)1024U) +#define MAX_RELEASE_CHECK_RATE 255 + +/* ------------------- size_t and alignment properties -------------------- */ + +/* The byte and bit size of a size_t */ +#define SIZE_T_SIZE (sizeof(size_t)) +#define SIZE_T_BITSIZE (sizeof(size_t) << 3) + +/* Some constants coerced to size_t */ +/* Annoying but necessary to avoid errors on some platforms */ +#define SIZE_T_ZERO ((size_t)0) +#define SIZE_T_ONE ((size_t)1) +#define SIZE_T_TWO ((size_t)2) +#define TWO_SIZE_T_SIZES (SIZE_T_SIZE<<1) +#define FOUR_SIZE_T_SIZES (SIZE_T_SIZE<<2) +#define SIX_SIZE_T_SIZES (FOUR_SIZE_T_SIZES+TWO_SIZE_T_SIZES) + +/* The bit mask value corresponding to MALLOC_ALIGNMENT */ +#define CHUNK_ALIGN_MASK (MALLOC_ALIGNMENT - SIZE_T_ONE) + +/* the number of bytes to offset an address to align it */ +#define align_offset(A)\ + ((((size_t)(A) & CHUNK_ALIGN_MASK) == 0)? 0 :\ + ((MALLOC_ALIGNMENT - ((size_t)(A) & CHUNK_ALIGN_MASK)) & CHUNK_ALIGN_MASK)) + +/* -------------------------- MMAP support ------------------------------- */ + +#define MFAIL ((void *)(MAX_SIZE_T)) +#define CMFAIL ((char *)(MFAIL)) /* defined for convenience */ + +#define IS_DIRECT_BIT (SIZE_T_ONE) + + +/* Determine system-specific block allocation method. */ +#if LJ_TARGET_WINDOWS + +#define WIN32_LEAN_AND_MEAN +#include + +#define LJ_ALLOC_VIRTUALALLOC 1 + +#if LJ_64 && !LJ_GC64 +#define LJ_ALLOC_NTAVM 1 +#endif + +#else + +#include +/* If this include fails, then rebuild with: -DLUAJIT_USE_SYSMALLOC */ +#include + +#define LJ_ALLOC_MMAP 1 + +#if LJ_64 + +#define LJ_ALLOC_MMAP_PROBE 1 + +#if LJ_GC64 +#define LJ_ALLOC_MBITS 47 /* 128 TB in LJ_GC64 mode. */ +#elif LJ_TARGET_X64 && LJ_HASJIT +/* Due to limitations in the x64 compiler backend. */ +#define LJ_ALLOC_MBITS 31 /* 2 GB on x64 with !LJ_GC64. */ +#else +#define LJ_ALLOC_MBITS 32 /* 4 GB on other archs with !LJ_GC64. */ +#endif + +#endif + +#if LJ_64 && !LJ_GC64 && defined(MAP_32BIT) +#define LJ_ALLOC_MMAP32 1 +#endif + +#if LJ_TARGET_LINUX +#define LJ_ALLOC_MREMAP 1 +#endif + +#endif + + +#if LJ_ALLOC_VIRTUALALLOC + +#if LJ_ALLOC_NTAVM +/* Undocumented, but hey, that's what we all love so much about Windows. */ +typedef long (*PNTAVM)(HANDLE handle, void **addr, ULONG zbits, + size_t *size, ULONG alloctype, ULONG prot); +static PNTAVM ntavm; + +/* Number of top bits of the lower 32 bits of an address that must be zero. +** Apparently 0 gives us full 64 bit addresses and 1 gives us the lower 2GB. +*/ +#define NTAVM_ZEROBITS 1 + +static void init_mmap(void) +{ + ntavm = (PNTAVM)GetProcAddress(GetModuleHandleA("ntdll.dll"), + "NtAllocateVirtualMemory"); +} +#define INIT_MMAP() init_mmap() + +/* Win64 32 bit MMAP via NtAllocateVirtualMemory. */ +static void *CALL_MMAP(size_t size) +{ + DWORD olderr = GetLastError(); + void *ptr = NULL; + long st = ntavm(INVALID_HANDLE_VALUE, &ptr, NTAVM_ZEROBITS, &size, + MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE); + SetLastError(olderr); + return st == 0 ? ptr : MFAIL; +} + +/* For direct MMAP, use MEM_TOP_DOWN to minimize interference */ +static void *DIRECT_MMAP(size_t size) +{ + DWORD olderr = GetLastError(); + void *ptr = NULL; + long st = ntavm(INVALID_HANDLE_VALUE, &ptr, NTAVM_ZEROBITS, &size, + MEM_RESERVE|MEM_COMMIT|MEM_TOP_DOWN, PAGE_READWRITE); + SetLastError(olderr); + return st == 0 ? ptr : MFAIL; +} + +#else + +/* Win32 MMAP via VirtualAlloc */ +static void *CALL_MMAP(size_t size) +{ + DWORD olderr = GetLastError(); + void *ptr = LJ_WIN_VALLOC(0, size, MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE); + SetLastError(olderr); + return ptr ? ptr : MFAIL; +} + +/* For direct MMAP, use MEM_TOP_DOWN to minimize interference */ +static void *DIRECT_MMAP(size_t size) +{ + DWORD olderr = GetLastError(); + void *ptr = LJ_WIN_VALLOC(0, size, MEM_RESERVE|MEM_COMMIT|MEM_TOP_DOWN, + PAGE_READWRITE); + SetLastError(olderr); + return ptr ? ptr : MFAIL; +} + +#endif + +/* This function supports releasing coalesed segments */ +static int CALL_MUNMAP(void *ptr, size_t size) +{ + DWORD olderr = GetLastError(); + MEMORY_BASIC_INFORMATION minfo; + char *cptr = (char *)ptr; + while (size) { + if (VirtualQuery(cptr, &minfo, sizeof(minfo)) == 0) + return -1; + if (minfo.BaseAddress != cptr || minfo.AllocationBase != cptr || + minfo.State != MEM_COMMIT || minfo.RegionSize > size) + return -1; + if (VirtualFree(cptr, 0, MEM_RELEASE) == 0) + return -1; + cptr += minfo.RegionSize; + size -= minfo.RegionSize; + } + SetLastError(olderr); + return 0; +} + +#elif LJ_ALLOC_MMAP + +#define MMAP_PROT (PROT_READ|PROT_WRITE) +#if !defined(MAP_ANONYMOUS) && defined(MAP_ANON) +#define MAP_ANONYMOUS MAP_ANON +#endif +#define MMAP_FLAGS (MAP_PRIVATE|MAP_ANONYMOUS) + +#if LJ_ALLOC_MMAP_PROBE + +#ifdef MAP_TRYFIXED +#define MMAP_FLAGS_PROBE (MMAP_FLAGS|MAP_TRYFIXED) +#else +#define MMAP_FLAGS_PROBE MMAP_FLAGS +#endif + +#define LJ_ALLOC_MMAP_PROBE_MAX 30 +#define LJ_ALLOC_MMAP_PROBE_LINEAR 5 + +#define LJ_ALLOC_MMAP_PROBE_LOWER ((uintptr_t)0x4000) + +/* No point in a giant ifdef mess. Just try to open /dev/urandom. +** It doesn't really matter if this fails, since we get some ASLR bits from +** every unsuitable allocation, too. And we prefer linear allocation, anyway. +*/ +#include +#include + +static uintptr_t mmap_probe_seed(void) +{ + uintptr_t val; + int fd = open("/dev/urandom", O_RDONLY); + if (fd != -1) { + int ok = ((size_t)read(fd, &val, sizeof(val)) == sizeof(val)); + (void)close(fd); + if (ok) return val; + } + return 1; /* Punt. */ +} + +static void *mmap_probe(size_t size) +{ + /* Hint for next allocation. Doesn't need to be thread-safe. */ + static uintptr_t hint_addr = 0; + static uintptr_t hint_prng = 0; + int olderr = errno; + int retry; + for (retry = 0; retry < LJ_ALLOC_MMAP_PROBE_MAX; retry++) { + void *p = mmap((void *)hint_addr, size, MMAP_PROT, MMAP_FLAGS_PROBE, -1, 0); + uintptr_t addr = (uintptr_t)p; + if ((addr >> LJ_ALLOC_MBITS) == 0 && addr >= LJ_ALLOC_MMAP_PROBE_LOWER && + ((addr + size) >> LJ_ALLOC_MBITS) == 0) { + /* We got a suitable address. Bump the hint address. */ + hint_addr = addr + size; + errno = olderr; + return p; + } + if (p != MFAIL) { + munmap(p, size); + } else if (errno == ENOMEM) { + return MFAIL; + } + if (hint_addr) { + /* First, try linear probing. */ + if (retry < LJ_ALLOC_MMAP_PROBE_LINEAR) { + hint_addr += 0x1000000; + if (((hint_addr + size) >> LJ_ALLOC_MBITS) != 0) + hint_addr = 0; + continue; + } else if (retry == LJ_ALLOC_MMAP_PROBE_LINEAR) { + /* Next, try a no-hint probe to get back an ASLR address. */ + hint_addr = 0; + continue; + } + } + /* Finally, try pseudo-random probing. */ + if (LJ_UNLIKELY(hint_prng == 0)) { + hint_prng = mmap_probe_seed(); + } + /* The unsuitable address we got has some ASLR PRNG bits. */ + hint_addr ^= addr & ~((uintptr_t)(LJ_PAGESIZE-1)); + do { /* The PRNG itself is very weak, but see above. */ + hint_prng = hint_prng * 1103515245 + 12345; + hint_addr ^= hint_prng * (uintptr_t)LJ_PAGESIZE; + hint_addr &= (((uintptr_t)1 << LJ_ALLOC_MBITS)-1); + } while (hint_addr < LJ_ALLOC_MMAP_PROBE_LOWER); + } + errno = olderr; + return MFAIL; +} + +#endif + +#if LJ_ALLOC_MMAP32 + +#if defined(__sun__) +#define LJ_ALLOC_MMAP32_START ((uintptr_t)0x1000) +#else +#define LJ_ALLOC_MMAP32_START ((uintptr_t)0) +#endif + +static void *mmap_map32(size_t size) +{ +#if LJ_ALLOC_MMAP_PROBE + static int fallback = 0; + if (fallback) + return mmap_probe(size); +#endif + { + int olderr = errno; + void *ptr = mmap((void *)LJ_ALLOC_MMAP32_START, size, MMAP_PROT, MAP_32BIT|MMAP_FLAGS, -1, 0); + errno = olderr; + /* This only allows 1GB on Linux. So fallback to probing to get 2GB. */ +#if LJ_ALLOC_MMAP_PROBE + if (ptr == MFAIL) { + fallback = 1; + return mmap_probe(size); + } +#endif + return ptr; + } +} + +#endif + +#if LJ_ALLOC_MMAP32 +#define CALL_MMAP(size) mmap_map32(size) +#elif LJ_ALLOC_MMAP_PROBE +#define CALL_MMAP(size) mmap_probe(size) +#else +static void *CALL_MMAP(size_t size) +{ + int olderr = errno; + void *ptr = mmap(NULL, size, MMAP_PROT, MMAP_FLAGS, -1, 0); + errno = olderr; + return ptr; +} +#endif + +#if LJ_64 && !LJ_GC64 && ((defined(__FreeBSD__) && __FreeBSD__ < 10) || defined(__FreeBSD_kernel__)) && !LJ_TARGET_PS4 + +#include + +static void init_mmap(void) +{ + struct rlimit rlim; + rlim.rlim_cur = rlim.rlim_max = 0x10000; + setrlimit(RLIMIT_DATA, &rlim); /* Ignore result. May fail later. */ +} +#define INIT_MMAP() init_mmap() + +#endif + +static int CALL_MUNMAP(void *ptr, size_t size) +{ + int olderr = errno; + int ret = munmap(ptr, size); + errno = olderr; + return ret; +} + +#if LJ_ALLOC_MREMAP +/* Need to define _GNU_SOURCE to get the mremap prototype. */ +static void *CALL_MREMAP_(void *ptr, size_t osz, size_t nsz, int flags) +{ + int olderr = errno; + ptr = mremap(ptr, osz, nsz, flags); + errno = olderr; + return ptr; +} + +#define CALL_MREMAP(addr, osz, nsz, mv) CALL_MREMAP_((addr), (osz), (nsz), (mv)) +#define CALL_MREMAP_NOMOVE 0 +#define CALL_MREMAP_MAYMOVE 1 +#if LJ_64 && !LJ_GC64 +#define CALL_MREMAP_MV CALL_MREMAP_NOMOVE +#else +#define CALL_MREMAP_MV CALL_MREMAP_MAYMOVE +#endif +#endif + +#endif + + +#ifndef INIT_MMAP +#define INIT_MMAP() ((void)0) +#endif + +#ifndef DIRECT_MMAP +#define DIRECT_MMAP(s) CALL_MMAP(s) +#endif + +#ifndef CALL_MREMAP +#define CALL_MREMAP(addr, osz, nsz, mv) ((void)osz, MFAIL) +#endif + +/* ----------------------- Chunk representations ------------------------ */ + +struct malloc_chunk { + size_t prev_foot; /* Size of previous chunk (if free). */ + size_t head; /* Size and inuse bits. */ + struct malloc_chunk *fd; /* double links -- used only if free. */ + struct malloc_chunk *bk; +}; + +typedef struct malloc_chunk mchunk; +typedef struct malloc_chunk *mchunkptr; +typedef struct malloc_chunk *sbinptr; /* The type of bins of chunks */ +typedef size_t bindex_t; /* Described below */ +typedef unsigned int binmap_t; /* Described below */ +typedef unsigned int flag_t; /* The type of various bit flag sets */ + +/* ------------------- Chunks sizes and alignments ----------------------- */ + +#define MCHUNK_SIZE (sizeof(mchunk)) + +#define CHUNK_OVERHEAD (SIZE_T_SIZE) + +/* Direct chunks need a second word of overhead ... */ +#define DIRECT_CHUNK_OVERHEAD (TWO_SIZE_T_SIZES) +/* ... and additional padding for fake next-chunk at foot */ +#define DIRECT_FOOT_PAD (FOUR_SIZE_T_SIZES) + +/* The smallest size we can malloc is an aligned minimal chunk */ +#define MIN_CHUNK_SIZE\ + ((MCHUNK_SIZE + CHUNK_ALIGN_MASK) & ~CHUNK_ALIGN_MASK) + +/* conversion from malloc headers to user pointers, and back */ +#define chunk2mem(p) ((void *)((char *)(p) + TWO_SIZE_T_SIZES)) +#define mem2chunk(mem) ((mchunkptr)((char *)(mem) - TWO_SIZE_T_SIZES)) +/* chunk associated with aligned address A */ +#define align_as_chunk(A) (mchunkptr)((A) + align_offset(chunk2mem(A))) + +/* Bounds on request (not chunk) sizes. */ +#define MAX_REQUEST ((~MIN_CHUNK_SIZE+1) << 2) +#define MIN_REQUEST (MIN_CHUNK_SIZE - CHUNK_OVERHEAD - SIZE_T_ONE) + +/* pad request bytes into a usable size */ +#define pad_request(req) \ + (((req) + CHUNK_OVERHEAD + CHUNK_ALIGN_MASK) & ~CHUNK_ALIGN_MASK) + +/* pad request, checking for minimum (but not maximum) */ +#define request2size(req) \ + (((req) < MIN_REQUEST)? MIN_CHUNK_SIZE : pad_request(req)) + +/* ------------------ Operations on head and foot fields ----------------- */ + +#define PINUSE_BIT (SIZE_T_ONE) +#define CINUSE_BIT (SIZE_T_TWO) +#define INUSE_BITS (PINUSE_BIT|CINUSE_BIT) + +/* Head value for fenceposts */ +#define FENCEPOST_HEAD (INUSE_BITS|SIZE_T_SIZE) + +/* extraction of fields from head words */ +#define cinuse(p) ((p)->head & CINUSE_BIT) +#define pinuse(p) ((p)->head & PINUSE_BIT) +#define chunksize(p) ((p)->head & ~(INUSE_BITS)) + +#define clear_pinuse(p) ((p)->head &= ~PINUSE_BIT) +#define clear_cinuse(p) ((p)->head &= ~CINUSE_BIT) + +/* Treat space at ptr +/- offset as a chunk */ +#define chunk_plus_offset(p, s) ((mchunkptr)(((char *)(p)) + (s))) +#define chunk_minus_offset(p, s) ((mchunkptr)(((char *)(p)) - (s))) + +/* Ptr to next or previous physical malloc_chunk. */ +#define next_chunk(p) ((mchunkptr)(((char *)(p)) + ((p)->head & ~INUSE_BITS))) +#define prev_chunk(p) ((mchunkptr)(((char *)(p)) - ((p)->prev_foot) )) + +/* extract next chunk's pinuse bit */ +#define next_pinuse(p) ((next_chunk(p)->head) & PINUSE_BIT) + +/* Get/set size at footer */ +#define get_foot(p, s) (((mchunkptr)((char *)(p) + (s)))->prev_foot) +#define set_foot(p, s) (((mchunkptr)((char *)(p) + (s)))->prev_foot = (s)) + +/* Set size, pinuse bit, and foot */ +#define set_size_and_pinuse_of_free_chunk(p, s)\ + ((p)->head = (s|PINUSE_BIT), set_foot(p, s)) + +/* Set size, pinuse bit, foot, and clear next pinuse */ +#define set_free_with_pinuse(p, s, n)\ + (clear_pinuse(n), set_size_and_pinuse_of_free_chunk(p, s)) + +#define is_direct(p)\ + (!((p)->head & PINUSE_BIT) && ((p)->prev_foot & IS_DIRECT_BIT)) + +/* Get the internal overhead associated with chunk p */ +#define overhead_for(p)\ + (is_direct(p)? DIRECT_CHUNK_OVERHEAD : CHUNK_OVERHEAD) + +/* ---------------------- Overlaid data structures ----------------------- */ + +struct malloc_tree_chunk { + /* The first four fields must be compatible with malloc_chunk */ + size_t prev_foot; + size_t head; + struct malloc_tree_chunk *fd; + struct malloc_tree_chunk *bk; + + struct malloc_tree_chunk *child[2]; + struct malloc_tree_chunk *parent; + bindex_t index; +}; + +typedef struct malloc_tree_chunk tchunk; +typedef struct malloc_tree_chunk *tchunkptr; +typedef struct malloc_tree_chunk *tbinptr; /* The type of bins of trees */ + +/* A little helper macro for trees */ +#define leftmost_child(t) ((t)->child[0] != 0? (t)->child[0] : (t)->child[1]) + +/* ----------------------------- Segments -------------------------------- */ + +struct malloc_segment { + char *base; /* base address */ + size_t size; /* allocated size */ + struct malloc_segment *next; /* ptr to next segment */ +}; + +typedef struct malloc_segment msegment; +typedef struct malloc_segment *msegmentptr; + +/* ---------------------------- malloc_state ----------------------------- */ + +/* Bin types, widths and sizes */ +#define NSMALLBINS (32U) +#define NTREEBINS (32U) +#define SMALLBIN_SHIFT (3U) +#define SMALLBIN_WIDTH (SIZE_T_ONE << SMALLBIN_SHIFT) +#define TREEBIN_SHIFT (8U) +#define MIN_LARGE_SIZE (SIZE_T_ONE << TREEBIN_SHIFT) +#define MAX_SMALL_SIZE (MIN_LARGE_SIZE - SIZE_T_ONE) +#define MAX_SMALL_REQUEST (MAX_SMALL_SIZE - CHUNK_ALIGN_MASK - CHUNK_OVERHEAD) + +struct malloc_state { + binmap_t smallmap; + binmap_t treemap; + size_t dvsize; + size_t topsize; + mchunkptr dv; + mchunkptr top; + size_t trim_check; + size_t release_checks; + mchunkptr smallbins[(NSMALLBINS+1)*2]; + tbinptr treebins[NTREEBINS]; + msegment seg; +}; + +typedef struct malloc_state *mstate; + +#define is_initialized(M) ((M)->top != 0) + +/* -------------------------- system alloc setup ------------------------- */ + +/* page-align a size */ +#define page_align(S)\ + (((S) + (LJ_PAGESIZE - SIZE_T_ONE)) & ~(LJ_PAGESIZE - SIZE_T_ONE)) + +/* granularity-align a size */ +#define granularity_align(S)\ + (((S) + (DEFAULT_GRANULARITY - SIZE_T_ONE))\ + & ~(DEFAULT_GRANULARITY - SIZE_T_ONE)) + +#if LJ_TARGET_WINDOWS +#define mmap_align(S) granularity_align(S) +#else +#define mmap_align(S) page_align(S) +#endif + +/* True if segment S holds address A */ +#define segment_holds(S, A)\ + ((char *)(A) >= S->base && (char *)(A) < S->base + S->size) + +/* Return segment holding given address */ +static msegmentptr segment_holding(mstate m, char *addr) +{ + msegmentptr sp = &m->seg; + for (;;) { + if (addr >= sp->base && addr < sp->base + sp->size) + return sp; + if ((sp = sp->next) == 0) + return 0; + } +} + +/* Return true if segment contains a segment link */ +static int has_segment_link(mstate m, msegmentptr ss) +{ + msegmentptr sp = &m->seg; + for (;;) { + if ((char *)sp >= ss->base && (char *)sp < ss->base + ss->size) + return 1; + if ((sp = sp->next) == 0) + return 0; + } +} + +/* + TOP_FOOT_SIZE is padding at the end of a segment, including space + that may be needed to place segment records and fenceposts when new + noncontiguous segments are added. +*/ +#define TOP_FOOT_SIZE\ + (align_offset(chunk2mem(0))+pad_request(sizeof(struct malloc_segment))+MIN_CHUNK_SIZE) + +/* ---------------------------- Indexing Bins ---------------------------- */ + +#define is_small(s) (((s) >> SMALLBIN_SHIFT) < NSMALLBINS) +#define small_index(s) ((s) >> SMALLBIN_SHIFT) +#define small_index2size(i) ((i) << SMALLBIN_SHIFT) +#define MIN_SMALL_INDEX (small_index(MIN_CHUNK_SIZE)) + +/* addressing by index. See above about smallbin repositioning */ +#define smallbin_at(M, i) ((sbinptr)((char *)&((M)->smallbins[(i)<<1]))) +#define treebin_at(M,i) (&((M)->treebins[i])) + +/* assign tree index for size S to variable I */ +#define compute_tree_index(S, I)\ +{\ + unsigned int X = (unsigned int)(S >> TREEBIN_SHIFT);\ + if (X == 0) {\ + I = 0;\ + } else if (X > 0xFFFF) {\ + I = NTREEBINS-1;\ + } else {\ + unsigned int K = lj_fls(X);\ + I = (bindex_t)((K << 1) + ((S >> (K + (TREEBIN_SHIFT-1)) & 1)));\ + }\ +} + +/* Bit representing maximum resolved size in a treebin at i */ +#define bit_for_tree_index(i) \ + (i == NTREEBINS-1)? (SIZE_T_BITSIZE-1) : (((i) >> 1) + TREEBIN_SHIFT - 2) + +/* Shift placing maximum resolved bit in a treebin at i as sign bit */ +#define leftshift_for_tree_index(i) \ + ((i == NTREEBINS-1)? 0 : \ + ((SIZE_T_BITSIZE-SIZE_T_ONE) - (((i) >> 1) + TREEBIN_SHIFT - 2))) + +/* The size of the smallest chunk held in bin with index i */ +#define minsize_for_tree_index(i) \ + ((SIZE_T_ONE << (((i) >> 1) + TREEBIN_SHIFT)) | \ + (((size_t)((i) & SIZE_T_ONE)) << (((i) >> 1) + TREEBIN_SHIFT - 1))) + +/* ------------------------ Operations on bin maps ----------------------- */ + +/* bit corresponding to given index */ +#define idx2bit(i) ((binmap_t)(1) << (i)) + +/* Mark/Clear bits with given index */ +#define mark_smallmap(M,i) ((M)->smallmap |= idx2bit(i)) +#define clear_smallmap(M,i) ((M)->smallmap &= ~idx2bit(i)) +#define smallmap_is_marked(M,i) ((M)->smallmap & idx2bit(i)) + +#define mark_treemap(M,i) ((M)->treemap |= idx2bit(i)) +#define clear_treemap(M,i) ((M)->treemap &= ~idx2bit(i)) +#define treemap_is_marked(M,i) ((M)->treemap & idx2bit(i)) + +/* mask with all bits to left of least bit of x on */ +#define left_bits(x) ((x<<1) | (~(x<<1)+1)) + +/* Set cinuse bit and pinuse bit of next chunk */ +#define set_inuse(M,p,s)\ + ((p)->head = (((p)->head & PINUSE_BIT)|s|CINUSE_BIT),\ + ((mchunkptr)(((char *)(p)) + (s)))->head |= PINUSE_BIT) + +/* Set cinuse and pinuse of this chunk and pinuse of next chunk */ +#define set_inuse_and_pinuse(M,p,s)\ + ((p)->head = (s|PINUSE_BIT|CINUSE_BIT),\ + ((mchunkptr)(((char *)(p)) + (s)))->head |= PINUSE_BIT) + +/* Set size, cinuse and pinuse bit of this chunk */ +#define set_size_and_pinuse_of_inuse_chunk(M, p, s)\ + ((p)->head = (s|PINUSE_BIT|CINUSE_BIT)) + +/* ----------------------- Operations on smallbins ----------------------- */ + +/* Link a free chunk into a smallbin */ +#define insert_small_chunk(M, P, S) {\ + bindex_t I = small_index(S);\ + mchunkptr B = smallbin_at(M, I);\ + mchunkptr F = B;\ + if (!smallmap_is_marked(M, I))\ + mark_smallmap(M, I);\ + else\ + F = B->fd;\ + B->fd = P;\ + F->bk = P;\ + P->fd = F;\ + P->bk = B;\ +} + +/* Unlink a chunk from a smallbin */ +#define unlink_small_chunk(M, P, S) {\ + mchunkptr F = P->fd;\ + mchunkptr B = P->bk;\ + bindex_t I = small_index(S);\ + if (F == B) {\ + clear_smallmap(M, I);\ + } else {\ + F->bk = B;\ + B->fd = F;\ + }\ +} + +/* Unlink the first chunk from a smallbin */ +#define unlink_first_small_chunk(M, B, P, I) {\ + mchunkptr F = P->fd;\ + if (B == F) {\ + clear_smallmap(M, I);\ + } else {\ + B->fd = F;\ + F->bk = B;\ + }\ +} + +/* Replace dv node, binning the old one */ +/* Used only when dvsize known to be small */ +#define replace_dv(M, P, S) {\ + size_t DVS = M->dvsize;\ + if (DVS != 0) {\ + mchunkptr DV = M->dv;\ + insert_small_chunk(M, DV, DVS);\ + }\ + M->dvsize = S;\ + M->dv = P;\ +} + +/* ------------------------- Operations on trees ------------------------- */ + +/* Insert chunk into tree */ +#define insert_large_chunk(M, X, S) {\ + tbinptr *H;\ + bindex_t I;\ + compute_tree_index(S, I);\ + H = treebin_at(M, I);\ + X->index = I;\ + X->child[0] = X->child[1] = 0;\ + if (!treemap_is_marked(M, I)) {\ + mark_treemap(M, I);\ + *H = X;\ + X->parent = (tchunkptr)H;\ + X->fd = X->bk = X;\ + } else {\ + tchunkptr T = *H;\ + size_t K = S << leftshift_for_tree_index(I);\ + for (;;) {\ + if (chunksize(T) != S) {\ + tchunkptr *C = &(T->child[(K >> (SIZE_T_BITSIZE-SIZE_T_ONE)) & 1]);\ + K <<= 1;\ + if (*C != 0) {\ + T = *C;\ + } else {\ + *C = X;\ + X->parent = T;\ + X->fd = X->bk = X;\ + break;\ + }\ + } else {\ + tchunkptr F = T->fd;\ + T->fd = F->bk = X;\ + X->fd = F;\ + X->bk = T;\ + X->parent = 0;\ + break;\ + }\ + }\ + }\ +} + +#define unlink_large_chunk(M, X) {\ + tchunkptr XP = X->parent;\ + tchunkptr R;\ + if (X->bk != X) {\ + tchunkptr F = X->fd;\ + R = X->bk;\ + F->bk = R;\ + R->fd = F;\ + } else {\ + tchunkptr *RP;\ + if (((R = *(RP = &(X->child[1]))) != 0) ||\ + ((R = *(RP = &(X->child[0]))) != 0)) {\ + tchunkptr *CP;\ + while ((*(CP = &(R->child[1])) != 0) ||\ + (*(CP = &(R->child[0])) != 0)) {\ + R = *(RP = CP);\ + }\ + *RP = 0;\ + }\ + }\ + if (XP != 0) {\ + tbinptr *H = treebin_at(M, X->index);\ + if (X == *H) {\ + if ((*H = R) == 0) \ + clear_treemap(M, X->index);\ + } else {\ + if (XP->child[0] == X) \ + XP->child[0] = R;\ + else \ + XP->child[1] = R;\ + }\ + if (R != 0) {\ + tchunkptr C0, C1;\ + R->parent = XP;\ + if ((C0 = X->child[0]) != 0) {\ + R->child[0] = C0;\ + C0->parent = R;\ + }\ + if ((C1 = X->child[1]) != 0) {\ + R->child[1] = C1;\ + C1->parent = R;\ + }\ + }\ + }\ +} + +/* Relays to large vs small bin operations */ + +#define insert_chunk(M, P, S)\ + if (is_small(S)) { insert_small_chunk(M, P, S)\ + } else { tchunkptr TP = (tchunkptr)(P); insert_large_chunk(M, TP, S); } + +#define unlink_chunk(M, P, S)\ + if (is_small(S)) { unlink_small_chunk(M, P, S)\ + } else { tchunkptr TP = (tchunkptr)(P); unlink_large_chunk(M, TP); } + +/* ----------------------- Direct-mmapping chunks ----------------------- */ + +static void *direct_alloc(size_t nb) +{ + size_t mmsize = mmap_align(nb + SIX_SIZE_T_SIZES + CHUNK_ALIGN_MASK); + if (LJ_LIKELY(mmsize > nb)) { /* Check for wrap around 0 */ + char *mm = (char *)(DIRECT_MMAP(mmsize)); + if (mm != CMFAIL) { + size_t offset = align_offset(chunk2mem(mm)); + size_t psize = mmsize - offset - DIRECT_FOOT_PAD; + mchunkptr p = (mchunkptr)(mm + offset); + p->prev_foot = offset | IS_DIRECT_BIT; + p->head = psize|CINUSE_BIT; + chunk_plus_offset(p, psize)->head = FENCEPOST_HEAD; + chunk_plus_offset(p, psize+SIZE_T_SIZE)->head = 0; + return chunk2mem(p); + } + } + return NULL; +} + +static mchunkptr direct_resize(mchunkptr oldp, size_t nb) +{ + size_t oldsize = chunksize(oldp); + if (is_small(nb)) /* Can't shrink direct regions below small size */ + return NULL; + /* Keep old chunk if big enough but not too big */ + if (oldsize >= nb + SIZE_T_SIZE && + (oldsize - nb) <= (DEFAULT_GRANULARITY >> 1)) { + return oldp; + } else { + size_t offset = oldp->prev_foot & ~IS_DIRECT_BIT; + size_t oldmmsize = oldsize + offset + DIRECT_FOOT_PAD; + size_t newmmsize = mmap_align(nb + SIX_SIZE_T_SIZES + CHUNK_ALIGN_MASK); + char *cp = (char *)CALL_MREMAP((char *)oldp - offset, + oldmmsize, newmmsize, CALL_MREMAP_MV); + if (cp != CMFAIL) { + mchunkptr newp = (mchunkptr)(cp + offset); + size_t psize = newmmsize - offset - DIRECT_FOOT_PAD; + newp->head = psize|CINUSE_BIT; + chunk_plus_offset(newp, psize)->head = FENCEPOST_HEAD; + chunk_plus_offset(newp, psize+SIZE_T_SIZE)->head = 0; + return newp; + } + } + return NULL; +} + +/* -------------------------- mspace management -------------------------- */ + +/* Initialize top chunk and its size */ +static void init_top(mstate m, mchunkptr p, size_t psize) +{ + /* Ensure alignment */ + size_t offset = align_offset(chunk2mem(p)); + p = (mchunkptr)((char *)p + offset); + psize -= offset; + + m->top = p; + m->topsize = psize; + p->head = psize | PINUSE_BIT; + /* set size of fake trailing chunk holding overhead space only once */ + chunk_plus_offset(p, psize)->head = TOP_FOOT_SIZE; + m->trim_check = DEFAULT_TRIM_THRESHOLD; /* reset on each update */ +} + +/* Initialize bins for a new mstate that is otherwise zeroed out */ +static void init_bins(mstate m) +{ + /* Establish circular links for smallbins */ + bindex_t i; + for (i = 0; i < NSMALLBINS; i++) { + sbinptr bin = smallbin_at(m,i); + bin->fd = bin->bk = bin; + } +} + +/* Allocate chunk and prepend remainder with chunk in successor base. */ +static void *prepend_alloc(mstate m, char *newbase, char *oldbase, size_t nb) +{ + mchunkptr p = align_as_chunk(newbase); + mchunkptr oldfirst = align_as_chunk(oldbase); + size_t psize = (size_t)((char *)oldfirst - (char *)p); + mchunkptr q = chunk_plus_offset(p, nb); + size_t qsize = psize - nb; + set_size_and_pinuse_of_inuse_chunk(m, p, nb); + + /* consolidate remainder with first chunk of old base */ + if (oldfirst == m->top) { + size_t tsize = m->topsize += qsize; + m->top = q; + q->head = tsize | PINUSE_BIT; + } else if (oldfirst == m->dv) { + size_t dsize = m->dvsize += qsize; + m->dv = q; + set_size_and_pinuse_of_free_chunk(q, dsize); + } else { + if (!cinuse(oldfirst)) { + size_t nsize = chunksize(oldfirst); + unlink_chunk(m, oldfirst, nsize); + oldfirst = chunk_plus_offset(oldfirst, nsize); + qsize += nsize; + } + set_free_with_pinuse(q, qsize, oldfirst); + insert_chunk(m, q, qsize); + } + + return chunk2mem(p); +} + +/* Add a segment to hold a new noncontiguous region */ +static void add_segment(mstate m, char *tbase, size_t tsize) +{ + /* Determine locations and sizes of segment, fenceposts, old top */ + char *old_top = (char *)m->top; + msegmentptr oldsp = segment_holding(m, old_top); + char *old_end = oldsp->base + oldsp->size; + size_t ssize = pad_request(sizeof(struct malloc_segment)); + char *rawsp = old_end - (ssize + FOUR_SIZE_T_SIZES + CHUNK_ALIGN_MASK); + size_t offset = align_offset(chunk2mem(rawsp)); + char *asp = rawsp + offset; + char *csp = (asp < (old_top + MIN_CHUNK_SIZE))? old_top : asp; + mchunkptr sp = (mchunkptr)csp; + msegmentptr ss = (msegmentptr)(chunk2mem(sp)); + mchunkptr tnext = chunk_plus_offset(sp, ssize); + mchunkptr p = tnext; + + /* reset top to new space */ + init_top(m, (mchunkptr)tbase, tsize - TOP_FOOT_SIZE); + + /* Set up segment record */ + set_size_and_pinuse_of_inuse_chunk(m, sp, ssize); + *ss = m->seg; /* Push current record */ + m->seg.base = tbase; + m->seg.size = tsize; + m->seg.next = ss; + + /* Insert trailing fenceposts */ + for (;;) { + mchunkptr nextp = chunk_plus_offset(p, SIZE_T_SIZE); + p->head = FENCEPOST_HEAD; + if ((char *)(&(nextp->head)) < old_end) + p = nextp; + else + break; + } + + /* Insert the rest of old top into a bin as an ordinary free chunk */ + if (csp != old_top) { + mchunkptr q = (mchunkptr)old_top; + size_t psize = (size_t)(csp - old_top); + mchunkptr tn = chunk_plus_offset(q, psize); + set_free_with_pinuse(q, psize, tn); + insert_chunk(m, q, psize); + } +} + +/* -------------------------- System allocation -------------------------- */ + +static void *alloc_sys(mstate m, size_t nb) +{ + char *tbase = CMFAIL; + size_t tsize = 0; + + /* Directly map large chunks */ + if (LJ_UNLIKELY(nb >= DEFAULT_MMAP_THRESHOLD)) { + void *mem = direct_alloc(nb); + if (mem != 0) + return mem; + } + + { + size_t req = nb + TOP_FOOT_SIZE + SIZE_T_ONE; + size_t rsize = granularity_align(req); + if (LJ_LIKELY(rsize > nb)) { /* Fail if wraps around zero */ + char *mp = (char *)(CALL_MMAP(rsize)); + if (mp != CMFAIL) { + tbase = mp; + tsize = rsize; + } + } + } + + if (tbase != CMFAIL) { + msegmentptr sp = &m->seg; + /* Try to merge with an existing segment */ + while (sp != 0 && tbase != sp->base + sp->size) + sp = sp->next; + if (sp != 0 && segment_holds(sp, m->top)) { /* append */ + sp->size += tsize; + init_top(m, m->top, m->topsize + tsize); + } else { + sp = &m->seg; + while (sp != 0 && sp->base != tbase + tsize) + sp = sp->next; + if (sp != 0) { + char *oldbase = sp->base; + sp->base = tbase; + sp->size += tsize; + return prepend_alloc(m, tbase, oldbase, nb); + } else { + add_segment(m, tbase, tsize); + } + } + + if (nb < m->topsize) { /* Allocate from new or extended top space */ + size_t rsize = m->topsize -= nb; + mchunkptr p = m->top; + mchunkptr r = m->top = chunk_plus_offset(p, nb); + r->head = rsize | PINUSE_BIT; + set_size_and_pinuse_of_inuse_chunk(m, p, nb); + return chunk2mem(p); + } + } + + return NULL; +} + +/* ----------------------- system deallocation -------------------------- */ + +/* Unmap and unlink any mmapped segments that don't contain used chunks */ +static size_t release_unused_segments(mstate m) +{ + size_t released = 0; + size_t nsegs = 0; + msegmentptr pred = &m->seg; + msegmentptr sp = pred->next; + while (sp != 0) { + char *base = sp->base; + size_t size = sp->size; + msegmentptr next = sp->next; + nsegs++; + { + mchunkptr p = align_as_chunk(base); + size_t psize = chunksize(p); + /* Can unmap if first chunk holds entire segment and not pinned */ + if (!cinuse(p) && (char *)p + psize >= base + size - TOP_FOOT_SIZE) { + tchunkptr tp = (tchunkptr)p; + if (p == m->dv) { + m->dv = 0; + m->dvsize = 0; + } else { + unlink_large_chunk(m, tp); + } + if (CALL_MUNMAP(base, size) == 0) { + released += size; + /* unlink obsoleted record */ + sp = pred; + sp->next = next; + } else { /* back out if cannot unmap */ + insert_large_chunk(m, tp, psize); + } + } + } + pred = sp; + sp = next; + } + /* Reset check counter */ + m->release_checks = nsegs > MAX_RELEASE_CHECK_RATE ? + nsegs : MAX_RELEASE_CHECK_RATE; + return released; +} + +static int alloc_trim(mstate m, size_t pad) +{ + size_t released = 0; + if (pad < MAX_REQUEST && is_initialized(m)) { + pad += TOP_FOOT_SIZE; /* ensure enough room for segment overhead */ + + if (m->topsize > pad) { + /* Shrink top space in granularity-size units, keeping at least one */ + size_t unit = DEFAULT_GRANULARITY; + size_t extra = ((m->topsize - pad + (unit - SIZE_T_ONE)) / unit - + SIZE_T_ONE) * unit; + msegmentptr sp = segment_holding(m, (char *)m->top); + + if (sp->size >= extra && + !has_segment_link(m, sp)) { /* can't shrink if pinned */ + size_t newsize = sp->size - extra; + /* Prefer mremap, fall back to munmap */ + if ((CALL_MREMAP(sp->base, sp->size, newsize, CALL_MREMAP_NOMOVE) != MFAIL) || + (CALL_MUNMAP(sp->base + newsize, extra) == 0)) { + released = extra; + } + } + + if (released != 0) { + sp->size -= released; + init_top(m, m->top, m->topsize - released); + } + } + + /* Unmap any unused mmapped segments */ + released += release_unused_segments(m); + + /* On failure, disable autotrim to avoid repeated failed future calls */ + if (released == 0 && m->topsize > m->trim_check) + m->trim_check = MAX_SIZE_T; + } + + return (released != 0)? 1 : 0; +} + +/* ---------------------------- malloc support --------------------------- */ + +/* allocate a large request from the best fitting chunk in a treebin */ +static void *tmalloc_large(mstate m, size_t nb) +{ + tchunkptr v = 0; + size_t rsize = ~nb+1; /* Unsigned negation */ + tchunkptr t; + bindex_t idx; + compute_tree_index(nb, idx); + + if ((t = *treebin_at(m, idx)) != 0) { + /* Traverse tree for this bin looking for node with size == nb */ + size_t sizebits = nb << leftshift_for_tree_index(idx); + tchunkptr rst = 0; /* The deepest untaken right subtree */ + for (;;) { + tchunkptr rt; + size_t trem = chunksize(t) - nb; + if (trem < rsize) { + v = t; + if ((rsize = trem) == 0) + break; + } + rt = t->child[1]; + t = t->child[(sizebits >> (SIZE_T_BITSIZE-SIZE_T_ONE)) & 1]; + if (rt != 0 && rt != t) + rst = rt; + if (t == 0) { + t = rst; /* set t to least subtree holding sizes > nb */ + break; + } + sizebits <<= 1; + } + } + + if (t == 0 && v == 0) { /* set t to root of next non-empty treebin */ + binmap_t leftbits = left_bits(idx2bit(idx)) & m->treemap; + if (leftbits != 0) + t = *treebin_at(m, lj_ffs(leftbits)); + } + + while (t != 0) { /* find smallest of tree or subtree */ + size_t trem = chunksize(t) - nb; + if (trem < rsize) { + rsize = trem; + v = t; + } + t = leftmost_child(t); + } + + /* If dv is a better fit, return NULL so malloc will use it */ + if (v != 0 && rsize < (size_t)(m->dvsize - nb)) { + mchunkptr r = chunk_plus_offset(v, nb); + unlink_large_chunk(m, v); + if (rsize < MIN_CHUNK_SIZE) { + set_inuse_and_pinuse(m, v, (rsize + nb)); + } else { + set_size_and_pinuse_of_inuse_chunk(m, v, nb); + set_size_and_pinuse_of_free_chunk(r, rsize); + insert_chunk(m, r, rsize); + } + return chunk2mem(v); + } + return NULL; +} + +/* allocate a small request from the best fitting chunk in a treebin */ +static void *tmalloc_small(mstate m, size_t nb) +{ + tchunkptr t, v; + mchunkptr r; + size_t rsize; + bindex_t i = lj_ffs(m->treemap); + + v = t = *treebin_at(m, i); + rsize = chunksize(t) - nb; + + while ((t = leftmost_child(t)) != 0) { + size_t trem = chunksize(t) - nb; + if (trem < rsize) { + rsize = trem; + v = t; + } + } + + r = chunk_plus_offset(v, nb); + unlink_large_chunk(m, v); + if (rsize < MIN_CHUNK_SIZE) { + set_inuse_and_pinuse(m, v, (rsize + nb)); + } else { + set_size_and_pinuse_of_inuse_chunk(m, v, nb); + set_size_and_pinuse_of_free_chunk(r, rsize); + replace_dv(m, r, rsize); + } + return chunk2mem(v); +} + +/* ----------------------------------------------------------------------- */ + +void *lj_alloc_create(void) +{ + size_t tsize = DEFAULT_GRANULARITY; + char *tbase; + INIT_MMAP(); + tbase = (char *)(CALL_MMAP(tsize)); + if (tbase != CMFAIL) { + size_t msize = pad_request(sizeof(struct malloc_state)); + mchunkptr mn; + mchunkptr msp = align_as_chunk(tbase); + mstate m = (mstate)(chunk2mem(msp)); + memset(m, 0, msize); + msp->head = (msize|PINUSE_BIT|CINUSE_BIT); + m->seg.base = tbase; + m->seg.size = tsize; + m->release_checks = MAX_RELEASE_CHECK_RATE; + init_bins(m); + mn = next_chunk(mem2chunk(m)); + init_top(m, mn, (size_t)((tbase + tsize) - (char *)mn) - TOP_FOOT_SIZE); + return m; + } + return NULL; +} + +void lj_alloc_destroy(void *msp) +{ + mstate ms = (mstate)msp; + msegmentptr sp = &ms->seg; + while (sp != 0) { + char *base = sp->base; + size_t size = sp->size; + sp = sp->next; + CALL_MUNMAP(base, size); + } +} + +static LJ_NOINLINE void *lj_alloc_malloc(void *msp, size_t nsize) +{ + mstate ms = (mstate)msp; + void *mem; + size_t nb; + if (nsize <= MAX_SMALL_REQUEST) { + bindex_t idx; + binmap_t smallbits; + nb = (nsize < MIN_REQUEST)? MIN_CHUNK_SIZE : pad_request(nsize); + idx = small_index(nb); + smallbits = ms->smallmap >> idx; + + if ((smallbits & 0x3U) != 0) { /* Remainderless fit to a smallbin. */ + mchunkptr b, p; + idx += ~smallbits & 1; /* Uses next bin if idx empty */ + b = smallbin_at(ms, idx); + p = b->fd; + unlink_first_small_chunk(ms, b, p, idx); + set_inuse_and_pinuse(ms, p, small_index2size(idx)); + mem = chunk2mem(p); + return mem; + } else if (nb > ms->dvsize) { + if (smallbits != 0) { /* Use chunk in next nonempty smallbin */ + mchunkptr b, p, r; + size_t rsize; + binmap_t leftbits = (smallbits << idx) & left_bits(idx2bit(idx)); + bindex_t i = lj_ffs(leftbits); + b = smallbin_at(ms, i); + p = b->fd; + unlink_first_small_chunk(ms, b, p, i); + rsize = small_index2size(i) - nb; + /* Fit here cannot be remainderless if 4byte sizes */ + if (SIZE_T_SIZE != 4 && rsize < MIN_CHUNK_SIZE) { + set_inuse_and_pinuse(ms, p, small_index2size(i)); + } else { + set_size_and_pinuse_of_inuse_chunk(ms, p, nb); + r = chunk_plus_offset(p, nb); + set_size_and_pinuse_of_free_chunk(r, rsize); + replace_dv(ms, r, rsize); + } + mem = chunk2mem(p); + return mem; + } else if (ms->treemap != 0 && (mem = tmalloc_small(ms, nb)) != 0) { + return mem; + } + } + } else if (nsize >= MAX_REQUEST) { + nb = MAX_SIZE_T; /* Too big to allocate. Force failure (in sys alloc) */ + } else { + nb = pad_request(nsize); + if (ms->treemap != 0 && (mem = tmalloc_large(ms, nb)) != 0) { + return mem; + } + } + + if (nb <= ms->dvsize) { + size_t rsize = ms->dvsize - nb; + mchunkptr p = ms->dv; + if (rsize >= MIN_CHUNK_SIZE) { /* split dv */ + mchunkptr r = ms->dv = chunk_plus_offset(p, nb); + ms->dvsize = rsize; + set_size_and_pinuse_of_free_chunk(r, rsize); + set_size_and_pinuse_of_inuse_chunk(ms, p, nb); + } else { /* exhaust dv */ + size_t dvs = ms->dvsize; + ms->dvsize = 0; + ms->dv = 0; + set_inuse_and_pinuse(ms, p, dvs); + } + mem = chunk2mem(p); + return mem; + } else if (nb < ms->topsize) { /* Split top */ + size_t rsize = ms->topsize -= nb; + mchunkptr p = ms->top; + mchunkptr r = ms->top = chunk_plus_offset(p, nb); + r->head = rsize | PINUSE_BIT; + set_size_and_pinuse_of_inuse_chunk(ms, p, nb); + mem = chunk2mem(p); + return mem; + } + return alloc_sys(ms, nb); +} + +static LJ_NOINLINE void *lj_alloc_free(void *msp, void *ptr) +{ + if (ptr != 0) { + mchunkptr p = mem2chunk(ptr); + mstate fm = (mstate)msp; + size_t psize = chunksize(p); + mchunkptr next = chunk_plus_offset(p, psize); + if (!pinuse(p)) { + size_t prevsize = p->prev_foot; + if ((prevsize & IS_DIRECT_BIT) != 0) { + prevsize &= ~IS_DIRECT_BIT; + psize += prevsize + DIRECT_FOOT_PAD; + CALL_MUNMAP((char *)p - prevsize, psize); + return NULL; + } else { + mchunkptr prev = chunk_minus_offset(p, prevsize); + psize += prevsize; + p = prev; + /* consolidate backward */ + if (p != fm->dv) { + unlink_chunk(fm, p, prevsize); + } else if ((next->head & INUSE_BITS) == INUSE_BITS) { + fm->dvsize = psize; + set_free_with_pinuse(p, psize, next); + return NULL; + } + } + } + if (!cinuse(next)) { /* consolidate forward */ + if (next == fm->top) { + size_t tsize = fm->topsize += psize; + fm->top = p; + p->head = tsize | PINUSE_BIT; + if (p == fm->dv) { + fm->dv = 0; + fm->dvsize = 0; + } + if (tsize > fm->trim_check) + alloc_trim(fm, 0); + return NULL; + } else if (next == fm->dv) { + size_t dsize = fm->dvsize += psize; + fm->dv = p; + set_size_and_pinuse_of_free_chunk(p, dsize); + return NULL; + } else { + size_t nsize = chunksize(next); + psize += nsize; + unlink_chunk(fm, next, nsize); + set_size_and_pinuse_of_free_chunk(p, psize); + if (p == fm->dv) { + fm->dvsize = psize; + return NULL; + } + } + } else { + set_free_with_pinuse(p, psize, next); + } + + if (is_small(psize)) { + insert_small_chunk(fm, p, psize); + } else { + tchunkptr tp = (tchunkptr)p; + insert_large_chunk(fm, tp, psize); + if (--fm->release_checks == 0) + release_unused_segments(fm); + } + } + return NULL; +} + +static LJ_NOINLINE void *lj_alloc_realloc(void *msp, void *ptr, size_t nsize) +{ + if (nsize >= MAX_REQUEST) { + return NULL; + } else { + mstate m = (mstate)msp; + mchunkptr oldp = mem2chunk(ptr); + size_t oldsize = chunksize(oldp); + mchunkptr next = chunk_plus_offset(oldp, oldsize); + mchunkptr newp = 0; + size_t nb = request2size(nsize); + + /* Try to either shrink or extend into top. Else malloc-copy-free */ + if (is_direct(oldp)) { + newp = direct_resize(oldp, nb); /* this may return NULL. */ + } else if (oldsize >= nb) { /* already big enough */ + size_t rsize = oldsize - nb; + newp = oldp; + if (rsize >= MIN_CHUNK_SIZE) { + mchunkptr rem = chunk_plus_offset(newp, nb); + set_inuse(m, newp, nb); + set_inuse(m, rem, rsize); + lj_alloc_free(m, chunk2mem(rem)); + } + } else if (next == m->top && oldsize + m->topsize > nb) { + /* Expand into top */ + size_t newsize = oldsize + m->topsize; + size_t newtopsize = newsize - nb; + mchunkptr newtop = chunk_plus_offset(oldp, nb); + set_inuse(m, oldp, nb); + newtop->head = newtopsize |PINUSE_BIT; + m->top = newtop; + m->topsize = newtopsize; + newp = oldp; + } + + if (newp != 0) { + return chunk2mem(newp); + } else { + void *newmem = lj_alloc_malloc(m, nsize); + if (newmem != 0) { + size_t oc = oldsize - overhead_for(oldp); + memcpy(newmem, ptr, oc < nsize ? oc : nsize); + lj_alloc_free(m, ptr); + } + return newmem; + } + } +} + +void *lj_alloc_f(void *msp, void *ptr, size_t osize, size_t nsize) +{ + (void)osize; + if (nsize == 0) { + return lj_alloc_free(msp, ptr); + } else if (ptr == NULL) { + return lj_alloc_malloc(msp, nsize); + } else { + return lj_alloc_realloc(msp, ptr, nsize); + } +} + +#endif diff --git a/lib/LuaJIT/lj_alloc.h b/lib/LuaJIT/lj_alloc.h new file mode 100644 index 0000000..f87a7cf --- /dev/null +++ b/lib/LuaJIT/lj_alloc.h @@ -0,0 +1,17 @@ +/* +** Bundled memory allocator. +** Donated to the public domain. +*/ + +#ifndef _LJ_ALLOC_H +#define _LJ_ALLOC_H + +#include "lj_def.h" + +#ifndef LUAJIT_USE_SYSMALLOC +LJ_FUNC void *lj_alloc_create(void); +LJ_FUNC void lj_alloc_destroy(void *msp); +LJ_FUNC void *lj_alloc_f(void *msp, void *ptr, size_t osize, size_t nsize); +#endif + +#endif diff --git a/lib/LuaJIT/lj_alloc.o b/lib/LuaJIT/lj_alloc.o new file mode 100644 index 0000000000000000000000000000000000000000..ef62aa0761b76e3fb36cd68a963eb4f37fbe4ab1 GIT binary patch literal 13248 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 - L->base)) +#define api_checkvalidindex(L, i) api_check(L, (i) != niltv(L)) + +static TValue *index2adr(lua_State *L, int idx) +{ + if (idx > 0) { + TValue *o = L->base + (idx - 1); + return o < L->top ? o : niltv(L); + } else if (idx > LUA_REGISTRYINDEX) { + api_check(L, idx != 0 && -idx <= L->top - L->base); + return L->top + idx; + } else if (idx == LUA_GLOBALSINDEX) { + TValue *o = &G(L)->tmptv; + settabV(L, o, tabref(L->env)); + return o; + } else if (idx == LUA_REGISTRYINDEX) { + return registry(L); + } else { + GCfunc *fn = curr_func(L); + api_check(L, fn->c.gct == ~LJ_TFUNC && !isluafunc(fn)); + if (idx == LUA_ENVIRONINDEX) { + TValue *o = &G(L)->tmptv; + settabV(L, o, tabref(fn->c.env)); + return o; + } else { + idx = LUA_GLOBALSINDEX - idx; + return idx <= fn->c.nupvalues ? &fn->c.upvalue[idx-1] : niltv(L); + } + } +} + +static TValue *stkindex2adr(lua_State *L, int idx) +{ + if (idx > 0) { + TValue *o = L->base + (idx - 1); + return o < L->top ? o : niltv(L); + } else { + api_check(L, idx != 0 && -idx <= L->top - L->base); + return L->top + idx; + } +} + +static GCtab *getcurrenv(lua_State *L) +{ + GCfunc *fn = curr_func(L); + return fn->c.gct == ~LJ_TFUNC ? tabref(fn->c.env) : tabref(L->env); +} + +/* -- Miscellaneous API functions ----------------------------------------- */ + +LUA_API int lua_status(lua_State *L) +{ + return L->status; +} + +LUA_API int lua_checkstack(lua_State *L, int size) +{ + if (size > LUAI_MAXCSTACK || (L->top - L->base + size) > LUAI_MAXCSTACK) { + return 0; /* Stack overflow. */ + } else if (size > 0) { + lj_state_checkstack(L, (MSize)size); + } + return 1; +} + +LUALIB_API void luaL_checkstack(lua_State *L, int size, const char *msg) +{ + if (!lua_checkstack(L, size)) + lj_err_callerv(L, LJ_ERR_STKOVM, msg); +} + +LUA_API void lua_xmove(lua_State *from, lua_State *to, int n) +{ + TValue *f, *t; + if (from == to) return; + api_checknelems(from, n); + api_check(from, G(from) == G(to)); + lj_state_checkstack(to, (MSize)n); + f = from->top; + t = to->top = to->top + n; + while (--n >= 0) copyTV(to, --t, --f); + from->top = f; +} + +LUA_API const lua_Number *lua_version(lua_State *L) +{ + static const lua_Number version = LUA_VERSION_NUM; + UNUSED(L); + return &version; +} + +/* -- Stack manipulation -------------------------------------------------- */ + +LUA_API int lua_gettop(lua_State *L) +{ + return (int)(L->top - L->base); +} + +LUA_API void lua_settop(lua_State *L, int idx) +{ + if (idx >= 0) { + api_check(L, idx <= tvref(L->maxstack) - L->base); + if (L->base + idx > L->top) { + if (L->base + idx >= tvref(L->maxstack)) + lj_state_growstack(L, (MSize)idx - (MSize)(L->top - L->base)); + do { setnilV(L->top++); } while (L->top < L->base + idx); + } else { + L->top = L->base + idx; + } + } else { + api_check(L, -(idx+1) <= (L->top - L->base)); + L->top += idx+1; /* Shrinks top (idx < 0). */ + } +} + +LUA_API void lua_remove(lua_State *L, int idx) +{ + TValue *p = stkindex2adr(L, idx); + api_checkvalidindex(L, p); + while (++p < L->top) copyTV(L, p-1, p); + L->top--; +} + +LUA_API void lua_insert(lua_State *L, int idx) +{ + TValue *q, *p = stkindex2adr(L, idx); + api_checkvalidindex(L, p); + for (q = L->top; q > p; q--) copyTV(L, q, q-1); + copyTV(L, p, L->top); +} + +static void copy_slot(lua_State *L, TValue *f, int idx) +{ + if (idx == LUA_GLOBALSINDEX) { + api_check(L, tvistab(f)); + /* NOBARRIER: A thread (i.e. L) is never black. */ + setgcref(L->env, obj2gco(tabV(f))); + } else if (idx == LUA_ENVIRONINDEX) { + GCfunc *fn = curr_func(L); + if (fn->c.gct != ~LJ_TFUNC) + lj_err_msg(L, LJ_ERR_NOENV); + api_check(L, tvistab(f)); + setgcref(fn->c.env, obj2gco(tabV(f))); + lj_gc_barrier(L, fn, f); + } else { + TValue *o = index2adr(L, idx); + api_checkvalidindex(L, o); + copyTV(L, o, f); + if (idx < LUA_GLOBALSINDEX) /* Need a barrier for upvalues. */ + lj_gc_barrier(L, curr_func(L), f); + } +} + +LUA_API void lua_replace(lua_State *L, int idx) +{ + api_checknelems(L, 1); + copy_slot(L, L->top - 1, idx); + L->top--; +} + +LUA_API void lua_copy(lua_State *L, int fromidx, int toidx) +{ + copy_slot(L, index2adr(L, fromidx), toidx); +} + +LUA_API void lua_pushvalue(lua_State *L, int idx) +{ + copyTV(L, L->top, index2adr(L, idx)); + incr_top(L); +} + +/* -- Stack getters ------------------------------------------------------- */ + +LUA_API int lua_type(lua_State *L, int idx) +{ + cTValue *o = index2adr(L, idx); + if (tvisnumber(o)) { + return LUA_TNUMBER; +#if LJ_64 && !LJ_GC64 + } else if (tvislightud(o)) { + return LUA_TLIGHTUSERDATA; +#endif + } else if (o == niltv(L)) { + return LUA_TNONE; + } else { /* Magic internal/external tag conversion. ORDER LJ_T */ + uint32_t t = ~itype(o); +#if LJ_64 + int tt = (int)((U64x(75a06,98042110) >> 4*t) & 15u); +#else + int tt = (int)(((t < 8 ? 0x98042110u : 0x75a06u) >> 4*(t&7)) & 15u); +#endif + lua_assert(tt != LUA_TNIL || tvisnil(o)); + return tt; + } +} + +LUALIB_API void luaL_checktype(lua_State *L, int idx, int tt) +{ + if (lua_type(L, idx) != tt) + lj_err_argt(L, idx, tt); +} + +LUALIB_API void luaL_checkany(lua_State *L, int idx) +{ + if (index2adr(L, idx) == niltv(L)) + lj_err_arg(L, idx, LJ_ERR_NOVAL); +} + +LUA_API const char *lua_typename(lua_State *L, int t) +{ + UNUSED(L); + return lj_obj_typename[t+1]; +} + +LUA_API int lua_iscfunction(lua_State *L, int idx) +{ + cTValue *o = index2adr(L, idx); + return tvisfunc(o) && !isluafunc(funcV(o)); +} + +LUA_API int lua_isnumber(lua_State *L, int idx) +{ + cTValue *o = index2adr(L, idx); + TValue tmp; + return (tvisnumber(o) || (tvisstr(o) && lj_strscan_number(strV(o), &tmp))); +} + +LUA_API int lua_isstring(lua_State *L, int idx) +{ + cTValue *o = index2adr(L, idx); + return (tvisstr(o) || tvisnumber(o)); +} + +LUA_API int lua_isuserdata(lua_State *L, int idx) +{ + cTValue *o = index2adr(L, idx); + return (tvisudata(o) || tvislightud(o)); +} + +LUA_API int lua_rawequal(lua_State *L, int idx1, int idx2) +{ + cTValue *o1 = index2adr(L, idx1); + cTValue *o2 = index2adr(L, idx2); + return (o1 == niltv(L) || o2 == niltv(L)) ? 0 : lj_obj_equal(o1, o2); +} + +LUA_API int lua_equal(lua_State *L, int idx1, int idx2) +{ + cTValue *o1 = index2adr(L, idx1); + cTValue *o2 = index2adr(L, idx2); + if (tvisint(o1) && tvisint(o2)) { + return intV(o1) == intV(o2); + } else if (tvisnumber(o1) && tvisnumber(o2)) { + return numberVnum(o1) == numberVnum(o2); + } else if (itype(o1) != itype(o2)) { + return 0; + } else if (tvispri(o1)) { + return o1 != niltv(L) && o2 != niltv(L); +#if LJ_64 && !LJ_GC64 + } else if (tvislightud(o1)) { + return o1->u64 == o2->u64; +#endif + } else if (gcrefeq(o1->gcr, o2->gcr)) { + return 1; + } else if (!tvistabud(o1)) { + return 0; + } else { + TValue *base = lj_meta_equal(L, gcV(o1), gcV(o2), 0); + if ((uintptr_t)base <= 1) { + return (int)(uintptr_t)base; + } else { + L->top = base+2; + lj_vm_call(L, base, 1+1); + L->top -= 2+LJ_FR2; + return tvistruecond(L->top+1+LJ_FR2); + } + } +} + +LUA_API int lua_lessthan(lua_State *L, int idx1, int idx2) +{ + cTValue *o1 = index2adr(L, idx1); + cTValue *o2 = index2adr(L, idx2); + if (o1 == niltv(L) || o2 == niltv(L)) { + return 0; + } else if (tvisint(o1) && tvisint(o2)) { + return intV(o1) < intV(o2); + } else if (tvisnumber(o1) && tvisnumber(o2)) { + return numberVnum(o1) < numberVnum(o2); + } else { + TValue *base = lj_meta_comp(L, o1, o2, 0); + if ((uintptr_t)base <= 1) { + return (int)(uintptr_t)base; + } else { + L->top = base+2; + lj_vm_call(L, base, 1+1); + L->top -= 2+LJ_FR2; + return tvistruecond(L->top+1+LJ_FR2); + } + } +} + +LUA_API lua_Number lua_tonumber(lua_State *L, int idx) +{ + cTValue *o = index2adr(L, idx); + TValue tmp; + if (LJ_LIKELY(tvisnumber(o))) + return numberVnum(o); + else if (tvisstr(o) && lj_strscan_num(strV(o), &tmp)) + return numV(&tmp); + else + return 0; +} + +LUA_API lua_Number lua_tonumberx(lua_State *L, int idx, int *ok) +{ + cTValue *o = index2adr(L, idx); + TValue tmp; + if (LJ_LIKELY(tvisnumber(o))) { + if (ok) *ok = 1; + return numberVnum(o); + } else if (tvisstr(o) && lj_strscan_num(strV(o), &tmp)) { + if (ok) *ok = 1; + return numV(&tmp); + } else { + if (ok) *ok = 0; + return 0; + } +} + +LUALIB_API lua_Number luaL_checknumber(lua_State *L, int idx) +{ + cTValue *o = index2adr(L, idx); + TValue tmp; + if (LJ_LIKELY(tvisnumber(o))) + return numberVnum(o); + else if (!(tvisstr(o) && lj_strscan_num(strV(o), &tmp))) + lj_err_argt(L, idx, LUA_TNUMBER); + return numV(&tmp); +} + +LUALIB_API lua_Number luaL_optnumber(lua_State *L, int idx, lua_Number def) +{ + cTValue *o = index2adr(L, idx); + TValue tmp; + if (LJ_LIKELY(tvisnumber(o))) + return numberVnum(o); + else if (tvisnil(o)) + return def; + else if (!(tvisstr(o) && lj_strscan_num(strV(o), &tmp))) + lj_err_argt(L, idx, LUA_TNUMBER); + return numV(&tmp); +} + +LUA_API lua_Integer lua_tointeger(lua_State *L, int idx) +{ + cTValue *o = index2adr(L, idx); + TValue tmp; + lua_Number n; + if (LJ_LIKELY(tvisint(o))) { + return intV(o); + } else if (LJ_LIKELY(tvisnum(o))) { + n = numV(o); + } else { + if (!(tvisstr(o) && lj_strscan_number(strV(o), &tmp))) + return 0; + if (tvisint(&tmp)) + return intV(&tmp); + n = numV(&tmp); + } +#if LJ_64 + return (lua_Integer)n; +#else + return lj_num2int(n); +#endif +} + +LUA_API lua_Integer lua_tointegerx(lua_State *L, int idx, int *ok) +{ + cTValue *o = index2adr(L, idx); + TValue tmp; + lua_Number n; + if (LJ_LIKELY(tvisint(o))) { + if (ok) *ok = 1; + return intV(o); + } else if (LJ_LIKELY(tvisnum(o))) { + n = numV(o); + } else { + if (!(tvisstr(o) && lj_strscan_number(strV(o), &tmp))) { + if (ok) *ok = 0; + return 0; + } + if (tvisint(&tmp)) { + if (ok) *ok = 1; + return intV(&tmp); + } + n = numV(&tmp); + } + if (ok) *ok = 1; +#if LJ_64 + return (lua_Integer)n; +#else + return lj_num2int(n); +#endif +} + +LUALIB_API lua_Integer luaL_checkinteger(lua_State *L, int idx) +{ + cTValue *o = index2adr(L, idx); + TValue tmp; + lua_Number n; + if (LJ_LIKELY(tvisint(o))) { + return intV(o); + } else if (LJ_LIKELY(tvisnum(o))) { + n = numV(o); + } else { + if (!(tvisstr(o) && lj_strscan_number(strV(o), &tmp))) + lj_err_argt(L, idx, LUA_TNUMBER); + if (tvisint(&tmp)) + return (lua_Integer)intV(&tmp); + n = numV(&tmp); + } +#if LJ_64 + return (lua_Integer)n; +#else + return lj_num2int(n); +#endif +} + +LUALIB_API lua_Integer luaL_optinteger(lua_State *L, int idx, lua_Integer def) +{ + cTValue *o = index2adr(L, idx); + TValue tmp; + lua_Number n; + if (LJ_LIKELY(tvisint(o))) { + return intV(o); + } else if (LJ_LIKELY(tvisnum(o))) { + n = numV(o); + } else if (tvisnil(o)) { + return def; + } else { + if (!(tvisstr(o) && lj_strscan_number(strV(o), &tmp))) + lj_err_argt(L, idx, LUA_TNUMBER); + if (tvisint(&tmp)) + return (lua_Integer)intV(&tmp); + n = numV(&tmp); + } +#if LJ_64 + return (lua_Integer)n; +#else + return lj_num2int(n); +#endif +} + +LUA_API int lua_toboolean(lua_State *L, int idx) +{ + cTValue *o = index2adr(L, idx); + return tvistruecond(o); +} + +LUA_API const char *lua_tolstring(lua_State *L, int idx, size_t *len) +{ + TValue *o = index2adr(L, idx); + GCstr *s; + if (LJ_LIKELY(tvisstr(o))) { + s = strV(o); + } else if (tvisnumber(o)) { + lj_gc_check(L); + o = index2adr(L, idx); /* GC may move the stack. */ + s = lj_strfmt_number(L, o); + setstrV(L, o, s); + } else { + if (len != NULL) *len = 0; + return NULL; + } + if (len != NULL) *len = s->len; + return strdata(s); +} + +LUALIB_API const char *luaL_checklstring(lua_State *L, int idx, size_t *len) +{ + TValue *o = index2adr(L, idx); + GCstr *s; + if (LJ_LIKELY(tvisstr(o))) { + s = strV(o); + } else if (tvisnumber(o)) { + lj_gc_check(L); + o = index2adr(L, idx); /* GC may move the stack. */ + s = lj_strfmt_number(L, o); + setstrV(L, o, s); + } else { + lj_err_argt(L, idx, LUA_TSTRING); + } + if (len != NULL) *len = s->len; + return strdata(s); +} + +LUALIB_API const char *luaL_optlstring(lua_State *L, int idx, + const char *def, size_t *len) +{ + TValue *o = index2adr(L, idx); + GCstr *s; + if (LJ_LIKELY(tvisstr(o))) { + s = strV(o); + } else if (tvisnil(o)) { + if (len != NULL) *len = def ? strlen(def) : 0; + return def; + } else if (tvisnumber(o)) { + lj_gc_check(L); + o = index2adr(L, idx); /* GC may move the stack. */ + s = lj_strfmt_number(L, o); + setstrV(L, o, s); + } else { + lj_err_argt(L, idx, LUA_TSTRING); + } + if (len != NULL) *len = s->len; + return strdata(s); +} + +LUALIB_API int luaL_checkoption(lua_State *L, int idx, const char *def, + const char *const lst[]) +{ + ptrdiff_t i; + const char *s = lua_tolstring(L, idx, NULL); + if (s == NULL && (s = def) == NULL) + lj_err_argt(L, idx, LUA_TSTRING); + for (i = 0; lst[i]; i++) + if (strcmp(lst[i], s) == 0) + return (int)i; + lj_err_argv(L, idx, LJ_ERR_INVOPTM, s); +} + +LUA_API size_t lua_objlen(lua_State *L, int idx) +{ + TValue *o = index2adr(L, idx); + if (tvisstr(o)) { + return strV(o)->len; + } else if (tvistab(o)) { + return (size_t)lj_tab_len(tabV(o)); + } else if (tvisudata(o)) { + return udataV(o)->len; + } else if (tvisnumber(o)) { + GCstr *s = lj_strfmt_number(L, o); + setstrV(L, o, s); + return s->len; + } else { + return 0; + } +} + +LUA_API lua_CFunction lua_tocfunction(lua_State *L, int idx) +{ + cTValue *o = index2adr(L, idx); + if (tvisfunc(o)) { + BCOp op = bc_op(*mref(funcV(o)->c.pc, BCIns)); + if (op == BC_FUNCC || op == BC_FUNCCW) + return funcV(o)->c.f; + } + return NULL; +} + +LUA_API void *lua_touserdata(lua_State *L, int idx) +{ + cTValue *o = index2adr(L, idx); + if (tvisudata(o)) + return uddata(udataV(o)); + else if (tvislightud(o)) + return lightudV(o); + else + return NULL; +} + +LUA_API lua_State *lua_tothread(lua_State *L, int idx) +{ + cTValue *o = index2adr(L, idx); + return (!tvisthread(o)) ? NULL : threadV(o); +} + +LUA_API const void *lua_topointer(lua_State *L, int idx) +{ + return lj_obj_ptr(index2adr(L, idx)); +} + +/* -- Stack setters (object creation) ------------------------------------- */ + +LUA_API void lua_pushnil(lua_State *L) +{ + setnilV(L->top); + incr_top(L); +} + +LUA_API void lua_pushnumber(lua_State *L, lua_Number n) +{ + setnumV(L->top, n); + if (LJ_UNLIKELY(tvisnan(L->top))) + setnanV(L->top); /* Canonicalize injected NaNs. */ + incr_top(L); +} + +LUA_API void lua_pushinteger(lua_State *L, lua_Integer n) +{ + setintptrV(L->top, n); + incr_top(L); +} + +LUA_API void lua_pushlstring(lua_State *L, const char *str, size_t len) +{ + GCstr *s; + lj_gc_check(L); + s = lj_str_new(L, str, len); + setstrV(L, L->top, s); + incr_top(L); +} + +LUA_API void lua_pushstring(lua_State *L, const char *str) +{ + if (str == NULL) { + setnilV(L->top); + } else { + GCstr *s; + lj_gc_check(L); + s = lj_str_newz(L, str); + setstrV(L, L->top, s); + } + incr_top(L); +} + +LUA_API const char *lua_pushvfstring(lua_State *L, const char *fmt, + va_list argp) +{ + lj_gc_check(L); + return lj_strfmt_pushvf(L, fmt, argp); +} + +LUA_API const char *lua_pushfstring(lua_State *L, const char *fmt, ...) +{ + const char *ret; + va_list argp; + lj_gc_check(L); + va_start(argp, fmt); + ret = lj_strfmt_pushvf(L, fmt, argp); + va_end(argp); + return ret; +} + +LUA_API void lua_pushcclosure(lua_State *L, lua_CFunction f, int n) +{ + GCfunc *fn; + lj_gc_check(L); + api_checknelems(L, n); + fn = lj_func_newC(L, (MSize)n, getcurrenv(L)); + fn->c.f = f; + L->top -= n; + while (n--) + copyTV(L, &fn->c.upvalue[n], L->top+n); + setfuncV(L, L->top, fn); + lua_assert(iswhite(obj2gco(fn))); + incr_top(L); +} + +LUA_API void lua_pushboolean(lua_State *L, int b) +{ + setboolV(L->top, (b != 0)); + incr_top(L); +} + +LUA_API void lua_pushlightuserdata(lua_State *L, void *p) +{ + setlightudV(L->top, checklightudptr(L, p)); + incr_top(L); +} + +LUA_API void lua_createtable(lua_State *L, int narray, int nrec) +{ + lj_gc_check(L); + settabV(L, L->top, lj_tab_new_ah(L, narray, nrec)); + incr_top(L); +} + +LUALIB_API int luaL_newmetatable(lua_State *L, const char *tname) +{ + GCtab *regt = tabV(registry(L)); + TValue *tv = lj_tab_setstr(L, regt, lj_str_newz(L, tname)); + if (tvisnil(tv)) { + GCtab *mt = lj_tab_new(L, 0, 1); + settabV(L, tv, mt); + settabV(L, L->top++, mt); + lj_gc_anybarriert(L, regt); + return 1; + } else { + copyTV(L, L->top++, tv); + return 0; + } +} + +LUA_API int lua_pushthread(lua_State *L) +{ + setthreadV(L, L->top, L); + incr_top(L); + return (mainthread(G(L)) == L); +} + +LUA_API lua_State *lua_newthread(lua_State *L) +{ + lua_State *L1; + lj_gc_check(L); + L1 = lj_state_new(L); + setthreadV(L, L->top, L1); + incr_top(L); + return L1; +} + +LUA_API void *lua_newuserdata(lua_State *L, size_t size) +{ + GCudata *ud; + lj_gc_check(L); + if (size > LJ_MAX_UDATA) + lj_err_msg(L, LJ_ERR_UDATAOV); + ud = lj_udata_new(L, (MSize)size, getcurrenv(L)); + setudataV(L, L->top, ud); + incr_top(L); + return uddata(ud); +} + +LUA_API void lua_concat(lua_State *L, int n) +{ + api_checknelems(L, n); + if (n >= 2) { + n--; + do { + TValue *top = lj_meta_cat(L, L->top-1, -n); + if (top == NULL) { + L->top -= n; + break; + } + n -= (int)(L->top - top); + L->top = top+2; + lj_vm_call(L, top, 1+1); + L->top -= 1+LJ_FR2; + copyTV(L, L->top-1, L->top+LJ_FR2); + } while (--n > 0); + } else if (n == 0) { /* Push empty string. */ + setstrV(L, L->top, &G(L)->strempty); + incr_top(L); + } + /* else n == 1: nothing to do. */ +} + +/* -- Object getters ------------------------------------------------------ */ + +LUA_API void lua_gettable(lua_State *L, int idx) +{ + cTValue *v, *t = index2adr(L, idx); + api_checkvalidindex(L, t); + v = lj_meta_tget(L, t, L->top-1); + if (v == NULL) { + L->top += 2; + lj_vm_call(L, L->top-2, 1+1); + L->top -= 2+LJ_FR2; + v = L->top+1+LJ_FR2; + } + copyTV(L, L->top-1, v); +} + +LUA_API void lua_getfield(lua_State *L, int idx, const char *k) +{ + cTValue *v, *t = index2adr(L, idx); + TValue key; + api_checkvalidindex(L, t); + setstrV(L, &key, lj_str_newz(L, k)); + v = lj_meta_tget(L, t, &key); + if (v == NULL) { + L->top += 2; + lj_vm_call(L, L->top-2, 1+1); + L->top -= 2+LJ_FR2; + v = L->top+1+LJ_FR2; + } + copyTV(L, L->top, v); + incr_top(L); +} + +LUA_API void lua_rawget(lua_State *L, int idx) +{ + cTValue *t = index2adr(L, idx); + api_check(L, tvistab(t)); + copyTV(L, L->top-1, lj_tab_get(L, tabV(t), L->top-1)); +} + +LUA_API void lua_rawgeti(lua_State *L, int idx, int n) +{ + cTValue *v, *t = index2adr(L, idx); + api_check(L, tvistab(t)); + v = lj_tab_getint(tabV(t), n); + if (v) { + copyTV(L, L->top, v); + } else { + setnilV(L->top); + } + incr_top(L); +} + +LUA_API int lua_getmetatable(lua_State *L, int idx) +{ + cTValue *o = index2adr(L, idx); + GCtab *mt = NULL; + if (tvistab(o)) + mt = tabref(tabV(o)->metatable); + else if (tvisudata(o)) + mt = tabref(udataV(o)->metatable); + else + mt = tabref(basemt_obj(G(L), o)); + if (mt == NULL) + return 0; + settabV(L, L->top, mt); + incr_top(L); + return 1; +} + +LUALIB_API int luaL_getmetafield(lua_State *L, int idx, const char *field) +{ + if (lua_getmetatable(L, idx)) { + cTValue *tv = lj_tab_getstr(tabV(L->top-1), lj_str_newz(L, field)); + if (tv && !tvisnil(tv)) { + copyTV(L, L->top-1, tv); + return 1; + } + L->top--; + } + return 0; +} + +LUA_API void lua_getfenv(lua_State *L, int idx) +{ + cTValue *o = index2adr(L, idx); + api_checkvalidindex(L, o); + if (tvisfunc(o)) { + settabV(L, L->top, tabref(funcV(o)->c.env)); + } else if (tvisudata(o)) { + settabV(L, L->top, tabref(udataV(o)->env)); + } else if (tvisthread(o)) { + settabV(L, L->top, tabref(threadV(o)->env)); + } else { + setnilV(L->top); + } + incr_top(L); +} + +LUA_API int lua_next(lua_State *L, int idx) +{ + cTValue *t = index2adr(L, idx); + int more; + api_check(L, tvistab(t)); + more = lj_tab_next(L, tabV(t), L->top-1); + if (more) { + incr_top(L); /* Return new key and value slot. */ + } else { /* End of traversal. */ + L->top--; /* Remove key slot. */ + } + return more; +} + +LUA_API const char *lua_getupvalue(lua_State *L, int idx, int n) +{ + TValue *val; + const char *name = lj_debug_uvnamev(index2adr(L, idx), (uint32_t)(n-1), &val); + if (name) { + copyTV(L, L->top, val); + incr_top(L); + } + return name; +} + +LUA_API void *lua_upvalueid(lua_State *L, int idx, int n) +{ + GCfunc *fn = funcV(index2adr(L, idx)); + n--; + api_check(L, (uint32_t)n < fn->l.nupvalues); + return isluafunc(fn) ? (void *)gcref(fn->l.uvptr[n]) : + (void *)&fn->c.upvalue[n]; +} + +LUA_API void lua_upvaluejoin(lua_State *L, int idx1, int n1, int idx2, int n2) +{ + GCfunc *fn1 = funcV(index2adr(L, idx1)); + GCfunc *fn2 = funcV(index2adr(L, idx2)); + n1--; n2--; + api_check(L, isluafunc(fn1) && (uint32_t)n1 < fn1->l.nupvalues); + api_check(L, isluafunc(fn2) && (uint32_t)n2 < fn2->l.nupvalues); + setgcrefr(fn1->l.uvptr[n1], fn2->l.uvptr[n2]); + lj_gc_objbarrier(L, fn1, gcref(fn1->l.uvptr[n1])); +} + +LUALIB_API void *luaL_testudata(lua_State *L, int idx, const char *tname) +{ + cTValue *o = index2adr(L, idx); + if (tvisudata(o)) { + GCudata *ud = udataV(o); + cTValue *tv = lj_tab_getstr(tabV(registry(L)), lj_str_newz(L, tname)); + if (tv && tvistab(tv) && tabV(tv) == tabref(ud->metatable)) + return uddata(ud); + } + return NULL; /* value is not a userdata with a metatable */ +} + +LUALIB_API void *luaL_checkudata(lua_State *L, int idx, const char *tname) +{ + void *p = luaL_testudata(L, idx, tname); + if (!p) lj_err_argtype(L, idx, tname); + return p; +} + +/* -- Object setters ------------------------------------------------------ */ + +LUA_API void lua_settable(lua_State *L, int idx) +{ + TValue *o; + cTValue *t = index2adr(L, idx); + api_checknelems(L, 2); + api_checkvalidindex(L, t); + o = lj_meta_tset(L, t, L->top-2); + if (o) { + /* NOBARRIER: lj_meta_tset ensures the table is not black. */ + L->top -= 2; + copyTV(L, o, L->top+1); + } else { + TValue *base = L->top; + copyTV(L, base+2, base-3-2*LJ_FR2); + L->top = base+3; + lj_vm_call(L, base, 0+1); + L->top -= 3+LJ_FR2; + } +} + +LUA_API void lua_setfield(lua_State *L, int idx, const char *k) +{ + TValue *o; + TValue key; + cTValue *t = index2adr(L, idx); + api_checknelems(L, 1); + api_checkvalidindex(L, t); + setstrV(L, &key, lj_str_newz(L, k)); + o = lj_meta_tset(L, t, &key); + if (o) { + /* NOBARRIER: lj_meta_tset ensures the table is not black. */ + copyTV(L, o, --L->top); + } else { + TValue *base = L->top; + copyTV(L, base+2, base-3-2*LJ_FR2); + L->top = base+3; + lj_vm_call(L, base, 0+1); + L->top -= 2+LJ_FR2; + } +} + +LUA_API void lua_rawset(lua_State *L, int idx) +{ + GCtab *t = tabV(index2adr(L, idx)); + TValue *dst, *key; + api_checknelems(L, 2); + key = L->top-2; + dst = lj_tab_set(L, t, key); + copyTV(L, dst, key+1); + lj_gc_anybarriert(L, t); + L->top = key; +} + +LUA_API void lua_rawseti(lua_State *L, int idx, int n) +{ + GCtab *t = tabV(index2adr(L, idx)); + TValue *dst, *src; + api_checknelems(L, 1); + dst = lj_tab_setint(L, t, n); + src = L->top-1; + copyTV(L, dst, src); + lj_gc_barriert(L, t, dst); + L->top = src; +} + +LUA_API int lua_setmetatable(lua_State *L, int idx) +{ + global_State *g; + GCtab *mt; + cTValue *o = index2adr(L, idx); + api_checknelems(L, 1); + api_checkvalidindex(L, o); + if (tvisnil(L->top-1)) { + mt = NULL; + } else { + api_check(L, tvistab(L->top-1)); + mt = tabV(L->top-1); + } + g = G(L); + if (tvistab(o)) { + setgcref(tabV(o)->metatable, obj2gco(mt)); + if (mt) + lj_gc_objbarriert(L, tabV(o), mt); + } else if (tvisudata(o)) { + setgcref(udataV(o)->metatable, obj2gco(mt)); + if (mt) + lj_gc_objbarrier(L, udataV(o), mt); + } else { + /* Flush cache, since traces specialize to basemt. But not during __gc. */ + if (lj_trace_flushall(L)) + lj_err_caller(L, LJ_ERR_NOGCMM); + if (tvisbool(o)) { + /* NOBARRIER: basemt is a GC root. */ + setgcref(basemt_it(g, LJ_TTRUE), obj2gco(mt)); + setgcref(basemt_it(g, LJ_TFALSE), obj2gco(mt)); + } else { + /* NOBARRIER: basemt is a GC root. */ + setgcref(basemt_obj(g, o), obj2gco(mt)); + } + } + L->top--; + return 1; +} + +LUALIB_API void luaL_setmetatable(lua_State *L, const char *tname) +{ + lua_getfield(L, LUA_REGISTRYINDEX, tname); + lua_setmetatable(L, -2); +} + +LUA_API int lua_setfenv(lua_State *L, int idx) +{ + cTValue *o = index2adr(L, idx); + GCtab *t; + api_checknelems(L, 1); + api_checkvalidindex(L, o); + api_check(L, tvistab(L->top-1)); + t = tabV(L->top-1); + if (tvisfunc(o)) { + setgcref(funcV(o)->c.env, obj2gco(t)); + } else if (tvisudata(o)) { + setgcref(udataV(o)->env, obj2gco(t)); + } else if (tvisthread(o)) { + setgcref(threadV(o)->env, obj2gco(t)); + } else { + L->top--; + return 0; + } + lj_gc_objbarrier(L, gcV(o), t); + L->top--; + return 1; +} + +LUA_API const char *lua_setupvalue(lua_State *L, int idx, int n) +{ + cTValue *f = index2adr(L, idx); + TValue *val; + const char *name; + api_checknelems(L, 1); + name = lj_debug_uvnamev(f, (uint32_t)(n-1), &val); + if (name) { + L->top--; + copyTV(L, val, L->top); + lj_gc_barrier(L, funcV(f), L->top); + } + return name; +} + +/* -- Calls --------------------------------------------------------------- */ + +#if LJ_FR2 +static TValue *api_call_base(lua_State *L, int nargs) +{ + TValue *o = L->top, *base = o - nargs; + L->top = o+1; + for (; o > base; o--) copyTV(L, o, o-1); + setnilV(o); + return o+1; +} +#else +#define api_call_base(L, nargs) (L->top - (nargs)) +#endif + +LUA_API void lua_call(lua_State *L, int nargs, int nresults) +{ + api_check(L, L->status == LUA_OK || L->status == LUA_ERRERR); + api_checknelems(L, nargs+1); + lj_vm_call(L, api_call_base(L, nargs), nresults+1); +} + +LUA_API int lua_pcall(lua_State *L, int nargs, int nresults, int errfunc) +{ + global_State *g = G(L); + uint8_t oldh = hook_save(g); + ptrdiff_t ef; + int status; + api_check(L, L->status == LUA_OK || L->status == LUA_ERRERR); + api_checknelems(L, nargs+1); + if (errfunc == 0) { + ef = 0; + } else { + cTValue *o = stkindex2adr(L, errfunc); + api_checkvalidindex(L, o); + ef = savestack(L, o); + } + status = lj_vm_pcall(L, api_call_base(L, nargs), nresults+1, ef); + if (status) hook_restore(g, oldh); + return status; +} + +static TValue *cpcall(lua_State *L, lua_CFunction func, void *ud) +{ + GCfunc *fn = lj_func_newC(L, 0, getcurrenv(L)); + TValue *top = L->top; + fn->c.f = func; + setfuncV(L, top++, fn); + if (LJ_FR2) setnilV(top++); + setlightudV(top++, checklightudptr(L, ud)); + cframe_nres(L->cframe) = 1+0; /* Zero results. */ + L->top = top; + return top-1; /* Now call the newly allocated C function. */ +} + +LUA_API int lua_cpcall(lua_State *L, lua_CFunction func, void *ud) +{ + global_State *g = G(L); + uint8_t oldh = hook_save(g); + int status; + api_check(L, L->status == LUA_OK || L->status == LUA_ERRERR); + status = lj_vm_cpcall(L, func, ud, cpcall); + if (status) hook_restore(g, oldh); + return status; +} + +LUALIB_API int luaL_callmeta(lua_State *L, int idx, const char *field) +{ + if (luaL_getmetafield(L, idx, field)) { + TValue *top = L->top--; + if (LJ_FR2) setnilV(top++); + copyTV(L, top++, index2adr(L, idx)); + L->top = top; + lj_vm_call(L, top-1, 1+1); + return 1; + } + return 0; +} + +/* -- Coroutine yield and resume ------------------------------------------ */ + +LUA_API int lua_isyieldable(lua_State *L) +{ + return cframe_canyield(L->cframe); +} + +LUA_API int lua_yield(lua_State *L, int nresults) +{ + void *cf = L->cframe; + global_State *g = G(L); + if (cframe_canyield(cf)) { + cf = cframe_raw(cf); + if (!hook_active(g)) { /* Regular yield: move results down if needed. */ + cTValue *f = L->top - nresults; + if (f > L->base) { + TValue *t = L->base; + while (--nresults >= 0) copyTV(L, t++, f++); + L->top = t; + } + L->cframe = NULL; + L->status = LUA_YIELD; + return -1; + } else { /* Yield from hook: add a pseudo-frame. */ + TValue *top = L->top; + hook_leave(g); + (top++)->u64 = cframe_multres(cf); + setcont(top, lj_cont_hook); + if (LJ_FR2) top++; + setframe_pc(top, cframe_pc(cf)-1); + if (LJ_FR2) top++; + setframe_gc(top, obj2gco(L), LJ_TTHREAD); + setframe_ftsz(top, ((char *)(top+1)-(char *)L->base)+FRAME_CONT); + L->top = L->base = top+1; +#if LJ_TARGET_X64 + lj_err_throw(L, LUA_YIELD); +#else + L->cframe = NULL; + L->status = LUA_YIELD; + lj_vm_unwind_c(cf, LUA_YIELD); +#endif + } + } + lj_err_msg(L, LJ_ERR_CYIELD); + return 0; /* unreachable */ +} + +LUA_API int lua_resume(lua_State *L, int nargs) +{ + if (L->cframe == NULL && L->status <= LUA_YIELD) + return lj_vm_resume(L, + L->status == LUA_OK ? api_call_base(L, nargs) : L->top - nargs, + 0, 0); + L->top = L->base; + setstrV(L, L->top, lj_err_str(L, LJ_ERR_COSUSP)); + incr_top(L); + return LUA_ERRRUN; +} + +/* -- GC and memory management -------------------------------------------- */ + +LUA_API int lua_gc(lua_State *L, int what, int data) +{ + global_State *g = G(L); + int res = 0; + switch (what) { + case LUA_GCSTOP: + g->gc.threshold = LJ_MAX_MEM; + break; + case LUA_GCRESTART: + g->gc.threshold = data == -1 ? (g->gc.total/100)*g->gc.pause : g->gc.total; + break; + case LUA_GCCOLLECT: + lj_gc_fullgc(L); + break; + case LUA_GCCOUNT: + res = (int)(g->gc.total >> 10); + break; + case LUA_GCCOUNTB: + res = (int)(g->gc.total & 0x3ff); + break; + case LUA_GCSTEP: { + GCSize a = (GCSize)data << 10; + g->gc.threshold = (a <= g->gc.total) ? (g->gc.total - a) : 0; + while (g->gc.total >= g->gc.threshold) + if (lj_gc_step(L) > 0) { + res = 1; + break; + } + break; + } + case LUA_GCSETPAUSE: + res = (int)(g->gc.pause); + g->gc.pause = (MSize)data; + break; + case LUA_GCSETSTEPMUL: + res = (int)(g->gc.stepmul); + g->gc.stepmul = (MSize)data; + break; + case LUA_GCISRUNNING: + res = (g->gc.threshold != LJ_MAX_MEM); + break; + default: + res = -1; /* Invalid option. */ + } + return res; +} + +LUA_API lua_Alloc lua_getallocf(lua_State *L, void **ud) +{ + global_State *g = G(L); + if (ud) *ud = g->allocd; + return g->allocf; +} + +LUA_API void lua_setallocf(lua_State *L, lua_Alloc f, void *ud) +{ + global_State *g = G(L); + g->allocd = ud; + g->allocf = f; +} + diff --git a/lib/LuaJIT/lj_api.o b/lib/LuaJIT/lj_api.o new file mode 100644 index 0000000000000000000000000000000000000000..433bae839ff9b1129730fd890edb70a4b3189520 GIT binary patch literal 25440 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 literal 0 HcmV?d00001 diff --git a/lib/LuaJIT/lj_api_dyn.o b/lib/LuaJIT/lj_api_dyn.o new file mode 100644 index 0000000000000000000000000000000000000000..3761973d8fdbbb8e979b62478f916ceb28b3a279 GIT binary patch literal 25480 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 F{Xe}7)As-X literal 0 HcmV?d00001 diff --git a/lib/LuaJIT/lj_arch.h b/lib/LuaJIT/lj_arch.h new file mode 100644 index 0000000..31a1159 --- /dev/null +++ b/lib/LuaJIT/lj_arch.h @@ -0,0 +1,599 @@ +/* +** Target architecture selection. +** Copyright (C) 2005-2017 Mike Pall. See Copyright Notice in luajit.h +*/ + +#ifndef _LJ_ARCH_H +#define _LJ_ARCH_H + +#include "lua.h" + +/* Target endianess. */ +#define LUAJIT_LE 0 +#define LUAJIT_BE 1 + +/* Target architectures. */ +#define LUAJIT_ARCH_X86 1 +#define LUAJIT_ARCH_x86 1 +#define LUAJIT_ARCH_X64 2 +#define LUAJIT_ARCH_x64 2 +#define LUAJIT_ARCH_ARM 3 +#define LUAJIT_ARCH_arm 3 +#define LUAJIT_ARCH_ARM64 4 +#define LUAJIT_ARCH_arm64 4 +#define LUAJIT_ARCH_PPC 5 +#define LUAJIT_ARCH_ppc 5 +#define LUAJIT_ARCH_MIPS 6 +#define LUAJIT_ARCH_mips 6 +#define LUAJIT_ARCH_MIPS32 6 +#define LUAJIT_ARCH_mips32 6 +#define LUAJIT_ARCH_MIPS64 7 +#define LUAJIT_ARCH_mips64 7 + +/* Target OS. */ +#define LUAJIT_OS_OTHER 0 +#define LUAJIT_OS_WINDOWS 1 +#define LUAJIT_OS_LINUX 2 +#define LUAJIT_OS_OSX 3 +#define LUAJIT_OS_BSD 4 +#define LUAJIT_OS_POSIX 5 + +/* Select native target if no target defined. */ +#ifndef LUAJIT_TARGET + +#if defined(__i386) || defined(__i386__) || defined(_M_IX86) +#define LUAJIT_TARGET LUAJIT_ARCH_X86 +#elif defined(__x86_64__) || defined(__x86_64) || defined(_M_X64) || defined(_M_AMD64) +#define LUAJIT_TARGET LUAJIT_ARCH_X64 +#elif defined(__arm__) || defined(__arm) || defined(__ARM__) || defined(__ARM) +#define LUAJIT_TARGET LUAJIT_ARCH_ARM +#elif defined(__aarch64__) +#define LUAJIT_TARGET LUAJIT_ARCH_ARM64 +#elif defined(__ppc__) || defined(__ppc) || defined(__PPC__) || defined(__PPC) || defined(__powerpc__) || defined(__powerpc) || defined(__POWERPC__) || defined(__POWERPC) || defined(_M_PPC) +#define LUAJIT_TARGET LUAJIT_ARCH_PPC +#elif defined(__mips64__) || defined(__mips64) || defined(__MIPS64__) || defined(__MIPS64) +#define LUAJIT_TARGET LUAJIT_ARCH_MIPS64 +#elif defined(__mips__) || defined(__mips) || defined(__MIPS__) || defined(__MIPS) +#define LUAJIT_TARGET LUAJIT_ARCH_MIPS32 +#else +#error "No support for this architecture (yet)" +#endif + +#endif + +/* Select native OS if no target OS defined. */ +#ifndef LUAJIT_OS + +#if defined(_WIN32) && !defined(_XBOX_VER) +#define LUAJIT_OS LUAJIT_OS_WINDOWS +#elif defined(__linux__) +#define LUAJIT_OS LUAJIT_OS_LINUX +#elif defined(__MACH__) && defined(__APPLE__) +#define LUAJIT_OS LUAJIT_OS_OSX +#elif (defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || \ + defined(__NetBSD__) || defined(__OpenBSD__) || \ + defined(__DragonFly__)) && !defined(__ORBIS__) +#define LUAJIT_OS LUAJIT_OS_BSD +#elif (defined(__sun__) && defined(__svr4__)) || defined(__HAIKU__) +#define LUAJIT_OS LUAJIT_OS_POSIX +#elif defined(__CYGWIN__) +#define LJ_TARGET_CYGWIN 1 +#define LUAJIT_OS LUAJIT_OS_POSIX +#else +#define LUAJIT_OS LUAJIT_OS_OTHER +#endif + +#endif + +/* Set target OS properties. */ +#if LUAJIT_OS == LUAJIT_OS_WINDOWS +#define LJ_OS_NAME "Windows" +#elif LUAJIT_OS == LUAJIT_OS_LINUX +#define LJ_OS_NAME "Linux" +#elif LUAJIT_OS == LUAJIT_OS_OSX +#define LJ_OS_NAME "OSX" +#elif LUAJIT_OS == LUAJIT_OS_BSD +#define LJ_OS_NAME "BSD" +#elif LUAJIT_OS == LUAJIT_OS_POSIX +#define LJ_OS_NAME "POSIX" +#else +#define LJ_OS_NAME "Other" +#endif + +#define LJ_TARGET_WINDOWS (LUAJIT_OS == LUAJIT_OS_WINDOWS) +#define LJ_TARGET_LINUX (LUAJIT_OS == LUAJIT_OS_LINUX) +#define LJ_TARGET_OSX (LUAJIT_OS == LUAJIT_OS_OSX) +#define LJ_TARGET_IOS (LJ_TARGET_OSX && (LUAJIT_TARGET == LUAJIT_ARCH_ARM || LUAJIT_TARGET == LUAJIT_ARCH_ARM64)) +#define LJ_TARGET_POSIX (LUAJIT_OS > LUAJIT_OS_WINDOWS) +#define LJ_TARGET_DLOPEN LJ_TARGET_POSIX + +#ifdef __CELLOS_LV2__ +#define LJ_TARGET_PS3 1 +#define LJ_TARGET_CONSOLE 1 +#endif + +#ifdef __ORBIS__ +#define LJ_TARGET_PS4 1 +#define LJ_TARGET_CONSOLE 1 +#undef NULL +#define NULL ((void*)0) +#endif + +#ifdef __psp2__ +#define LJ_TARGET_PSVITA 1 +#define LJ_TARGET_CONSOLE 1 +#endif + +#if _XBOX_VER >= 200 +#define LJ_TARGET_XBOX360 1 +#define LJ_TARGET_CONSOLE 1 +#endif + +#ifdef _DURANGO +#define LJ_TARGET_XBOXONE 1 +#define LJ_TARGET_CONSOLE 1 +#define LJ_TARGET_GC64 1 +#endif + +#ifdef _UWP +#define LJ_TARGET_UWP 1 +#if LUAJIT_TARGET == LUAJIT_ARCH_X64 +#define LJ_TARGET_GC64 1 +#endif +#endif + +#define LJ_NUMMODE_SINGLE 0 /* Single-number mode only. */ +#define LJ_NUMMODE_SINGLE_DUAL 1 /* Default to single-number mode. */ +#define LJ_NUMMODE_DUAL 2 /* Dual-number mode only. */ +#define LJ_NUMMODE_DUAL_SINGLE 3 /* Default to dual-number mode. */ + +/* Set target architecture properties. */ +#if LUAJIT_TARGET == LUAJIT_ARCH_X86 + +#define LJ_ARCH_NAME "x86" +#define LJ_ARCH_BITS 32 +#define LJ_ARCH_ENDIAN LUAJIT_LE +#if LJ_TARGET_WINDOWS || LJ_TARGET_CYGWIN +#define LJ_ABI_WIN 1 +#else +#define LJ_ABI_WIN 0 +#endif +#define LJ_TARGET_X86 1 +#define LJ_TARGET_X86ORX64 1 +#define LJ_TARGET_EHRETREG 0 +#define LJ_TARGET_MASKSHIFT 1 +#define LJ_TARGET_MASKROT 1 +#define LJ_TARGET_UNALIGNED 1 +#define LJ_ARCH_NUMMODE LJ_NUMMODE_SINGLE_DUAL + +#elif LUAJIT_TARGET == LUAJIT_ARCH_X64 + +#define LJ_ARCH_NAME "x64" +#define LJ_ARCH_BITS 64 +#define LJ_ARCH_ENDIAN LUAJIT_LE +#if LJ_TARGET_WINDOWS || LJ_TARGET_CYGWIN +#define LJ_ABI_WIN 1 +#else +#define LJ_ABI_WIN 0 +#endif +#define LJ_TARGET_X64 1 +#define LJ_TARGET_X86ORX64 1 +#define LJ_TARGET_EHRETREG 0 +#define LJ_TARGET_JUMPRANGE 31 /* +-2^31 = +-2GB */ +#define LJ_TARGET_MASKSHIFT 1 +#define LJ_TARGET_MASKROT 1 +#define LJ_TARGET_UNALIGNED 1 +#define LJ_ARCH_NUMMODE LJ_NUMMODE_SINGLE_DUAL +#ifdef LUAJIT_ENABLE_GC64 +#define LJ_TARGET_GC64 1 +#endif + +#elif LUAJIT_TARGET == LUAJIT_ARCH_ARM + +#define LJ_ARCH_NAME "arm" +#define LJ_ARCH_BITS 32 +#define LJ_ARCH_ENDIAN LUAJIT_LE +#if !defined(LJ_ARCH_HASFPU) && __SOFTFP__ +#define LJ_ARCH_HASFPU 0 +#endif +#if !defined(LJ_ABI_SOFTFP) && !__ARM_PCS_VFP +#define LJ_ABI_SOFTFP 1 +#endif +#define LJ_ABI_EABI 1 +#define LJ_TARGET_ARM 1 +#define LJ_TARGET_EHRETREG 0 +#define LJ_TARGET_JUMPRANGE 25 /* +-2^25 = +-32MB */ +#define LJ_TARGET_MASKSHIFT 0 +#define LJ_TARGET_MASKROT 1 +#define LJ_TARGET_UNIFYROT 2 /* Want only IR_BROR. */ +#define LJ_ARCH_NUMMODE LJ_NUMMODE_DUAL + +#if __ARM_ARCH_8__ || __ARM_ARCH_8A__ +#define LJ_ARCH_VERSION 80 +#elif __ARM_ARCH_7__ || __ARM_ARCH_7A__ || __ARM_ARCH_7R__ || __ARM_ARCH_7S__ || __ARM_ARCH_7VE__ +#define LJ_ARCH_VERSION 70 +#elif __ARM_ARCH_6T2__ +#define LJ_ARCH_VERSION 61 +#elif __ARM_ARCH_6__ || __ARM_ARCH_6J__ || __ARM_ARCH_6K__ || __ARM_ARCH_6Z__ || __ARM_ARCH_6ZK__ +#define LJ_ARCH_VERSION 60 +#else +#define LJ_ARCH_VERSION 50 +#endif + +#elif LUAJIT_TARGET == LUAJIT_ARCH_ARM64 + +#define LJ_ARCH_BITS 64 +#if defined(__AARCH64EB__) +#define LJ_ARCH_NAME "arm64be" +#define LJ_ARCH_ENDIAN LUAJIT_BE +#else +#define LJ_ARCH_NAME "arm64" +#define LJ_ARCH_ENDIAN LUAJIT_LE +#endif +#define LJ_TARGET_ARM64 1 +#define LJ_TARGET_EHRETREG 0 +#define LJ_TARGET_JUMPRANGE 27 /* +-2^27 = +-128MB */ +#define LJ_TARGET_MASKSHIFT 1 +#define LJ_TARGET_MASKROT 1 +#define LJ_TARGET_UNIFYROT 2 /* Want only IR_BROR. */ +#define LJ_TARGET_GC64 1 +#define LJ_ARCH_NUMMODE LJ_NUMMODE_DUAL + +#define LJ_ARCH_VERSION 80 + +#elif LUAJIT_TARGET == LUAJIT_ARCH_PPC + +#ifndef LJ_ARCH_ENDIAN +#if __BYTE_ORDER__ != __ORDER_BIG_ENDIAN__ +#define LJ_ARCH_ENDIAN LUAJIT_LE +#else +#define LJ_ARCH_ENDIAN LUAJIT_BE +#endif +#endif + +#if _LP64 +#define LJ_ARCH_BITS 64 +#if LJ_ARCH_ENDIAN == LUAJIT_LE +#define LJ_ARCH_NAME "ppc64le" +#else +#define LJ_ARCH_NAME "ppc64" +#endif +#else +#define LJ_ARCH_BITS 32 +#define LJ_ARCH_NAME "ppc" + +#if !defined(LJ_ARCH_HASFPU) +#if defined(_SOFT_FLOAT) || defined(_SOFT_DOUBLE) +#define LJ_ARCH_HASFPU 0 +#else +#define LJ_ARCH_HASFPU 1 +#endif +#endif + +#if !defined(LJ_ABI_SOFTFP) +#if defined(_SOFT_FLOAT) || defined(_SOFT_DOUBLE) +#define LJ_ABI_SOFTFP 1 +#else +#define LJ_ABI_SOFTFP 0 +#endif +#endif +#endif + +#if LJ_ABI_SOFTFP +#define LJ_ARCH_NUMMODE LJ_NUMMODE_DUAL +#else +#define LJ_ARCH_NUMMODE LJ_NUMMODE_DUAL_SINGLE +#endif + +#define LJ_TARGET_PPC 1 +#define LJ_TARGET_EHRETREG 3 +#define LJ_TARGET_JUMPRANGE 25 /* +-2^25 = +-32MB */ +#define LJ_TARGET_MASKSHIFT 0 +#define LJ_TARGET_MASKROT 1 +#define LJ_TARGET_UNIFYROT 1 /* Want only IR_BROL. */ + +#if LJ_TARGET_CONSOLE +#define LJ_ARCH_PPC32ON64 1 +#define LJ_ARCH_NOFFI 1 +#elif LJ_ARCH_BITS == 64 +#define LJ_ARCH_PPC64 1 +#define LJ_TARGET_GC64 1 +#define LJ_ARCH_NOJIT 1 /* NYI */ +#endif + +#if _ARCH_PWR7 +#define LJ_ARCH_VERSION 70 +#elif _ARCH_PWR6 +#define LJ_ARCH_VERSION 60 +#elif _ARCH_PWR5X +#define LJ_ARCH_VERSION 51 +#elif _ARCH_PWR5 +#define LJ_ARCH_VERSION 50 +#elif _ARCH_PWR4 +#define LJ_ARCH_VERSION 40 +#else +#define LJ_ARCH_VERSION 0 +#endif +#if _ARCH_PPCSQ +#define LJ_ARCH_SQRT 1 +#endif +#if _ARCH_PWR5X +#define LJ_ARCH_ROUND 1 +#endif +#if __PPU__ +#define LJ_ARCH_CELL 1 +#endif +#if LJ_TARGET_XBOX360 +#define LJ_ARCH_XENON 1 +#endif + +#elif LUAJIT_TARGET == LUAJIT_ARCH_MIPS32 || LUAJIT_TARGET == LUAJIT_ARCH_MIPS64 + +#if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) +#if LUAJIT_TARGET == LUAJIT_ARCH_MIPS32 +#define LJ_ARCH_NAME "mipsel" +#else +#define LJ_ARCH_NAME "mips64el" +#endif +#define LJ_ARCH_ENDIAN LUAJIT_LE +#else +#if LUAJIT_TARGET == LUAJIT_ARCH_MIPS32 +#define LJ_ARCH_NAME "mips" +#else +#define LJ_ARCH_NAME "mips64" +#endif +#define LJ_ARCH_ENDIAN LUAJIT_BE +#endif + +#if !defined(LJ_ARCH_HASFPU) +#ifdef __mips_soft_float +#define LJ_ARCH_HASFPU 0 +#else +#define LJ_ARCH_HASFPU 1 +#endif +#endif + +#if !defined(LJ_ABI_SOFTFP) +#ifdef __mips_soft_float +#define LJ_ABI_SOFTFP 1 +#else +#define LJ_ABI_SOFTFP 0 +#endif +#endif + +#if LUAJIT_TARGET == LUAJIT_ARCH_MIPS32 +#define LJ_ARCH_BITS 32 +#define LJ_TARGET_MIPS32 1 +#else +#define LJ_ARCH_BITS 64 +#define LJ_TARGET_MIPS64 1 +#define LJ_TARGET_GC64 1 +#endif +#define LJ_TARGET_MIPS 1 +#define LJ_TARGET_EHRETREG 4 +#define LJ_TARGET_JUMPRANGE 27 /* 2*2^27 = 256MB-aligned region */ +#define LJ_TARGET_MASKSHIFT 1 +#define LJ_TARGET_MASKROT 1 +#define LJ_TARGET_UNIFYROT 2 /* Want only IR_BROR. */ +#define LJ_ARCH_NUMMODE LJ_NUMMODE_DUAL + +#if _MIPS_ARCH_MIPS32R2 || _MIPS_ARCH_MIPS64R2 +#define LJ_ARCH_VERSION 20 +#else +#define LJ_ARCH_VERSION 10 +#endif + +#else +#error "No target architecture defined" +#endif + +#ifndef LJ_PAGESIZE +#define LJ_PAGESIZE 4096 +#endif + +/* Check for minimum required compiler versions. */ +#if defined(__GNUC__) +#if LJ_TARGET_X86 +#if (__GNUC__ < 3) || ((__GNUC__ == 3) && __GNUC_MINOR__ < 4) +#error "Need at least GCC 3.4 or newer" +#endif +#elif LJ_TARGET_X64 +#if __GNUC__ < 4 +#error "Need at least GCC 4.0 or newer" +#endif +#elif LJ_TARGET_ARM +#if (__GNUC__ < 4) || ((__GNUC__ == 4) && __GNUC_MINOR__ < 2) +#error "Need at least GCC 4.2 or newer" +#endif +#elif LJ_TARGET_ARM64 +#if __clang__ +#if ((__clang_major__ < 3) || ((__clang_major__ == 3) && __clang_minor__ < 5)) && !defined(__NX_TOOLCHAIN_MAJOR__) +#error "Need at least Clang 3.5 or newer" +#endif +#else +#if (__GNUC__ < 4) || ((__GNUC__ == 4) && __GNUC_MINOR__ < 8) +#error "Need at least GCC 4.8 or newer" +#endif +#endif +#elif !LJ_TARGET_PS3 +#if (__GNUC__ < 4) || ((__GNUC__ == 4) && __GNUC_MINOR__ < 3) +#error "Need at least GCC 4.3 or newer" +#endif +#endif +#endif + +/* Check target-specific constraints. */ +#ifndef _BUILDVM_H +#if LJ_TARGET_X64 +#if __USING_SJLJ_EXCEPTIONS__ +#error "Need a C compiler with native exception handling on x64" +#endif +#elif LJ_TARGET_ARM +#if defined(__ARMEB__) +#error "No support for big-endian ARM" +#endif +#if __ARM_ARCH_6M__ || __ARM_ARCH_7M__ || __ARM_ARCH_7EM__ +#error "No support for Cortex-M CPUs" +#endif +#if !(__ARM_EABI__ || LJ_TARGET_IOS) +#error "Only ARM EABI or iOS 3.0+ ABI is supported" +#endif +#elif LJ_TARGET_ARM64 +#if defined(_ILP32) +#error "No support for ILP32 model on ARM64" +#endif +#elif LJ_TARGET_PPC +#if !LJ_ARCH_PPC64 && (defined(_LITTLE_ENDIAN) && (!defined(_BYTE_ORDER) || (_BYTE_ORDER == _LITTLE_ENDIAN))) +#error "No support for little-endian PPC32" +#endif +#if defined(__NO_FPRS__) && !defined(_SOFT_FLOAT) +#error "No support for PPC/e500 anymore (use LuaJIT 2.0)" +#endif +#elif LJ_TARGET_MIPS32 +#if !((defined(_MIPS_SIM_ABI32) && _MIPS_SIM == _MIPS_SIM_ABI32) || (defined(_ABIO32) && _MIPS_SIM == _ABIO32)) +#error "Only o32 ABI supported for MIPS32" +#endif +#elif LJ_TARGET_MIPS64 +#if !((defined(_MIPS_SIM_ABI64) && _MIPS_SIM == _MIPS_SIM_ABI64) || (defined(_ABI64) && _MIPS_SIM == _ABI64)) +#error "Only n64 ABI supported for MIPS64" +#endif +#endif +#endif + +/* Enable or disable the dual-number mode for the VM. */ +#if (LJ_ARCH_NUMMODE == LJ_NUMMODE_SINGLE && LUAJIT_NUMMODE == 2) || \ + (LJ_ARCH_NUMMODE == LJ_NUMMODE_DUAL && LUAJIT_NUMMODE == 1) +#error "No support for this number mode on this architecture" +#endif +#if LJ_ARCH_NUMMODE == LJ_NUMMODE_DUAL || \ + (LJ_ARCH_NUMMODE == LJ_NUMMODE_DUAL_SINGLE && LUAJIT_NUMMODE != 1) || \ + (LJ_ARCH_NUMMODE == LJ_NUMMODE_SINGLE_DUAL && LUAJIT_NUMMODE == 2) +#define LJ_DUALNUM 1 +#else +#define LJ_DUALNUM 0 +#endif + +#if LJ_TARGET_IOS || LJ_TARGET_CONSOLE +/* Runtime code generation is restricted on iOS. Complain to Apple, not me. */ +/* Ditto for the consoles. Complain to Sony or MS, not me. */ +#ifndef LUAJIT_ENABLE_JIT +#define LJ_OS_NOJIT 1 +#endif +#endif + +/* 64 bit GC references. */ +#if LJ_TARGET_GC64 +#define LJ_GC64 1 +#else +#define LJ_GC64 0 +#endif + +/* 2-slot frame info. */ +#if LJ_GC64 +#define LJ_FR2 1 +#else +#define LJ_FR2 0 +#endif + +/* Disable or enable the JIT compiler. */ +#if defined(LUAJIT_DISABLE_JIT) || defined(LJ_ARCH_NOJIT) || defined(LJ_OS_NOJIT) +#define LJ_HASJIT 0 +#else +#define LJ_HASJIT 1 +#endif + +/* Disable or enable the FFI extension. */ +#if defined(LUAJIT_DISABLE_FFI) || defined(LJ_ARCH_NOFFI) +#define LJ_HASFFI 0 +#else +#define LJ_HASFFI 1 +#endif + +#if defined(LUAJIT_DISABLE_PROFILE) +#define LJ_HASPROFILE 0 +#elif LJ_TARGET_POSIX +#define LJ_HASPROFILE 1 +#define LJ_PROFILE_SIGPROF 1 +#elif LJ_TARGET_PS3 +#define LJ_HASPROFILE 1 +#define LJ_PROFILE_PTHREAD 1 +#elif LJ_TARGET_WINDOWS || LJ_TARGET_XBOX360 +#define LJ_HASPROFILE 1 +#define LJ_PROFILE_WTHREAD 1 +#else +#define LJ_HASPROFILE 0 +#endif + +#ifndef LJ_ARCH_HASFPU +#define LJ_ARCH_HASFPU 1 +#endif +#ifndef LJ_ABI_SOFTFP +#define LJ_ABI_SOFTFP 0 +#endif +#define LJ_SOFTFP (!LJ_ARCH_HASFPU) +#define LJ_SOFTFP32 (LJ_SOFTFP && LJ_32) + +#if LJ_ARCH_ENDIAN == LUAJIT_BE +#define LJ_LE 0 +#define LJ_BE 1 +#define LJ_ENDIAN_SELECT(le, be) be +#define LJ_ENDIAN_LOHI(lo, hi) hi lo +#else +#define LJ_LE 1 +#define LJ_BE 0 +#define LJ_ENDIAN_SELECT(le, be) le +#define LJ_ENDIAN_LOHI(lo, hi) lo hi +#endif + +#if LJ_ARCH_BITS == 32 +#define LJ_32 1 +#define LJ_64 0 +#else +#define LJ_32 0 +#define LJ_64 1 +#endif + +#ifndef LJ_TARGET_UNALIGNED +#define LJ_TARGET_UNALIGNED 0 +#endif + +/* Various workarounds for embedded operating systems or weak C runtimes. */ +#if defined(__ANDROID__) || defined(__symbian__) || LJ_TARGET_XBOX360 || LJ_TARGET_WINDOWS +#define LUAJIT_NO_LOG2 +#endif +#if defined(__symbian__) || LJ_TARGET_WINDOWS +#define LUAJIT_NO_EXP2 +#endif +#if LJ_TARGET_CONSOLE || (LJ_TARGET_IOS && __IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_8_0) +#define LJ_NO_SYSTEM 1 +#endif + +#if !defined(LUAJIT_NO_UNWIND) && __GNU_COMPACT_EH__ +/* NYI: no support for compact unwind specification, yet. */ +#define LUAJIT_NO_UNWIND 1 +#endif + +#if defined(LUAJIT_NO_UNWIND) || defined(__symbian__) || LJ_TARGET_IOS || LJ_TARGET_PS3 || LJ_TARGET_PS4 +#define LJ_NO_UNWIND 1 +#endif + +#if LJ_TARGET_WINDOWS +#if LJ_TARGET_UWP +#define LJ_WIN_VALLOC VirtualAllocFromApp +#define LJ_WIN_VPROTECT VirtualProtectFromApp +extern void *LJ_WIN_LOADLIBA(const char *path); +#else +#define LJ_WIN_VALLOC VirtualAlloc +#define LJ_WIN_VPROTECT VirtualProtect +#define LJ_WIN_LOADLIBA(path) LoadLibraryExA((path), NULL, 0) +#endif +#endif + +/* Compatibility with Lua 5.1 vs. 5.2. */ +#ifdef LUAJIT_ENABLE_LUA52COMPAT +#define LJ_52 1 +#else +#define LJ_52 0 +#endif + +#endif diff --git a/lib/LuaJIT/lj_asm.c b/lib/LuaJIT/lj_asm.c new file mode 100644 index 0000000..992dcf5 --- /dev/null +++ b/lib/LuaJIT/lj_asm.c @@ -0,0 +1,2414 @@ +/* +** IR assembler (SSA IR -> machine code). +** Copyright (C) 2005-2017 Mike Pall. See Copyright Notice in luajit.h +*/ + +#define lj_asm_c +#define LUA_CORE + +#include "lj_obj.h" + +#if LJ_HASJIT + +#include "lj_gc.h" +#include "lj_str.h" +#include "lj_tab.h" +#include "lj_frame.h" +#if LJ_HASFFI +#include "lj_ctype.h" +#endif +#include "lj_ir.h" +#include "lj_jit.h" +#include "lj_ircall.h" +#include "lj_iropt.h" +#include "lj_mcode.h" +#include "lj_iropt.h" +#include "lj_trace.h" +#include "lj_snap.h" +#include "lj_asm.h" +#include "lj_dispatch.h" +#include "lj_vm.h" +#include "lj_target.h" + +#ifdef LUA_USE_ASSERT +#include +#endif + +/* -- Assembler state and common macros ----------------------------------- */ + +/* Assembler state. */ +typedef struct ASMState { + RegCost cost[RID_MAX]; /* Reference and blended allocation cost for regs. */ + + MCode *mcp; /* Current MCode pointer (grows down). */ + MCode *mclim; /* Lower limit for MCode memory + red zone. */ +#ifdef LUA_USE_ASSERT + MCode *mcp_prev; /* Red zone overflow check. */ +#endif + + IRIns *ir; /* Copy of pointer to IR instructions/constants. */ + jit_State *J; /* JIT compiler state. */ + +#if LJ_TARGET_X86ORX64 + x86ModRM mrm; /* Fused x86 address operand. */ +#endif + + RegSet freeset; /* Set of free registers. */ + RegSet modset; /* Set of registers modified inside the loop. */ + RegSet weakset; /* Set of weakly referenced registers. */ + RegSet phiset; /* Set of PHI registers. */ + + uint32_t flags; /* Copy of JIT compiler flags. */ + int loopinv; /* Loop branch inversion (0:no, 1:yes, 2:yes+CC_P). */ + + int32_t evenspill; /* Next even spill slot. */ + int32_t oddspill; /* Next odd spill slot (or 0). */ + + IRRef curins; /* Reference of current instruction. */ + IRRef stopins; /* Stop assembly before hitting this instruction. */ + IRRef orignins; /* Original T->nins. */ + + IRRef snapref; /* Current snapshot is active after this reference. */ + IRRef snaprename; /* Rename highwater mark for snapshot check. */ + SnapNo snapno; /* Current snapshot number. */ + SnapNo loopsnapno; /* Loop snapshot number. */ + + IRRef fuseref; /* Fusion limit (loopref, 0 or FUSE_DISABLED). */ + IRRef sectref; /* Section base reference (loopref or 0). */ + IRRef loopref; /* Reference of LOOP instruction (or 0). */ + + BCReg topslot; /* Number of slots for stack check (unless 0). */ + int32_t gcsteps; /* Accumulated number of GC steps (per section). */ + + GCtrace *T; /* Trace to assemble. */ + GCtrace *parent; /* Parent trace (or NULL). */ + + MCode *mcbot; /* Bottom of reserved MCode. */ + MCode *mctop; /* Top of generated MCode. */ + MCode *mcloop; /* Pointer to loop MCode (or NULL). */ + MCode *invmcp; /* Points to invertible loop branch (or NULL). */ + MCode *flagmcp; /* Pending opportunity to merge flag setting ins. */ + MCode *realign; /* Realign loop if not NULL. */ + +#ifdef RID_NUM_KREF + intptr_t krefk[RID_NUM_KREF]; +#endif + IRRef1 phireg[RID_MAX]; /* PHI register references. */ + uint16_t parentmap[LJ_MAX_JSLOTS]; /* Parent instruction to RegSP map. */ +} ASMState; + +#define IR(ref) (&as->ir[(ref)]) + +#define ASMREF_TMP1 REF_TRUE /* Temp. register. */ +#define ASMREF_TMP2 REF_FALSE /* Temp. register. */ +#define ASMREF_L REF_NIL /* Stores register for L. */ + +/* Check for variant to invariant references. */ +#define iscrossref(as, ref) ((ref) < as->sectref) + +/* Inhibit memory op fusion from variant to invariant references. */ +#define FUSE_DISABLED (~(IRRef)0) +#define mayfuse(as, ref) ((ref) > as->fuseref) +#define neverfuse(as) (as->fuseref == FUSE_DISABLED) +#define canfuse(as, ir) (!neverfuse(as) && !irt_isphi((ir)->t)) +#define opisfusableload(o) \ + ((o) == IR_ALOAD || (o) == IR_HLOAD || (o) == IR_ULOAD || \ + (o) == IR_FLOAD || (o) == IR_XLOAD || (o) == IR_SLOAD || (o) == IR_VLOAD) + +/* Sparse limit checks using a red zone before the actual limit. */ +#define MCLIM_REDZONE 64 + +static LJ_NORET LJ_NOINLINE void asm_mclimit(ASMState *as) +{ + lj_mcode_limiterr(as->J, (size_t)(as->mctop - as->mcp + 4*MCLIM_REDZONE)); +} + +static LJ_AINLINE void checkmclim(ASMState *as) +{ +#ifdef LUA_USE_ASSERT + if (as->mcp + MCLIM_REDZONE < as->mcp_prev) { + IRIns *ir = IR(as->curins+1); + fprintf(stderr, "RED ZONE OVERFLOW: %p IR %04d %02d %04d %04d\n", as->mcp, + as->curins+1-REF_BIAS, ir->o, ir->op1-REF_BIAS, ir->op2-REF_BIAS); + lua_assert(0); + } +#endif + if (LJ_UNLIKELY(as->mcp < as->mclim)) asm_mclimit(as); +#ifdef LUA_USE_ASSERT + as->mcp_prev = as->mcp; +#endif +} + +#ifdef RID_NUM_KREF +#define ra_iskref(ref) ((ref) < RID_NUM_KREF) +#define ra_krefreg(ref) ((Reg)(RID_MIN_KREF + (Reg)(ref))) +#define ra_krefk(as, ref) (as->krefk[(ref)]) + +static LJ_AINLINE void ra_setkref(ASMState *as, Reg r, intptr_t k) +{ + IRRef ref = (IRRef)(r - RID_MIN_KREF); + as->krefk[ref] = k; + as->cost[r] = REGCOST(ref, ref); +} + +#else +#define ra_iskref(ref) 0 +#define ra_krefreg(ref) RID_MIN_GPR +#define ra_krefk(as, ref) 0 +#endif + +/* Arch-specific field offsets. */ +static const uint8_t field_ofs[IRFL__MAX+1] = { +#define FLOFS(name, ofs) (uint8_t)(ofs), +IRFLDEF(FLOFS) +#undef FLOFS + 0 +}; + +/* -- Target-specific instruction emitter --------------------------------- */ + +#if LJ_TARGET_X86ORX64 +#include "lj_emit_x86.h" +#elif LJ_TARGET_ARM +#include "lj_emit_arm.h" +#elif LJ_TARGET_ARM64 +#include "lj_emit_arm64.h" +#elif LJ_TARGET_PPC +#include "lj_emit_ppc.h" +#elif LJ_TARGET_MIPS +#include "lj_emit_mips.h" +#else +#error "Missing instruction emitter for target CPU" +#endif + +/* Generic load/store of register from/to stack slot. */ +#define emit_spload(as, ir, r, ofs) \ + emit_loadofs(as, ir, (r), RID_SP, (ofs)) +#define emit_spstore(as, ir, r, ofs) \ + emit_storeofs(as, ir, (r), RID_SP, (ofs)) + +/* -- Register allocator debugging ---------------------------------------- */ + +/* #define LUAJIT_DEBUG_RA */ + +#ifdef LUAJIT_DEBUG_RA + +#include +#include + +#define RIDNAME(name) #name, +static const char *const ra_regname[] = { + GPRDEF(RIDNAME) + FPRDEF(RIDNAME) + VRIDDEF(RIDNAME) + NULL +}; +#undef RIDNAME + +static char ra_dbg_buf[65536]; +static char *ra_dbg_p; +static char *ra_dbg_merge; +static MCode *ra_dbg_mcp; + +static void ra_dstart(void) +{ + ra_dbg_p = ra_dbg_buf; + ra_dbg_merge = NULL; + ra_dbg_mcp = NULL; +} + +static void ra_dflush(void) +{ + fwrite(ra_dbg_buf, 1, (size_t)(ra_dbg_p-ra_dbg_buf), stdout); + ra_dstart(); +} + +static void ra_dprintf(ASMState *as, const char *fmt, ...) +{ + char *p; + va_list argp; + va_start(argp, fmt); + p = ra_dbg_mcp == as->mcp ? ra_dbg_merge : ra_dbg_p; + ra_dbg_mcp = NULL; + p += sprintf(p, "%08x \e[36m%04d ", (uintptr_t)as->mcp, as->curins-REF_BIAS); + for (;;) { + const char *e = strchr(fmt, '$'); + if (e == NULL) break; + memcpy(p, fmt, (size_t)(e-fmt)); + p += e-fmt; + if (e[1] == 'r') { + Reg r = va_arg(argp, Reg) & RID_MASK; + if (r <= RID_MAX) { + const char *q; + for (q = ra_regname[r]; *q; q++) + *p++ = *q >= 'A' && *q <= 'Z' ? *q + 0x20 : *q; + } else { + *p++ = '?'; + lua_assert(0); + } + } else if (e[1] == 'f' || e[1] == 'i') { + IRRef ref; + if (e[1] == 'f') + ref = va_arg(argp, IRRef); + else + ref = va_arg(argp, IRIns *) - as->ir; + if (ref >= REF_BIAS) + p += sprintf(p, "%04d", ref - REF_BIAS); + else + p += sprintf(p, "K%03d", REF_BIAS - ref); + } else if (e[1] == 's') { + uint32_t slot = va_arg(argp, uint32_t); + p += sprintf(p, "[sp+0x%x]", sps_scale(slot)); + } else if (e[1] == 'x') { + p += sprintf(p, "%08x", va_arg(argp, int32_t)); + } else { + lua_assert(0); + } + fmt = e+2; + } + va_end(argp); + while (*fmt) + *p++ = *fmt++; + *p++ = '\e'; *p++ = '['; *p++ = 'm'; *p++ = '\n'; + if (p > ra_dbg_buf+sizeof(ra_dbg_buf)-256) { + fwrite(ra_dbg_buf, 1, (size_t)(p-ra_dbg_buf), stdout); + p = ra_dbg_buf; + } + ra_dbg_p = p; +} + +#define RA_DBG_START() ra_dstart() +#define RA_DBG_FLUSH() ra_dflush() +#define RA_DBG_REF() \ + do { char *_p = ra_dbg_p; ra_dprintf(as, ""); \ + ra_dbg_merge = _p; ra_dbg_mcp = as->mcp; } while (0) +#define RA_DBGX(x) ra_dprintf x + +#else +#define RA_DBG_START() ((void)0) +#define RA_DBG_FLUSH() ((void)0) +#define RA_DBG_REF() ((void)0) +#define RA_DBGX(x) ((void)0) +#endif + +/* -- Register allocator -------------------------------------------------- */ + +#define ra_free(as, r) rset_set(as->freeset, (r)) +#define ra_modified(as, r) rset_set(as->modset, (r)) +#define ra_weak(as, r) rset_set(as->weakset, (r)) +#define ra_noweak(as, r) rset_clear(as->weakset, (r)) + +#define ra_used(ir) (ra_hasreg((ir)->r) || ra_hasspill((ir)->s)) + +/* Setup register allocator. */ +static void ra_setup(ASMState *as) +{ + Reg r; + /* Initially all regs (except the stack pointer) are free for use. */ + as->freeset = RSET_INIT; + as->modset = RSET_EMPTY; + as->weakset = RSET_EMPTY; + as->phiset = RSET_EMPTY; + memset(as->phireg, 0, sizeof(as->phireg)); + for (r = RID_MIN_GPR; r < RID_MAX; r++) + as->cost[r] = REGCOST(~0u, 0u); +} + +/* Rematerialize constants. */ +static Reg ra_rematk(ASMState *as, IRRef ref) +{ + IRIns *ir; + Reg r; + if (ra_iskref(ref)) { + r = ra_krefreg(ref); + lua_assert(!rset_test(as->freeset, r)); + ra_free(as, r); + ra_modified(as, r); +#if LJ_64 + emit_loadu64(as, r, ra_krefk(as, ref)); +#else + emit_loadi(as, r, ra_krefk(as, ref)); +#endif + return r; + } + ir = IR(ref); + r = ir->r; + lua_assert(ra_hasreg(r) && !ra_hasspill(ir->s)); + ra_free(as, r); + ra_modified(as, r); + ir->r = RID_INIT; /* Do not keep any hint. */ + RA_DBGX((as, "remat $i $r", ir, r)); +#if !LJ_SOFTFP32 + if (ir->o == IR_KNUM) { + emit_loadk64(as, r, ir); + } else +#endif + if (emit_canremat(REF_BASE) && ir->o == IR_BASE) { + ra_sethint(ir->r, RID_BASE); /* Restore BASE register hint. */ + emit_getgl(as, r, jit_base); + } else if (emit_canremat(ASMREF_L) && ir->o == IR_KPRI) { + lua_assert(irt_isnil(ir->t)); /* REF_NIL stores ASMREF_L register. */ + emit_getgl(as, r, cur_L); +#if LJ_64 + } else if (ir->o == IR_KINT64) { + emit_loadu64(as, r, ir_kint64(ir)->u64); +#if LJ_GC64 + } else if (ir->o == IR_KGC) { + emit_loadu64(as, r, (uintptr_t)ir_kgc(ir)); + } else if (ir->o == IR_KPTR || ir->o == IR_KKPTR) { + emit_loadu64(as, r, (uintptr_t)ir_kptr(ir)); +#endif +#endif + } else { + lua_assert(ir->o == IR_KINT || ir->o == IR_KGC || + ir->o == IR_KPTR || ir->o == IR_KKPTR || ir->o == IR_KNULL); + emit_loadi(as, r, ir->i); + } + return r; +} + +/* Force a spill. Allocate a new spill slot if needed. */ +static int32_t ra_spill(ASMState *as, IRIns *ir) +{ + int32_t slot = ir->s; + lua_assert(ir >= as->ir + REF_TRUE); + if (!ra_hasspill(slot)) { + if (irt_is64(ir->t)) { + slot = as->evenspill; + as->evenspill += 2; + } else if (as->oddspill) { + slot = as->oddspill; + as->oddspill = 0; + } else { + slot = as->evenspill; + as->oddspill = slot+1; + as->evenspill += 2; + } + if (as->evenspill > 256) + lj_trace_err(as->J, LJ_TRERR_SPILLOV); + ir->s = (uint8_t)slot; + } + return sps_scale(slot); +} + +/* Release the temporarily allocated register in ASMREF_TMP1/ASMREF_TMP2. */ +static Reg ra_releasetmp(ASMState *as, IRRef ref) +{ + IRIns *ir = IR(ref); + Reg r = ir->r; + lua_assert(ra_hasreg(r) && !ra_hasspill(ir->s)); + ra_free(as, r); + ra_modified(as, r); + ir->r = RID_INIT; + return r; +} + +/* Restore a register (marked as free). Rematerialize or force a spill. */ +static Reg ra_restore(ASMState *as, IRRef ref) +{ + if (emit_canremat(ref)) { + return ra_rematk(as, ref); + } else { + IRIns *ir = IR(ref); + int32_t ofs = ra_spill(as, ir); /* Force a spill slot. */ + Reg r = ir->r; + lua_assert(ra_hasreg(r)); + ra_sethint(ir->r, r); /* Keep hint. */ + ra_free(as, r); + if (!rset_test(as->weakset, r)) { /* Only restore non-weak references. */ + ra_modified(as, r); + RA_DBGX((as, "restore $i $r", ir, r)); + emit_spload(as, ir, r, ofs); + } + return r; + } +} + +/* Save a register to a spill slot. */ +static void ra_save(ASMState *as, IRIns *ir, Reg r) +{ + RA_DBGX((as, "save $i $r", ir, r)); + emit_spstore(as, ir, r, sps_scale(ir->s)); +} + +#define MINCOST(name) \ + if (rset_test(RSET_ALL, RID_##name) && \ + LJ_LIKELY(allow&RID2RSET(RID_##name)) && as->cost[RID_##name] < cost) \ + cost = as->cost[RID_##name]; + +/* Evict the register with the lowest cost, forcing a restore. */ +static Reg ra_evict(ASMState *as, RegSet allow) +{ + IRRef ref; + RegCost cost = ~(RegCost)0; + lua_assert(allow != RSET_EMPTY); + if (RID_NUM_FPR == 0 || allow < RID2RSET(RID_MAX_GPR)) { + GPRDEF(MINCOST) + } else { + FPRDEF(MINCOST) + } + ref = regcost_ref(cost); + lua_assert(ra_iskref(ref) || (ref >= as->T->nk && ref < as->T->nins)); + /* Preferably pick any weak ref instead of a non-weak, non-const ref. */ + if (!irref_isk(ref) && (as->weakset & allow)) { + IRIns *ir = IR(ref); + if (!rset_test(as->weakset, ir->r)) + ref = regcost_ref(as->cost[rset_pickbot((as->weakset & allow))]); + } + return ra_restore(as, ref); +} + +/* Pick any register (marked as free). Evict on-demand. */ +static Reg ra_pick(ASMState *as, RegSet allow) +{ + RegSet pick = as->freeset & allow; + if (!pick) + return ra_evict(as, allow); + else + return rset_picktop(pick); +} + +/* Get a scratch register (marked as free). */ +static Reg ra_scratch(ASMState *as, RegSet allow) +{ + Reg r = ra_pick(as, allow); + ra_modified(as, r); + RA_DBGX((as, "scratch $r", r)); + return r; +} + +/* Evict all registers from a set (if not free). */ +static void ra_evictset(ASMState *as, RegSet drop) +{ + RegSet work; + as->modset |= drop; +#if !LJ_SOFTFP + work = (drop & ~as->freeset) & RSET_FPR; + while (work) { + Reg r = rset_pickbot(work); + ra_restore(as, regcost_ref(as->cost[r])); + rset_clear(work, r); + checkmclim(as); + } +#endif + work = (drop & ~as->freeset); + while (work) { + Reg r = rset_pickbot(work); + ra_restore(as, regcost_ref(as->cost[r])); + rset_clear(work, r); + checkmclim(as); + } +} + +/* Evict (rematerialize) all registers allocated to constants. */ +static void ra_evictk(ASMState *as) +{ + RegSet work; +#if !LJ_SOFTFP + work = ~as->freeset & RSET_FPR; + while (work) { + Reg r = rset_pickbot(work); + IRRef ref = regcost_ref(as->cost[r]); + if (emit_canremat(ref) && irref_isk(ref)) { + ra_rematk(as, ref); + checkmclim(as); + } + rset_clear(work, r); + } +#endif + work = ~as->freeset & RSET_GPR; + while (work) { + Reg r = rset_pickbot(work); + IRRef ref = regcost_ref(as->cost[r]); + if (emit_canremat(ref) && irref_isk(ref)) { + ra_rematk(as, ref); + checkmclim(as); + } + rset_clear(work, r); + } +} + +#ifdef RID_NUM_KREF +/* Allocate a register for a constant. */ +static Reg ra_allock(ASMState *as, intptr_t k, RegSet allow) +{ + /* First try to find a register which already holds the same constant. */ + RegSet pick, work = ~as->freeset & RSET_GPR; + Reg r; + while (work) { + IRRef ref; + r = rset_pickbot(work); + ref = regcost_ref(as->cost[r]); +#if LJ_64 + if (ref < ASMREF_L) { + if (ra_iskref(ref)) { + if (k == ra_krefk(as, ref)) + return r; + } else { + IRIns *ir = IR(ref); + if ((ir->o == IR_KINT64 && k == (int64_t)ir_kint64(ir)->u64) || +#if LJ_GC64 + (ir->o == IR_KINT && k == ir->i) || + (ir->o == IR_KGC && k == (intptr_t)ir_kgc(ir)) || + ((ir->o == IR_KPTR || ir->o == IR_KKPTR) && + k == (intptr_t)ir_kptr(ir)) +#else + (ir->o != IR_KINT64 && k == ir->i) +#endif + ) + return r; + } + } +#else + if (ref < ASMREF_L && + k == (ra_iskref(ref) ? ra_krefk(as, ref) : IR(ref)->i)) + return r; +#endif + rset_clear(work, r); + } + pick = as->freeset & allow; + if (pick) { + /* Constants should preferably get unmodified registers. */ + if ((pick & ~as->modset)) + pick &= ~as->modset; + r = rset_pickbot(pick); /* Reduce conflicts with inverse allocation. */ + } else { + r = ra_evict(as, allow); + } + RA_DBGX((as, "allock $x $r", k, r)); + ra_setkref(as, r, k); + rset_clear(as->freeset, r); + ra_noweak(as, r); + return r; +} + +/* Allocate a specific register for a constant. */ +static void ra_allockreg(ASMState *as, intptr_t k, Reg r) +{ + Reg kr = ra_allock(as, k, RID2RSET(r)); + if (kr != r) { + IRIns irdummy; + irdummy.t.irt = IRT_INT; + ra_scratch(as, RID2RSET(r)); + emit_movrr(as, &irdummy, r, kr); + } +} +#else +#define ra_allockreg(as, k, r) emit_loadi(as, (r), (k)) +#endif + +/* Allocate a register for ref from the allowed set of registers. +** Note: this function assumes the ref does NOT have a register yet! +** Picks an optimal register, sets the cost and marks the register as non-free. +*/ +static Reg ra_allocref(ASMState *as, IRRef ref, RegSet allow) +{ + IRIns *ir = IR(ref); + RegSet pick = as->freeset & allow; + Reg r; + lua_assert(ra_noreg(ir->r)); + if (pick) { + /* First check register hint from propagation or PHI. */ + if (ra_hashint(ir->r)) { + r = ra_gethint(ir->r); + if (rset_test(pick, r)) /* Use hint register if possible. */ + goto found; + /* Rematerialization is cheaper than missing a hint. */ + if (rset_test(allow, r) && emit_canremat(regcost_ref(as->cost[r]))) { + ra_rematk(as, regcost_ref(as->cost[r])); + goto found; + } + RA_DBGX((as, "hintmiss $f $r", ref, r)); + } + /* Invariants should preferably get unmodified registers. */ + if (ref < as->loopref && !irt_isphi(ir->t)) { + if ((pick & ~as->modset)) + pick &= ~as->modset; + r = rset_pickbot(pick); /* Reduce conflicts with inverse allocation. */ + } else { + /* We've got plenty of regs, so get callee-save regs if possible. */ + if (RID_NUM_GPR > 8 && (pick & ~RSET_SCRATCH)) + pick &= ~RSET_SCRATCH; + r = rset_picktop(pick); + } + } else { + r = ra_evict(as, allow); + } +found: + RA_DBGX((as, "alloc $f $r", ref, r)); + ir->r = (uint8_t)r; + rset_clear(as->freeset, r); + ra_noweak(as, r); + as->cost[r] = REGCOST_REF_T(ref, irt_t(ir->t)); + return r; +} + +/* Allocate a register on-demand. */ +static Reg ra_alloc1(ASMState *as, IRRef ref, RegSet allow) +{ + Reg r = IR(ref)->r; + /* Note: allow is ignored if the register is already allocated. */ + if (ra_noreg(r)) r = ra_allocref(as, ref, allow); + ra_noweak(as, r); + return r; +} + +/* Add a register rename to the IR. */ +static void ra_addrename(ASMState *as, Reg down, IRRef ref, SnapNo snapno) +{ + IRRef ren; + lj_ir_set(as->J, IRT(IR_RENAME, IRT_NIL), ref, snapno); + ren = tref_ref(lj_ir_emit(as->J)); + as->J->cur.ir[ren].r = (uint8_t)down; + as->J->cur.ir[ren].s = SPS_NONE; +} + +/* Rename register allocation and emit move. */ +static void ra_rename(ASMState *as, Reg down, Reg up) +{ + IRRef ref = regcost_ref(as->cost[up] = as->cost[down]); + IRIns *ir = IR(ref); + ir->r = (uint8_t)up; + as->cost[down] = 0; + lua_assert((down < RID_MAX_GPR) == (up < RID_MAX_GPR)); + lua_assert(!rset_test(as->freeset, down) && rset_test(as->freeset, up)); + ra_free(as, down); /* 'down' is free ... */ + ra_modified(as, down); + rset_clear(as->freeset, up); /* ... and 'up' is now allocated. */ + ra_noweak(as, up); + RA_DBGX((as, "rename $f $r $r", regcost_ref(as->cost[up]), down, up)); + emit_movrr(as, ir, down, up); /* Backwards codegen needs inverse move. */ + if (!ra_hasspill(IR(ref)->s)) { /* Add the rename to the IR. */ + ra_addrename(as, down, ref, as->snapno); + } +} + +/* Pick a destination register (marked as free). +** Caveat: allow is ignored if there's already a destination register. +** Use ra_destreg() to get a specific register. +*/ +static Reg ra_dest(ASMState *as, IRIns *ir, RegSet allow) +{ + Reg dest = ir->r; + if (ra_hasreg(dest)) { + ra_free(as, dest); + ra_modified(as, dest); + } else { + if (ra_hashint(dest) && rset_test((as->freeset&allow), ra_gethint(dest))) { + dest = ra_gethint(dest); + ra_modified(as, dest); + RA_DBGX((as, "dest $r", dest)); + } else { + dest = ra_scratch(as, allow); + } + ir->r = dest; + } + if (LJ_UNLIKELY(ra_hasspill(ir->s))) ra_save(as, ir, dest); + return dest; +} + +/* Force a specific destination register (marked as free). */ +static void ra_destreg(ASMState *as, IRIns *ir, Reg r) +{ + Reg dest = ra_dest(as, ir, RID2RSET(r)); + if (dest != r) { + lua_assert(rset_test(as->freeset, r)); + ra_modified(as, r); + emit_movrr(as, ir, dest, r); + } +} + +#if LJ_TARGET_X86ORX64 +/* Propagate dest register to left reference. Emit moves as needed. +** This is a required fixup step for all 2-operand machine instructions. +*/ +static void ra_left(ASMState *as, Reg dest, IRRef lref) +{ + IRIns *ir = IR(lref); + Reg left = ir->r; + if (ra_noreg(left)) { + if (irref_isk(lref)) { + if (ir->o == IR_KNUM) { + /* FP remat needs a load except for +0. Still better than eviction. */ + if (tvispzero(ir_knum(ir)) || !(as->freeset & RSET_FPR)) { + emit_loadk64(as, dest, ir); + return; + } +#if LJ_64 + } else if (ir->o == IR_KINT64) { + emit_loadk64(as, dest, ir); + return; +#if LJ_GC64 + } else if (ir->o == IR_KGC || ir->o == IR_KPTR || ir->o == IR_KKPTR) { + emit_loadk64(as, dest, ir); + return; +#endif +#endif + } else if (ir->o != IR_KPRI) { + lua_assert(ir->o == IR_KINT || ir->o == IR_KGC || + ir->o == IR_KPTR || ir->o == IR_KKPTR || ir->o == IR_KNULL); + emit_loadi(as, dest, ir->i); + return; + } + } + if (!ra_hashint(left) && !iscrossref(as, lref)) + ra_sethint(ir->r, dest); /* Propagate register hint. */ + left = ra_allocref(as, lref, dest < RID_MAX_GPR ? RSET_GPR : RSET_FPR); + } + ra_noweak(as, left); + /* Move needed for true 3-operand instruction: y=a+b ==> y=a; y+=b. */ + if (dest != left) { + /* Use register renaming if dest is the PHI reg. */ + if (irt_isphi(ir->t) && as->phireg[dest] == lref) { + ra_modified(as, left); + ra_rename(as, left, dest); + } else { + emit_movrr(as, ir, dest, left); + } + } +} +#else +/* Similar to ra_left, except we override any hints. */ +static void ra_leftov(ASMState *as, Reg dest, IRRef lref) +{ + IRIns *ir = IR(lref); + Reg left = ir->r; + if (ra_noreg(left)) { + ra_sethint(ir->r, dest); /* Propagate register hint. */ + left = ra_allocref(as, lref, + (LJ_SOFTFP || dest < RID_MAX_GPR) ? RSET_GPR : RSET_FPR); + } + ra_noweak(as, left); + if (dest != left) { + /* Use register renaming if dest is the PHI reg. */ + if (irt_isphi(ir->t) && as->phireg[dest] == lref) { + ra_modified(as, left); + ra_rename(as, left, dest); + } else { + emit_movrr(as, ir, dest, left); + } + } +} +#endif + +#if !LJ_64 +/* Force a RID_RETLO/RID_RETHI destination register pair (marked as free). */ +static void ra_destpair(ASMState *as, IRIns *ir) +{ + Reg destlo = ir->r, desthi = (ir+1)->r; + /* First spill unrelated refs blocking the destination registers. */ + if (!rset_test(as->freeset, RID_RETLO) && + destlo != RID_RETLO && desthi != RID_RETLO) + ra_restore(as, regcost_ref(as->cost[RID_RETLO])); + if (!rset_test(as->freeset, RID_RETHI) && + destlo != RID_RETHI && desthi != RID_RETHI) + ra_restore(as, regcost_ref(as->cost[RID_RETHI])); + /* Next free the destination registers (if any). */ + if (ra_hasreg(destlo)) { + ra_free(as, destlo); + ra_modified(as, destlo); + } else { + destlo = RID_RETLO; + } + if (ra_hasreg(desthi)) { + ra_free(as, desthi); + ra_modified(as, desthi); + } else { + desthi = RID_RETHI; + } + /* Check for conflicts and shuffle the registers as needed. */ + if (destlo == RID_RETHI) { + if (desthi == RID_RETLO) { +#if LJ_TARGET_X86 + *--as->mcp = XI_XCHGa + RID_RETHI; +#else + emit_movrr(as, ir, RID_RETHI, RID_TMP); + emit_movrr(as, ir, RID_RETLO, RID_RETHI); + emit_movrr(as, ir, RID_TMP, RID_RETLO); +#endif + } else { + emit_movrr(as, ir, RID_RETHI, RID_RETLO); + if (desthi != RID_RETHI) emit_movrr(as, ir, desthi, RID_RETHI); + } + } else if (desthi == RID_RETLO) { + emit_movrr(as, ir, RID_RETLO, RID_RETHI); + if (destlo != RID_RETLO) emit_movrr(as, ir, destlo, RID_RETLO); + } else { + if (desthi != RID_RETHI) emit_movrr(as, ir, desthi, RID_RETHI); + if (destlo != RID_RETLO) emit_movrr(as, ir, destlo, RID_RETLO); + } + /* Restore spill slots (if any). */ + if (ra_hasspill((ir+1)->s)) ra_save(as, ir+1, RID_RETHI); + if (ra_hasspill(ir->s)) ra_save(as, ir, RID_RETLO); +} +#endif + +/* -- Snapshot handling --------- ----------------------------------------- */ + +/* Can we rematerialize a KNUM instead of forcing a spill? */ +static int asm_snap_canremat(ASMState *as) +{ + Reg r; + for (r = RID_MIN_FPR; r < RID_MAX_FPR; r++) + if (irref_isk(regcost_ref(as->cost[r]))) + return 1; + return 0; +} + +/* Check whether a sunk store corresponds to an allocation. */ +static int asm_sunk_store(ASMState *as, IRIns *ira, IRIns *irs) +{ + if (irs->s == 255) { + if (irs->o == IR_ASTORE || irs->o == IR_HSTORE || + irs->o == IR_FSTORE || irs->o == IR_XSTORE) { + IRIns *irk = IR(irs->op1); + if (irk->o == IR_AREF || irk->o == IR_HREFK) + irk = IR(irk->op1); + return (IR(irk->op1) == ira); + } + return 0; + } else { + return (ira + irs->s == irs); /* Quick check. */ + } +} + +/* Allocate register or spill slot for a ref that escapes to a snapshot. */ +static void asm_snap_alloc1(ASMState *as, IRRef ref) +{ + IRIns *ir = IR(ref); + if (!irref_isk(ref) && (!(ra_used(ir) || ir->r == RID_SUNK))) { + if (ir->r == RID_SINK) { + ir->r = RID_SUNK; +#if LJ_HASFFI + if (ir->o == IR_CNEWI) { /* Allocate CNEWI value. */ + asm_snap_alloc1(as, ir->op2); + if (LJ_32 && (ir+1)->o == IR_HIOP) + asm_snap_alloc1(as, (ir+1)->op2); + } else +#endif + { /* Allocate stored values for TNEW, TDUP and CNEW. */ + IRIns *irs; + lua_assert(ir->o == IR_TNEW || ir->o == IR_TDUP || ir->o == IR_CNEW); + for (irs = IR(as->snapref-1); irs > ir; irs--) + if (irs->r == RID_SINK && asm_sunk_store(as, ir, irs)) { + lua_assert(irs->o == IR_ASTORE || irs->o == IR_HSTORE || + irs->o == IR_FSTORE || irs->o == IR_XSTORE); + asm_snap_alloc1(as, irs->op2); + if (LJ_32 && (irs+1)->o == IR_HIOP) + asm_snap_alloc1(as, (irs+1)->op2); + } + } + } else { + RegSet allow; + if (ir->o == IR_CONV && ir->op2 == IRCONV_NUM_INT) { + IRIns *irc; + for (irc = IR(as->curins); irc > ir; irc--) + if ((irc->op1 == ref || irc->op2 == ref) && + !(irc->r == RID_SINK || irc->r == RID_SUNK)) + goto nosink; /* Don't sink conversion if result is used. */ + asm_snap_alloc1(as, ir->op1); + return; + } + nosink: + allow = (!LJ_SOFTFP && irt_isfp(ir->t)) ? RSET_FPR : RSET_GPR; + if ((as->freeset & allow) || + (allow == RSET_FPR && asm_snap_canremat(as))) { + /* Get a weak register if we have a free one or can rematerialize. */ + Reg r = ra_allocref(as, ref, allow); /* Allocate a register. */ + if (!irt_isphi(ir->t)) + ra_weak(as, r); /* But mark it as weakly referenced. */ + checkmclim(as); + RA_DBGX((as, "snapreg $f $r", ref, ir->r)); + } else { + ra_spill(as, ir); /* Otherwise force a spill slot. */ + RA_DBGX((as, "snapspill $f $s", ref, ir->s)); + } + } + } +} + +/* Allocate refs escaping to a snapshot. */ +static void asm_snap_alloc(ASMState *as) +{ + SnapShot *snap = &as->T->snap[as->snapno]; + SnapEntry *map = &as->T->snapmap[snap->mapofs]; + MSize n, nent = snap->nent; + for (n = 0; n < nent; n++) { + SnapEntry sn = map[n]; + IRRef ref = snap_ref(sn); + if (!irref_isk(ref)) { + asm_snap_alloc1(as, ref); + if (LJ_SOFTFP && (sn & SNAP_SOFTFPNUM)) { + lua_assert(irt_type(IR(ref+1)->t) == IRT_SOFTFP); + asm_snap_alloc1(as, ref+1); + } + } + } +} + +/* All guards for a snapshot use the same exitno. This is currently the +** same as the snapshot number. Since the exact origin of the exit cannot +** be determined, all guards for the same snapshot must exit with the same +** RegSP mapping. +** A renamed ref which has been used in a prior guard for the same snapshot +** would cause an inconsistency. The easy way out is to force a spill slot. +*/ +static int asm_snap_checkrename(ASMState *as, IRRef ren) +{ + SnapShot *snap = &as->T->snap[as->snapno]; + SnapEntry *map = &as->T->snapmap[snap->mapofs]; + MSize n, nent = snap->nent; + for (n = 0; n < nent; n++) { + SnapEntry sn = map[n]; + IRRef ref = snap_ref(sn); + if (ref == ren || (LJ_SOFTFP && (sn & SNAP_SOFTFPNUM) && ++ref == ren)) { + IRIns *ir = IR(ref); + ra_spill(as, ir); /* Register renamed, so force a spill slot. */ + RA_DBGX((as, "snaprensp $f $s", ref, ir->s)); + return 1; /* Found. */ + } + } + return 0; /* Not found. */ +} + +/* Prepare snapshot for next guard instruction. */ +static void asm_snap_prep(ASMState *as) +{ + if (as->curins < as->snapref) { + do { + if (as->snapno == 0) return; /* Called by sunk stores before snap #0. */ + as->snapno--; + as->snapref = as->T->snap[as->snapno].ref; + } while (as->curins < as->snapref); + asm_snap_alloc(as); + as->snaprename = as->T->nins; + } else { + /* Process any renames above the highwater mark. */ + for (; as->snaprename < as->T->nins; as->snaprename++) { + IRIns *ir = &as->T->ir[as->snaprename]; + if (asm_snap_checkrename(as, ir->op1)) + ir->op2 = REF_BIAS-1; /* Kill rename. */ + } + } +} + +/* -- Miscellaneous helpers ----------------------------------------------- */ + +/* Calculate stack adjustment. */ +static int32_t asm_stack_adjust(ASMState *as) +{ + if (as->evenspill <= SPS_FIXED) + return 0; + return sps_scale(sps_align(as->evenspill)); +} + +/* Must match with hash*() in lj_tab.c. */ +static uint32_t ir_khash(IRIns *ir) +{ + uint32_t lo, hi; + if (irt_isstr(ir->t)) { + return ir_kstr(ir)->hash; + } else if (irt_isnum(ir->t)) { + lo = ir_knum(ir)->u32.lo; + hi = ir_knum(ir)->u32.hi << 1; + } else if (irt_ispri(ir->t)) { + lua_assert(!irt_isnil(ir->t)); + return irt_type(ir->t)-IRT_FALSE; + } else { + lua_assert(irt_isgcv(ir->t)); + lo = u32ptr(ir_kgc(ir)); +#if LJ_GC64 + hi = (uint32_t)(u64ptr(ir_kgc(ir)) >> 32) | (irt_toitype(ir->t) << 15); +#else + hi = lo + HASH_BIAS; +#endif + } + return hashrot(lo, hi); +} + +/* -- Allocations --------------------------------------------------------- */ + +static void asm_gencall(ASMState *as, const CCallInfo *ci, IRRef *args); +static void asm_setupresult(ASMState *as, IRIns *ir, const CCallInfo *ci); + +static void asm_snew(ASMState *as, IRIns *ir) +{ + const CCallInfo *ci = &lj_ir_callinfo[IRCALL_lj_str_new]; + IRRef args[3]; + args[0] = ASMREF_L; /* lua_State *L */ + args[1] = ir->op1; /* const char *str */ + args[2] = ir->op2; /* size_t len */ + as->gcsteps++; + asm_setupresult(as, ir, ci); /* GCstr * */ + asm_gencall(as, ci, args); +} + +static void asm_tnew(ASMState *as, IRIns *ir) +{ + const CCallInfo *ci = &lj_ir_callinfo[IRCALL_lj_tab_new1]; + IRRef args[2]; + args[0] = ASMREF_L; /* lua_State *L */ + args[1] = ASMREF_TMP1; /* uint32_t ahsize */ + as->gcsteps++; + asm_setupresult(as, ir, ci); /* GCtab * */ + asm_gencall(as, ci, args); + ra_allockreg(as, ir->op1 | (ir->op2 << 24), ra_releasetmp(as, ASMREF_TMP1)); +} + +static void asm_tdup(ASMState *as, IRIns *ir) +{ + const CCallInfo *ci = &lj_ir_callinfo[IRCALL_lj_tab_dup]; + IRRef args[2]; + args[0] = ASMREF_L; /* lua_State *L */ + args[1] = ir->op1; /* const GCtab *kt */ + as->gcsteps++; + asm_setupresult(as, ir, ci); /* GCtab * */ + asm_gencall(as, ci, args); +} + +static void asm_gc_check(ASMState *as); + +/* Explicit GC step. */ +static void asm_gcstep(ASMState *as, IRIns *ir) +{ + IRIns *ira; + for (ira = IR(as->stopins+1); ira < ir; ira++) + if ((ira->o == IR_TNEW || ira->o == IR_TDUP || + (LJ_HASFFI && (ira->o == IR_CNEW || ira->o == IR_CNEWI))) && + ra_used(ira)) + as->gcsteps++; + if (as->gcsteps) + asm_gc_check(as); + as->gcsteps = 0x80000000; /* Prevent implicit GC check further up. */ +} + +/* -- Buffer operations --------------------------------------------------- */ + +static void asm_tvptr(ASMState *as, Reg dest, IRRef ref); + +static void asm_bufhdr(ASMState *as, IRIns *ir) +{ + Reg sb = ra_dest(as, ir, RSET_GPR); + if ((ir->op2 & IRBUFHDR_APPEND)) { + /* Rematerialize const buffer pointer instead of likely spill. */ + IRIns *irp = IR(ir->op1); + if (!(ra_hasreg(irp->r) || irp == ir-1 || + (irp == ir-2 && !ra_used(ir-1)))) { + while (!(irp->o == IR_BUFHDR && !(irp->op2 & IRBUFHDR_APPEND))) + irp = IR(irp->op1); + if (irref_isk(irp->op1)) { + ra_weak(as, ra_allocref(as, ir->op1, RSET_GPR)); + ir = irp; + } + } + } else { + Reg tmp = ra_scratch(as, rset_exclude(RSET_GPR, sb)); + /* Passing ir isn't strictly correct, but it's an IRT_PGC, too. */ + emit_storeofs(as, ir, tmp, sb, offsetof(SBuf, p)); + emit_loadofs(as, ir, tmp, sb, offsetof(SBuf, b)); + } +#if LJ_TARGET_X86ORX64 + ra_left(as, sb, ir->op1); +#else + ra_leftov(as, sb, ir->op1); +#endif +} + +static void asm_bufput(ASMState *as, IRIns *ir) +{ + const CCallInfo *ci = &lj_ir_callinfo[IRCALL_lj_buf_putstr]; + IRRef args[3]; + IRIns *irs; + int kchar = -129; + args[0] = ir->op1; /* SBuf * */ + args[1] = ir->op2; /* GCstr * */ + irs = IR(ir->op2); + lua_assert(irt_isstr(irs->t)); + if (irs->o == IR_KGC) { + GCstr *s = ir_kstr(irs); + if (s->len == 1) { /* Optimize put of single-char string constant. */ + kchar = (int8_t)strdata(s)[0]; /* Signed! */ + args[1] = ASMREF_TMP1; /* int, truncated to char */ + ci = &lj_ir_callinfo[IRCALL_lj_buf_putchar]; + } + } else if (mayfuse(as, ir->op2) && ra_noreg(irs->r)) { + if (irs->o == IR_TOSTR) { /* Fuse number to string conversions. */ + if (irs->op2 == IRTOSTR_NUM) { + args[1] = ASMREF_TMP1; /* TValue * */ + ci = &lj_ir_callinfo[IRCALL_lj_strfmt_putnum]; + } else { + lua_assert(irt_isinteger(IR(irs->op1)->t)); + args[1] = irs->op1; /* int */ + if (irs->op2 == IRTOSTR_INT) + ci = &lj_ir_callinfo[IRCALL_lj_strfmt_putint]; + else + ci = &lj_ir_callinfo[IRCALL_lj_buf_putchar]; + } + } else if (irs->o == IR_SNEW) { /* Fuse string allocation. */ + args[1] = irs->op1; /* const void * */ + args[2] = irs->op2; /* MSize */ + ci = &lj_ir_callinfo[IRCALL_lj_buf_putmem]; + } + } + asm_setupresult(as, ir, ci); /* SBuf * */ + asm_gencall(as, ci, args); + if (args[1] == ASMREF_TMP1) { + Reg tmp = ra_releasetmp(as, ASMREF_TMP1); + if (kchar == -129) + asm_tvptr(as, tmp, irs->op1); + else + ra_allockreg(as, kchar, tmp); + } +} + +static void asm_bufstr(ASMState *as, IRIns *ir) +{ + const CCallInfo *ci = &lj_ir_callinfo[IRCALL_lj_buf_tostr]; + IRRef args[1]; + args[0] = ir->op1; /* SBuf *sb */ + as->gcsteps++; + asm_setupresult(as, ir, ci); /* GCstr * */ + asm_gencall(as, ci, args); +} + +/* -- Type conversions ---------------------------------------------------- */ + +static void asm_tostr(ASMState *as, IRIns *ir) +{ + const CCallInfo *ci; + IRRef args[2]; + args[0] = ASMREF_L; + as->gcsteps++; + if (ir->op2 == IRTOSTR_NUM) { + args[1] = ASMREF_TMP1; /* cTValue * */ + ci = &lj_ir_callinfo[IRCALL_lj_strfmt_num]; + } else { + args[1] = ir->op1; /* int32_t k */ + if (ir->op2 == IRTOSTR_INT) + ci = &lj_ir_callinfo[IRCALL_lj_strfmt_int]; + else + ci = &lj_ir_callinfo[IRCALL_lj_strfmt_char]; + } + asm_setupresult(as, ir, ci); /* GCstr * */ + asm_gencall(as, ci, args); + if (ir->op2 == IRTOSTR_NUM) + asm_tvptr(as, ra_releasetmp(as, ASMREF_TMP1), ir->op1); +} + +#if LJ_32 && LJ_HASFFI && !LJ_SOFTFP && !LJ_TARGET_X86 +static void asm_conv64(ASMState *as, IRIns *ir) +{ + IRType st = (IRType)((ir-1)->op2 & IRCONV_SRCMASK); + IRType dt = (((ir-1)->op2 & IRCONV_DSTMASK) >> IRCONV_DSH); + IRCallID id; + IRRef args[2]; + lua_assert((ir-1)->o == IR_CONV && ir->o == IR_HIOP); + args[LJ_BE] = (ir-1)->op1; + args[LJ_LE] = ir->op1; + if (st == IRT_NUM || st == IRT_FLOAT) { + id = IRCALL_fp64_d2l + ((st == IRT_FLOAT) ? 2 : 0) + (dt - IRT_I64); + ir--; + } else { + id = IRCALL_fp64_l2d + ((dt == IRT_FLOAT) ? 2 : 0) + (st - IRT_I64); + } + { +#if LJ_TARGET_ARM && !LJ_ABI_SOFTFP + CCallInfo cim = lj_ir_callinfo[id], *ci = &cim; + cim.flags |= CCI_VARARG; /* These calls don't use the hard-float ABI! */ +#else + const CCallInfo *ci = &lj_ir_callinfo[id]; +#endif + asm_setupresult(as, ir, ci); + asm_gencall(as, ci, args); + } +} +#endif + +/* -- Memory references --------------------------------------------------- */ + +static void asm_newref(ASMState *as, IRIns *ir) +{ + const CCallInfo *ci = &lj_ir_callinfo[IRCALL_lj_tab_newkey]; + IRRef args[3]; + if (ir->r == RID_SINK) + return; + args[0] = ASMREF_L; /* lua_State *L */ + args[1] = ir->op1; /* GCtab *t */ + args[2] = ASMREF_TMP1; /* cTValue *key */ + asm_setupresult(as, ir, ci); /* TValue * */ + asm_gencall(as, ci, args); + asm_tvptr(as, ra_releasetmp(as, ASMREF_TMP1), ir->op2); +} + +static void asm_lref(ASMState *as, IRIns *ir) +{ + Reg r = ra_dest(as, ir, RSET_GPR); +#if LJ_TARGET_X86ORX64 + ra_left(as, r, ASMREF_L); +#else + ra_leftov(as, r, ASMREF_L); +#endif +} + +/* -- Calls --------------------------------------------------------------- */ + +/* Collect arguments from CALL* and CARG instructions. */ +static void asm_collectargs(ASMState *as, IRIns *ir, + const CCallInfo *ci, IRRef *args) +{ + uint32_t n = CCI_XNARGS(ci); + lua_assert(n <= CCI_NARGS_MAX*2); /* Account for split args. */ + if ((ci->flags & CCI_L)) { *args++ = ASMREF_L; n--; } + while (n-- > 1) { + ir = IR(ir->op1); + lua_assert(ir->o == IR_CARG); + args[n] = ir->op2 == REF_NIL ? 0 : ir->op2; + } + args[0] = ir->op1 == REF_NIL ? 0 : ir->op1; + lua_assert(IR(ir->op1)->o != IR_CARG); +} + +/* Reconstruct CCallInfo flags for CALLX*. */ +static uint32_t asm_callx_flags(ASMState *as, IRIns *ir) +{ + uint32_t nargs = 0; + if (ir->op1 != REF_NIL) { /* Count number of arguments first. */ + IRIns *ira = IR(ir->op1); + nargs++; + while (ira->o == IR_CARG) { nargs++; ira = IR(ira->op1); } + } +#if LJ_HASFFI + if (IR(ir->op2)->o == IR_CARG) { /* Copy calling convention info. */ + CTypeID id = (CTypeID)IR(IR(ir->op2)->op2)->i; + CType *ct = ctype_get(ctype_ctsG(J2G(as->J)), id); + nargs |= ((ct->info & CTF_VARARG) ? CCI_VARARG : 0); +#if LJ_TARGET_X86 + nargs |= (ctype_cconv(ct->info) << CCI_CC_SHIFT); +#endif + } +#endif + return (nargs | (ir->t.irt << CCI_OTSHIFT)); +} + +static void asm_callid(ASMState *as, IRIns *ir, IRCallID id) +{ + const CCallInfo *ci = &lj_ir_callinfo[id]; + IRRef args[2]; + args[0] = ir->op1; + args[1] = ir->op2; + asm_setupresult(as, ir, ci); + asm_gencall(as, ci, args); +} + +static void asm_call(ASMState *as, IRIns *ir) +{ + IRRef args[CCI_NARGS_MAX]; + const CCallInfo *ci = &lj_ir_callinfo[ir->op2]; + asm_collectargs(as, ir, ci, args); + asm_setupresult(as, ir, ci); + asm_gencall(as, ci, args); +} + +#if !LJ_SOFTFP32 +static void asm_fppow(ASMState *as, IRIns *ir, IRRef lref, IRRef rref) +{ + const CCallInfo *ci = &lj_ir_callinfo[IRCALL_pow]; + IRRef args[2]; + args[0] = lref; + args[1] = rref; + asm_setupresult(as, ir, ci); + asm_gencall(as, ci, args); +} + +static int asm_fpjoin_pow(ASMState *as, IRIns *ir) +{ + IRIns *irp = IR(ir->op1); + if (irp == ir-1 && irp->o == IR_MUL && !ra_used(irp)) { + IRIns *irpp = IR(irp->op1); + if (irpp == ir-2 && irpp->o == IR_FPMATH && + irpp->op2 == IRFPM_LOG2 && !ra_used(irpp)) { + asm_fppow(as, ir, irpp->op1, irp->op2); + return 1; + } + } + return 0; +} +#endif + +/* -- PHI and loop handling ----------------------------------------------- */ + +/* Break a PHI cycle by renaming to a free register (evict if needed). */ +static void asm_phi_break(ASMState *as, RegSet blocked, RegSet blockedby, + RegSet allow) +{ + RegSet candidates = blocked & allow; + if (candidates) { /* If this register file has candidates. */ + /* Note: the set for ra_pick cannot be empty, since each register file + ** has some registers never allocated to PHIs. + */ + Reg down, up = ra_pick(as, ~blocked & allow); /* Get a free register. */ + if (candidates & ~blockedby) /* Optimize shifts, else it's a cycle. */ + candidates = candidates & ~blockedby; + down = rset_picktop(candidates); /* Pick candidate PHI register. */ + ra_rename(as, down, up); /* And rename it to the free register. */ + } +} + +/* PHI register shuffling. +** +** The allocator tries hard to preserve PHI register assignments across +** the loop body. Most of the time this loop does nothing, since there +** are no register mismatches. +** +** If a register mismatch is detected and ... +** - the register is currently free: rename it. +** - the register is blocked by an invariant: restore/remat and rename it. +** - Otherwise the register is used by another PHI, so mark it as blocked. +** +** The renames are order-sensitive, so just retry the loop if a register +** is marked as blocked, but has been freed in the meantime. A cycle is +** detected if all of the blocked registers are allocated. To break the +** cycle rename one of them to a free register and retry. +** +** Note that PHI spill slots are kept in sync and don't need to be shuffled. +*/ +static void asm_phi_shuffle(ASMState *as) +{ + RegSet work; + + /* Find and resolve PHI register mismatches. */ + for (;;) { + RegSet blocked = RSET_EMPTY; + RegSet blockedby = RSET_EMPTY; + RegSet phiset = as->phiset; + while (phiset) { /* Check all left PHI operand registers. */ + Reg r = rset_pickbot(phiset); + IRIns *irl = IR(as->phireg[r]); + Reg left = irl->r; + if (r != left) { /* Mismatch? */ + if (!rset_test(as->freeset, r)) { /* PHI register blocked? */ + IRRef ref = regcost_ref(as->cost[r]); + /* Blocked by other PHI (w/reg)? */ + if (!ra_iskref(ref) && irt_ismarked(IR(ref)->t)) { + rset_set(blocked, r); + if (ra_hasreg(left)) + rset_set(blockedby, left); + left = RID_NONE; + } else { /* Otherwise grab register from invariant. */ + ra_restore(as, ref); + checkmclim(as); + } + } + if (ra_hasreg(left)) { + ra_rename(as, left, r); + checkmclim(as); + } + } + rset_clear(phiset, r); + } + if (!blocked) break; /* Finished. */ + if (!(as->freeset & blocked)) { /* Break cycles if none are free. */ + asm_phi_break(as, blocked, blockedby, RSET_GPR); + if (!LJ_SOFTFP) asm_phi_break(as, blocked, blockedby, RSET_FPR); + checkmclim(as); + } /* Else retry some more renames. */ + } + + /* Restore/remat invariants whose registers are modified inside the loop. */ +#if !LJ_SOFTFP + work = as->modset & ~(as->freeset | as->phiset) & RSET_FPR; + while (work) { + Reg r = rset_pickbot(work); + ra_restore(as, regcost_ref(as->cost[r])); + rset_clear(work, r); + checkmclim(as); + } +#endif + work = as->modset & ~(as->freeset | as->phiset); + while (work) { + Reg r = rset_pickbot(work); + ra_restore(as, regcost_ref(as->cost[r])); + rset_clear(work, r); + checkmclim(as); + } + + /* Allocate and save all unsaved PHI regs and clear marks. */ + work = as->phiset; + while (work) { + Reg r = rset_picktop(work); + IRRef lref = as->phireg[r]; + IRIns *ir = IR(lref); + if (ra_hasspill(ir->s)) { /* Left PHI gained a spill slot? */ + irt_clearmark(ir->t); /* Handled here, so clear marker now. */ + ra_alloc1(as, lref, RID2RSET(r)); + ra_save(as, ir, r); /* Save to spill slot inside the loop. */ + checkmclim(as); + } + rset_clear(work, r); + } +} + +/* Copy unsynced left/right PHI spill slots. Rarely needed. */ +static void asm_phi_copyspill(ASMState *as) +{ + int need = 0; + IRIns *ir; + for (ir = IR(as->orignins-1); ir->o == IR_PHI; ir--) + if (ra_hasspill(ir->s) && ra_hasspill(IR(ir->op1)->s)) + need |= irt_isfp(ir->t) ? 2 : 1; /* Unsynced spill slot? */ + if ((need & 1)) { /* Copy integer spill slots. */ +#if !LJ_TARGET_X86ORX64 + Reg r = RID_TMP; +#else + Reg r = RID_RET; + if ((as->freeset & RSET_GPR)) + r = rset_pickbot((as->freeset & RSET_GPR)); + else + emit_spload(as, IR(regcost_ref(as->cost[r])), r, SPOFS_TMP); +#endif + for (ir = IR(as->orignins-1); ir->o == IR_PHI; ir--) { + if (ra_hasspill(ir->s)) { + IRIns *irl = IR(ir->op1); + if (ra_hasspill(irl->s) && !irt_isfp(ir->t)) { + emit_spstore(as, irl, r, sps_scale(irl->s)); + emit_spload(as, ir, r, sps_scale(ir->s)); + checkmclim(as); + } + } + } +#if LJ_TARGET_X86ORX64 + if (!rset_test(as->freeset, r)) + emit_spstore(as, IR(regcost_ref(as->cost[r])), r, SPOFS_TMP); +#endif + } +#if !LJ_SOFTFP + if ((need & 2)) { /* Copy FP spill slots. */ +#if LJ_TARGET_X86 + Reg r = RID_XMM0; +#else + Reg r = RID_FPRET; +#endif + if ((as->freeset & RSET_FPR)) + r = rset_pickbot((as->freeset & RSET_FPR)); + if (!rset_test(as->freeset, r)) + emit_spload(as, IR(regcost_ref(as->cost[r])), r, SPOFS_TMP); + for (ir = IR(as->orignins-1); ir->o == IR_PHI; ir--) { + if (ra_hasspill(ir->s)) { + IRIns *irl = IR(ir->op1); + if (ra_hasspill(irl->s) && irt_isfp(ir->t)) { + emit_spstore(as, irl, r, sps_scale(irl->s)); + emit_spload(as, ir, r, sps_scale(ir->s)); + checkmclim(as); + } + } + } + if (!rset_test(as->freeset, r)) + emit_spstore(as, IR(regcost_ref(as->cost[r])), r, SPOFS_TMP); + } +#endif +} + +/* Emit renames for left PHIs which are only spilled outside the loop. */ +static void asm_phi_fixup(ASMState *as) +{ + RegSet work = as->phiset; + while (work) { + Reg r = rset_picktop(work); + IRRef lref = as->phireg[r]; + IRIns *ir = IR(lref); + if (irt_ismarked(ir->t)) { + irt_clearmark(ir->t); + /* Left PHI gained a spill slot before the loop? */ + if (ra_hasspill(ir->s)) { + ra_addrename(as, r, lref, as->loopsnapno); + } + } + rset_clear(work, r); + } +} + +/* Setup right PHI reference. */ +static void asm_phi(ASMState *as, IRIns *ir) +{ + RegSet allow = ((!LJ_SOFTFP && irt_isfp(ir->t)) ? RSET_FPR : RSET_GPR) & + ~as->phiset; + RegSet afree = (as->freeset & allow); + IRIns *irl = IR(ir->op1); + IRIns *irr = IR(ir->op2); + if (ir->r == RID_SINK) /* Sink PHI. */ + return; + /* Spill slot shuffling is not implemented yet (but rarely needed). */ + if (ra_hasspill(irl->s) || ra_hasspill(irr->s)) + lj_trace_err(as->J, LJ_TRERR_NYIPHI); + /* Leave at least one register free for non-PHIs (and PHI cycle breaking). */ + if ((afree & (afree-1))) { /* Two or more free registers? */ + Reg r; + if (ra_noreg(irr->r)) { /* Get a register for the right PHI. */ + r = ra_allocref(as, ir->op2, allow); + } else { /* Duplicate right PHI, need a copy (rare). */ + r = ra_scratch(as, allow); + emit_movrr(as, irr, r, irr->r); + } + ir->r = (uint8_t)r; + rset_set(as->phiset, r); + as->phireg[r] = (IRRef1)ir->op1; + irt_setmark(irl->t); /* Marks left PHIs _with_ register. */ + if (ra_noreg(irl->r)) + ra_sethint(irl->r, r); /* Set register hint for left PHI. */ + } else { /* Otherwise allocate a spill slot. */ + /* This is overly restrictive, but it triggers only on synthetic code. */ + if (ra_hasreg(irl->r) || ra_hasreg(irr->r)) + lj_trace_err(as->J, LJ_TRERR_NYIPHI); + ra_spill(as, ir); + irr->s = ir->s; /* Set right PHI spill slot. Sync left slot later. */ + } +} + +static void asm_loop_fixup(ASMState *as); + +/* Middle part of a loop. */ +static void asm_loop(ASMState *as) +{ + MCode *mcspill; + /* LOOP is a guard, so the snapno is up to date. */ + as->loopsnapno = as->snapno; + if (as->gcsteps) + asm_gc_check(as); + /* LOOP marks the transition from the variant to the invariant part. */ + as->flagmcp = as->invmcp = NULL; + as->sectref = 0; + if (!neverfuse(as)) as->fuseref = 0; + asm_phi_shuffle(as); + mcspill = as->mcp; + asm_phi_copyspill(as); + asm_loop_fixup(as); + as->mcloop = as->mcp; + RA_DBGX((as, "===== LOOP =====")); + if (!as->realign) RA_DBG_FLUSH(); + if (as->mcp != mcspill) + emit_jmp(as, mcspill); +} + +/* -- Target-specific assembler ------------------------------------------- */ + +#if LJ_TARGET_X86ORX64 +#include "lj_asm_x86.h" +#elif LJ_TARGET_ARM +#include "lj_asm_arm.h" +#elif LJ_TARGET_ARM64 +#include "lj_asm_arm64.h" +#elif LJ_TARGET_PPC +#include "lj_asm_ppc.h" +#elif LJ_TARGET_MIPS +#include "lj_asm_mips.h" +#else +#error "Missing assembler for target CPU" +#endif + +/* -- Instruction dispatch ------------------------------------------------ */ + +/* Assemble a single instruction. */ +static void asm_ir(ASMState *as, IRIns *ir) +{ + switch ((IROp)ir->o) { + /* Miscellaneous ops. */ + case IR_LOOP: asm_loop(as); break; + case IR_NOP: case IR_XBAR: lua_assert(!ra_used(ir)); break; + case IR_USE: + ra_alloc1(as, ir->op1, irt_isfp(ir->t) ? RSET_FPR : RSET_GPR); break; + case IR_PHI: asm_phi(as, ir); break; + case IR_HIOP: asm_hiop(as, ir); break; + case IR_GCSTEP: asm_gcstep(as, ir); break; + case IR_PROF: asm_prof(as, ir); break; + + /* Guarded assertions. */ + case IR_LT: case IR_GE: case IR_LE: case IR_GT: + case IR_ULT: case IR_UGE: case IR_ULE: case IR_UGT: + case IR_ABC: + asm_comp(as, ir); + break; + case IR_EQ: case IR_NE: + if ((ir-1)->o == IR_HREF && ir->op1 == as->curins-1) { + as->curins--; + asm_href(as, ir-1, (IROp)ir->o); + } else { + asm_equal(as, ir); + } + break; + + case IR_RETF: asm_retf(as, ir); break; + + /* Bit ops. */ + case IR_BNOT: asm_bnot(as, ir); break; + case IR_BSWAP: asm_bswap(as, ir); break; + case IR_BAND: asm_band(as, ir); break; + case IR_BOR: asm_bor(as, ir); break; + case IR_BXOR: asm_bxor(as, ir); break; + case IR_BSHL: asm_bshl(as, ir); break; + case IR_BSHR: asm_bshr(as, ir); break; + case IR_BSAR: asm_bsar(as, ir); break; + case IR_BROL: asm_brol(as, ir); break; + case IR_BROR: asm_bror(as, ir); break; + + /* Arithmetic ops. */ + case IR_ADD: asm_add(as, ir); break; + case IR_SUB: asm_sub(as, ir); break; + case IR_MUL: asm_mul(as, ir); break; + case IR_MOD: asm_mod(as, ir); break; + case IR_NEG: asm_neg(as, ir); break; +#if LJ_SOFTFP32 + case IR_DIV: case IR_POW: case IR_ABS: + case IR_ATAN2: case IR_LDEXP: case IR_FPMATH: case IR_TOBIT: + lua_assert(0); /* Unused for LJ_SOFTFP32. */ + break; +#else + case IR_DIV: asm_div(as, ir); break; + case IR_POW: asm_pow(as, ir); break; + case IR_ABS: asm_abs(as, ir); break; + case IR_ATAN2: asm_atan2(as, ir); break; + case IR_LDEXP: asm_ldexp(as, ir); break; + case IR_FPMATH: asm_fpmath(as, ir); break; + case IR_TOBIT: asm_tobit(as, ir); break; +#endif + case IR_MIN: asm_min(as, ir); break; + case IR_MAX: asm_max(as, ir); break; + + /* Overflow-checking arithmetic ops. */ + case IR_ADDOV: asm_addov(as, ir); break; + case IR_SUBOV: asm_subov(as, ir); break; + case IR_MULOV: asm_mulov(as, ir); break; + + /* Memory references. */ + case IR_AREF: asm_aref(as, ir); break; + case IR_HREF: asm_href(as, ir, 0); break; + case IR_HREFK: asm_hrefk(as, ir); break; + case IR_NEWREF: asm_newref(as, ir); break; + case IR_UREFO: case IR_UREFC: asm_uref(as, ir); break; + case IR_FREF: asm_fref(as, ir); break; + case IR_STRREF: asm_strref(as, ir); break; + case IR_LREF: asm_lref(as, ir); break; + + /* Loads and stores. */ + case IR_ALOAD: case IR_HLOAD: case IR_ULOAD: case IR_VLOAD: + asm_ahuvload(as, ir); + break; + case IR_FLOAD: asm_fload(as, ir); break; + case IR_XLOAD: asm_xload(as, ir); break; + case IR_SLOAD: asm_sload(as, ir); break; + + case IR_ASTORE: case IR_HSTORE: case IR_USTORE: asm_ahustore(as, ir); break; + case IR_FSTORE: asm_fstore(as, ir); break; + case IR_XSTORE: asm_xstore(as, ir); break; + + /* Allocations. */ + case IR_SNEW: case IR_XSNEW: asm_snew(as, ir); break; + case IR_TNEW: asm_tnew(as, ir); break; + case IR_TDUP: asm_tdup(as, ir); break; + case IR_CNEW: case IR_CNEWI: asm_cnew(as, ir); break; + + /* Buffer operations. */ + case IR_BUFHDR: asm_bufhdr(as, ir); break; + case IR_BUFPUT: asm_bufput(as, ir); break; + case IR_BUFSTR: asm_bufstr(as, ir); break; + + /* Write barriers. */ + case IR_TBAR: asm_tbar(as, ir); break; + case IR_OBAR: asm_obar(as, ir); break; + + /* Type conversions. */ + case IR_CONV: asm_conv(as, ir); break; + case IR_TOSTR: asm_tostr(as, ir); break; + case IR_STRTO: asm_strto(as, ir); break; + + /* Calls. */ + case IR_CALLA: + as->gcsteps++; + /* fallthrough */ + case IR_CALLN: case IR_CALLL: case IR_CALLS: asm_call(as, ir); break; + case IR_CALLXS: asm_callx(as, ir); break; + case IR_CARG: break; + + default: + setintV(&as->J->errinfo, ir->o); + lj_trace_err_info(as->J, LJ_TRERR_NYIIR); + break; + } +} + +/* -- Head of trace ------------------------------------------------------- */ + +/* Head of a root trace. */ +static void asm_head_root(ASMState *as) +{ + int32_t spadj; + asm_head_root_base(as); + emit_setvmstate(as, (int32_t)as->T->traceno); + spadj = asm_stack_adjust(as); + as->T->spadjust = (uint16_t)spadj; + emit_spsub(as, spadj); + /* Root traces assume a checked stack for the starting proto. */ + as->T->topslot = gcref(as->T->startpt)->pt.framesize; +} + +/* Head of a side trace. +** +** The current simplistic algorithm requires that all slots inherited +** from the parent are live in a register between pass 2 and pass 3. This +** avoids the complexity of stack slot shuffling. But of course this may +** overflow the register set in some cases and cause the dreaded error: +** "NYI: register coalescing too complex". A refined algorithm is needed. +*/ +static void asm_head_side(ASMState *as) +{ + IRRef1 sloadins[RID_MAX]; + RegSet allow = RSET_ALL; /* Inverse of all coalesced registers. */ + RegSet live = RSET_EMPTY; /* Live parent registers. */ + IRIns *irp = &as->parent->ir[REF_BASE]; /* Parent base. */ + int32_t spadj, spdelta; + int pass2 = 0; + int pass3 = 0; + IRRef i; + + if (as->snapno && as->topslot > as->parent->topslot) { + /* Force snap #0 alloc to prevent register overwrite in stack check. */ + as->snapno = 0; + asm_snap_alloc(as); + } + allow = asm_head_side_base(as, irp, allow); + + /* Scan all parent SLOADs and collect register dependencies. */ + for (i = as->stopins; i > REF_BASE; i--) { + IRIns *ir = IR(i); + RegSP rs; + lua_assert((ir->o == IR_SLOAD && (ir->op2 & IRSLOAD_PARENT)) || + (LJ_SOFTFP && ir->o == IR_HIOP) || ir->o == IR_PVAL); + rs = as->parentmap[i - REF_FIRST]; + if (ra_hasreg(ir->r)) { + rset_clear(allow, ir->r); + if (ra_hasspill(ir->s)) { + ra_save(as, ir, ir->r); + checkmclim(as); + } + } else if (ra_hasspill(ir->s)) { + irt_setmark(ir->t); + pass2 = 1; + } + if (ir->r == rs) { /* Coalesce matching registers right now. */ + ra_free(as, ir->r); + } else if (ra_hasspill(regsp_spill(rs))) { + if (ra_hasreg(ir->r)) + pass3 = 1; + } else if (ra_used(ir)) { + sloadins[rs] = (IRRef1)i; + rset_set(live, rs); /* Block live parent register. */ + } + } + + /* Calculate stack frame adjustment. */ + spadj = asm_stack_adjust(as); + spdelta = spadj - (int32_t)as->parent->spadjust; + if (spdelta < 0) { /* Don't shrink the stack frame. */ + spadj = (int32_t)as->parent->spadjust; + spdelta = 0; + } + as->T->spadjust = (uint16_t)spadj; + + /* Reload spilled target registers. */ + if (pass2) { + for (i = as->stopins; i > REF_BASE; i--) { + IRIns *ir = IR(i); + if (irt_ismarked(ir->t)) { + RegSet mask; + Reg r; + RegSP rs; + irt_clearmark(ir->t); + rs = as->parentmap[i - REF_FIRST]; + if (!ra_hasspill(regsp_spill(rs))) + ra_sethint(ir->r, rs); /* Hint may be gone, set it again. */ + else if (sps_scale(regsp_spill(rs))+spdelta == sps_scale(ir->s)) + continue; /* Same spill slot, do nothing. */ + mask = ((!LJ_SOFTFP && irt_isfp(ir->t)) ? RSET_FPR : RSET_GPR) & allow; + if (mask == RSET_EMPTY) + lj_trace_err(as->J, LJ_TRERR_NYICOAL); + r = ra_allocref(as, i, mask); + ra_save(as, ir, r); + rset_clear(allow, r); + if (r == rs) { /* Coalesce matching registers right now. */ + ra_free(as, r); + rset_clear(live, r); + } else if (ra_hasspill(regsp_spill(rs))) { + pass3 = 1; + } + checkmclim(as); + } + } + } + + /* Store trace number and adjust stack frame relative to the parent. */ + emit_setvmstate(as, (int32_t)as->T->traceno); + emit_spsub(as, spdelta); + +#if !LJ_TARGET_X86ORX64 + /* Restore BASE register from parent spill slot. */ + if (ra_hasspill(irp->s)) + emit_spload(as, IR(REF_BASE), IR(REF_BASE)->r, sps_scale(irp->s)); +#endif + + /* Restore target registers from parent spill slots. */ + if (pass3) { + RegSet work = ~as->freeset & RSET_ALL; + while (work) { + Reg r = rset_pickbot(work); + IRRef ref = regcost_ref(as->cost[r]); + RegSP rs = as->parentmap[ref - REF_FIRST]; + rset_clear(work, r); + if (ra_hasspill(regsp_spill(rs))) { + int32_t ofs = sps_scale(regsp_spill(rs)); + ra_free(as, r); + emit_spload(as, IR(ref), r, ofs); + checkmclim(as); + } + } + } + + /* Shuffle registers to match up target regs with parent regs. */ + for (;;) { + RegSet work; + + /* Repeatedly coalesce free live registers by moving to their target. */ + while ((work = as->freeset & live) != RSET_EMPTY) { + Reg rp = rset_pickbot(work); + IRIns *ir = IR(sloadins[rp]); + rset_clear(live, rp); + rset_clear(allow, rp); + ra_free(as, ir->r); + emit_movrr(as, ir, ir->r, rp); + checkmclim(as); + } + + /* We're done if no live registers remain. */ + if (live == RSET_EMPTY) + break; + + /* Break cycles by renaming one target to a temp. register. */ + if (live & RSET_GPR) { + RegSet tmpset = as->freeset & ~live & allow & RSET_GPR; + if (tmpset == RSET_EMPTY) + lj_trace_err(as->J, LJ_TRERR_NYICOAL); + ra_rename(as, rset_pickbot(live & RSET_GPR), rset_pickbot(tmpset)); + } + if (!LJ_SOFTFP && (live & RSET_FPR)) { + RegSet tmpset = as->freeset & ~live & allow & RSET_FPR; + if (tmpset == RSET_EMPTY) + lj_trace_err(as->J, LJ_TRERR_NYICOAL); + ra_rename(as, rset_pickbot(live & RSET_FPR), rset_pickbot(tmpset)); + } + checkmclim(as); + /* Continue with coalescing to fix up the broken cycle(s). */ + } + + /* Inherit top stack slot already checked by parent trace. */ + as->T->topslot = as->parent->topslot; + if (as->topslot > as->T->topslot) { /* Need to check for higher slot? */ +#ifdef EXITSTATE_CHECKEXIT + /* Highest exit + 1 indicates stack check. */ + ExitNo exitno = as->T->nsnap; +#else + /* Reuse the parent exit in the context of the parent trace. */ + ExitNo exitno = as->J->exitno; +#endif + as->T->topslot = (uint8_t)as->topslot; /* Remember for child traces. */ + asm_stack_check(as, as->topslot, irp, allow & RSET_GPR, exitno); + } +} + +/* -- Tail of trace ------------------------------------------------------- */ + +/* Get base slot for a snapshot. */ +static BCReg asm_baseslot(ASMState *as, SnapShot *snap, int *gotframe) +{ + SnapEntry *map = &as->T->snapmap[snap->mapofs]; + MSize n; + for (n = snap->nent; n > 0; n--) { + SnapEntry sn = map[n-1]; + if ((sn & SNAP_FRAME)) { + *gotframe = 1; + return snap_slot(sn) - LJ_FR2; + } + } + return 0; +} + +/* Link to another trace. */ +static void asm_tail_link(ASMState *as) +{ + SnapNo snapno = as->T->nsnap-1; /* Last snapshot. */ + SnapShot *snap = &as->T->snap[snapno]; + int gotframe = 0; + BCReg baseslot = asm_baseslot(as, snap, &gotframe); + + as->topslot = snap->topslot; + checkmclim(as); + ra_allocref(as, REF_BASE, RID2RSET(RID_BASE)); + + if (as->T->link == 0) { + /* Setup fixed registers for exit to interpreter. */ + const BCIns *pc = snap_pc(&as->T->snapmap[snap->mapofs + snap->nent]); + int32_t mres; + if (bc_op(*pc) == BC_JLOOP) { /* NYI: find a better way to do this. */ + BCIns *retpc = &traceref(as->J, bc_d(*pc))->startins; + if (bc_isret(bc_op(*retpc))) + pc = retpc; + } +#if LJ_GC64 + emit_loadu64(as, RID_LPC, u64ptr(pc)); +#else + ra_allockreg(as, i32ptr(J2GG(as->J)->dispatch), RID_DISPATCH); + ra_allockreg(as, i32ptr(pc), RID_LPC); +#endif + mres = (int32_t)(snap->nslots - baseslot - LJ_FR2); + switch (bc_op(*pc)) { + case BC_CALLM: case BC_CALLMT: + mres -= (int32_t)(1 + LJ_FR2 + bc_a(*pc) + bc_c(*pc)); break; + case BC_RETM: mres -= (int32_t)(bc_a(*pc) + bc_d(*pc)); break; + case BC_TSETM: mres -= (int32_t)bc_a(*pc); break; + default: if (bc_op(*pc) < BC_FUNCF) mres = 0; break; + } + ra_allockreg(as, mres, RID_RET); /* Return MULTRES or 0. */ + } else if (baseslot) { + /* Save modified BASE for linking to trace with higher start frame. */ + emit_setgl(as, RID_BASE, jit_base); + } + emit_addptr(as, RID_BASE, 8*(int32_t)baseslot); + + if (as->J->ktrace) { /* Patch ktrace slot with the final GCtrace pointer. */ + setgcref(IR(as->J->ktrace)[LJ_GC64].gcr, obj2gco(as->J->curfinal)); + IR(as->J->ktrace)->o = IR_KGC; + } + + /* Sync the interpreter state with the on-trace state. */ + asm_stack_restore(as, snap); + + /* Root traces that add frames need to check the stack at the end. */ + if (!as->parent && gotframe) + asm_stack_check(as, as->topslot, NULL, as->freeset & RSET_GPR, snapno); +} + +/* -- Trace setup --------------------------------------------------------- */ + +/* Clear reg/sp for all instructions and add register hints. */ +static void asm_setup_regsp(ASMState *as) +{ + GCtrace *T = as->T; + int sink = T->sinktags; + IRRef nins = T->nins; + IRIns *ir, *lastir; + int inloop; +#if LJ_TARGET_ARM + uint32_t rload = 0xa6402a64; +#endif + + ra_setup(as); + + /* Clear reg/sp for constants. */ + for (ir = IR(T->nk), lastir = IR(REF_BASE); ir < lastir; ir++) { + ir->prev = REGSP_INIT; + if (irt_is64(ir->t) && ir->o != IR_KNULL) { +#if LJ_GC64 + /* The false-positive of irt_is64() for ASMREF_L (REF_NIL) is OK here. */ + ir->i = 0; /* Will become non-zero only for RIP-relative addresses. */ +#else + /* Make life easier for backends by putting address of constant in i. */ + ir->i = (int32_t)(intptr_t)(ir+1); +#endif + ir++; + } + } + + /* REF_BASE is used for implicit references to the BASE register. */ + lastir->prev = REGSP_HINT(RID_BASE); + + as->snaprename = nins; + as->snapref = nins; + as->snapno = T->nsnap; + + as->stopins = REF_BASE; + as->orignins = nins; + as->curins = nins; + + /* Setup register hints for parent link instructions. */ + ir = IR(REF_FIRST); + if (as->parent) { + uint16_t *p; + lastir = lj_snap_regspmap(as->parent, as->J->exitno, ir); + if (lastir - ir > LJ_MAX_JSLOTS) + lj_trace_err(as->J, LJ_TRERR_NYICOAL); + as->stopins = (IRRef)((lastir-1) - as->ir); + for (p = as->parentmap; ir < lastir; ir++) { + RegSP rs = ir->prev; + *p++ = (uint16_t)rs; /* Copy original parent RegSP to parentmap. */ + if (!ra_hasspill(regsp_spill(rs))) + ir->prev = (uint16_t)REGSP_HINT(regsp_reg(rs)); + else + ir->prev = REGSP_INIT; + } + } + + inloop = 0; + as->evenspill = SPS_FIRST; + for (lastir = IR(nins); ir < lastir; ir++) { + if (sink) { + if (ir->r == RID_SINK) + continue; + if (ir->r == RID_SUNK) { /* Revert after ASM restart. */ + ir->r = RID_SINK; + continue; + } + } + switch (ir->o) { + case IR_LOOP: + inloop = 1; + break; +#if LJ_TARGET_ARM + case IR_SLOAD: + if (!((ir->op2 & IRSLOAD_TYPECHECK) || (ir+1)->o == IR_HIOP)) + break; + /* fallthrough */ + case IR_ALOAD: case IR_HLOAD: case IR_ULOAD: case IR_VLOAD: + if (!LJ_SOFTFP && irt_isnum(ir->t)) break; + ir->prev = (uint16_t)REGSP_HINT((rload & 15)); + rload = lj_ror(rload, 4); + continue; +#endif + case IR_CALLXS: { + CCallInfo ci; + ci.flags = asm_callx_flags(as, ir); + ir->prev = asm_setup_call_slots(as, ir, &ci); + if (inloop) + as->modset |= RSET_SCRATCH; + continue; + } + case IR_CALLN: case IR_CALLA: case IR_CALLL: case IR_CALLS: { + const CCallInfo *ci = &lj_ir_callinfo[ir->op2]; + ir->prev = asm_setup_call_slots(as, ir, ci); + if (inloop) + as->modset |= (ci->flags & CCI_NOFPRCLOBBER) ? + (RSET_SCRATCH & ~RSET_FPR) : RSET_SCRATCH; + continue; + } +#if LJ_SOFTFP || (LJ_32 && LJ_HASFFI) + case IR_HIOP: + switch ((ir-1)->o) { +#if LJ_SOFTFP && LJ_TARGET_ARM + case IR_SLOAD: case IR_ALOAD: case IR_HLOAD: case IR_ULOAD: case IR_VLOAD: + if (ra_hashint((ir-1)->r)) { + ir->prev = (ir-1)->prev + 1; + continue; + } + break; +#endif +#if !LJ_SOFTFP && LJ_NEED_FP64 + case IR_CONV: + if (irt_isfp((ir-1)->t)) { + ir->prev = REGSP_HINT(RID_FPRET); + continue; + } + /* fallthrough */ +#endif + case IR_CALLN: case IR_CALLXS: +#if LJ_SOFTFP + case IR_MIN: case IR_MAX: +#endif + (ir-1)->prev = REGSP_HINT(RID_RETLO); + ir->prev = REGSP_HINT(RID_RETHI); + continue; + default: + break; + } + break; +#endif +#if LJ_SOFTFP + case IR_MIN: case IR_MAX: + if ((ir+1)->o != IR_HIOP) break; + /* fallthrough */ +#endif + /* C calls evict all scratch regs and return results in RID_RET. */ + case IR_SNEW: case IR_XSNEW: case IR_NEWREF: case IR_BUFPUT: + if (REGARG_NUMGPR < 3 && as->evenspill < 3) + as->evenspill = 3; /* lj_str_new and lj_tab_newkey need 3 args. */ +#if LJ_TARGET_X86 && LJ_HASFFI + if (0) { + case IR_CNEW: + if (ir->op2 != REF_NIL && as->evenspill < 4) + as->evenspill = 4; /* lj_cdata_newv needs 4 args. */ + } + /* fallthrough */ +#else + /* fallthrough */ + case IR_CNEW: +#endif + /* fallthrough */ + case IR_TNEW: case IR_TDUP: case IR_CNEWI: case IR_TOSTR: + case IR_BUFSTR: + ir->prev = REGSP_HINT(RID_RET); + if (inloop) + as->modset = RSET_SCRATCH; + continue; + case IR_STRTO: case IR_OBAR: + if (inloop) + as->modset = RSET_SCRATCH; + break; +#if !LJ_SOFTFP + case IR_ATAN2: +#if LJ_TARGET_X86 + if (as->evenspill < 4) /* Leave room to call atan2(). */ + as->evenspill = 4; +#endif +#if !LJ_TARGET_X86ORX64 + case IR_LDEXP: +#endif +#endif + /* fallthrough */ + case IR_POW: + if (!LJ_SOFTFP && irt_isnum(ir->t)) { + if (inloop) + as->modset |= RSET_SCRATCH; +#if LJ_TARGET_X86 + break; +#else + ir->prev = REGSP_HINT(RID_FPRET); + continue; +#endif + } + /* fallthrough */ /* for integer POW */ + case IR_DIV: case IR_MOD: + if (!irt_isnum(ir->t)) { + ir->prev = REGSP_HINT(RID_RET); + if (inloop) + as->modset |= (RSET_SCRATCH & RSET_GPR); + continue; + } + break; + case IR_FPMATH: +#if LJ_TARGET_X86ORX64 + if (ir->op2 <= IRFPM_TRUNC) { + if (!(as->flags & JIT_F_SSE4_1)) { + ir->prev = REGSP_HINT(RID_XMM0); + if (inloop) + as->modset |= RSET_RANGE(RID_XMM0, RID_XMM3+1)|RID2RSET(RID_EAX); + continue; + } + break; + } else if (ir->op2 == IRFPM_EXP2 && !LJ_64) { + if (as->evenspill < 4) /* Leave room to call pow(). */ + as->evenspill = 4; + } +#endif + if (inloop) + as->modset |= RSET_SCRATCH; +#if LJ_TARGET_X86 + break; +#else + ir->prev = REGSP_HINT(RID_FPRET); + continue; +#endif +#if LJ_TARGET_X86ORX64 + /* Non-constant shift counts need to be in RID_ECX on x86/x64. */ + case IR_BSHL: case IR_BSHR: case IR_BSAR: + if ((as->flags & JIT_F_BMI2)) /* Except if BMI2 is available. */ + break; + /* fallthrough */ + case IR_BROL: case IR_BROR: + if (!irref_isk(ir->op2) && !ra_hashint(IR(ir->op2)->r)) { + IR(ir->op2)->r = REGSP_HINT(RID_ECX); + if (inloop) + rset_set(as->modset, RID_ECX); + } + break; +#endif + /* Do not propagate hints across type conversions or loads. */ + case IR_TOBIT: + case IR_XLOAD: +#if !LJ_TARGET_ARM + case IR_ALOAD: case IR_HLOAD: case IR_ULOAD: case IR_VLOAD: +#endif + break; + case IR_CONV: + if (irt_isfp(ir->t) || (ir->op2 & IRCONV_SRCMASK) == IRT_NUM || + (ir->op2 & IRCONV_SRCMASK) == IRT_FLOAT) + break; + /* fallthrough */ + default: + /* Propagate hints across likely 'op reg, imm' or 'op reg'. */ + if (irref_isk(ir->op2) && !irref_isk(ir->op1) && + ra_hashint(regsp_reg(IR(ir->op1)->prev))) { + ir->prev = IR(ir->op1)->prev; + continue; + } + break; + } + ir->prev = REGSP_INIT; + } + if ((as->evenspill & 1)) + as->oddspill = as->evenspill++; + else + as->oddspill = 0; +} + +/* -- Assembler core ------------------------------------------------------ */ + +/* Assemble a trace. */ +void lj_asm_trace(jit_State *J, GCtrace *T) +{ + ASMState as_; + ASMState *as = &as_; + MCode *origtop; + + /* Remove nops/renames left over from ASM restart due to LJ_TRERR_MCODELM. */ + { + IRRef nins = T->nins; + IRIns *ir = &T->ir[nins-1]; + if (ir->o == IR_NOP || ir->o == IR_RENAME) { + do { ir--; nins--; } while (ir->o == IR_NOP || ir->o == IR_RENAME); + T->nins = nins; + } + } + + /* Ensure an initialized instruction beyond the last one for HIOP checks. */ + /* This also allows one RENAME to be added without reallocating curfinal. */ + as->orignins = lj_ir_nextins(J); + J->cur.ir[as->orignins].o = IR_NOP; + + /* Setup initial state. Copy some fields to reduce indirections. */ + as->J = J; + as->T = T; + J->curfinal = lj_trace_alloc(J->L, T); /* This copies the IR, too. */ + as->flags = J->flags; + as->loopref = J->loopref; + as->realign = NULL; + as->loopinv = 0; + as->parent = J->parent ? traceref(J, J->parent) : NULL; + + /* Reserve MCode memory. */ + as->mctop = origtop = lj_mcode_reserve(J, &as->mcbot); + as->mcp = as->mctop; + as->mclim = as->mcbot + MCLIM_REDZONE; + asm_setup_target(as); + + /* + ** This is a loop, because the MCode may have to be (re-)assembled + ** multiple times: + ** + ** 1. as->realign is set (and the assembly aborted), if the arch-specific + ** backend wants the MCode to be aligned differently. + ** + ** This is currently only the case on x86/x64, where small loops get + ** an aligned loop body plus a short branch. Not much effort is wasted, + ** because the abort happens very quickly and only once. + ** + ** 2. The IR is immovable, since the MCode embeds pointers to various + ** constants inside the IR. But RENAMEs may need to be added to the IR + ** during assembly, which might grow and reallocate the IR. We check + ** at the end if the IR (in J->cur.ir) has actually grown, resize the + ** copy (in J->curfinal.ir) and try again. + ** + ** 95% of all traces have zero RENAMEs, 3% have one RENAME, 1.5% have + ** 2 RENAMEs and only 0.5% have more than that. That's why we opt to + ** always have one spare slot in the IR (see above), which means we + ** have to redo the assembly for only ~2% of all traces. + ** + ** Very, very rarely, this needs to be done repeatedly, since the + ** location of constants inside the IR (actually, reachability from + ** a global pointer) may affect register allocation and thus the + ** number of RENAMEs. + */ + for (;;) { + as->mcp = as->mctop; +#ifdef LUA_USE_ASSERT + as->mcp_prev = as->mcp; +#endif + as->ir = J->curfinal->ir; /* Use the copied IR. */ + as->curins = J->cur.nins = as->orignins; + + RA_DBG_START(); + RA_DBGX((as, "===== STOP =====")); + + /* General trace setup. Emit tail of trace. */ + asm_tail_prep(as); + as->mcloop = NULL; + as->flagmcp = NULL; + as->topslot = 0; + as->gcsteps = 0; + as->sectref = as->loopref; + as->fuseref = (as->flags & JIT_F_OPT_FUSE) ? as->loopref : FUSE_DISABLED; + asm_setup_regsp(as); + if (!as->loopref) + asm_tail_link(as); + + /* Assemble a trace in linear backwards order. */ + for (as->curins--; as->curins > as->stopins; as->curins--) { + IRIns *ir = IR(as->curins); + lua_assert(!(LJ_32 && irt_isint64(ir->t))); /* Handled by SPLIT. */ + if (!ra_used(ir) && !ir_sideeff(ir) && (as->flags & JIT_F_OPT_DCE)) + continue; /* Dead-code elimination can be soooo easy. */ + if (irt_isguard(ir->t)) + asm_snap_prep(as); + RA_DBG_REF(); + checkmclim(as); + asm_ir(as, ir); + } + + if (as->realign && J->curfinal->nins >= T->nins) + continue; /* Retry in case only the MCode needs to be realigned. */ + + /* Emit head of trace. */ + RA_DBG_REF(); + checkmclim(as); + if (as->gcsteps > 0) { + as->curins = as->T->snap[0].ref; + asm_snap_prep(as); /* The GC check is a guard. */ + asm_gc_check(as); + as->curins = as->stopins; + } + ra_evictk(as); + if (as->parent) + asm_head_side(as); + else + asm_head_root(as); + asm_phi_fixup(as); + + if (J->curfinal->nins >= T->nins) { /* IR didn't grow? */ + lua_assert(J->curfinal->nk == T->nk); + memcpy(J->curfinal->ir + as->orignins, T->ir + as->orignins, + (T->nins - as->orignins) * sizeof(IRIns)); /* Copy RENAMEs. */ + T->nins = J->curfinal->nins; + break; /* Done. */ + } + + /* Otherwise try again with a bigger IR. */ + lj_trace_free(J2G(J), J->curfinal); + J->curfinal = NULL; /* In case lj_trace_alloc() OOMs. */ + J->curfinal = lj_trace_alloc(J->L, T); + as->realign = NULL; + } + + RA_DBGX((as, "===== START ====")); + RA_DBG_FLUSH(); + if (as->freeset != RSET_ALL) + lj_trace_err(as->J, LJ_TRERR_BADRA); /* Ouch! Should never happen. */ + + /* Set trace entry point before fixing up tail to allow link to self. */ + T->mcode = as->mcp; + T->mcloop = as->mcloop ? (MSize)((char *)as->mcloop - (char *)as->mcp) : 0; + if (!as->loopref) + asm_tail_fixup(as, T->link); /* Note: this may change as->mctop! */ + T->szmcode = (MSize)((char *)as->mctop - (char *)as->mcp); +#if LJ_TARGET_MCODE_FIXUP + asm_mcode_fixup(T->mcode, T->szmcode); +#endif + lj_mcode_sync(T->mcode, origtop); +} + +#undef IR + +#endif diff --git a/lib/LuaJIT/lj_asm.h b/lib/LuaJIT/lj_asm.h new file mode 100644 index 0000000..2819481 --- /dev/null +++ b/lib/LuaJIT/lj_asm.h @@ -0,0 +1,17 @@ +/* +** IR assembler (SSA IR -> machine code). +** Copyright (C) 2005-2017 Mike Pall. See Copyright Notice in luajit.h +*/ + +#ifndef _LJ_ASM_H +#define _LJ_ASM_H + +#include "lj_jit.h" + +#if LJ_HASJIT +LJ_FUNC void lj_asm_trace(jit_State *J, GCtrace *T); +LJ_FUNC void lj_asm_patchexit(jit_State *J, GCtrace *T, ExitNo exitno, + MCode *target); +#endif + +#endif diff --git a/lib/LuaJIT/lj_asm.o b/lib/LuaJIT/lj_asm.o new file mode 100644 index 0000000000000000000000000000000000000000..efdff7f15561738cd88fe53466b02ecc1e921ebc GIT binary patch literal 57712 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 literal 0 HcmV?d00001 diff --git a/lib/LuaJIT/lj_asm_arm.h b/lib/LuaJIT/lj_asm_arm.h new file mode 100644 index 0000000..37bfa40 --- /dev/null +++ b/lib/LuaJIT/lj_asm_arm.h @@ -0,0 +1,2210 @@ +/* +** ARM IR assembler (SSA IR -> machine code). +** Copyright (C) 2005-2017 Mike Pall. See Copyright Notice in luajit.h +*/ + +/* -- Register allocator extensions --------------------------------------- */ + +/* Allocate a register with a hint. */ +static Reg ra_hintalloc(ASMState *as, IRRef ref, Reg hint, RegSet allow) +{ + Reg r = IR(ref)->r; + if (ra_noreg(r)) { + if (!ra_hashint(r) && !iscrossref(as, ref)) + ra_sethint(IR(ref)->r, hint); /* Propagate register hint. */ + r = ra_allocref(as, ref, allow); + } + ra_noweak(as, r); + return r; +} + +/* Allocate a scratch register pair. */ +static Reg ra_scratchpair(ASMState *as, RegSet allow) +{ + RegSet pick1 = as->freeset & allow; + RegSet pick2 = pick1 & (pick1 >> 1) & RSET_GPREVEN; + Reg r; + if (pick2) { + r = rset_picktop(pick2); + } else { + RegSet pick = pick1 & (allow >> 1) & RSET_GPREVEN; + if (pick) { + r = rset_picktop(pick); + ra_restore(as, regcost_ref(as->cost[r+1])); + } else { + pick = pick1 & (allow << 1) & RSET_GPRODD; + if (pick) { + r = ra_restore(as, regcost_ref(as->cost[rset_picktop(pick)-1])); + } else { + r = ra_evict(as, allow & (allow >> 1) & RSET_GPREVEN); + ra_restore(as, regcost_ref(as->cost[r+1])); + } + } + } + lua_assert(rset_test(RSET_GPREVEN, r)); + ra_modified(as, r); + ra_modified(as, r+1); + RA_DBGX((as, "scratchpair $r $r", r, r+1)); + return r; +} + +#if !LJ_SOFTFP +/* Allocate two source registers for three-operand instructions. */ +static Reg ra_alloc2(ASMState *as, IRIns *ir, RegSet allow) +{ + IRIns *irl = IR(ir->op1), *irr = IR(ir->op2); + Reg left = irl->r, right = irr->r; + if (ra_hasreg(left)) { + ra_noweak(as, left); + if (ra_noreg(right)) + right = ra_allocref(as, ir->op2, rset_exclude(allow, left)); + else + ra_noweak(as, right); + } else if (ra_hasreg(right)) { + ra_noweak(as, right); + left = ra_allocref(as, ir->op1, rset_exclude(allow, right)); + } else if (ra_hashint(right)) { + right = ra_allocref(as, ir->op2, allow); + left = ra_alloc1(as, ir->op1, rset_exclude(allow, right)); + } else { + left = ra_allocref(as, ir->op1, allow); + right = ra_alloc1(as, ir->op2, rset_exclude(allow, left)); + } + return left | (right << 8); +} +#endif + +/* -- Guard handling ------------------------------------------------------ */ + +/* Generate an exit stub group at the bottom of the reserved MCode memory. */ +static MCode *asm_exitstub_gen(ASMState *as, ExitNo group) +{ + MCode *mxp = as->mcbot; + int i; + if (mxp + 4*4+4*EXITSTUBS_PER_GROUP >= as->mctop) + asm_mclimit(as); + /* str lr, [sp]; bl ->vm_exit_handler; .long DISPATCH_address, group. */ + *mxp++ = ARMI_STR|ARMI_LS_P|ARMI_LS_U|ARMF_D(RID_LR)|ARMF_N(RID_SP); + *mxp = ARMI_BL|((((MCode *)(void *)lj_vm_exit_handler-mxp)-2)&0x00ffffffu); + mxp++; + *mxp++ = (MCode)i32ptr(J2GG(as->J)->dispatch); /* DISPATCH address */ + *mxp++ = group*EXITSTUBS_PER_GROUP; + for (i = 0; i < EXITSTUBS_PER_GROUP; i++) + *mxp++ = ARMI_B|((-6-i)&0x00ffffffu); + lj_mcode_sync(as->mcbot, mxp); + lj_mcode_commitbot(as->J, mxp); + as->mcbot = mxp; + as->mclim = as->mcbot + MCLIM_REDZONE; + return mxp - EXITSTUBS_PER_GROUP; +} + +/* Setup all needed exit stubs. */ +static void asm_exitstub_setup(ASMState *as, ExitNo nexits) +{ + ExitNo i; + if (nexits >= EXITSTUBS_PER_GROUP*LJ_MAX_EXITSTUBGR) + lj_trace_err(as->J, LJ_TRERR_SNAPOV); + for (i = 0; i < (nexits+EXITSTUBS_PER_GROUP-1)/EXITSTUBS_PER_GROUP; i++) + if (as->J->exitstubgroup[i] == NULL) + as->J->exitstubgroup[i] = asm_exitstub_gen(as, i); +} + +/* Emit conditional branch to exit for guard. */ +static void asm_guardcc(ASMState *as, ARMCC cc) +{ + MCode *target = exitstub_addr(as->J, as->snapno); + MCode *p = as->mcp; + if (LJ_UNLIKELY(p == as->invmcp)) { + as->loopinv = 1; + *p = ARMI_BL | ((target-p-2) & 0x00ffffffu); + emit_branch(as, ARMF_CC(ARMI_B, cc^1), p+1); + return; + } + emit_branch(as, ARMF_CC(ARMI_BL, cc), target); +} + +/* -- Operand fusion ------------------------------------------------------ */ + +/* Limit linear search to this distance. Avoids O(n^2) behavior. */ +#define CONFLICT_SEARCH_LIM 31 + +/* Check if there's no conflicting instruction between curins and ref. */ +static int noconflict(ASMState *as, IRRef ref, IROp conflict) +{ + IRIns *ir = as->ir; + IRRef i = as->curins; + if (i > ref + CONFLICT_SEARCH_LIM) + return 0; /* Give up, ref is too far away. */ + while (--i > ref) + if (ir[i].o == conflict) + return 0; /* Conflict found. */ + return 1; /* Ok, no conflict. */ +} + +/* Fuse the array base of colocated arrays. */ +static int32_t asm_fuseabase(ASMState *as, IRRef ref) +{ + IRIns *ir = IR(ref); + if (ir->o == IR_TNEW && ir->op1 <= LJ_MAX_COLOSIZE && + !neverfuse(as) && noconflict(as, ref, IR_NEWREF)) + return (int32_t)sizeof(GCtab); + return 0; +} + +/* Fuse array/hash/upvalue reference into register+offset operand. */ +static Reg asm_fuseahuref(ASMState *as, IRRef ref, int32_t *ofsp, RegSet allow, + int lim) +{ + IRIns *ir = IR(ref); + if (ra_noreg(ir->r)) { + if (ir->o == IR_AREF) { + if (mayfuse(as, ref)) { + if (irref_isk(ir->op2)) { + IRRef tab = IR(ir->op1)->op1; + int32_t ofs = asm_fuseabase(as, tab); + IRRef refa = ofs ? tab : ir->op1; + ofs += 8*IR(ir->op2)->i; + if (ofs > -lim && ofs < lim) { + *ofsp = ofs; + return ra_alloc1(as, refa, allow); + } + } + } + } else if (ir->o == IR_HREFK) { + if (mayfuse(as, ref)) { + int32_t ofs = (int32_t)(IR(ir->op2)->op2 * sizeof(Node)); + if (ofs < lim) { + *ofsp = ofs; + return ra_alloc1(as, ir->op1, allow); + } + } + } else if (ir->o == IR_UREFC) { + if (irref_isk(ir->op1)) { + GCfunc *fn = ir_kfunc(IR(ir->op1)); + int32_t ofs = i32ptr(&gcref(fn->l.uvptr[(ir->op2 >> 8)])->uv.tv); + *ofsp = (ofs & 255); /* Mask out less bits to allow LDRD. */ + return ra_allock(as, (ofs & ~255), allow); + } + } + } + *ofsp = 0; + return ra_alloc1(as, ref, allow); +} + +/* Fuse m operand into arithmetic/logic instructions. */ +static uint32_t asm_fuseopm(ASMState *as, ARMIns ai, IRRef ref, RegSet allow) +{ + IRIns *ir = IR(ref); + if (ra_hasreg(ir->r)) { + ra_noweak(as, ir->r); + return ARMF_M(ir->r); + } else if (irref_isk(ref)) { + uint32_t k = emit_isk12(ai, ir->i); + if (k) + return k; + } else if (mayfuse(as, ref)) { + if (ir->o >= IR_BSHL && ir->o <= IR_BROR) { + Reg m = ra_alloc1(as, ir->op1, allow); + ARMShift sh = ir->o == IR_BSHL ? ARMSH_LSL : + ir->o == IR_BSHR ? ARMSH_LSR : + ir->o == IR_BSAR ? ARMSH_ASR : ARMSH_ROR; + if (irref_isk(ir->op2)) { + return m | ARMF_SH(sh, (IR(ir->op2)->i & 31)); + } else { + Reg s = ra_alloc1(as, ir->op2, rset_exclude(allow, m)); + return m | ARMF_RSH(sh, s); + } + } else if (ir->o == IR_ADD && ir->op1 == ir->op2) { + Reg m = ra_alloc1(as, ir->op1, allow); + return m | ARMF_SH(ARMSH_LSL, 1); + } + } + return ra_allocref(as, ref, allow); +} + +/* Fuse shifts into loads/stores. Only bother with BSHL 2 => lsl #2. */ +static IRRef asm_fuselsl2(ASMState *as, IRRef ref) +{ + IRIns *ir = IR(ref); + if (ra_noreg(ir->r) && mayfuse(as, ref) && ir->o == IR_BSHL && + irref_isk(ir->op2) && IR(ir->op2)->i == 2) + return ir->op1; + return 0; /* No fusion. */ +} + +/* Fuse XLOAD/XSTORE reference into load/store operand. */ +static void asm_fusexref(ASMState *as, ARMIns ai, Reg rd, IRRef ref, + RegSet allow, int32_t ofs) +{ + IRIns *ir = IR(ref); + Reg base; + if (ra_noreg(ir->r) && canfuse(as, ir)) { + int32_t lim = (!LJ_SOFTFP && (ai & 0x08000000)) ? 1024 : + (ai & 0x04000000) ? 4096 : 256; + if (ir->o == IR_ADD) { + int32_t ofs2; + if (irref_isk(ir->op2) && + (ofs2 = ofs + IR(ir->op2)->i) > -lim && ofs2 < lim && + (!(!LJ_SOFTFP && (ai & 0x08000000)) || !(ofs2 & 3))) { + ofs = ofs2; + ref = ir->op1; + } else if (ofs == 0 && !(!LJ_SOFTFP && (ai & 0x08000000))) { + IRRef lref = ir->op1, rref = ir->op2; + Reg rn, rm; + if ((ai & 0x04000000)) { + IRRef sref = asm_fuselsl2(as, rref); + if (sref) { + rref = sref; + ai |= ARMF_SH(ARMSH_LSL, 2); + } else if ((sref = asm_fuselsl2(as, lref)) != 0) { + lref = rref; + rref = sref; + ai |= ARMF_SH(ARMSH_LSL, 2); + } + } + rn = ra_alloc1(as, lref, allow); + rm = ra_alloc1(as, rref, rset_exclude(allow, rn)); + if ((ai & 0x04000000)) ai |= ARMI_LS_R; + emit_dnm(as, ai|ARMI_LS_P|ARMI_LS_U, rd, rn, rm); + return; + } + } else if (ir->o == IR_STRREF && !(!LJ_SOFTFP && (ai & 0x08000000))) { + lua_assert(ofs == 0); + ofs = (int32_t)sizeof(GCstr); + if (irref_isk(ir->op2)) { + ofs += IR(ir->op2)->i; + ref = ir->op1; + } else if (irref_isk(ir->op1)) { + ofs += IR(ir->op1)->i; + ref = ir->op2; + } else { + /* NYI: Fuse ADD with constant. */ + Reg rn = ra_alloc1(as, ir->op1, allow); + uint32_t m = asm_fuseopm(as, 0, ir->op2, rset_exclude(allow, rn)); + if ((ai & 0x04000000)) + emit_lso(as, ai, rd, rd, ofs); + else + emit_lsox(as, ai, rd, rd, ofs); + emit_dn(as, ARMI_ADD^m, rd, rn); + return; + } + if (ofs <= -lim || ofs >= lim) { + Reg rn = ra_alloc1(as, ref, allow); + Reg rm = ra_allock(as, ofs, rset_exclude(allow, rn)); + if ((ai & 0x04000000)) ai |= ARMI_LS_R; + emit_dnm(as, ai|ARMI_LS_P|ARMI_LS_U, rd, rn, rm); + return; + } + } + } + base = ra_alloc1(as, ref, allow); +#if !LJ_SOFTFP + if ((ai & 0x08000000)) + emit_vlso(as, ai, rd, base, ofs); + else +#endif + if ((ai & 0x04000000)) + emit_lso(as, ai, rd, base, ofs); + else + emit_lsox(as, ai, rd, base, ofs); +} + +#if !LJ_SOFTFP +/* Fuse to multiply-add/sub instruction. */ +static int asm_fusemadd(ASMState *as, IRIns *ir, ARMIns ai, ARMIns air) +{ + IRRef lref = ir->op1, rref = ir->op2; + IRIns *irm; + if (lref != rref && + ((mayfuse(as, lref) && (irm = IR(lref), irm->o == IR_MUL) && + ra_noreg(irm->r)) || + (mayfuse(as, rref) && (irm = IR(rref), irm->o == IR_MUL) && + (rref = lref, ai = air, ra_noreg(irm->r))))) { + Reg dest = ra_dest(as, ir, RSET_FPR); + Reg add = ra_hintalloc(as, rref, dest, RSET_FPR); + Reg right, left = ra_alloc2(as, irm, + rset_exclude(rset_exclude(RSET_FPR, dest), add)); + right = (left >> 8); left &= 255; + emit_dnm(as, ai, (dest & 15), (left & 15), (right & 15)); + if (dest != add) emit_dm(as, ARMI_VMOV_D, (dest & 15), (add & 15)); + return 1; + } + return 0; +} +#endif + +/* -- Calls --------------------------------------------------------------- */ + +/* Generate a call to a C function. */ +static void asm_gencall(ASMState *as, const CCallInfo *ci, IRRef *args) +{ + uint32_t n, nargs = CCI_XNARGS(ci); + int32_t ofs = 0; +#if LJ_SOFTFP + Reg gpr = REGARG_FIRSTGPR; +#else + Reg gpr, fpr = REGARG_FIRSTFPR, fprodd = 0; +#endif + if ((void *)ci->func) + emit_call(as, (void *)ci->func); +#if !LJ_SOFTFP + for (gpr = REGARG_FIRSTGPR; gpr <= REGARG_LASTGPR; gpr++) + as->cost[gpr] = REGCOST(~0u, ASMREF_L); + gpr = REGARG_FIRSTGPR; +#endif + for (n = 0; n < nargs; n++) { /* Setup args. */ + IRRef ref = args[n]; + IRIns *ir = IR(ref); +#if !LJ_SOFTFP + if (ref && irt_isfp(ir->t)) { + RegSet of = as->freeset; + Reg src; + if (!LJ_ABI_SOFTFP && !(ci->flags & CCI_VARARG)) { + if (irt_isnum(ir->t)) { + if (fpr <= REGARG_LASTFPR) { + ra_leftov(as, fpr, ref); + fpr++; + continue; + } + } else if (fprodd) { /* Ick. */ + src = ra_alloc1(as, ref, RSET_FPR); + emit_dm(as, ARMI_VMOV_S, (fprodd & 15), (src & 15) | 0x00400000); + fprodd = 0; + continue; + } else if (fpr <= REGARG_LASTFPR) { + ra_leftov(as, fpr, ref); + fprodd = fpr++; + continue; + } + /* Workaround to protect argument GPRs from being used for remat. */ + as->freeset &= ~RSET_RANGE(REGARG_FIRSTGPR, REGARG_LASTGPR+1); + src = ra_alloc1(as, ref, RSET_FPR); /* May alloc GPR to remat FPR. */ + as->freeset |= (of & RSET_RANGE(REGARG_FIRSTGPR, REGARG_LASTGPR+1)); + fprodd = 0; + goto stackfp; + } + /* Workaround to protect argument GPRs from being used for remat. */ + as->freeset &= ~RSET_RANGE(REGARG_FIRSTGPR, REGARG_LASTGPR+1); + src = ra_alloc1(as, ref, RSET_FPR); /* May alloc GPR to remat FPR. */ + as->freeset |= (of & RSET_RANGE(REGARG_FIRSTGPR, REGARG_LASTGPR+1)); + if (irt_isnum(ir->t)) gpr = (gpr+1) & ~1u; + if (gpr <= REGARG_LASTGPR) { + lua_assert(rset_test(as->freeset, gpr)); /* Must have been evicted. */ + if (irt_isnum(ir->t)) { + lua_assert(rset_test(as->freeset, gpr+1)); /* Ditto. */ + emit_dnm(as, ARMI_VMOV_RR_D, gpr, gpr+1, (src & 15)); + gpr += 2; + } else { + emit_dn(as, ARMI_VMOV_R_S, gpr, (src & 15)); + gpr++; + } + } else { + stackfp: + if (irt_isnum(ir->t)) ofs = (ofs + 4) & ~4; + emit_spstore(as, ir, src, ofs); + ofs += irt_isnum(ir->t) ? 8 : 4; + } + } else +#endif + { + if (gpr <= REGARG_LASTGPR) { + lua_assert(rset_test(as->freeset, gpr)); /* Must have been evicted. */ + if (ref) ra_leftov(as, gpr, ref); + gpr++; + } else { + if (ref) { + Reg r = ra_alloc1(as, ref, RSET_GPR); + emit_spstore(as, ir, r, ofs); + } + ofs += 4; + } + } + } +} + +/* Setup result reg/sp for call. Evict scratch regs. */ +static void asm_setupresult(ASMState *as, IRIns *ir, const CCallInfo *ci) +{ + RegSet drop = RSET_SCRATCH; + int hiop = ((ir+1)->o == IR_HIOP && !irt_isnil((ir+1)->t)); + if (ra_hasreg(ir->r)) + rset_clear(drop, ir->r); /* Dest reg handled below. */ + if (hiop && ra_hasreg((ir+1)->r)) + rset_clear(drop, (ir+1)->r); /* Dest reg handled below. */ + ra_evictset(as, drop); /* Evictions must be performed first. */ + if (ra_used(ir)) { + lua_assert(!irt_ispri(ir->t)); + if (!LJ_SOFTFP && irt_isfp(ir->t)) { + if (LJ_ABI_SOFTFP || (ci->flags & (CCI_CASTU64|CCI_VARARG))) { + Reg dest = (ra_dest(as, ir, RSET_FPR) & 15); + if (irt_isnum(ir->t)) + emit_dnm(as, ARMI_VMOV_D_RR, RID_RETLO, RID_RETHI, dest); + else + emit_dn(as, ARMI_VMOV_S_R, RID_RET, dest); + } else { + ra_destreg(as, ir, RID_FPRET); + } + } else if (hiop) { + ra_destpair(as, ir); + } else { + ra_destreg(as, ir, RID_RET); + } + } + UNUSED(ci); +} + +static void asm_callx(ASMState *as, IRIns *ir) +{ + IRRef args[CCI_NARGS_MAX*2]; + CCallInfo ci; + IRRef func; + IRIns *irf; + ci.flags = asm_callx_flags(as, ir); + asm_collectargs(as, ir, &ci, args); + asm_setupresult(as, ir, &ci); + func = ir->op2; irf = IR(func); + if (irf->o == IR_CARG) { func = irf->op1; irf = IR(func); } + if (irref_isk(func)) { /* Call to constant address. */ + ci.func = (ASMFunction)(void *)(irf->i); + } else { /* Need a non-argument register for indirect calls. */ + Reg freg = ra_alloc1(as, func, RSET_RANGE(RID_R4, RID_R12+1)); + emit_m(as, ARMI_BLXr, freg); + ci.func = (ASMFunction)(void *)0; + } + asm_gencall(as, &ci, args); +} + +/* -- Returns ------------------------------------------------------------- */ + +/* Return to lower frame. Guard that it goes to the right spot. */ +static void asm_retf(ASMState *as, IRIns *ir) +{ + Reg base = ra_alloc1(as, REF_BASE, RSET_GPR); + void *pc = ir_kptr(IR(ir->op2)); + int32_t delta = 1+LJ_FR2+bc_a(*((const BCIns *)pc - 1)); + as->topslot -= (BCReg)delta; + if ((int32_t)as->topslot < 0) as->topslot = 0; + irt_setmark(IR(REF_BASE)->t); /* Children must not coalesce with BASE reg. */ + /* Need to force a spill on REF_BASE now to update the stack slot. */ + emit_lso(as, ARMI_STR, base, RID_SP, ra_spill(as, IR(REF_BASE))); + emit_setgl(as, base, jit_base); + emit_addptr(as, base, -8*delta); + asm_guardcc(as, CC_NE); + emit_nm(as, ARMI_CMP, RID_TMP, + ra_allock(as, i32ptr(pc), rset_exclude(RSET_GPR, base))); + emit_lso(as, ARMI_LDR, RID_TMP, base, -4); +} + +/* -- Type conversions ---------------------------------------------------- */ + +#if !LJ_SOFTFP +static void asm_tointg(ASMState *as, IRIns *ir, Reg left) +{ + Reg tmp = ra_scratch(as, rset_exclude(RSET_FPR, left)); + Reg dest = ra_dest(as, ir, RSET_GPR); + asm_guardcc(as, CC_NE); + emit_d(as, ARMI_VMRS, 0); + emit_dm(as, ARMI_VCMP_D, (tmp & 15), (left & 15)); + emit_dm(as, ARMI_VCVT_F64_S32, (tmp & 15), (tmp & 15)); + emit_dn(as, ARMI_VMOV_R_S, dest, (tmp & 15)); + emit_dm(as, ARMI_VCVT_S32_F64, (tmp & 15), (left & 15)); +} + +static void asm_tobit(ASMState *as, IRIns *ir) +{ + RegSet allow = RSET_FPR; + Reg left = ra_alloc1(as, ir->op1, allow); + Reg right = ra_alloc1(as, ir->op2, rset_clear(allow, left)); + Reg tmp = ra_scratch(as, rset_clear(allow, right)); + Reg dest = ra_dest(as, ir, RSET_GPR); + emit_dn(as, ARMI_VMOV_R_S, dest, (tmp & 15)); + emit_dnm(as, ARMI_VADD_D, (tmp & 15), (left & 15), (right & 15)); +} +#endif + +static void asm_conv(ASMState *as, IRIns *ir) +{ + IRType st = (IRType)(ir->op2 & IRCONV_SRCMASK); +#if !LJ_SOFTFP + int stfp = (st == IRT_NUM || st == IRT_FLOAT); +#endif + IRRef lref = ir->op1; + /* 64 bit integer conversions are handled by SPLIT. */ + lua_assert(!irt_isint64(ir->t) && !(st == IRT_I64 || st == IRT_U64)); +#if LJ_SOFTFP + /* FP conversions are handled by SPLIT. */ + lua_assert(!irt_isfp(ir->t) && !(st == IRT_NUM || st == IRT_FLOAT)); + /* Can't check for same types: SPLIT uses CONV int.int + BXOR for sfp NEG. */ +#else + lua_assert(irt_type(ir->t) != st); + if (irt_isfp(ir->t)) { + Reg dest = ra_dest(as, ir, RSET_FPR); + if (stfp) { /* FP to FP conversion. */ + emit_dm(as, st == IRT_NUM ? ARMI_VCVT_F32_F64 : ARMI_VCVT_F64_F32, + (dest & 15), (ra_alloc1(as, lref, RSET_FPR) & 15)); + } else { /* Integer to FP conversion. */ + Reg left = ra_alloc1(as, lref, RSET_GPR); + ARMIns ai = irt_isfloat(ir->t) ? + (st == IRT_INT ? ARMI_VCVT_F32_S32 : ARMI_VCVT_F32_U32) : + (st == IRT_INT ? ARMI_VCVT_F64_S32 : ARMI_VCVT_F64_U32); + emit_dm(as, ai, (dest & 15), (dest & 15)); + emit_dn(as, ARMI_VMOV_S_R, left, (dest & 15)); + } + } else if (stfp) { /* FP to integer conversion. */ + if (irt_isguard(ir->t)) { + /* Checked conversions are only supported from number to int. */ + lua_assert(irt_isint(ir->t) && st == IRT_NUM); + asm_tointg(as, ir, ra_alloc1(as, lref, RSET_FPR)); + } else { + Reg left = ra_alloc1(as, lref, RSET_FPR); + Reg tmp = ra_scratch(as, rset_exclude(RSET_FPR, left)); + Reg dest = ra_dest(as, ir, RSET_GPR); + ARMIns ai; + emit_dn(as, ARMI_VMOV_R_S, dest, (tmp & 15)); + ai = irt_isint(ir->t) ? + (st == IRT_NUM ? ARMI_VCVT_S32_F64 : ARMI_VCVT_S32_F32) : + (st == IRT_NUM ? ARMI_VCVT_U32_F64 : ARMI_VCVT_U32_F32); + emit_dm(as, ai, (tmp & 15), (left & 15)); + } + } else +#endif + { + Reg dest = ra_dest(as, ir, RSET_GPR); + if (st >= IRT_I8 && st <= IRT_U16) { /* Extend to 32 bit integer. */ + Reg left = ra_alloc1(as, lref, RSET_GPR); + lua_assert(irt_isint(ir->t) || irt_isu32(ir->t)); + if ((as->flags & JIT_F_ARMV6)) { + ARMIns ai = st == IRT_I8 ? ARMI_SXTB : + st == IRT_U8 ? ARMI_UXTB : + st == IRT_I16 ? ARMI_SXTH : ARMI_UXTH; + emit_dm(as, ai, dest, left); + } else if (st == IRT_U8) { + emit_dn(as, ARMI_AND|ARMI_K12|255, dest, left); + } else { + uint32_t shift = st == IRT_I8 ? 24 : 16; + ARMShift sh = st == IRT_U16 ? ARMSH_LSR : ARMSH_ASR; + emit_dm(as, ARMI_MOV|ARMF_SH(sh, shift), dest, RID_TMP); + emit_dm(as, ARMI_MOV|ARMF_SH(ARMSH_LSL, shift), RID_TMP, left); + } + } else { /* Handle 32/32 bit no-op (cast). */ + ra_leftov(as, dest, lref); /* Do nothing, but may need to move regs. */ + } + } +} + +static void asm_strto(ASMState *as, IRIns *ir) +{ + const CCallInfo *ci = &lj_ir_callinfo[IRCALL_lj_strscan_num]; + IRRef args[2]; + Reg rlo = 0, rhi = 0, tmp; + int destused = ra_used(ir); + int32_t ofs = 0; + ra_evictset(as, RSET_SCRATCH); +#if LJ_SOFTFP + if (destused) { + if (ra_hasspill(ir->s) && ra_hasspill((ir+1)->s) && + (ir->s & 1) == 0 && ir->s + 1 == (ir+1)->s) { + int i; + for (i = 0; i < 2; i++) { + Reg r = (ir+i)->r; + if (ra_hasreg(r)) { + ra_free(as, r); + ra_modified(as, r); + emit_spload(as, ir+i, r, sps_scale((ir+i)->s)); + } + } + ofs = sps_scale(ir->s); + destused = 0; + } else { + rhi = ra_dest(as, ir+1, RSET_GPR); + rlo = ra_dest(as, ir, rset_exclude(RSET_GPR, rhi)); + } + } + asm_guardcc(as, CC_EQ); + if (destused) { + emit_lso(as, ARMI_LDR, rhi, RID_SP, 4); + emit_lso(as, ARMI_LDR, rlo, RID_SP, 0); + } +#else + UNUSED(rhi); + if (destused) { + if (ra_hasspill(ir->s)) { + ofs = sps_scale(ir->s); + destused = 0; + if (ra_hasreg(ir->r)) { + ra_free(as, ir->r); + ra_modified(as, ir->r); + emit_spload(as, ir, ir->r, ofs); + } + } else { + rlo = ra_dest(as, ir, RSET_FPR); + } + } + asm_guardcc(as, CC_EQ); + if (destused) + emit_vlso(as, ARMI_VLDR_D, rlo, RID_SP, 0); +#endif + emit_n(as, ARMI_CMP|ARMI_K12|0, RID_RET); /* Test return status. */ + args[0] = ir->op1; /* GCstr *str */ + args[1] = ASMREF_TMP1; /* TValue *n */ + asm_gencall(as, ci, args); + tmp = ra_releasetmp(as, ASMREF_TMP1); + if (ofs == 0) + emit_dm(as, ARMI_MOV, tmp, RID_SP); + else + emit_opk(as, ARMI_ADD, tmp, RID_SP, ofs, RSET_GPR); +} + +/* -- Memory references --------------------------------------------------- */ + +/* Get pointer to TValue. */ +static void asm_tvptr(ASMState *as, Reg dest, IRRef ref) +{ + IRIns *ir = IR(ref); + if (irt_isnum(ir->t)) { + if (irref_isk(ref)) { + /* Use the number constant itself as a TValue. */ + ra_allockreg(as, i32ptr(ir_knum(ir)), dest); + } else { +#if LJ_SOFTFP + lua_assert(0); +#else + /* Otherwise force a spill and use the spill slot. */ + emit_opk(as, ARMI_ADD, dest, RID_SP, ra_spill(as, ir), RSET_GPR); +#endif + } + } else { + /* Otherwise use [sp] and [sp+4] to hold the TValue. */ + RegSet allow = rset_exclude(RSET_GPR, dest); + Reg type; + emit_dm(as, ARMI_MOV, dest, RID_SP); + if (!irt_ispri(ir->t)) { + Reg src = ra_alloc1(as, ref, allow); + emit_lso(as, ARMI_STR, src, RID_SP, 0); + } + if (LJ_SOFTFP && (ir+1)->o == IR_HIOP) + type = ra_alloc1(as, ref+1, allow); + else + type = ra_allock(as, irt_toitype(ir->t), allow); + emit_lso(as, ARMI_STR, type, RID_SP, 4); + } +} + +static void asm_aref(ASMState *as, IRIns *ir) +{ + Reg dest = ra_dest(as, ir, RSET_GPR); + Reg idx, base; + if (irref_isk(ir->op2)) { + IRRef tab = IR(ir->op1)->op1; + int32_t ofs = asm_fuseabase(as, tab); + IRRef refa = ofs ? tab : ir->op1; + uint32_t k = emit_isk12(ARMI_ADD, ofs + 8*IR(ir->op2)->i); + if (k) { + base = ra_alloc1(as, refa, RSET_GPR); + emit_dn(as, ARMI_ADD^k, dest, base); + return; + } + } + base = ra_alloc1(as, ir->op1, RSET_GPR); + idx = ra_alloc1(as, ir->op2, rset_exclude(RSET_GPR, base)); + emit_dnm(as, ARMI_ADD|ARMF_SH(ARMSH_LSL, 3), dest, base, idx); +} + +/* Inlined hash lookup. Specialized for key type and for const keys. +** The equivalent C code is: +** Node *n = hashkey(t, key); +** do { +** if (lj_obj_equal(&n->key, key)) return &n->val; +** } while ((n = nextnode(n))); +** return niltv(L); +*/ +static void asm_href(ASMState *as, IRIns *ir, IROp merge) +{ + RegSet allow = RSET_GPR; + int destused = ra_used(ir); + Reg dest = ra_dest(as, ir, allow); + Reg tab = ra_alloc1(as, ir->op1, rset_clear(allow, dest)); + Reg key = 0, keyhi = 0, keynumhi = RID_NONE, tmp = RID_TMP; + IRRef refkey = ir->op2; + IRIns *irkey = IR(refkey); + IRType1 kt = irkey->t; + int32_t k = 0, khi = emit_isk12(ARMI_CMP, irt_toitype(kt)); + uint32_t khash; + MCLabel l_end, l_loop; + rset_clear(allow, tab); + if (!irref_isk(refkey) || irt_isstr(kt)) { +#if LJ_SOFTFP + key = ra_alloc1(as, refkey, allow); + rset_clear(allow, key); + if (irkey[1].o == IR_HIOP) { + if (ra_hasreg((irkey+1)->r)) { + keynumhi = (irkey+1)->r; + keyhi = RID_TMP; + ra_noweak(as, keynumhi); + } else { + keyhi = keynumhi = ra_allocref(as, refkey+1, allow); + } + rset_clear(allow, keynumhi); + khi = 0; + } +#else + if (irt_isnum(kt)) { + key = ra_scratch(as, allow); + rset_clear(allow, key); + keyhi = keynumhi = ra_scratch(as, allow); + rset_clear(allow, keyhi); + khi = 0; + } else { + key = ra_alloc1(as, refkey, allow); + rset_clear(allow, key); + } +#endif + } else if (irt_isnum(kt)) { + int32_t val = (int32_t)ir_knum(irkey)->u32.lo; + k = emit_isk12(ARMI_CMP, val); + if (!k) { + key = ra_allock(as, val, allow); + rset_clear(allow, key); + } + val = (int32_t)ir_knum(irkey)->u32.hi; + khi = emit_isk12(ARMI_CMP, val); + if (!khi) { + keyhi = ra_allock(as, val, allow); + rset_clear(allow, keyhi); + } + } else if (!irt_ispri(kt)) { + k = emit_isk12(ARMI_CMP, irkey->i); + if (!k) { + key = ra_alloc1(as, refkey, allow); + rset_clear(allow, key); + } + } + if (!irt_ispri(kt)) + tmp = ra_scratchpair(as, allow); + + /* Key not found in chain: jump to exit (if merged) or load niltv. */ + l_end = emit_label(as); + as->invmcp = NULL; + if (merge == IR_NE) + asm_guardcc(as, CC_AL); + else if (destused) + emit_loada(as, dest, niltvg(J2G(as->J))); + + /* Follow hash chain until the end. */ + l_loop = --as->mcp; + emit_n(as, ARMI_CMP|ARMI_K12|0, dest); + emit_lso(as, ARMI_LDR, dest, dest, (int32_t)offsetof(Node, next)); + + /* Type and value comparison. */ + if (merge == IR_EQ) + asm_guardcc(as, CC_EQ); + else + emit_branch(as, ARMF_CC(ARMI_B, CC_EQ), l_end); + if (!irt_ispri(kt)) { + emit_nm(as, ARMF_CC(ARMI_CMP, CC_EQ)^k, tmp, key); + emit_nm(as, ARMI_CMP^khi, tmp+1, keyhi); + emit_lsox(as, ARMI_LDRD, tmp, dest, (int32_t)offsetof(Node, key)); + } else { + emit_n(as, ARMI_CMP^khi, tmp); + emit_lso(as, ARMI_LDR, tmp, dest, (int32_t)offsetof(Node, key.it)); + } + *l_loop = ARMF_CC(ARMI_B, CC_NE) | ((as->mcp-l_loop-2) & 0x00ffffffu); + + /* Load main position relative to tab->node into dest. */ + khash = irref_isk(refkey) ? ir_khash(irkey) : 1; + if (khash == 0) { + emit_lso(as, ARMI_LDR, dest, tab, (int32_t)offsetof(GCtab, node)); + } else { + emit_dnm(as, ARMI_ADD|ARMF_SH(ARMSH_LSL, 3), dest, dest, tmp); + emit_dnm(as, ARMI_ADD|ARMF_SH(ARMSH_LSL, 1), tmp, tmp, tmp); + if (irt_isstr(kt)) { /* Fetch of str->hash is cheaper than ra_allock. */ + emit_dnm(as, ARMI_AND, tmp, tmp+1, RID_TMP); + emit_lso(as, ARMI_LDR, dest, tab, (int32_t)offsetof(GCtab, node)); + emit_lso(as, ARMI_LDR, tmp+1, key, (int32_t)offsetof(GCstr, hash)); + emit_lso(as, ARMI_LDR, RID_TMP, tab, (int32_t)offsetof(GCtab, hmask)); + } else if (irref_isk(refkey)) { + emit_opk(as, ARMI_AND, tmp, RID_TMP, (int32_t)khash, + rset_exclude(rset_exclude(RSET_GPR, tab), dest)); + emit_lso(as, ARMI_LDR, dest, tab, (int32_t)offsetof(GCtab, node)); + emit_lso(as, ARMI_LDR, RID_TMP, tab, (int32_t)offsetof(GCtab, hmask)); + } else { /* Must match with hash*() in lj_tab.c. */ + if (ra_hasreg(keynumhi)) { /* Canonicalize +-0.0 to 0.0. */ + if (keyhi == RID_TMP) + emit_dm(as, ARMF_CC(ARMI_MOV, CC_NE), keyhi, keynumhi); + emit_d(as, ARMF_CC(ARMI_MOV, CC_EQ)|ARMI_K12|0, keyhi); + } + emit_dnm(as, ARMI_AND, tmp, tmp, RID_TMP); + emit_dnm(as, ARMI_SUB|ARMF_SH(ARMSH_ROR, 32-HASH_ROT3), tmp, tmp, tmp+1); + emit_lso(as, ARMI_LDR, dest, tab, (int32_t)offsetof(GCtab, node)); + emit_dnm(as, ARMI_EOR|ARMF_SH(ARMSH_ROR, 32-((HASH_ROT2+HASH_ROT1)&31)), + tmp, tmp+1, tmp); + emit_lso(as, ARMI_LDR, RID_TMP, tab, (int32_t)offsetof(GCtab, hmask)); + emit_dnm(as, ARMI_SUB|ARMF_SH(ARMSH_ROR, 32-HASH_ROT1), tmp+1, tmp+1, tmp); + if (ra_hasreg(keynumhi)) { + emit_dnm(as, ARMI_EOR, tmp+1, tmp, key); + emit_dnm(as, ARMI_ORR|ARMI_S, RID_TMP, tmp, key); /* Test for +-0.0. */ + emit_dnm(as, ARMI_ADD, tmp, keynumhi, keynumhi); +#if !LJ_SOFTFP + emit_dnm(as, ARMI_VMOV_RR_D, key, keynumhi, + (ra_alloc1(as, refkey, RSET_FPR) & 15)); +#endif + } else { + emit_dnm(as, ARMI_EOR, tmp+1, tmp, key); + emit_opk(as, ARMI_ADD, tmp, key, (int32_t)HASH_BIAS, + rset_exclude(rset_exclude(RSET_GPR, tab), key)); + } + } + } +} + +static void asm_hrefk(ASMState *as, IRIns *ir) +{ + IRIns *kslot = IR(ir->op2); + IRIns *irkey = IR(kslot->op1); + int32_t ofs = (int32_t)(kslot->op2 * sizeof(Node)); + int32_t kofs = ofs + (int32_t)offsetof(Node, key); + Reg dest = (ra_used(ir) || ofs > 4095) ? ra_dest(as, ir, RSET_GPR) : RID_NONE; + Reg node = ra_alloc1(as, ir->op1, RSET_GPR); + Reg key = RID_NONE, type = RID_TMP, idx = node; + RegSet allow = rset_exclude(RSET_GPR, node); + lua_assert(ofs % sizeof(Node) == 0); + if (ofs > 4095) { + idx = dest; + rset_clear(allow, dest); + kofs = (int32_t)offsetof(Node, key); + } else if (ra_hasreg(dest)) { + emit_opk(as, ARMI_ADD, dest, node, ofs, allow); + } + asm_guardcc(as, CC_NE); + if (!irt_ispri(irkey->t)) { + RegSet even = (as->freeset & allow); + even = even & (even >> 1) & RSET_GPREVEN; + if (even) { + key = ra_scratch(as, even); + if (rset_test(as->freeset, key+1)) { + type = key+1; + ra_modified(as, type); + } + } else { + key = ra_scratch(as, allow); + } + rset_clear(allow, key); + } + rset_clear(allow, type); + if (irt_isnum(irkey->t)) { + emit_opk(as, ARMF_CC(ARMI_CMP, CC_EQ), 0, type, + (int32_t)ir_knum(irkey)->u32.hi, allow); + emit_opk(as, ARMI_CMP, 0, key, + (int32_t)ir_knum(irkey)->u32.lo, allow); + } else { + if (ra_hasreg(key)) + emit_opk(as, ARMF_CC(ARMI_CMP, CC_EQ), 0, key, irkey->i, allow); + emit_n(as, ARMI_CMN|ARMI_K12|-irt_toitype(irkey->t), type); + } + emit_lso(as, ARMI_LDR, type, idx, kofs+4); + if (ra_hasreg(key)) emit_lso(as, ARMI_LDR, key, idx, kofs); + if (ofs > 4095) + emit_opk(as, ARMI_ADD, dest, node, ofs, RSET_GPR); +} + +static void asm_uref(ASMState *as, IRIns *ir) +{ + Reg dest = ra_dest(as, ir, RSET_GPR); + if (irref_isk(ir->op1)) { + GCfunc *fn = ir_kfunc(IR(ir->op1)); + MRef *v = &gcref(fn->l.uvptr[(ir->op2 >> 8)])->uv.v; + emit_lsptr(as, ARMI_LDR, dest, v); + } else { + Reg uv = ra_scratch(as, RSET_GPR); + Reg func = ra_alloc1(as, ir->op1, RSET_GPR); + if (ir->o == IR_UREFC) { + asm_guardcc(as, CC_NE); + emit_n(as, ARMI_CMP|ARMI_K12|1, RID_TMP); + emit_opk(as, ARMI_ADD, dest, uv, + (int32_t)offsetof(GCupval, tv), RSET_GPR); + emit_lso(as, ARMI_LDRB, RID_TMP, uv, (int32_t)offsetof(GCupval, closed)); + } else { + emit_lso(as, ARMI_LDR, dest, uv, (int32_t)offsetof(GCupval, v)); + } + emit_lso(as, ARMI_LDR, uv, func, + (int32_t)offsetof(GCfuncL, uvptr) + 4*(int32_t)(ir->op2 >> 8)); + } +} + +static void asm_fref(ASMState *as, IRIns *ir) +{ + UNUSED(as); UNUSED(ir); + lua_assert(!ra_used(ir)); +} + +static void asm_strref(ASMState *as, IRIns *ir) +{ + Reg dest = ra_dest(as, ir, RSET_GPR); + IRRef ref = ir->op2, refk = ir->op1; + Reg r; + if (irref_isk(ref)) { + IRRef tmp = refk; refk = ref; ref = tmp; + } else if (!irref_isk(refk)) { + uint32_t k, m = ARMI_K12|sizeof(GCstr); + Reg right, left = ra_alloc1(as, ir->op1, RSET_GPR); + IRIns *irr = IR(ir->op2); + if (ra_hasreg(irr->r)) { + ra_noweak(as, irr->r); + right = irr->r; + } else if (mayfuse(as, irr->op2) && + irr->o == IR_ADD && irref_isk(irr->op2) && + (k = emit_isk12(ARMI_ADD, + (int32_t)sizeof(GCstr) + IR(irr->op2)->i))) { + m = k; + right = ra_alloc1(as, irr->op1, rset_exclude(RSET_GPR, left)); + } else { + right = ra_allocref(as, ir->op2, rset_exclude(RSET_GPR, left)); + } + emit_dn(as, ARMI_ADD^m, dest, dest); + emit_dnm(as, ARMI_ADD, dest, left, right); + return; + } + r = ra_alloc1(as, ref, RSET_GPR); + emit_opk(as, ARMI_ADD, dest, r, + sizeof(GCstr) + IR(refk)->i, rset_exclude(RSET_GPR, r)); +} + +/* -- Loads and stores ---------------------------------------------------- */ + +static ARMIns asm_fxloadins(IRIns *ir) +{ + switch (irt_type(ir->t)) { + case IRT_I8: return ARMI_LDRSB; + case IRT_U8: return ARMI_LDRB; + case IRT_I16: return ARMI_LDRSH; + case IRT_U16: return ARMI_LDRH; + case IRT_NUM: lua_assert(!LJ_SOFTFP); return ARMI_VLDR_D; + case IRT_FLOAT: if (!LJ_SOFTFP) return ARMI_VLDR_S; + default: return ARMI_LDR; + } +} + +static ARMIns asm_fxstoreins(IRIns *ir) +{ + switch (irt_type(ir->t)) { + case IRT_I8: case IRT_U8: return ARMI_STRB; + case IRT_I16: case IRT_U16: return ARMI_STRH; + case IRT_NUM: lua_assert(!LJ_SOFTFP); return ARMI_VSTR_D; + case IRT_FLOAT: if (!LJ_SOFTFP) return ARMI_VSTR_S; + default: return ARMI_STR; + } +} + +static void asm_fload(ASMState *as, IRIns *ir) +{ + if (ir->op1 == REF_NIL) { + lua_assert(!ra_used(ir)); /* We can end up here if DCE is turned off. */ + } else { + Reg dest = ra_dest(as, ir, RSET_GPR); + Reg idx = ra_alloc1(as, ir->op1, RSET_GPR); + ARMIns ai = asm_fxloadins(ir); + int32_t ofs; + if (ir->op2 == IRFL_TAB_ARRAY) { + ofs = asm_fuseabase(as, ir->op1); + if (ofs) { /* Turn the t->array load into an add for colocated arrays. */ + emit_dn(as, ARMI_ADD|ARMI_K12|ofs, dest, idx); + return; + } + } + ofs = field_ofs[ir->op2]; + if ((ai & 0x04000000)) + emit_lso(as, ai, dest, idx, ofs); + else + emit_lsox(as, ai, dest, idx, ofs); + } +} + +static void asm_fstore(ASMState *as, IRIns *ir) +{ + if (ir->r != RID_SINK) { + Reg src = ra_alloc1(as, ir->op2, RSET_GPR); + IRIns *irf = IR(ir->op1); + Reg idx = ra_alloc1(as, irf->op1, rset_exclude(RSET_GPR, src)); + int32_t ofs = field_ofs[irf->op2]; + ARMIns ai = asm_fxstoreins(ir); + if ((ai & 0x04000000)) + emit_lso(as, ai, src, idx, ofs); + else + emit_lsox(as, ai, src, idx, ofs); + } +} + +static void asm_xload(ASMState *as, IRIns *ir) +{ + Reg dest = ra_dest(as, ir, + (!LJ_SOFTFP && irt_isfp(ir->t)) ? RSET_FPR : RSET_GPR); + lua_assert(!(ir->op2 & IRXLOAD_UNALIGNED)); + asm_fusexref(as, asm_fxloadins(ir), dest, ir->op1, RSET_GPR, 0); +} + +static void asm_xstore_(ASMState *as, IRIns *ir, int32_t ofs) +{ + if (ir->r != RID_SINK) { + Reg src = ra_alloc1(as, ir->op2, + (!LJ_SOFTFP && irt_isfp(ir->t)) ? RSET_FPR : RSET_GPR); + asm_fusexref(as, asm_fxstoreins(ir), src, ir->op1, + rset_exclude(RSET_GPR, src), ofs); + } +} + +#define asm_xstore(as, ir) asm_xstore_(as, ir, 0) + +static void asm_ahuvload(ASMState *as, IRIns *ir) +{ + int hiop = (LJ_SOFTFP && (ir+1)->o == IR_HIOP); + IRType t = hiop ? IRT_NUM : irt_type(ir->t); + Reg dest = RID_NONE, type = RID_NONE, idx; + RegSet allow = RSET_GPR; + int32_t ofs = 0; + if (hiop && ra_used(ir+1)) { + type = ra_dest(as, ir+1, allow); + rset_clear(allow, type); + } + if (ra_used(ir)) { + lua_assert((LJ_SOFTFP ? 0 : irt_isnum(ir->t)) || + irt_isint(ir->t) || irt_isaddr(ir->t)); + dest = ra_dest(as, ir, (!LJ_SOFTFP && t == IRT_NUM) ? RSET_FPR : allow); + rset_clear(allow, dest); + } + idx = asm_fuseahuref(as, ir->op1, &ofs, allow, + (!LJ_SOFTFP && t == IRT_NUM) ? 1024 : 4096); + if (!hiop || type == RID_NONE) { + rset_clear(allow, idx); + if (ofs < 256 && ra_hasreg(dest) && (dest & 1) == 0 && + rset_test((as->freeset & allow), dest+1)) { + type = dest+1; + ra_modified(as, type); + } else { + type = RID_TMP; + } + } + asm_guardcc(as, t == IRT_NUM ? CC_HS : CC_NE); + emit_n(as, ARMI_CMN|ARMI_K12|-irt_toitype_(t), type); + if (ra_hasreg(dest)) { +#if !LJ_SOFTFP + if (t == IRT_NUM) + emit_vlso(as, ARMI_VLDR_D, dest, idx, ofs); + else +#endif + emit_lso(as, ARMI_LDR, dest, idx, ofs); + } + emit_lso(as, ARMI_LDR, type, idx, ofs+4); +} + +static void asm_ahustore(ASMState *as, IRIns *ir) +{ + if (ir->r != RID_SINK) { + RegSet allow = RSET_GPR; + Reg idx, src = RID_NONE, type = RID_NONE; + int32_t ofs = 0; +#if !LJ_SOFTFP + if (irt_isnum(ir->t)) { + src = ra_alloc1(as, ir->op2, RSET_FPR); + idx = asm_fuseahuref(as, ir->op1, &ofs, allow, 1024); + emit_vlso(as, ARMI_VSTR_D, src, idx, ofs); + } else +#endif + { + int hiop = (LJ_SOFTFP && (ir+1)->o == IR_HIOP); + if (!irt_ispri(ir->t)) { + src = ra_alloc1(as, ir->op2, allow); + rset_clear(allow, src); + } + if (hiop) + type = ra_alloc1(as, (ir+1)->op2, allow); + else + type = ra_allock(as, (int32_t)irt_toitype(ir->t), allow); + idx = asm_fuseahuref(as, ir->op1, &ofs, rset_exclude(allow, type), 4096); + if (ra_hasreg(src)) emit_lso(as, ARMI_STR, src, idx, ofs); + emit_lso(as, ARMI_STR, type, idx, ofs+4); + } + } +} + +static void asm_sload(ASMState *as, IRIns *ir) +{ + int32_t ofs = 8*((int32_t)ir->op1-1) + ((ir->op2 & IRSLOAD_FRAME) ? 4 : 0); + int hiop = (LJ_SOFTFP && (ir+1)->o == IR_HIOP); + IRType t = hiop ? IRT_NUM : irt_type(ir->t); + Reg dest = RID_NONE, type = RID_NONE, base; + RegSet allow = RSET_GPR; + lua_assert(!(ir->op2 & IRSLOAD_PARENT)); /* Handled by asm_head_side(). */ + lua_assert(irt_isguard(ir->t) || !(ir->op2 & IRSLOAD_TYPECHECK)); +#if LJ_SOFTFP + lua_assert(!(ir->op2 & IRSLOAD_CONVERT)); /* Handled by LJ_SOFTFP SPLIT. */ + if (hiop && ra_used(ir+1)) { + type = ra_dest(as, ir+1, allow); + rset_clear(allow, type); + } +#else + if ((ir->op2 & IRSLOAD_CONVERT) && irt_isguard(ir->t) && t == IRT_INT) { + dest = ra_scratch(as, RSET_FPR); + asm_tointg(as, ir, dest); + t = IRT_NUM; /* Continue with a regular number type check. */ + } else +#endif + if (ra_used(ir)) { + Reg tmp = RID_NONE; + if ((ir->op2 & IRSLOAD_CONVERT)) + tmp = ra_scratch(as, t == IRT_INT ? RSET_FPR : RSET_GPR); + lua_assert((LJ_SOFTFP ? 0 : irt_isnum(ir->t)) || + irt_isint(ir->t) || irt_isaddr(ir->t)); + dest = ra_dest(as, ir, (!LJ_SOFTFP && t == IRT_NUM) ? RSET_FPR : allow); + rset_clear(allow, dest); + base = ra_alloc1(as, REF_BASE, allow); + if ((ir->op2 & IRSLOAD_CONVERT)) { + if (t == IRT_INT) { + emit_dn(as, ARMI_VMOV_R_S, dest, (tmp & 15)); + emit_dm(as, ARMI_VCVT_S32_F64, (tmp & 15), (tmp & 15)); + t = IRT_NUM; /* Check for original type. */ + } else { + emit_dm(as, ARMI_VCVT_F64_S32, (dest & 15), (dest & 15)); + emit_dn(as, ARMI_VMOV_S_R, tmp, (dest & 15)); + t = IRT_INT; /* Check for original type. */ + } + dest = tmp; + } + goto dotypecheck; + } + base = ra_alloc1(as, REF_BASE, allow); +dotypecheck: + rset_clear(allow, base); + if ((ir->op2 & IRSLOAD_TYPECHECK)) { + if (ra_noreg(type)) { + if (ofs < 256 && ra_hasreg(dest) && (dest & 1) == 0 && + rset_test((as->freeset & allow), dest+1)) { + type = dest+1; + ra_modified(as, type); + } else { + type = RID_TMP; + } + } + asm_guardcc(as, t == IRT_NUM ? CC_HS : CC_NE); + emit_n(as, ARMI_CMN|ARMI_K12|-irt_toitype_(t), type); + } + if (ra_hasreg(dest)) { +#if !LJ_SOFTFP + if (t == IRT_NUM) { + if (ofs < 1024) { + emit_vlso(as, ARMI_VLDR_D, dest, base, ofs); + } else { + if (ra_hasreg(type)) emit_lso(as, ARMI_LDR, type, base, ofs+4); + emit_vlso(as, ARMI_VLDR_D, dest, RID_TMP, 0); + emit_opk(as, ARMI_ADD, RID_TMP, base, ofs, allow); + return; + } + } else +#endif + emit_lso(as, ARMI_LDR, dest, base, ofs); + } + if (ra_hasreg(type)) emit_lso(as, ARMI_LDR, type, base, ofs+4); +} + +/* -- Allocations --------------------------------------------------------- */ + +#if LJ_HASFFI +static void asm_cnew(ASMState *as, IRIns *ir) +{ + CTState *cts = ctype_ctsG(J2G(as->J)); + CTypeID id = (CTypeID)IR(ir->op1)->i; + CTSize sz; + CTInfo info = lj_ctype_info(cts, id, &sz); + const CCallInfo *ci = &lj_ir_callinfo[IRCALL_lj_mem_newgco]; + IRRef args[4]; + RegSet allow = (RSET_GPR & ~RSET_SCRATCH); + RegSet drop = RSET_SCRATCH; + lua_assert(sz != CTSIZE_INVALID || (ir->o == IR_CNEW && ir->op2 != REF_NIL)); + + as->gcsteps++; + if (ra_hasreg(ir->r)) + rset_clear(drop, ir->r); /* Dest reg handled below. */ + ra_evictset(as, drop); + if (ra_used(ir)) + ra_destreg(as, ir, RID_RET); /* GCcdata * */ + + /* Initialize immutable cdata object. */ + if (ir->o == IR_CNEWI) { + int32_t ofs = sizeof(GCcdata); + lua_assert(sz == 4 || sz == 8); + if (sz == 8) { + ofs += 4; ir++; + lua_assert(ir->o == IR_HIOP); + } + for (;;) { + Reg r = ra_alloc1(as, ir->op2, allow); + emit_lso(as, ARMI_STR, r, RID_RET, ofs); + rset_clear(allow, r); + if (ofs == sizeof(GCcdata)) break; + ofs -= 4; ir--; + } + } else if (ir->op2 != REF_NIL) { /* Create VLA/VLS/aligned cdata. */ + ci = &lj_ir_callinfo[IRCALL_lj_cdata_newv]; + args[0] = ASMREF_L; /* lua_State *L */ + args[1] = ir->op1; /* CTypeID id */ + args[2] = ir->op2; /* CTSize sz */ + args[3] = ASMREF_TMP1; /* CTSize align */ + asm_gencall(as, ci, args); + emit_loadi(as, ra_releasetmp(as, ASMREF_TMP1), (int32_t)ctype_align(info)); + return; + } + + /* Initialize gct and ctypeid. lj_mem_newgco() already sets marked. */ + { + uint32_t k = emit_isk12(ARMI_MOV, id); + Reg r = k ? RID_R1 : ra_allock(as, id, allow); + emit_lso(as, ARMI_STRB, RID_TMP, RID_RET, offsetof(GCcdata, gct)); + emit_lsox(as, ARMI_STRH, r, RID_RET, offsetof(GCcdata, ctypeid)); + emit_d(as, ARMI_MOV|ARMI_K12|~LJ_TCDATA, RID_TMP); + if (k) emit_d(as, ARMI_MOV^k, RID_R1); + } + args[0] = ASMREF_L; /* lua_State *L */ + args[1] = ASMREF_TMP1; /* MSize size */ + asm_gencall(as, ci, args); + ra_allockreg(as, (int32_t)(sz+sizeof(GCcdata)), + ra_releasetmp(as, ASMREF_TMP1)); +} +#else +#define asm_cnew(as, ir) ((void)0) +#endif + +/* -- Write barriers ------------------------------------------------------ */ + +static void asm_tbar(ASMState *as, IRIns *ir) +{ + Reg tab = ra_alloc1(as, ir->op1, RSET_GPR); + Reg link = ra_scratch(as, rset_exclude(RSET_GPR, tab)); + Reg gr = ra_allock(as, i32ptr(J2G(as->J)), + rset_exclude(rset_exclude(RSET_GPR, tab), link)); + Reg mark = RID_TMP; + MCLabel l_end = emit_label(as); + emit_lso(as, ARMI_STR, link, tab, (int32_t)offsetof(GCtab, gclist)); + emit_lso(as, ARMI_STRB, mark, tab, (int32_t)offsetof(GCtab, marked)); + emit_lso(as, ARMI_STR, tab, gr, + (int32_t)offsetof(global_State, gc.grayagain)); + emit_dn(as, ARMI_BIC|ARMI_K12|LJ_GC_BLACK, mark, mark); + emit_lso(as, ARMI_LDR, link, gr, + (int32_t)offsetof(global_State, gc.grayagain)); + emit_branch(as, ARMF_CC(ARMI_B, CC_EQ), l_end); + emit_n(as, ARMI_TST|ARMI_K12|LJ_GC_BLACK, mark); + emit_lso(as, ARMI_LDRB, mark, tab, (int32_t)offsetof(GCtab, marked)); +} + +static void asm_obar(ASMState *as, IRIns *ir) +{ + const CCallInfo *ci = &lj_ir_callinfo[IRCALL_lj_gc_barrieruv]; + IRRef args[2]; + MCLabel l_end; + Reg obj, val, tmp; + /* No need for other object barriers (yet). */ + lua_assert(IR(ir->op1)->o == IR_UREFC); + ra_evictset(as, RSET_SCRATCH); + l_end = emit_label(as); + args[0] = ASMREF_TMP1; /* global_State *g */ + args[1] = ir->op1; /* TValue *tv */ + asm_gencall(as, ci, args); + if ((l_end[-1] >> 28) == CC_AL) + l_end[-1] = ARMF_CC(l_end[-1], CC_NE); + else + emit_branch(as, ARMF_CC(ARMI_B, CC_EQ), l_end); + ra_allockreg(as, i32ptr(J2G(as->J)), ra_releasetmp(as, ASMREF_TMP1)); + obj = IR(ir->op1)->r; + tmp = ra_scratch(as, rset_exclude(RSET_GPR, obj)); + emit_n(as, ARMF_CC(ARMI_TST, CC_NE)|ARMI_K12|LJ_GC_BLACK, tmp); + emit_n(as, ARMI_TST|ARMI_K12|LJ_GC_WHITES, RID_TMP); + val = ra_alloc1(as, ir->op2, rset_exclude(RSET_GPR, obj)); + emit_lso(as, ARMI_LDRB, tmp, obj, + (int32_t)offsetof(GCupval, marked)-(int32_t)offsetof(GCupval, tv)); + emit_lso(as, ARMI_LDRB, RID_TMP, val, (int32_t)offsetof(GChead, marked)); +} + +/* -- Arithmetic and logic operations ------------------------------------- */ + +#if !LJ_SOFTFP +static void asm_fparith(ASMState *as, IRIns *ir, ARMIns ai) +{ + Reg dest = ra_dest(as, ir, RSET_FPR); + Reg right, left = ra_alloc2(as, ir, RSET_FPR); + right = (left >> 8); left &= 255; + emit_dnm(as, ai, (dest & 15), (left & 15), (right & 15)); +} + +static void asm_fpunary(ASMState *as, IRIns *ir, ARMIns ai) +{ + Reg dest = ra_dest(as, ir, RSET_FPR); + Reg left = ra_hintalloc(as, ir->op1, dest, RSET_FPR); + emit_dm(as, ai, (dest & 15), (left & 15)); +} + +static void asm_callround(ASMState *as, IRIns *ir, int id) +{ + /* The modified regs must match with the *.dasc implementation. */ + RegSet drop = RID2RSET(RID_R0)|RID2RSET(RID_R1)|RID2RSET(RID_R2)| + RID2RSET(RID_R3)|RID2RSET(RID_R12); + RegSet of; + Reg dest, src; + ra_evictset(as, drop); + dest = ra_dest(as, ir, RSET_FPR); + emit_dnm(as, ARMI_VMOV_D_RR, RID_RETLO, RID_RETHI, (dest & 15)); + emit_call(as, id == IRFPM_FLOOR ? (void *)lj_vm_floor_sf : + id == IRFPM_CEIL ? (void *)lj_vm_ceil_sf : + (void *)lj_vm_trunc_sf); + /* Workaround to protect argument GPRs from being used for remat. */ + of = as->freeset; + as->freeset &= ~RSET_RANGE(RID_R0, RID_R1+1); + as->cost[RID_R0] = as->cost[RID_R1] = REGCOST(~0u, ASMREF_L); + src = ra_alloc1(as, ir->op1, RSET_FPR); /* May alloc GPR to remat FPR. */ + as->freeset |= (of & RSET_RANGE(RID_R0, RID_R1+1)); + emit_dnm(as, ARMI_VMOV_RR_D, RID_R0, RID_R1, (src & 15)); +} + +static void asm_fpmath(ASMState *as, IRIns *ir) +{ + if (ir->op2 == IRFPM_EXP2 && asm_fpjoin_pow(as, ir)) + return; + if (ir->op2 <= IRFPM_TRUNC) + asm_callround(as, ir, ir->op2); + else if (ir->op2 == IRFPM_SQRT) + asm_fpunary(as, ir, ARMI_VSQRT_D); + else + asm_callid(as, ir, IRCALL_lj_vm_floor + ir->op2); +} +#endif + +static int asm_swapops(ASMState *as, IRRef lref, IRRef rref) +{ + IRIns *ir; + if (irref_isk(rref)) + return 0; /* Don't swap constants to the left. */ + if (irref_isk(lref)) + return 1; /* But swap constants to the right. */ + ir = IR(rref); + if ((ir->o >= IR_BSHL && ir->o <= IR_BROR) || + (ir->o == IR_ADD && ir->op1 == ir->op2)) + return 0; /* Don't swap fusable operands to the left. */ + ir = IR(lref); + if ((ir->o >= IR_BSHL && ir->o <= IR_BROR) || + (ir->o == IR_ADD && ir->op1 == ir->op2)) + return 1; /* But swap fusable operands to the right. */ + return 0; /* Otherwise don't swap. */ +} + +static void asm_intop(ASMState *as, IRIns *ir, ARMIns ai) +{ + IRRef lref = ir->op1, rref = ir->op2; + Reg left, dest = ra_dest(as, ir, RSET_GPR); + uint32_t m; + if (asm_swapops(as, lref, rref)) { + IRRef tmp = lref; lref = rref; rref = tmp; + if ((ai & ~ARMI_S) == ARMI_SUB || (ai & ~ARMI_S) == ARMI_SBC) + ai ^= (ARMI_SUB^ARMI_RSB); + } + left = ra_hintalloc(as, lref, dest, RSET_GPR); + m = asm_fuseopm(as, ai, rref, rset_exclude(RSET_GPR, left)); + if (irt_isguard(ir->t)) { /* For IR_ADDOV etc. */ + asm_guardcc(as, CC_VS); + ai |= ARMI_S; + } + emit_dn(as, ai^m, dest, left); +} + +static void asm_intop_s(ASMState *as, IRIns *ir, ARMIns ai) +{ + if (as->flagmcp == as->mcp) { /* Drop cmp r, #0. */ + as->flagmcp = NULL; + as->mcp++; + ai |= ARMI_S; + } + asm_intop(as, ir, ai); +} + +static void asm_intneg(ASMState *as, IRIns *ir, ARMIns ai) +{ + Reg dest = ra_dest(as, ir, RSET_GPR); + Reg left = ra_hintalloc(as, ir->op1, dest, RSET_GPR); + emit_dn(as, ai|ARMI_K12|0, dest, left); +} + +/* NYI: use add/shift for MUL(OV) with constants. FOLD only does 2^k. */ +static void asm_intmul(ASMState *as, IRIns *ir) +{ + Reg dest = ra_dest(as, ir, RSET_GPR); + Reg left = ra_alloc1(as, ir->op1, rset_exclude(RSET_GPR, dest)); + Reg right = ra_alloc1(as, ir->op2, rset_exclude(RSET_GPR, left)); + Reg tmp = RID_NONE; + /* ARMv5 restriction: dest != left and dest_hi != left. */ + if (dest == left && left != right) { left = right; right = dest; } + if (irt_isguard(ir->t)) { /* IR_MULOV */ + if (!(as->flags & JIT_F_ARMV6) && dest == left) + tmp = left = ra_scratch(as, rset_exclude(RSET_GPR, left)); + asm_guardcc(as, CC_NE); + emit_nm(as, ARMI_TEQ|ARMF_SH(ARMSH_ASR, 31), RID_TMP, dest); + emit_dnm(as, ARMI_SMULL|ARMF_S(right), dest, RID_TMP, left); + } else { + if (!(as->flags & JIT_F_ARMV6) && dest == left) tmp = left = RID_TMP; + emit_nm(as, ARMI_MUL|ARMF_S(right), dest, left); + } + /* Only need this for the dest == left == right case. */ + if (ra_hasreg(tmp)) emit_dm(as, ARMI_MOV, tmp, right); +} + +static void asm_add(ASMState *as, IRIns *ir) +{ +#if !LJ_SOFTFP + if (irt_isnum(ir->t)) { + if (!asm_fusemadd(as, ir, ARMI_VMLA_D, ARMI_VMLA_D)) + asm_fparith(as, ir, ARMI_VADD_D); + return; + } +#endif + asm_intop_s(as, ir, ARMI_ADD); +} + +static void asm_sub(ASMState *as, IRIns *ir) +{ +#if !LJ_SOFTFP + if (irt_isnum(ir->t)) { + if (!asm_fusemadd(as, ir, ARMI_VNMLS_D, ARMI_VMLS_D)) + asm_fparith(as, ir, ARMI_VSUB_D); + return; + } +#endif + asm_intop_s(as, ir, ARMI_SUB); +} + +static void asm_mul(ASMState *as, IRIns *ir) +{ +#if !LJ_SOFTFP + if (irt_isnum(ir->t)) { + asm_fparith(as, ir, ARMI_VMUL_D); + return; + } +#endif + asm_intmul(as, ir); +} + +#define asm_addov(as, ir) asm_add(as, ir) +#define asm_subov(as, ir) asm_sub(as, ir) +#define asm_mulov(as, ir) asm_mul(as, ir) + +#if !LJ_SOFTFP +#define asm_div(as, ir) asm_fparith(as, ir, ARMI_VDIV_D) +#define asm_pow(as, ir) asm_callid(as, ir, IRCALL_lj_vm_powi) +#define asm_abs(as, ir) asm_fpunary(as, ir, ARMI_VABS_D) +#define asm_atan2(as, ir) asm_callid(as, ir, IRCALL_atan2) +#define asm_ldexp(as, ir) asm_callid(as, ir, IRCALL_ldexp) +#endif + +#define asm_mod(as, ir) asm_callid(as, ir, IRCALL_lj_vm_modi) + +static void asm_neg(ASMState *as, IRIns *ir) +{ +#if !LJ_SOFTFP + if (irt_isnum(ir->t)) { + asm_fpunary(as, ir, ARMI_VNEG_D); + return; + } +#endif + asm_intneg(as, ir, ARMI_RSB); +} + +static void asm_bitop(ASMState *as, IRIns *ir, ARMIns ai) +{ + if (as->flagmcp == as->mcp) { /* Try to drop cmp r, #0. */ + uint32_t cc = (as->mcp[1] >> 28); + as->flagmcp = NULL; + if (cc <= CC_NE) { + as->mcp++; + ai |= ARMI_S; + } else if (cc == CC_GE) { + *++as->mcp ^= ((CC_GE^CC_PL) << 28); + ai |= ARMI_S; + } else if (cc == CC_LT) { + *++as->mcp ^= ((CC_LT^CC_MI) << 28); + ai |= ARMI_S; + } /* else: other conds don't work with bit ops. */ + } + if (ir->op2 == 0) { + Reg dest = ra_dest(as, ir, RSET_GPR); + uint32_t m = asm_fuseopm(as, ai, ir->op1, RSET_GPR); + emit_d(as, ai^m, dest); + } else { + /* NYI: Turn BAND !k12 into uxtb, uxth or bfc or shl+shr. */ + asm_intop(as, ir, ai); + } +} + +#define asm_bnot(as, ir) asm_bitop(as, ir, ARMI_MVN) + +static void asm_bswap(ASMState *as, IRIns *ir) +{ + Reg dest = ra_dest(as, ir, RSET_GPR); + Reg left = ra_alloc1(as, ir->op1, RSET_GPR); + if ((as->flags & JIT_F_ARMV6)) { + emit_dm(as, ARMI_REV, dest, left); + } else { + Reg tmp2 = dest; + if (tmp2 == left) + tmp2 = ra_scratch(as, rset_exclude(rset_exclude(RSET_GPR, dest), left)); + emit_dnm(as, ARMI_EOR|ARMF_SH(ARMSH_LSR, 8), dest, tmp2, RID_TMP); + emit_dm(as, ARMI_MOV|ARMF_SH(ARMSH_ROR, 8), tmp2, left); + emit_dn(as, ARMI_BIC|ARMI_K12|256*8|255, RID_TMP, RID_TMP); + emit_dnm(as, ARMI_EOR|ARMF_SH(ARMSH_ROR, 16), RID_TMP, left, left); + } +} + +#define asm_band(as, ir) asm_bitop(as, ir, ARMI_AND) +#define asm_bor(as, ir) asm_bitop(as, ir, ARMI_ORR) +#define asm_bxor(as, ir) asm_bitop(as, ir, ARMI_EOR) + +static void asm_bitshift(ASMState *as, IRIns *ir, ARMShift sh) +{ + if (irref_isk(ir->op2)) { /* Constant shifts. */ + /* NYI: Turn SHL+SHR or BAND+SHR into uxtb, uxth or ubfx. */ + /* NYI: Turn SHL+ASR into sxtb, sxth or sbfx. */ + Reg dest = ra_dest(as, ir, RSET_GPR); + Reg left = ra_alloc1(as, ir->op1, RSET_GPR); + int32_t shift = (IR(ir->op2)->i & 31); + emit_dm(as, ARMI_MOV|ARMF_SH(sh, shift), dest, left); + } else { + Reg dest = ra_dest(as, ir, RSET_GPR); + Reg left = ra_alloc1(as, ir->op1, RSET_GPR); + Reg right = ra_alloc1(as, ir->op2, rset_exclude(RSET_GPR, left)); + emit_dm(as, ARMI_MOV|ARMF_RSH(sh, right), dest, left); + } +} + +#define asm_bshl(as, ir) asm_bitshift(as, ir, ARMSH_LSL) +#define asm_bshr(as, ir) asm_bitshift(as, ir, ARMSH_LSR) +#define asm_bsar(as, ir) asm_bitshift(as, ir, ARMSH_ASR) +#define asm_bror(as, ir) asm_bitshift(as, ir, ARMSH_ROR) +#define asm_brol(as, ir) lua_assert(0) + +static void asm_intmin_max(ASMState *as, IRIns *ir, int cc) +{ + uint32_t kcmp = 0, kmov = 0; + Reg dest = ra_dest(as, ir, RSET_GPR); + Reg left = ra_hintalloc(as, ir->op1, dest, RSET_GPR); + Reg right = 0; + if (irref_isk(ir->op2)) { + kcmp = emit_isk12(ARMI_CMP, IR(ir->op2)->i); + if (kcmp) kmov = emit_isk12(ARMI_MOV, IR(ir->op2)->i); + } + if (!kmov) { + kcmp = 0; + right = ra_alloc1(as, ir->op2, rset_exclude(RSET_GPR, left)); + } + if (kmov || dest != right) { + emit_dm(as, ARMF_CC(ARMI_MOV, cc)^kmov, dest, right); + cc ^= 1; /* Must use opposite conditions for paired moves. */ + } else { + cc ^= (CC_LT^CC_GT); /* Otherwise may swap CC_LT <-> CC_GT. */ + } + if (dest != left) emit_dm(as, ARMF_CC(ARMI_MOV, cc), dest, left); + emit_nm(as, ARMI_CMP^kcmp, left, right); +} + +#if LJ_SOFTFP +static void asm_sfpmin_max(ASMState *as, IRIns *ir, int cc) +{ + const CCallInfo *ci = &lj_ir_callinfo[IRCALL_softfp_cmp]; + RegSet drop = RSET_SCRATCH; + Reg r; + IRRef args[4]; + args[0] = ir->op1; args[1] = (ir+1)->op1; + args[2] = ir->op2; args[3] = (ir+1)->op2; + /* __aeabi_cdcmple preserves r0-r3. */ + if (ra_hasreg(ir->r)) rset_clear(drop, ir->r); + if (ra_hasreg((ir+1)->r)) rset_clear(drop, (ir+1)->r); + if (!rset_test(as->freeset, RID_R2) && + regcost_ref(as->cost[RID_R2]) == args[2]) rset_clear(drop, RID_R2); + if (!rset_test(as->freeset, RID_R3) && + regcost_ref(as->cost[RID_R3]) == args[3]) rset_clear(drop, RID_R3); + ra_evictset(as, drop); + ra_destpair(as, ir); + emit_dm(as, ARMF_CC(ARMI_MOV, cc), RID_RETHI, RID_R3); + emit_dm(as, ARMF_CC(ARMI_MOV, cc), RID_RETLO, RID_R2); + emit_call(as, (void *)ci->func); + for (r = RID_R0; r <= RID_R3; r++) + ra_leftov(as, r, args[r-RID_R0]); +} +#else +static void asm_fpmin_max(ASMState *as, IRIns *ir, int cc) +{ + Reg dest = (ra_dest(as, ir, RSET_FPR) & 15); + Reg right, left = ra_alloc2(as, ir, RSET_FPR); + right = ((left >> 8) & 15); left &= 15; + if (dest != left) emit_dm(as, ARMF_CC(ARMI_VMOV_D, cc^1), dest, left); + if (dest != right) emit_dm(as, ARMF_CC(ARMI_VMOV_D, cc), dest, right); + emit_d(as, ARMI_VMRS, 0); + emit_dm(as, ARMI_VCMP_D, left, right); +} +#endif + +static void asm_min_max(ASMState *as, IRIns *ir, int cc, int fcc) +{ +#if LJ_SOFTFP + UNUSED(fcc); +#else + if (irt_isnum(ir->t)) + asm_fpmin_max(as, ir, fcc); + else +#endif + asm_intmin_max(as, ir, cc); +} + +#define asm_min(as, ir) asm_min_max(as, ir, CC_GT, CC_HI) +#define asm_max(as, ir) asm_min_max(as, ir, CC_LT, CC_LO) + +/* -- Comparisons --------------------------------------------------------- */ + +/* Map of comparisons to flags. ORDER IR. */ +static const uint8_t asm_compmap[IR_ABC+1] = { + /* op FP swp int cc FP cc */ + /* LT */ CC_GE + (CC_HS << 4), + /* GE x */ CC_LT + (CC_HI << 4), + /* LE */ CC_GT + (CC_HI << 4), + /* GT x */ CC_LE + (CC_HS << 4), + /* ULT x */ CC_HS + (CC_LS << 4), + /* UGE */ CC_LO + (CC_LO << 4), + /* ULE x */ CC_HI + (CC_LO << 4), + /* UGT */ CC_LS + (CC_LS << 4), + /* EQ */ CC_NE + (CC_NE << 4), + /* NE */ CC_EQ + (CC_EQ << 4), + /* ABC */ CC_LS + (CC_LS << 4) /* Same as UGT. */ +}; + +#if LJ_SOFTFP +/* FP comparisons. */ +static void asm_sfpcomp(ASMState *as, IRIns *ir) +{ + const CCallInfo *ci = &lj_ir_callinfo[IRCALL_softfp_cmp]; + RegSet drop = RSET_SCRATCH; + Reg r; + IRRef args[4]; + int swp = (((ir->o ^ (ir->o >> 2)) & ~(ir->o >> 3) & 1) << 1); + args[swp^0] = ir->op1; args[swp^1] = (ir+1)->op1; + args[swp^2] = ir->op2; args[swp^3] = (ir+1)->op2; + /* __aeabi_cdcmple preserves r0-r3. This helps to reduce spills. */ + for (r = RID_R0; r <= RID_R3; r++) + if (!rset_test(as->freeset, r) && + regcost_ref(as->cost[r]) == args[r-RID_R0]) rset_clear(drop, r); + ra_evictset(as, drop); + asm_guardcc(as, (asm_compmap[ir->o] >> 4)); + emit_call(as, (void *)ci->func); + for (r = RID_R0; r <= RID_R3; r++) + ra_leftov(as, r, args[r-RID_R0]); +} +#else +/* FP comparisons. */ +static void asm_fpcomp(ASMState *as, IRIns *ir) +{ + Reg left, right; + ARMIns ai; + int swp = ((ir->o ^ (ir->o >> 2)) & ~(ir->o >> 3) & 1); + if (!swp && irref_isk(ir->op2) && ir_knum(IR(ir->op2))->u64 == 0) { + left = (ra_alloc1(as, ir->op1, RSET_FPR) & 15); + right = 0; + ai = ARMI_VCMPZ_D; + } else { + left = ra_alloc2(as, ir, RSET_FPR); + if (swp) { + right = (left & 15); left = ((left >> 8) & 15); + } else { + right = ((left >> 8) & 15); left &= 15; + } + ai = ARMI_VCMP_D; + } + asm_guardcc(as, (asm_compmap[ir->o] >> 4)); + emit_d(as, ARMI_VMRS, 0); + emit_dm(as, ai, left, right); +} +#endif + +/* Integer comparisons. */ +static void asm_intcomp(ASMState *as, IRIns *ir) +{ + ARMCC cc = (asm_compmap[ir->o] & 15); + IRRef lref = ir->op1, rref = ir->op2; + Reg left; + uint32_t m; + int cmpprev0 = 0; + lua_assert(irt_isint(ir->t) || irt_isu32(ir->t) || irt_isaddr(ir->t)); + if (asm_swapops(as, lref, rref)) { + Reg tmp = lref; lref = rref; rref = tmp; + if (cc >= CC_GE) cc ^= 7; /* LT <-> GT, LE <-> GE */ + else if (cc > CC_NE) cc ^= 11; /* LO <-> HI, LS <-> HS */ + } + if (irref_isk(rref) && IR(rref)->i == 0) { + IRIns *irl = IR(lref); + cmpprev0 = (irl+1 == ir); + /* Combine comp(BAND(left, right), 0) into tst left, right. */ + if (cmpprev0 && irl->o == IR_BAND && !ra_used(irl)) { + IRRef blref = irl->op1, brref = irl->op2; + uint32_t m2 = 0; + Reg bleft; + if (asm_swapops(as, blref, brref)) { + Reg tmp = blref; blref = brref; brref = tmp; + } + if (irref_isk(brref)) { + m2 = emit_isk12(ARMI_AND, IR(brref)->i); + if ((m2 & (ARMI_AND^ARMI_BIC))) + goto notst; /* Not beneficial if we miss a constant operand. */ + } + if (cc == CC_GE) cc = CC_PL; + else if (cc == CC_LT) cc = CC_MI; + else if (cc > CC_NE) goto notst; /* Other conds don't work with tst. */ + bleft = ra_alloc1(as, blref, RSET_GPR); + if (!m2) m2 = asm_fuseopm(as, 0, brref, rset_exclude(RSET_GPR, bleft)); + asm_guardcc(as, cc); + emit_n(as, ARMI_TST^m2, bleft); + return; + } + } +notst: + left = ra_alloc1(as, lref, RSET_GPR); + m = asm_fuseopm(as, ARMI_CMP, rref, rset_exclude(RSET_GPR, left)); + asm_guardcc(as, cc); + emit_n(as, ARMI_CMP^m, left); + /* Signed comparison with zero and referencing previous ins? */ + if (cmpprev0 && (cc <= CC_NE || cc >= CC_GE)) + as->flagmcp = as->mcp; /* Allow elimination of the compare. */ +} + +static void asm_comp(ASMState *as, IRIns *ir) +{ +#if !LJ_SOFTFP + if (irt_isnum(ir->t)) + asm_fpcomp(as, ir); + else +#endif + asm_intcomp(as, ir); +} + +#define asm_equal(as, ir) asm_comp(as, ir) + +#if LJ_HASFFI +/* 64 bit integer comparisons. */ +static void asm_int64comp(ASMState *as, IRIns *ir) +{ + int signedcomp = (ir->o <= IR_GT); + ARMCC cclo, cchi; + Reg leftlo, lefthi; + uint32_t mlo, mhi; + RegSet allow = RSET_GPR, oldfree; + + /* Always use unsigned comparison for loword. */ + cclo = asm_compmap[ir->o + (signedcomp ? 4 : 0)] & 15; + leftlo = ra_alloc1(as, ir->op1, allow); + oldfree = as->freeset; + mlo = asm_fuseopm(as, ARMI_CMP, ir->op2, rset_clear(allow, leftlo)); + allow &= ~(oldfree & ~as->freeset); /* Update for allocs of asm_fuseopm. */ + + /* Use signed or unsigned comparison for hiword. */ + cchi = asm_compmap[ir->o] & 15; + lefthi = ra_alloc1(as, (ir+1)->op1, allow); + mhi = asm_fuseopm(as, ARMI_CMP, (ir+1)->op2, rset_clear(allow, lefthi)); + + /* All register allocations must be performed _before_ this point. */ + if (signedcomp) { + MCLabel l_around = emit_label(as); + asm_guardcc(as, cclo); + emit_n(as, ARMI_CMP^mlo, leftlo); + emit_branch(as, ARMF_CC(ARMI_B, CC_NE), l_around); + if (cchi == CC_GE || cchi == CC_LE) cchi ^= 6; /* GE -> GT, LE -> LT */ + asm_guardcc(as, cchi); + } else { + asm_guardcc(as, cclo); + emit_n(as, ARMF_CC(ARMI_CMP, CC_EQ)^mlo, leftlo); + } + emit_n(as, ARMI_CMP^mhi, lefthi); +} +#endif + +/* -- Support for 64 bit ops in 32 bit mode ------------------------------- */ + +/* Hiword op of a split 64 bit op. Previous op must be the loword op. */ +static void asm_hiop(ASMState *as, IRIns *ir) +{ +#if LJ_HASFFI || LJ_SOFTFP + /* HIOP is marked as a store because it needs its own DCE logic. */ + int uselo = ra_used(ir-1), usehi = ra_used(ir); /* Loword/hiword used? */ + if (LJ_UNLIKELY(!(as->flags & JIT_F_OPT_DCE))) uselo = usehi = 1; + if ((ir-1)->o <= IR_NE) { /* 64 bit integer or FP comparisons. ORDER IR. */ + as->curins--; /* Always skip the loword comparison. */ +#if LJ_SOFTFP + if (!irt_isint(ir->t)) { + asm_sfpcomp(as, ir-1); + return; + } +#endif +#if LJ_HASFFI + asm_int64comp(as, ir-1); +#endif + return; +#if LJ_SOFTFP + } else if ((ir-1)->o == IR_MIN || (ir-1)->o == IR_MAX) { + as->curins--; /* Always skip the loword min/max. */ + if (uselo || usehi) + asm_sfpmin_max(as, ir-1, (ir-1)->o == IR_MIN ? CC_HI : CC_LO); + return; +#elif LJ_HASFFI + } else if ((ir-1)->o == IR_CONV) { + as->curins--; /* Always skip the CONV. */ + if (usehi || uselo) + asm_conv64(as, ir); + return; +#endif + } else if ((ir-1)->o == IR_XSTORE) { + if ((ir-1)->r != RID_SINK) + asm_xstore_(as, ir, 4); + return; + } + if (!usehi) return; /* Skip unused hiword op for all remaining ops. */ + switch ((ir-1)->o) { +#if LJ_HASFFI + case IR_ADD: + as->curins--; + asm_intop(as, ir, ARMI_ADC); + asm_intop(as, ir-1, ARMI_ADD|ARMI_S); + break; + case IR_SUB: + as->curins--; + asm_intop(as, ir, ARMI_SBC); + asm_intop(as, ir-1, ARMI_SUB|ARMI_S); + break; + case IR_NEG: + as->curins--; + asm_intneg(as, ir, ARMI_RSC); + asm_intneg(as, ir-1, ARMI_RSB|ARMI_S); + break; +#endif +#if LJ_SOFTFP + case IR_SLOAD: case IR_ALOAD: case IR_HLOAD: case IR_ULOAD: case IR_VLOAD: + case IR_STRTO: + if (!uselo) + ra_allocref(as, ir->op1, RSET_GPR); /* Mark lo op as used. */ + break; +#endif + case IR_CALLN: + case IR_CALLS: + case IR_CALLXS: + if (!uselo) + ra_allocref(as, ir->op1, RID2RSET(RID_RETLO)); /* Mark lo op as used. */ + break; +#if LJ_SOFTFP + case IR_ASTORE: case IR_HSTORE: case IR_USTORE: case IR_TOSTR: +#endif + case IR_CNEWI: + /* Nothing to do here. Handled by lo op itself. */ + break; + default: lua_assert(0); break; + } +#else + UNUSED(as); UNUSED(ir); lua_assert(0); +#endif +} + +/* -- Profiling ----------------------------------------------------------- */ + +static void asm_prof(ASMState *as, IRIns *ir) +{ + UNUSED(ir); + asm_guardcc(as, CC_NE); + emit_n(as, ARMI_TST|ARMI_K12|HOOK_PROFILE, RID_TMP); + emit_lsptr(as, ARMI_LDRB, RID_TMP, (void *)&J2G(as->J)->hookmask); +} + +/* -- Stack handling ------------------------------------------------------ */ + +/* Check Lua stack size for overflow. Use exit handler as fallback. */ +static void asm_stack_check(ASMState *as, BCReg topslot, + IRIns *irp, RegSet allow, ExitNo exitno) +{ + Reg pbase; + uint32_t k; + if (irp) { + if (!ra_hasspill(irp->s)) { + pbase = irp->r; + lua_assert(ra_hasreg(pbase)); + } else if (allow) { + pbase = rset_pickbot(allow); + } else { + pbase = RID_RET; + emit_lso(as, ARMI_LDR, RID_RET, RID_SP, 0); /* Restore temp. register. */ + } + } else { + pbase = RID_BASE; + } + emit_branch(as, ARMF_CC(ARMI_BL, CC_LS), exitstub_addr(as->J, exitno)); + k = emit_isk12(0, (int32_t)(8*topslot)); + lua_assert(k); + emit_n(as, ARMI_CMP^k, RID_TMP); + emit_dnm(as, ARMI_SUB, RID_TMP, RID_TMP, pbase); + emit_lso(as, ARMI_LDR, RID_TMP, RID_TMP, + (int32_t)offsetof(lua_State, maxstack)); + if (irp) { /* Must not spill arbitrary registers in head of side trace. */ + int32_t i = i32ptr(&J2G(as->J)->cur_L); + if (ra_hasspill(irp->s)) + emit_lso(as, ARMI_LDR, pbase, RID_SP, sps_scale(irp->s)); + emit_lso(as, ARMI_LDR, RID_TMP, RID_TMP, (i & 4095)); + if (ra_hasspill(irp->s) && !allow) + emit_lso(as, ARMI_STR, RID_RET, RID_SP, 0); /* Save temp. register. */ + emit_loadi(as, RID_TMP, (i & ~4095)); + } else { + emit_getgl(as, RID_TMP, cur_L); + } +} + +/* Restore Lua stack from on-trace state. */ +static void asm_stack_restore(ASMState *as, SnapShot *snap) +{ + SnapEntry *map = &as->T->snapmap[snap->mapofs]; + SnapEntry *flinks = &as->T->snapmap[snap_nextofs(as->T, snap)-1]; + MSize n, nent = snap->nent; + /* Store the value of all modified slots to the Lua stack. */ + for (n = 0; n < nent; n++) { + SnapEntry sn = map[n]; + BCReg s = snap_slot(sn); + int32_t ofs = 8*((int32_t)s-1); + IRRef ref = snap_ref(sn); + IRIns *ir = IR(ref); + if ((sn & SNAP_NORESTORE)) + continue; + if (irt_isnum(ir->t)) { +#if LJ_SOFTFP + RegSet odd = rset_exclude(RSET_GPRODD, RID_BASE); + Reg tmp; + lua_assert(irref_isk(ref)); /* LJ_SOFTFP: must be a number constant. */ + tmp = ra_allock(as, (int32_t)ir_knum(ir)->u32.lo, + rset_exclude(RSET_GPREVEN, RID_BASE)); + emit_lso(as, ARMI_STR, tmp, RID_BASE, ofs); + if (rset_test(as->freeset, tmp+1)) odd = RID2RSET(tmp+1); + tmp = ra_allock(as, (int32_t)ir_knum(ir)->u32.hi, odd); + emit_lso(as, ARMI_STR, tmp, RID_BASE, ofs+4); +#else + Reg src = ra_alloc1(as, ref, RSET_FPR); + emit_vlso(as, ARMI_VSTR_D, src, RID_BASE, ofs); +#endif + } else { + RegSet odd = rset_exclude(RSET_GPRODD, RID_BASE); + Reg type; + lua_assert(irt_ispri(ir->t) || irt_isaddr(ir->t) || irt_isinteger(ir->t)); + if (!irt_ispri(ir->t)) { + Reg src = ra_alloc1(as, ref, rset_exclude(RSET_GPREVEN, RID_BASE)); + emit_lso(as, ARMI_STR, src, RID_BASE, ofs); + if (rset_test(as->freeset, src+1)) odd = RID2RSET(src+1); + } + if ((sn & (SNAP_CONT|SNAP_FRAME))) { + if (s == 0) continue; /* Do not overwrite link to previous frame. */ + type = ra_allock(as, (int32_t)(*flinks--), odd); +#if LJ_SOFTFP + } else if ((sn & SNAP_SOFTFPNUM)) { + type = ra_alloc1(as, ref+1, rset_exclude(RSET_GPRODD, RID_BASE)); +#endif + } else { + type = ra_allock(as, (int32_t)irt_toitype(ir->t), odd); + } + emit_lso(as, ARMI_STR, type, RID_BASE, ofs+4); + } + checkmclim(as); + } + lua_assert(map + nent == flinks); +} + +/* -- GC handling --------------------------------------------------------- */ + +/* Check GC threshold and do one or more GC steps. */ +static void asm_gc_check(ASMState *as) +{ + const CCallInfo *ci = &lj_ir_callinfo[IRCALL_lj_gc_step_jit]; + IRRef args[2]; + MCLabel l_end; + Reg tmp1, tmp2; + ra_evictset(as, RSET_SCRATCH); + l_end = emit_label(as); + /* Exit trace if in GCSatomic or GCSfinalize. Avoids syncing GC objects. */ + asm_guardcc(as, CC_NE); /* Assumes asm_snap_prep() already done. */ + emit_n(as, ARMI_CMP|ARMI_K12|0, RID_RET); + args[0] = ASMREF_TMP1; /* global_State *g */ + args[1] = ASMREF_TMP2; /* MSize steps */ + asm_gencall(as, ci, args); + tmp1 = ra_releasetmp(as, ASMREF_TMP1); + tmp2 = ra_releasetmp(as, ASMREF_TMP2); + emit_loadi(as, tmp2, as->gcsteps); + /* Jump around GC step if GC total < GC threshold. */ + emit_branch(as, ARMF_CC(ARMI_B, CC_LS), l_end); + emit_nm(as, ARMI_CMP, RID_TMP, tmp2); + emit_lso(as, ARMI_LDR, tmp2, tmp1, + (int32_t)offsetof(global_State, gc.threshold)); + emit_lso(as, ARMI_LDR, RID_TMP, tmp1, + (int32_t)offsetof(global_State, gc.total)); + ra_allockreg(as, i32ptr(J2G(as->J)), tmp1); + as->gcsteps = 0; + checkmclim(as); +} + +/* -- Loop handling ------------------------------------------------------- */ + +/* Fixup the loop branch. */ +static void asm_loop_fixup(ASMState *as) +{ + MCode *p = as->mctop; + MCode *target = as->mcp; + if (as->loopinv) { /* Inverted loop branch? */ + /* asm_guardcc already inverted the bcc and patched the final bl. */ + p[-2] |= ((uint32_t)(target-p) & 0x00ffffffu); + } else { + p[-1] = ARMI_B | ((uint32_t)((target-p)-1) & 0x00ffffffu); + } +} + +/* -- Head of trace ------------------------------------------------------- */ + +/* Reload L register from g->cur_L. */ +static void asm_head_lreg(ASMState *as) +{ + IRIns *ir = IR(ASMREF_L); + if (ra_used(ir)) { + Reg r = ra_dest(as, ir, RSET_GPR); + emit_getgl(as, r, cur_L); + ra_evictk(as); + } +} + +/* Coalesce BASE register for a root trace. */ +static void asm_head_root_base(ASMState *as) +{ + IRIns *ir; + asm_head_lreg(as); + ir = IR(REF_BASE); + if (ra_hasreg(ir->r) && (rset_test(as->modset, ir->r) || irt_ismarked(ir->t))) + ra_spill(as, ir); + ra_destreg(as, ir, RID_BASE); +} + +/* Coalesce BASE register for a side trace. */ +static RegSet asm_head_side_base(ASMState *as, IRIns *irp, RegSet allow) +{ + IRIns *ir; + asm_head_lreg(as); + ir = IR(REF_BASE); + if (ra_hasreg(ir->r) && (rset_test(as->modset, ir->r) || irt_ismarked(ir->t))) + ra_spill(as, ir); + if (ra_hasspill(irp->s)) { + rset_clear(allow, ra_dest(as, ir, allow)); + } else { + Reg r = irp->r; + lua_assert(ra_hasreg(r)); + rset_clear(allow, r); + if (r != ir->r && !rset_test(as->freeset, r)) + ra_restore(as, regcost_ref(as->cost[r])); + ra_destreg(as, ir, r); + } + return allow; +} + +/* -- Tail of trace ------------------------------------------------------- */ + +/* Fixup the tail code. */ +static void asm_tail_fixup(ASMState *as, TraceNo lnk) +{ + MCode *p = as->mctop; + MCode *target; + int32_t spadj = as->T->spadjust; + if (spadj == 0) { + as->mctop = --p; + } else { + /* Patch stack adjustment. */ + uint32_t k = emit_isk12(ARMI_ADD, spadj); + lua_assert(k); + p[-2] = (ARMI_ADD^k) | ARMF_D(RID_SP) | ARMF_N(RID_SP); + } + /* Patch exit branch. */ + target = lnk ? traceref(as->J, lnk)->mcode : (MCode *)lj_vm_exit_interp; + p[-1] = ARMI_B|(((target-p)-1)&0x00ffffffu); +} + +/* Prepare tail of code. */ +static void asm_tail_prep(ASMState *as) +{ + MCode *p = as->mctop - 1; /* Leave room for exit branch. */ + if (as->loopref) { + as->invmcp = as->mcp = p; + } else { + as->mcp = p-1; /* Leave room for stack pointer adjustment. */ + as->invmcp = NULL; + } + *p = 0; /* Prevent load/store merging. */ +} + +/* -- Trace setup --------------------------------------------------------- */ + +/* Ensure there are enough stack slots for call arguments. */ +static Reg asm_setup_call_slots(ASMState *as, IRIns *ir, const CCallInfo *ci) +{ + IRRef args[CCI_NARGS_MAX*2]; + uint32_t i, nargs = CCI_XNARGS(ci); + int nslots = 0, ngpr = REGARG_NUMGPR, nfpr = REGARG_NUMFPR, fprodd = 0; + asm_collectargs(as, ir, ci, args); + for (i = 0; i < nargs; i++) { + if (!LJ_SOFTFP && args[i] && irt_isfp(IR(args[i])->t)) { + if (!LJ_ABI_SOFTFP && !(ci->flags & CCI_VARARG)) { + if (irt_isnum(IR(args[i])->t)) { + if (nfpr > 0) nfpr--; + else fprodd = 0, nslots = (nslots + 3) & ~1; + } else { + if (fprodd) fprodd--; + else if (nfpr > 0) fprodd = 1, nfpr--; + else nslots++; + } + } else if (irt_isnum(IR(args[i])->t)) { + ngpr &= ~1; + if (ngpr > 0) ngpr -= 2; else nslots += 2; + } else { + if (ngpr > 0) ngpr--; else nslots++; + } + } else { + if (ngpr > 0) ngpr--; else nslots++; + } + } + if (nslots > as->evenspill) /* Leave room for args in stack slots. */ + as->evenspill = nslots; + return REGSP_HINT(RID_RET); +} + +static void asm_setup_target(ASMState *as) +{ + /* May need extra exit for asm_stack_check on side traces. */ + asm_exitstub_setup(as, as->T->nsnap + (as->parent ? 1 : 0)); +} + +/* -- Trace patching ------------------------------------------------------ */ + +/* Patch exit jumps of existing machine code to a new target. */ +void lj_asm_patchexit(jit_State *J, GCtrace *T, ExitNo exitno, MCode *target) +{ + MCode *p = T->mcode; + MCode *pe = (MCode *)((char *)p + T->szmcode); + MCode *cstart = NULL, *cend = p; + MCode *mcarea = lj_mcode_patch(J, p, 0); + MCode *px = exitstub_addr(J, exitno) - 2; + for (; p < pe; p++) { + /* Look for bl_cc exitstub, replace with b_cc target. */ + uint32_t ins = *p; + if ((ins & 0x0f000000u) == 0x0b000000u && ins < 0xf0000000u && + ((ins ^ (px-p)) & 0x00ffffffu) == 0) { + *p = (ins & 0xfe000000u) | (((target-p)-2) & 0x00ffffffu); + cend = p+1; + if (!cstart) cstart = p; + } + } + lua_assert(cstart != NULL); + lj_mcode_sync(cstart, cend); + lj_mcode_patch(J, mcarea, 1); +} + diff --git a/lib/LuaJIT/lj_asm_arm64.h b/lib/LuaJIT/lj_asm_arm64.h new file mode 100644 index 0000000..baafa21 --- /dev/null +++ b/lib/LuaJIT/lj_asm_arm64.h @@ -0,0 +1,2031 @@ +/* +** ARM64 IR assembler (SSA IR -> machine code). +** Copyright (C) 2005-2017 Mike Pall. See Copyright Notice in luajit.h +** +** Contributed by Djordje Kovacevic and Stefan Pejic from RT-RK.com. +** Sponsored by Cisco Systems, Inc. +*/ + +/* -- Register allocator extensions --------------------------------------- */ + +/* Allocate a register with a hint. */ +static Reg ra_hintalloc(ASMState *as, IRRef ref, Reg hint, RegSet allow) +{ + Reg r = IR(ref)->r; + if (ra_noreg(r)) { + if (!ra_hashint(r) && !iscrossref(as, ref)) + ra_sethint(IR(ref)->r, hint); /* Propagate register hint. */ + r = ra_allocref(as, ref, allow); + } + ra_noweak(as, r); + return r; +} + +/* Allocate two source registers for three-operand instructions. */ +static Reg ra_alloc2(ASMState *as, IRIns *ir, RegSet allow) +{ + IRIns *irl = IR(ir->op1), *irr = IR(ir->op2); + Reg left = irl->r, right = irr->r; + if (ra_hasreg(left)) { + ra_noweak(as, left); + if (ra_noreg(right)) + right = ra_allocref(as, ir->op2, rset_exclude(allow, left)); + else + ra_noweak(as, right); + } else if (ra_hasreg(right)) { + ra_noweak(as, right); + left = ra_allocref(as, ir->op1, rset_exclude(allow, right)); + } else if (ra_hashint(right)) { + right = ra_allocref(as, ir->op2, allow); + left = ra_alloc1(as, ir->op1, rset_exclude(allow, right)); + } else { + left = ra_allocref(as, ir->op1, allow); + right = ra_alloc1(as, ir->op2, rset_exclude(allow, left)); + } + return left | (right << 8); +} + +/* -- Guard handling ------------------------------------------------------ */ + +/* Setup all needed exit stubs. */ +static void asm_exitstub_setup(ASMState *as, ExitNo nexits) +{ + ExitNo i; + MCode *mxp = as->mctop; + if (mxp - (nexits + 3 + MCLIM_REDZONE) < as->mclim) + asm_mclimit(as); + /* 1: str lr,[sp]; bl ->vm_exit_handler; movz w0,traceno; bl <1; bl <1; ... */ + for (i = nexits-1; (int32_t)i >= 0; i--) + *--mxp = A64I_LE(A64I_BL | A64F_S26(-3-i)); + *--mxp = A64I_LE(A64I_MOVZw | A64F_U16(as->T->traceno)); + mxp--; + *mxp = A64I_LE(A64I_BL | A64F_S26(((MCode *)(void *)lj_vm_exit_handler-mxp))); + *--mxp = A64I_LE(A64I_STRx | A64F_D(RID_LR) | A64F_N(RID_SP)); + as->mctop = mxp; +} + +static MCode *asm_exitstub_addr(ASMState *as, ExitNo exitno) +{ + /* Keep this in-sync with exitstub_trace_addr(). */ + return as->mctop + exitno + 3; +} + +/* Emit conditional branch to exit for guard. */ +static void asm_guardcc(ASMState *as, A64CC cc) +{ + MCode *target = asm_exitstub_addr(as, as->snapno); + MCode *p = as->mcp; + if (LJ_UNLIKELY(p == as->invmcp)) { + as->loopinv = 1; + *p = A64I_B | A64F_S26(target-p); + emit_cond_branch(as, cc^1, p-1); + return; + } + emit_cond_branch(as, cc, target); +} + +/* Emit test and branch instruction to exit for guard. */ +static void asm_guardtnb(ASMState *as, A64Ins ai, Reg r, uint32_t bit) +{ + MCode *target = asm_exitstub_addr(as, as->snapno); + MCode *p = as->mcp; + if (LJ_UNLIKELY(p == as->invmcp)) { + as->loopinv = 1; + *p = A64I_B | A64F_S26(target-p); + emit_tnb(as, ai^0x01000000u, r, bit, p-1); + return; + } + emit_tnb(as, ai, r, bit, target); +} + +/* Emit compare and branch instruction to exit for guard. */ +static void asm_guardcnb(ASMState *as, A64Ins ai, Reg r) +{ + MCode *target = asm_exitstub_addr(as, as->snapno); + MCode *p = as->mcp; + if (LJ_UNLIKELY(p == as->invmcp)) { + as->loopinv = 1; + *p = A64I_B | A64F_S26(target-p); + emit_cnb(as, ai^0x01000000u, r, p-1); + return; + } + emit_cnb(as, ai, r, target); +} + +/* -- Operand fusion ------------------------------------------------------ */ + +/* Limit linear search to this distance. Avoids O(n^2) behavior. */ +#define CONFLICT_SEARCH_LIM 31 + +static int asm_isk32(ASMState *as, IRRef ref, int32_t *k) +{ + if (irref_isk(ref)) { + IRIns *ir = IR(ref); + if (ir->o == IR_KNULL || !irt_is64(ir->t)) { + *k = ir->i; + return 1; + } else if (checki32((int64_t)ir_k64(ir)->u64)) { + *k = (int32_t)ir_k64(ir)->u64; + return 1; + } + } + return 0; +} + +/* Check if there's no conflicting instruction between curins and ref. */ +static int noconflict(ASMState *as, IRRef ref, IROp conflict) +{ + IRIns *ir = as->ir; + IRRef i = as->curins; + if (i > ref + CONFLICT_SEARCH_LIM) + return 0; /* Give up, ref is too far away. */ + while (--i > ref) + if (ir[i].o == conflict) + return 0; /* Conflict found. */ + return 1; /* Ok, no conflict. */ +} + +/* Fuse the array base of colocated arrays. */ +static int32_t asm_fuseabase(ASMState *as, IRRef ref) +{ + IRIns *ir = IR(ref); + if (ir->o == IR_TNEW && ir->op1 <= LJ_MAX_COLOSIZE && + !neverfuse(as) && noconflict(as, ref, IR_NEWREF)) + return (int32_t)sizeof(GCtab); + return 0; +} + +#define FUSE_REG 0x40000000 + +/* Fuse array/hash/upvalue reference into register+offset operand. */ +static Reg asm_fuseahuref(ASMState *as, IRRef ref, int32_t *ofsp, RegSet allow, + A64Ins ins) +{ + IRIns *ir = IR(ref); + if (ra_noreg(ir->r)) { + if (ir->o == IR_AREF) { + if (mayfuse(as, ref)) { + if (irref_isk(ir->op2)) { + IRRef tab = IR(ir->op1)->op1; + int32_t ofs = asm_fuseabase(as, tab); + IRRef refa = ofs ? tab : ir->op1; + ofs += 8*IR(ir->op2)->i; + if (emit_checkofs(ins, ofs)) { + *ofsp = ofs; + return ra_alloc1(as, refa, allow); + } + } else { + Reg base = ra_alloc1(as, ir->op1, allow); + *ofsp = FUSE_REG|ra_alloc1(as, ir->op2, rset_exclude(allow, base)); + return base; + } + } + } else if (ir->o == IR_HREFK) { + if (mayfuse(as, ref)) { + int32_t ofs = (int32_t)(IR(ir->op2)->op2 * sizeof(Node)); + if (emit_checkofs(ins, ofs)) { + *ofsp = ofs; + return ra_alloc1(as, ir->op1, allow); + } + } + } else if (ir->o == IR_UREFC) { + if (irref_isk(ir->op1)) { + GCfunc *fn = ir_kfunc(IR(ir->op1)); + GCupval *uv = &gcref(fn->l.uvptr[(ir->op2 >> 8)])->uv; + int64_t ofs = glofs(as, &uv->tv); + if (emit_checkofs(ins, ofs)) { + *ofsp = (int32_t)ofs; + return RID_GL; + } + } + } + } + *ofsp = 0; + return ra_alloc1(as, ref, allow); +} + +/* Fuse m operand into arithmetic/logic instructions. */ +static uint32_t asm_fuseopm(ASMState *as, A64Ins ai, IRRef ref, RegSet allow) +{ + IRIns *ir = IR(ref); + if (ra_hasreg(ir->r)) { + ra_noweak(as, ir->r); + return A64F_M(ir->r); + } else if (irref_isk(ref)) { + uint32_t m; + int64_t k = get_k64val(ir); + if ((ai & 0x1f000000) == 0x0a000000) + m = emit_isk13(k, irt_is64(ir->t)); + else + m = emit_isk12(k); + if (m) + return m; + } else if (mayfuse(as, ref)) { + if ((ir->o >= IR_BSHL && ir->o <= IR_BSAR && irref_isk(ir->op2)) || + (ir->o == IR_ADD && ir->op1 == ir->op2)) { + A64Shift sh = ir->o == IR_BSHR ? A64SH_LSR : + ir->o == IR_BSAR ? A64SH_ASR : A64SH_LSL; + int shift = ir->o == IR_ADD ? 1 : + (IR(ir->op2)->i & (irt_is64(ir->t) ? 63 : 31)); + IRIns *irl = IR(ir->op1); + if (sh == A64SH_LSL && + irl->o == IR_CONV && + irl->op2 == ((IRT_I64<op1, allow); + return A64F_M(m) | A64F_EXSH(A64EX_SXTW, shift); + } else { + Reg m = ra_alloc1(as, ir->op1, allow); + return A64F_M(m) | A64F_SH(sh, shift); + } + } else if (ir->o == IR_CONV && + ir->op2 == ((IRT_I64<op1, allow); + return A64F_M(m) | A64F_EX(A64EX_SXTW); + } + } + return A64F_M(ra_allocref(as, ref, allow)); +} + +/* Fuse XLOAD/XSTORE reference into load/store operand. */ +static void asm_fusexref(ASMState *as, A64Ins ai, Reg rd, IRRef ref, + RegSet allow) +{ + IRIns *ir = IR(ref); + Reg base; + int32_t ofs = 0; + if (ra_noreg(ir->r) && canfuse(as, ir)) { + if (ir->o == IR_ADD) { + if (asm_isk32(as, ir->op2, &ofs) && emit_checkofs(ai, ofs)) { + ref = ir->op1; + } else { + Reg rn, rm; + IRRef lref = ir->op1, rref = ir->op2; + IRIns *irl = IR(lref); + if (mayfuse(as, irl->op1)) { + unsigned int shift = 4; + if (irl->o == IR_BSHL && irref_isk(irl->op2)) { + shift = (IR(irl->op2)->i & 63); + } else if (irl->o == IR_ADD && irl->op1 == irl->op2) { + shift = 1; + } + if ((ai >> 30) == shift) { + lref = irl->op1; + irl = IR(lref); + ai |= A64I_LS_SH; + } + } + if (irl->o == IR_CONV && + irl->op2 == ((IRT_I64<op1; + ai |= A64I_LS_SXTWx; + } else { + ai |= A64I_LS_LSLx; + } + rm = ra_alloc1(as, lref, allow); + rn = ra_alloc1(as, rref, rset_exclude(allow, rm)); + emit_dnm(as, (ai^A64I_LS_R), (rd & 31), rn, rm); + return; + } + } else if (ir->o == IR_STRREF) { + if (asm_isk32(as, ir->op2, &ofs)) { + ref = ir->op1; + } else if (asm_isk32(as, ir->op1, &ofs)) { + ref = ir->op2; + } else { + Reg rn = ra_alloc1(as, ir->op1, allow); + IRIns *irr = IR(ir->op2); + uint32_t m; + if (irr+1 == ir && !ra_used(irr) && + irr->o == IR_ADD && irref_isk(irr->op2)) { + ofs = sizeof(GCstr) + IR(irr->op2)->i; + if (emit_checkofs(ai, ofs)) { + Reg rm = ra_alloc1(as, irr->op1, rset_exclude(allow, rn)); + m = A64F_M(rm) | A64F_EX(A64EX_SXTW); + goto skipopm; + } + } + m = asm_fuseopm(as, 0, ir->op2, rset_exclude(allow, rn)); + ofs = sizeof(GCstr); + skipopm: + emit_lso(as, ai, rd, rd, ofs); + emit_dn(as, A64I_ADDx^m, rd, rn); + return; + } + ofs += sizeof(GCstr); + if (!emit_checkofs(ai, ofs)) { + Reg rn = ra_alloc1(as, ref, allow); + Reg rm = ra_allock(as, ofs, rset_exclude(allow, rn)); + emit_dnm(as, (ai^A64I_LS_R)|A64I_LS_UXTWx, rd, rn, rm); + return; + } + } + } + base = ra_alloc1(as, ref, allow); + emit_lso(as, ai, (rd & 31), base, ofs); +} + +/* Fuse FP multiply-add/sub. */ +static int asm_fusemadd(ASMState *as, IRIns *ir, A64Ins ai, A64Ins air) +{ + IRRef lref = ir->op1, rref = ir->op2; + IRIns *irm; + if (lref != rref && + ((mayfuse(as, lref) && (irm = IR(lref), irm->o == IR_MUL) && + ra_noreg(irm->r)) || + (mayfuse(as, rref) && (irm = IR(rref), irm->o == IR_MUL) && + (rref = lref, ai = air, ra_noreg(irm->r))))) { + Reg dest = ra_dest(as, ir, RSET_FPR); + Reg add = ra_hintalloc(as, rref, dest, RSET_FPR); + Reg left = ra_alloc2(as, irm, + rset_exclude(rset_exclude(RSET_FPR, dest), add)); + Reg right = (left >> 8); left &= 255; + emit_dnma(as, ai, (dest & 31), (left & 31), (right & 31), (add & 31)); + return 1; + } + return 0; +} + +/* Fuse BAND + BSHL/BSHR into UBFM. */ +static int asm_fuseandshift(ASMState *as, IRIns *ir) +{ + IRIns *irl = IR(ir->op1); + lua_assert(ir->o == IR_BAND); + if (canfuse(as, irl) && irref_isk(ir->op2)) { + uint64_t mask = get_k64val(IR(ir->op2)); + if (irref_isk(irl->op2) && (irl->o == IR_BSHR || irl->o == IR_BSHL)) { + int32_t shmask = irt_is64(irl->t) ? 63 : 31; + int32_t shift = (IR(irl->op2)->i & shmask); + int32_t imms = shift; + if (irl->o == IR_BSHL) { + mask >>= shift; + shift = (shmask-shift+1) & shmask; + imms = 0; + } + if (mask && !((mask+1) & mask)) { /* Contiguous 1-bits at the bottom. */ + Reg dest = ra_dest(as, ir, RSET_GPR); + Reg left = ra_alloc1(as, irl->op1, RSET_GPR); + A64Ins ai = shmask == 63 ? A64I_UBFMx : A64I_UBFMw; + imms += 63 - emit_clz64(mask); + if (imms > shmask) imms = shmask; + emit_dn(as, ai | A64F_IMMS(imms) | A64F_IMMR(shift), dest, left); + return 1; + } + } + } + return 0; +} + +/* Fuse BOR(BSHL, BSHR) into EXTR/ROR. */ +static int asm_fuseorshift(ASMState *as, IRIns *ir) +{ + IRIns *irl = IR(ir->op1), *irr = IR(ir->op2); + lua_assert(ir->o == IR_BOR); + if (canfuse(as, irl) && canfuse(as, irr) && + ((irl->o == IR_BSHR && irr->o == IR_BSHL) || + (irl->o == IR_BSHL && irr->o == IR_BSHR))) { + if (irref_isk(irl->op2) && irref_isk(irr->op2)) { + IRRef lref = irl->op1, rref = irr->op1; + uint32_t lshift = IR(irl->op2)->i, rshift = IR(irr->op2)->i; + if (irl->o == IR_BSHR) { /* BSHR needs to be the right operand. */ + uint32_t tmp2; + IRRef tmp1 = lref; lref = rref; rref = tmp1; + tmp2 = lshift; lshift = rshift; rshift = tmp2; + } + if (rshift + lshift == (irt_is64(ir->t) ? 64 : 32)) { + A64Ins ai = irt_is64(ir->t) ? A64I_EXTRx : A64I_EXTRw; + Reg dest = ra_dest(as, ir, RSET_GPR); + Reg left = ra_alloc1(as, lref, RSET_GPR); + Reg right = ra_alloc1(as, rref, rset_exclude(RSET_GPR, left)); + emit_dnm(as, ai | A64F_IMMS(rshift), dest, left, right); + return 1; + } + } + } + return 0; +} + +/* -- Calls --------------------------------------------------------------- */ + +/* Generate a call to a C function. */ +static void asm_gencall(ASMState *as, const CCallInfo *ci, IRRef *args) +{ + uint32_t n, nargs = CCI_XNARGS(ci); + int32_t ofs = 0; + Reg gpr, fpr = REGARG_FIRSTFPR; + if ((void *)ci->func) + emit_call(as, (void *)ci->func); + for (gpr = REGARG_FIRSTGPR; gpr <= REGARG_LASTGPR; gpr++) + as->cost[gpr] = REGCOST(~0u, ASMREF_L); + gpr = REGARG_FIRSTGPR; + for (n = 0; n < nargs; n++) { /* Setup args. */ + IRRef ref = args[n]; + IRIns *ir = IR(ref); + if (ref) { + if (irt_isfp(ir->t)) { + if (fpr <= REGARG_LASTFPR) { + lua_assert(rset_test(as->freeset, fpr)); /* Must have been evicted. */ + ra_leftov(as, fpr, ref); + fpr++; + } else { + Reg r = ra_alloc1(as, ref, RSET_FPR); + emit_spstore(as, ir, r, ofs + ((LJ_BE && !irt_isnum(ir->t)) ? 4 : 0)); + ofs += 8; + } + } else { + if (gpr <= REGARG_LASTGPR) { + lua_assert(rset_test(as->freeset, gpr)); /* Must have been evicted. */ + ra_leftov(as, gpr, ref); + gpr++; + } else { + Reg r = ra_alloc1(as, ref, RSET_GPR); + emit_spstore(as, ir, r, ofs + ((LJ_BE && !irt_is64(ir->t)) ? 4 : 0)); + ofs += 8; + } + } + } + } +} + +/* Setup result reg/sp for call. Evict scratch regs. */ +static void asm_setupresult(ASMState *as, IRIns *ir, const CCallInfo *ci) +{ + RegSet drop = RSET_SCRATCH; + if (ra_hasreg(ir->r)) + rset_clear(drop, ir->r); /* Dest reg handled below. */ + ra_evictset(as, drop); /* Evictions must be performed first. */ + if (ra_used(ir)) { + lua_assert(!irt_ispri(ir->t)); + if (irt_isfp(ir->t)) { + if (ci->flags & CCI_CASTU64) { + Reg dest = ra_dest(as, ir, RSET_FPR) & 31; + emit_dn(as, irt_isnum(ir->t) ? A64I_FMOV_D_R : A64I_FMOV_S_R, + dest, RID_RET); + } else { + ra_destreg(as, ir, RID_FPRET); + } + } else { + ra_destreg(as, ir, RID_RET); + } + } + UNUSED(ci); +} + +static void asm_callx(ASMState *as, IRIns *ir) +{ + IRRef args[CCI_NARGS_MAX*2]; + CCallInfo ci; + IRRef func; + IRIns *irf; + ci.flags = asm_callx_flags(as, ir); + asm_collectargs(as, ir, &ci, args); + asm_setupresult(as, ir, &ci); + func = ir->op2; irf = IR(func); + if (irf->o == IR_CARG) { func = irf->op1; irf = IR(func); } + if (irref_isk(func)) { /* Call to constant address. */ + ci.func = (ASMFunction)(ir_k64(irf)->u64); + } else { /* Need a non-argument register for indirect calls. */ + Reg freg = ra_alloc1(as, func, RSET_RANGE(RID_X8, RID_MAX_GPR)-RSET_FIXED); + emit_n(as, A64I_BLR, freg); + ci.func = (ASMFunction)(void *)0; + } + asm_gencall(as, &ci, args); +} + +/* -- Returns ------------------------------------------------------------- */ + +/* Return to lower frame. Guard that it goes to the right spot. */ +static void asm_retf(ASMState *as, IRIns *ir) +{ + Reg base = ra_alloc1(as, REF_BASE, RSET_GPR); + void *pc = ir_kptr(IR(ir->op2)); + int32_t delta = 1+LJ_FR2+bc_a(*((const BCIns *)pc - 1)); + as->topslot -= (BCReg)delta; + if ((int32_t)as->topslot < 0) as->topslot = 0; + irt_setmark(IR(REF_BASE)->t); /* Children must not coalesce with BASE reg. */ + /* Need to force a spill on REF_BASE now to update the stack slot. */ + emit_lso(as, A64I_STRx, base, RID_SP, ra_spill(as, IR(REF_BASE))); + emit_setgl(as, base, jit_base); + emit_addptr(as, base, -8*delta); + asm_guardcc(as, CC_NE); + emit_nm(as, A64I_CMPx, RID_TMP, + ra_allock(as, i64ptr(pc), rset_exclude(RSET_GPR, base))); + emit_lso(as, A64I_LDRx, RID_TMP, base, -8); +} + +/* -- Type conversions ---------------------------------------------------- */ + +static void asm_tointg(ASMState *as, IRIns *ir, Reg left) +{ + Reg tmp = ra_scratch(as, rset_exclude(RSET_FPR, left)); + Reg dest = ra_dest(as, ir, RSET_GPR); + asm_guardcc(as, CC_NE); + emit_nm(as, A64I_FCMPd, (tmp & 31), (left & 31)); + emit_dn(as, A64I_FCVT_F64_S32, (tmp & 31), dest); + emit_dn(as, A64I_FCVT_S32_F64, dest, (left & 31)); +} + +static void asm_tobit(ASMState *as, IRIns *ir) +{ + RegSet allow = RSET_FPR; + Reg left = ra_alloc1(as, ir->op1, allow); + Reg right = ra_alloc1(as, ir->op2, rset_clear(allow, left)); + Reg tmp = ra_scratch(as, rset_clear(allow, right)); + Reg dest = ra_dest(as, ir, RSET_GPR); + emit_dn(as, A64I_FMOV_R_S, dest, (tmp & 31)); + emit_dnm(as, A64I_FADDd, (tmp & 31), (left & 31), (right & 31)); +} + +static void asm_conv(ASMState *as, IRIns *ir) +{ + IRType st = (IRType)(ir->op2 & IRCONV_SRCMASK); + int st64 = (st == IRT_I64 || st == IRT_U64 || st == IRT_P64); + int stfp = (st == IRT_NUM || st == IRT_FLOAT); + IRRef lref = ir->op1; + lua_assert(irt_type(ir->t) != st); + if (irt_isfp(ir->t)) { + Reg dest = ra_dest(as, ir, RSET_FPR); + if (stfp) { /* FP to FP conversion. */ + emit_dn(as, st == IRT_NUM ? A64I_FCVT_F32_F64 : A64I_FCVT_F64_F32, + (dest & 31), (ra_alloc1(as, lref, RSET_FPR) & 31)); + } else { /* Integer to FP conversion. */ + Reg left = ra_alloc1(as, lref, RSET_GPR); + A64Ins ai = irt_isfloat(ir->t) ? + (((IRT_IS64 >> st) & 1) ? + (st == IRT_I64 ? A64I_FCVT_F32_S64 : A64I_FCVT_F32_U64) : + (st == IRT_INT ? A64I_FCVT_F32_S32 : A64I_FCVT_F32_U32)) : + (((IRT_IS64 >> st) & 1) ? + (st == IRT_I64 ? A64I_FCVT_F64_S64 : A64I_FCVT_F64_U64) : + (st == IRT_INT ? A64I_FCVT_F64_S32 : A64I_FCVT_F64_U32)); + emit_dn(as, ai, (dest & 31), left); + } + } else if (stfp) { /* FP to integer conversion. */ + if (irt_isguard(ir->t)) { + /* Checked conversions are only supported from number to int. */ + lua_assert(irt_isint(ir->t) && st == IRT_NUM); + asm_tointg(as, ir, ra_alloc1(as, lref, RSET_FPR)); + } else { + Reg left = ra_alloc1(as, lref, RSET_FPR); + Reg dest = ra_dest(as, ir, RSET_GPR); + A64Ins ai = irt_is64(ir->t) ? + (st == IRT_NUM ? + (irt_isi64(ir->t) ? A64I_FCVT_S64_F64 : A64I_FCVT_U64_F64) : + (irt_isi64(ir->t) ? A64I_FCVT_S64_F32 : A64I_FCVT_U64_F32)) : + (st == IRT_NUM ? + (irt_isint(ir->t) ? A64I_FCVT_S32_F64 : A64I_FCVT_U32_F64) : + (irt_isint(ir->t) ? A64I_FCVT_S32_F32 : A64I_FCVT_U32_F32)); + emit_dn(as, ai, dest, (left & 31)); + } + } else if (st >= IRT_I8 && st <= IRT_U16) { /* Extend to 32 bit integer. */ + Reg dest = ra_dest(as, ir, RSET_GPR); + Reg left = ra_alloc1(as, lref, RSET_GPR); + A64Ins ai = st == IRT_I8 ? A64I_SXTBw : + st == IRT_U8 ? A64I_UXTBw : + st == IRT_I16 ? A64I_SXTHw : A64I_UXTHw; + lua_assert(irt_isint(ir->t) || irt_isu32(ir->t)); + emit_dn(as, ai, dest, left); + } else { + Reg dest = ra_dest(as, ir, RSET_GPR); + if (irt_is64(ir->t)) { + if (st64 || !(ir->op2 & IRCONV_SEXT)) { + /* 64/64 bit no-op (cast) or 32 to 64 bit zero extension. */ + ra_leftov(as, dest, lref); /* Do nothing, but may need to move regs. */ + } else { /* 32 to 64 bit sign extension. */ + Reg left = ra_alloc1(as, lref, RSET_GPR); + emit_dn(as, A64I_SXTW, dest, left); + } + } else { + if (st64) { + /* This is either a 32 bit reg/reg mov which zeroes the hiword + ** or a load of the loword from a 64 bit address. + */ + Reg left = ra_alloc1(as, lref, RSET_GPR); + emit_dm(as, A64I_MOVw, dest, left); + } else { /* 32/32 bit no-op (cast). */ + ra_leftov(as, dest, lref); /* Do nothing, but may need to move regs. */ + } + } + } +} + +static void asm_strto(ASMState *as, IRIns *ir) +{ + const CCallInfo *ci = &lj_ir_callinfo[IRCALL_lj_strscan_num]; + IRRef args[2]; + Reg dest = 0, tmp; + int destused = ra_used(ir); + int32_t ofs = 0; + ra_evictset(as, RSET_SCRATCH); + if (destused) { + if (ra_hasspill(ir->s)) { + ofs = sps_scale(ir->s); + destused = 0; + if (ra_hasreg(ir->r)) { + ra_free(as, ir->r); + ra_modified(as, ir->r); + emit_spload(as, ir, ir->r, ofs); + } + } else { + dest = ra_dest(as, ir, RSET_FPR); + } + } + if (destused) + emit_lso(as, A64I_LDRd, (dest & 31), RID_SP, 0); + asm_guardcnb(as, A64I_CBZ, RID_RET); + args[0] = ir->op1; /* GCstr *str */ + args[1] = ASMREF_TMP1; /* TValue *n */ + asm_gencall(as, ci, args); + tmp = ra_releasetmp(as, ASMREF_TMP1); + emit_opk(as, A64I_ADDx, tmp, RID_SP, ofs, RSET_GPR); +} + +/* -- Memory references --------------------------------------------------- */ + +/* Store tagged value for ref at base+ofs. */ +static void asm_tvstore64(ASMState *as, Reg base, int32_t ofs, IRRef ref) +{ + RegSet allow = rset_exclude(RSET_GPR, base); + IRIns *ir = IR(ref); + lua_assert(irt_ispri(ir->t) || irt_isaddr(ir->t) || irt_isinteger(ir->t)); + if (irref_isk(ref)) { + TValue k; + lj_ir_kvalue(as->J->L, &k, ir); + emit_lso(as, A64I_STRx, ra_allock(as, k.u64, allow), base, ofs); + } else { + Reg src = ra_alloc1(as, ref, allow); + rset_clear(allow, src); + if (irt_isinteger(ir->t)) { + Reg type = ra_allock(as, (int64_t)irt_toitype(ir->t) << 47, allow); + emit_lso(as, A64I_STRx, RID_TMP, base, ofs); + emit_dnm(as, A64I_ADDx | A64F_EX(A64EX_UXTW), RID_TMP, type, src); + } else { + Reg type = ra_allock(as, (int32_t)irt_toitype(ir->t), allow); + emit_lso(as, A64I_STRx, RID_TMP, base, ofs); + emit_dnm(as, A64I_ADDx | A64F_SH(A64SH_LSL, 47), RID_TMP, src, type); + } + } +} + +/* Get pointer to TValue. */ +static void asm_tvptr(ASMState *as, Reg dest, IRRef ref) +{ + IRIns *ir = IR(ref); + if (irt_isnum(ir->t)) { + if (irref_isk(ref)) { + /* Use the number constant itself as a TValue. */ + ra_allockreg(as, i64ptr(ir_knum(ir)), dest); + } else { + /* Otherwise force a spill and use the spill slot. */ + emit_opk(as, A64I_ADDx, dest, RID_SP, ra_spill(as, ir), RSET_GPR); + } + } else { + /* Otherwise use g->tmptv to hold the TValue. */ + asm_tvstore64(as, dest, 0, ref); + ra_allockreg(as, i64ptr(&J2G(as->J)->tmptv), dest); + } +} + +static void asm_aref(ASMState *as, IRIns *ir) +{ + Reg dest = ra_dest(as, ir, RSET_GPR); + Reg idx, base; + if (irref_isk(ir->op2)) { + IRRef tab = IR(ir->op1)->op1; + int32_t ofs = asm_fuseabase(as, tab); + IRRef refa = ofs ? tab : ir->op1; + uint32_t k = emit_isk12(ofs + 8*IR(ir->op2)->i); + if (k) { + base = ra_alloc1(as, refa, RSET_GPR); + emit_dn(as, A64I_ADDx^k, dest, base); + return; + } + } + base = ra_alloc1(as, ir->op1, RSET_GPR); + idx = ra_alloc1(as, ir->op2, rset_exclude(RSET_GPR, base)); + emit_dnm(as, A64I_ADDx | A64F_EXSH(A64EX_UXTW, 3), dest, base, idx); +} + +/* Inlined hash lookup. Specialized for key type and for const keys. +** The equivalent C code is: +** Node *n = hashkey(t, key); +** do { +** if (lj_obj_equal(&n->key, key)) return &n->val; +** } while ((n = nextnode(n))); +** return niltv(L); +*/ +static void asm_href(ASMState *as, IRIns *ir, IROp merge) +{ + RegSet allow = RSET_GPR; + int destused = ra_used(ir); + Reg dest = ra_dest(as, ir, allow); + Reg tab = ra_alloc1(as, ir->op1, rset_clear(allow, dest)); + Reg key = 0, tmp = RID_TMP; + IRRef refkey = ir->op2; + IRIns *irkey = IR(refkey); + int isk = irref_isk(ir->op2); + IRType1 kt = irkey->t; + uint32_t k = 0; + uint32_t khash; + MCLabel l_end, l_loop, l_next; + rset_clear(allow, tab); + + if (!isk) { + key = ra_alloc1(as, ir->op2, irt_isnum(kt) ? RSET_FPR : allow); + rset_clear(allow, key); + if (!irt_isstr(kt)) { + tmp = ra_scratch(as, allow); + rset_clear(allow, tmp); + } + } else if (irt_isnum(kt)) { + int64_t val = (int64_t)ir_knum(irkey)->u64; + if (!(k = emit_isk12(val))) { + key = ra_allock(as, val, allow); + rset_clear(allow, key); + } + } else if (!irt_ispri(kt)) { + if (!(k = emit_isk12(irkey->i))) { + key = ra_alloc1(as, refkey, allow); + rset_clear(allow, key); + } + } + + /* Key not found in chain: jump to exit (if merged) or load niltv. */ + l_end = emit_label(as); + as->invmcp = NULL; + if (merge == IR_NE) + asm_guardcc(as, CC_AL); + else if (destused) + emit_loada(as, dest, niltvg(J2G(as->J))); + + /* Follow hash chain until the end. */ + l_loop = --as->mcp; + emit_n(as, A64I_CMPx^A64I_K12^0, dest); + emit_lso(as, A64I_LDRx, dest, dest, offsetof(Node, next)); + l_next = emit_label(as); + + /* Type and value comparison. */ + if (merge == IR_EQ) + asm_guardcc(as, CC_EQ); + else + emit_cond_branch(as, CC_EQ, l_end); + + if (irt_isnum(kt)) { + if (isk) { + /* Assumes -0.0 is already canonicalized to +0.0. */ + if (k) + emit_n(as, A64I_CMPx^k, tmp); + else + emit_nm(as, A64I_CMPx, key, tmp); + emit_lso(as, A64I_LDRx, tmp, dest, offsetof(Node, key.u64)); + } else { + Reg tisnum = ra_allock(as, LJ_TISNUM << 15, allow); + Reg ftmp = ra_scratch(as, rset_exclude(RSET_FPR, key)); + rset_clear(allow, tisnum); + emit_nm(as, A64I_FCMPd, key, ftmp); + emit_dn(as, A64I_FMOV_D_R, (ftmp & 31), (tmp & 31)); + emit_cond_branch(as, CC_LO, l_next); + emit_nm(as, A64I_CMPx | A64F_SH(A64SH_LSR, 32), tisnum, tmp); + emit_lso(as, A64I_LDRx, tmp, dest, offsetof(Node, key.n)); + } + } else if (irt_isaddr(kt)) { + Reg scr; + if (isk) { + int64_t kk = ((int64_t)irt_toitype(irkey->t) << 47) | irkey[1].tv.u64; + scr = ra_allock(as, kk, allow); + emit_nm(as, A64I_CMPx, scr, tmp); + emit_lso(as, A64I_LDRx, tmp, dest, offsetof(Node, key.u64)); + } else { + scr = ra_scratch(as, allow); + emit_nm(as, A64I_CMPx, tmp, scr); + emit_lso(as, A64I_LDRx, scr, dest, offsetof(Node, key.u64)); + } + rset_clear(allow, scr); + } else { + Reg type, scr; + lua_assert(irt_ispri(kt) && !irt_isnil(kt)); + type = ra_allock(as, ~((int64_t)~irt_toitype(ir->t) << 47), allow); + scr = ra_scratch(as, rset_clear(allow, type)); + rset_clear(allow, scr); + emit_nm(as, A64I_CMPw, scr, type); + emit_lso(as, A64I_LDRx, scr, dest, offsetof(Node, key)); + } + + *l_loop = A64I_BCC | A64F_S19(as->mcp - l_loop) | CC_NE; + if (!isk && irt_isaddr(kt)) { + Reg type = ra_allock(as, (int32_t)irt_toitype(kt), allow); + emit_dnm(as, A64I_ADDx | A64F_SH(A64SH_LSL, 47), tmp, key, type); + rset_clear(allow, type); + } + /* Load main position relative to tab->node into dest. */ + khash = isk ? ir_khash(irkey) : 1; + if (khash == 0) { + emit_lso(as, A64I_LDRx, dest, tab, offsetof(GCtab, node)); + } else { + emit_dnm(as, A64I_ADDx | A64F_SH(A64SH_LSL, 3), dest, tmp, dest); + emit_dnm(as, A64I_ADDx | A64F_SH(A64SH_LSL, 1), dest, dest, dest); + emit_lso(as, A64I_LDRx, tmp, tab, offsetof(GCtab, node)); + if (isk) { + Reg tmphash = ra_allock(as, khash, allow); + emit_dnm(as, A64I_ANDw, dest, dest, tmphash); + emit_lso(as, A64I_LDRw, dest, tab, offsetof(GCtab, hmask)); + } else if (irt_isstr(kt)) { + /* Fetch of str->hash is cheaper than ra_allock. */ + emit_dnm(as, A64I_ANDw, dest, dest, tmp); + emit_lso(as, A64I_LDRw, tmp, key, offsetof(GCstr, hash)); + emit_lso(as, A64I_LDRw, dest, tab, offsetof(GCtab, hmask)); + } else { /* Must match with hash*() in lj_tab.c. */ + emit_dnm(as, A64I_ANDw, dest, dest, tmp); + emit_lso(as, A64I_LDRw, tmp, tab, offsetof(GCtab, hmask)); + emit_dnm(as, A64I_SUBw, dest, dest, tmp); + emit_dnm(as, A64I_EXTRw | (A64F_IMMS(32-HASH_ROT3)), tmp, tmp, tmp); + emit_dnm(as, A64I_EORw, dest, dest, tmp); + emit_dnm(as, A64I_EXTRw | (A64F_IMMS(32-HASH_ROT2)), dest, dest, dest); + emit_dnm(as, A64I_SUBw, tmp, tmp, dest); + emit_dnm(as, A64I_EXTRw | (A64F_IMMS(32-HASH_ROT1)), dest, dest, dest); + emit_dnm(as, A64I_EORw, tmp, tmp, dest); + if (irt_isnum(kt)) { + emit_dnm(as, A64I_ADDw, dest, dest, dest); + emit_dn(as, A64I_LSRx | A64F_IMMR(32)|A64F_IMMS(32), dest, dest); + emit_dm(as, A64I_MOVw, tmp, dest); + emit_dn(as, A64I_FMOV_R_D, dest, (key & 31)); + } else { + checkmclim(as); + emit_dm(as, A64I_MOVw, tmp, key); + emit_dnm(as, A64I_EORw, dest, dest, + ra_allock(as, irt_toitype(kt) << 15, allow)); + emit_dn(as, A64I_LSRx | A64F_IMMR(32)|A64F_IMMS(32), dest, dest); + emit_dm(as, A64I_MOVx, dest, key); + } + } + } +} + +static void asm_hrefk(ASMState *as, IRIns *ir) +{ + IRIns *kslot = IR(ir->op2); + IRIns *irkey = IR(kslot->op1); + int32_t ofs = (int32_t)(kslot->op2 * sizeof(Node)); + int32_t kofs = ofs + (int32_t)offsetof(Node, key); + int bigofs = !emit_checkofs(A64I_LDRx, ofs); + Reg dest = (ra_used(ir) || bigofs) ? ra_dest(as, ir, RSET_GPR) : RID_NONE; + Reg node = ra_alloc1(as, ir->op1, RSET_GPR); + Reg key, idx = node; + RegSet allow = rset_exclude(RSET_GPR, node); + uint64_t k; + lua_assert(ofs % sizeof(Node) == 0); + if (bigofs) { + idx = dest; + rset_clear(allow, dest); + kofs = (int32_t)offsetof(Node, key); + } else if (ra_hasreg(dest)) { + emit_opk(as, A64I_ADDx, dest, node, ofs, allow); + } + asm_guardcc(as, CC_NE); + if (irt_ispri(irkey->t)) { + k = ~((int64_t)~irt_toitype(irkey->t) << 47); + } else if (irt_isnum(irkey->t)) { + k = ir_knum(irkey)->u64; + } else { + k = ((uint64_t)irt_toitype(irkey->t) << 47) | (uint64_t)ir_kgc(irkey); + } + key = ra_scratch(as, allow); + emit_nm(as, A64I_CMPx, key, ra_allock(as, k, rset_exclude(allow, key))); + emit_lso(as, A64I_LDRx, key, idx, kofs); + if (bigofs) + emit_opk(as, A64I_ADDx, dest, node, ofs, RSET_GPR); +} + +static void asm_uref(ASMState *as, IRIns *ir) +{ + Reg dest = ra_dest(as, ir, RSET_GPR); + if (irref_isk(ir->op1)) { + GCfunc *fn = ir_kfunc(IR(ir->op1)); + MRef *v = &gcref(fn->l.uvptr[(ir->op2 >> 8)])->uv.v; + emit_lsptr(as, A64I_LDRx, dest, v); + } else { + Reg uv = ra_scratch(as, RSET_GPR); + Reg func = ra_alloc1(as, ir->op1, RSET_GPR); + if (ir->o == IR_UREFC) { + asm_guardcc(as, CC_NE); + emit_n(as, (A64I_CMPx^A64I_K12) | A64F_U12(1), RID_TMP); + emit_opk(as, A64I_ADDx, dest, uv, + (int32_t)offsetof(GCupval, tv), RSET_GPR); + emit_lso(as, A64I_LDRB, RID_TMP, uv, (int32_t)offsetof(GCupval, closed)); + } else { + emit_lso(as, A64I_LDRx, dest, uv, (int32_t)offsetof(GCupval, v)); + } + emit_lso(as, A64I_LDRx, uv, func, + (int32_t)offsetof(GCfuncL, uvptr) + 8*(int32_t)(ir->op2 >> 8)); + } +} + +static void asm_fref(ASMState *as, IRIns *ir) +{ + UNUSED(as); UNUSED(ir); + lua_assert(!ra_used(ir)); +} + +static void asm_strref(ASMState *as, IRIns *ir) +{ + RegSet allow = RSET_GPR; + Reg dest = ra_dest(as, ir, allow); + Reg base = ra_alloc1(as, ir->op1, allow); + IRIns *irr = IR(ir->op2); + int32_t ofs = sizeof(GCstr); + uint32_t m; + rset_clear(allow, base); + if (irref_isk(ir->op2) && (m = emit_isk12(ofs + irr->i))) { + emit_dn(as, A64I_ADDx^m, dest, base); + } else { + emit_dn(as, (A64I_ADDx^A64I_K12) | A64F_U12(ofs), dest, dest); + emit_dnm(as, A64I_ADDx, dest, base, ra_alloc1(as, ir->op2, allow)); + } +} + +/* -- Loads and stores ---------------------------------------------------- */ + +static A64Ins asm_fxloadins(IRIns *ir) +{ + switch (irt_type(ir->t)) { + case IRT_I8: return A64I_LDRB ^ A64I_LS_S; + case IRT_U8: return A64I_LDRB; + case IRT_I16: return A64I_LDRH ^ A64I_LS_S; + case IRT_U16: return A64I_LDRH; + case IRT_NUM: return A64I_LDRd; + case IRT_FLOAT: return A64I_LDRs; + default: return irt_is64(ir->t) ? A64I_LDRx : A64I_LDRw; + } +} + +static A64Ins asm_fxstoreins(IRIns *ir) +{ + switch (irt_type(ir->t)) { + case IRT_I8: case IRT_U8: return A64I_STRB; + case IRT_I16: case IRT_U16: return A64I_STRH; + case IRT_NUM: return A64I_STRd; + case IRT_FLOAT: return A64I_STRs; + default: return irt_is64(ir->t) ? A64I_STRx : A64I_STRw; + } +} + +static void asm_fload(ASMState *as, IRIns *ir) +{ + Reg dest = ra_dest(as, ir, RSET_GPR); + Reg idx; + A64Ins ai = asm_fxloadins(ir); + int32_t ofs; + if (ir->op1 == REF_NIL) { + idx = RID_GL; + ofs = (ir->op2 << 2) - GG_OFS(g); + } else { + idx = ra_alloc1(as, ir->op1, RSET_GPR); + if (ir->op2 == IRFL_TAB_ARRAY) { + ofs = asm_fuseabase(as, ir->op1); + if (ofs) { /* Turn the t->array load into an add for colocated arrays. */ + emit_dn(as, (A64I_ADDx^A64I_K12) | A64F_U12(ofs), dest, idx); + return; + } + } + ofs = field_ofs[ir->op2]; + } + emit_lso(as, ai, (dest & 31), idx, ofs); +} + +static void asm_fstore(ASMState *as, IRIns *ir) +{ + if (ir->r != RID_SINK) { + Reg src = ra_alloc1(as, ir->op2, RSET_GPR); + IRIns *irf = IR(ir->op1); + Reg idx = ra_alloc1(as, irf->op1, rset_exclude(RSET_GPR, src)); + int32_t ofs = field_ofs[irf->op2]; + emit_lso(as, asm_fxstoreins(ir), (src & 31), idx, ofs); + } +} + +static void asm_xload(ASMState *as, IRIns *ir) +{ + Reg dest = ra_dest(as, ir, irt_isfp(ir->t) ? RSET_FPR : RSET_GPR); + lua_assert(!(ir->op2 & IRXLOAD_UNALIGNED)); + asm_fusexref(as, asm_fxloadins(ir), dest, ir->op1, RSET_GPR); +} + +static void asm_xstore(ASMState *as, IRIns *ir) +{ + if (ir->r != RID_SINK) { + Reg src = ra_alloc1(as, ir->op2, irt_isfp(ir->t) ? RSET_FPR : RSET_GPR); + asm_fusexref(as, asm_fxstoreins(ir), src, ir->op1, + rset_exclude(RSET_GPR, src)); + } +} + +static void asm_ahuvload(ASMState *as, IRIns *ir) +{ + Reg idx, tmp, type; + int32_t ofs = 0; + RegSet gpr = RSET_GPR, allow = irt_isnum(ir->t) ? RSET_FPR : RSET_GPR; + lua_assert(irt_isnum(ir->t) || irt_ispri(ir->t) || irt_isaddr(ir->t) || + irt_isint(ir->t)); + if (ra_used(ir)) { + Reg dest = ra_dest(as, ir, allow); + tmp = irt_isnum(ir->t) ? ra_scratch(as, rset_clear(gpr, dest)) : dest; + if (irt_isaddr(ir->t)) { + emit_dn(as, A64I_ANDx^emit_isk13(LJ_GCVMASK, 1), dest, dest); + } else if (irt_isnum(ir->t)) { + emit_dn(as, A64I_FMOV_D_R, (dest & 31), tmp); + } else if (irt_isint(ir->t)) { + emit_dm(as, A64I_MOVw, dest, dest); + } + } else { + tmp = ra_scratch(as, gpr); + } + type = ra_scratch(as, rset_clear(gpr, tmp)); + idx = asm_fuseahuref(as, ir->op1, &ofs, rset_clear(gpr, type), A64I_LDRx); + /* Always do the type check, even if the load result is unused. */ + asm_guardcc(as, irt_isnum(ir->t) ? CC_LS : CC_NE); + if (irt_type(ir->t) >= IRT_NUM) { + lua_assert(irt_isinteger(ir->t) || irt_isnum(ir->t)); + emit_nm(as, A64I_CMPx | A64F_SH(A64SH_LSR, 32), + ra_allock(as, LJ_TISNUM << 15, rset_exclude(gpr, idx)), tmp); + } else if (irt_isaddr(ir->t)) { + emit_n(as, (A64I_CMNx^A64I_K12) | A64F_U12(-irt_toitype(ir->t)), type); + emit_dn(as, A64I_ASRx | A64F_IMMR(47), type, tmp); + } else if (irt_isnil(ir->t)) { + emit_n(as, (A64I_CMNx^A64I_K12) | A64F_U12(1), tmp); + } else { + emit_nm(as, A64I_CMPx | A64F_SH(A64SH_LSR, 32), + ra_allock(as, (irt_toitype(ir->t) << 15) | 0x7fff, allow), tmp); + } + if (ofs & FUSE_REG) + emit_dnm(as, (A64I_LDRx^A64I_LS_R)|A64I_LS_UXTWx|A64I_LS_SH, tmp, idx, (ofs & 31)); + else + emit_lso(as, A64I_LDRx, tmp, idx, ofs); +} + +static void asm_ahustore(ASMState *as, IRIns *ir) +{ + if (ir->r != RID_SINK) { + RegSet allow = RSET_GPR; + Reg idx, src = RID_NONE, tmp = RID_TMP, type = RID_NONE; + int32_t ofs = 0; + if (irt_isnum(ir->t)) { + src = ra_alloc1(as, ir->op2, RSET_FPR); + idx = asm_fuseahuref(as, ir->op1, &ofs, allow, A64I_STRd); + if (ofs & FUSE_REG) + emit_dnm(as, (A64I_STRd^A64I_LS_R)|A64I_LS_UXTWx|A64I_LS_SH, (src & 31), idx, (ofs &31)); + else + emit_lso(as, A64I_STRd, (src & 31), idx, ofs); + } else { + if (!irt_ispri(ir->t)) { + src = ra_alloc1(as, ir->op2, allow); + rset_clear(allow, src); + if (irt_isinteger(ir->t)) + type = ra_allock(as, (uint64_t)(int32_t)LJ_TISNUM << 47, allow); + else + type = ra_allock(as, irt_toitype(ir->t), allow); + } else { + tmp = type = ra_allock(as, ~((int64_t)~irt_toitype(ir->t)<<47), allow); + } + idx = asm_fuseahuref(as, ir->op1, &ofs, rset_exclude(allow, type), + A64I_STRx); + if (ofs & FUSE_REG) + emit_dnm(as, (A64I_STRx^A64I_LS_R)|A64I_LS_UXTWx|A64I_LS_SH, tmp, idx, (ofs & 31)); + else + emit_lso(as, A64I_STRx, tmp, idx, ofs); + if (ra_hasreg(src)) { + if (irt_isinteger(ir->t)) { + emit_dnm(as, A64I_ADDx | A64F_EX(A64EX_UXTW), tmp, type, src); + } else { + emit_dnm(as, A64I_ADDx | A64F_SH(A64SH_LSL, 47), tmp, src, type); + } + } + } + } +} + +static void asm_sload(ASMState *as, IRIns *ir) +{ + int32_t ofs = 8*((int32_t)ir->op1-2); + IRType1 t = ir->t; + Reg dest = RID_NONE, base; + RegSet allow = RSET_GPR; + lua_assert(!(ir->op2 & IRSLOAD_PARENT)); /* Handled by asm_head_side(). */ + lua_assert(irt_isguard(t) || !(ir->op2 & IRSLOAD_TYPECHECK)); + if ((ir->op2 & IRSLOAD_CONVERT) && irt_isguard(t) && irt_isint(t)) { + dest = ra_scratch(as, RSET_FPR); + asm_tointg(as, ir, dest); + t.irt = IRT_NUM; /* Continue with a regular number type check. */ + } else if (ra_used(ir)) { + Reg tmp = RID_NONE; + if ((ir->op2 & IRSLOAD_CONVERT)) + tmp = ra_scratch(as, irt_isint(t) ? RSET_FPR : RSET_GPR); + lua_assert((irt_isnum(t)) || irt_isint(t) || irt_isaddr(t)); + dest = ra_dest(as, ir, irt_isnum(t) ? RSET_FPR : allow); + base = ra_alloc1(as, REF_BASE, rset_clear(allow, dest)); + if (irt_isaddr(t)) { + emit_dn(as, A64I_ANDx^emit_isk13(LJ_GCVMASK, 1), dest, dest); + } else if ((ir->op2 & IRSLOAD_CONVERT)) { + if (irt_isint(t)) { + emit_dn(as, A64I_FCVT_S32_F64, dest, (tmp & 31)); + /* If value is already loaded for type check, move it to FPR. */ + if ((ir->op2 & IRSLOAD_TYPECHECK)) + emit_dn(as, A64I_FMOV_D_R, (tmp & 31), dest); + else + dest = tmp; + t.irt = IRT_NUM; /* Check for original type. */ + } else { + emit_dn(as, A64I_FCVT_F64_S32, (dest & 31), tmp); + dest = tmp; + t.irt = IRT_INT; /* Check for original type. */ + } + } else if (irt_isint(t) && (ir->op2 & IRSLOAD_TYPECHECK)) { + emit_dm(as, A64I_MOVw, dest, dest); + } + goto dotypecheck; + } + base = ra_alloc1(as, REF_BASE, allow); +dotypecheck: + rset_clear(allow, base); + if ((ir->op2 & IRSLOAD_TYPECHECK)) { + Reg tmp; + if (ra_hasreg(dest) && rset_test(RSET_GPR, dest)) { + tmp = dest; + } else { + tmp = ra_scratch(as, allow); + rset_clear(allow, tmp); + } + if (irt_isnum(t) && !(ir->op2 & IRSLOAD_CONVERT)) + emit_dn(as, A64I_FMOV_D_R, (dest & 31), tmp); + /* Need type check, even if the load result is unused. */ + asm_guardcc(as, irt_isnum(t) ? CC_LS : CC_NE); + if (irt_type(t) >= IRT_NUM) { + lua_assert(irt_isinteger(t) || irt_isnum(t)); + emit_nm(as, A64I_CMPx | A64F_SH(A64SH_LSR, 32), + ra_allock(as, LJ_TISNUM << 15, allow), tmp); + } else if (irt_isnil(t)) { + emit_n(as, (A64I_CMNx^A64I_K12) | A64F_U12(1), tmp); + } else if (irt_ispri(t)) { + emit_nm(as, A64I_CMPx, + ra_allock(as, ~((int64_t)~irt_toitype(t) << 47) , allow), tmp); + } else { + Reg type = ra_scratch(as, allow); + emit_n(as, (A64I_CMNx^A64I_K12) | A64F_U12(-irt_toitype(t)), type); + emit_dn(as, A64I_ASRx | A64F_IMMR(47), type, tmp); + } + emit_lso(as, A64I_LDRx, tmp, base, ofs); + return; + } + if (ra_hasreg(dest)) { + emit_lso(as, irt_isnum(t) ? A64I_LDRd : + (irt_isint(t) ? A64I_LDRw : A64I_LDRx), (dest & 31), base, + ofs ^ ((LJ_BE && irt_isint(t) ? 4 : 0))); + } +} + +/* -- Allocations --------------------------------------------------------- */ + +#if LJ_HASFFI +static void asm_cnew(ASMState *as, IRIns *ir) +{ + CTState *cts = ctype_ctsG(J2G(as->J)); + CTypeID id = (CTypeID)IR(ir->op1)->i; + CTSize sz; + CTInfo info = lj_ctype_info(cts, id, &sz); + const CCallInfo *ci = &lj_ir_callinfo[IRCALL_lj_mem_newgco]; + IRRef args[4]; + RegSet allow = (RSET_GPR & ~RSET_SCRATCH); + lua_assert(sz != CTSIZE_INVALID || (ir->o == IR_CNEW && ir->op2 != REF_NIL)); + + as->gcsteps++; + asm_setupresult(as, ir, ci); /* GCcdata * */ + /* Initialize immutable cdata object. */ + if (ir->o == IR_CNEWI) { + int32_t ofs = sizeof(GCcdata); + Reg r = ra_alloc1(as, ir->op2, allow); + lua_assert(sz == 4 || sz == 8); + emit_lso(as, sz == 8 ? A64I_STRx : A64I_STRw, r, RID_RET, ofs); + } else if (ir->op2 != REF_NIL) { /* Create VLA/VLS/aligned cdata. */ + ci = &lj_ir_callinfo[IRCALL_lj_cdata_newv]; + args[0] = ASMREF_L; /* lua_State *L */ + args[1] = ir->op1; /* CTypeID id */ + args[2] = ir->op2; /* CTSize sz */ + args[3] = ASMREF_TMP1; /* CTSize align */ + asm_gencall(as, ci, args); + emit_loadi(as, ra_releasetmp(as, ASMREF_TMP1), (int32_t)ctype_align(info)); + return; + } + + /* Initialize gct and ctypeid. lj_mem_newgco() already sets marked. */ + { + Reg r = (id < 65536) ? RID_X1 : ra_allock(as, id, allow); + emit_lso(as, A64I_STRB, RID_TMP, RID_RET, offsetof(GCcdata, gct)); + emit_lso(as, A64I_STRH, r, RID_RET, offsetof(GCcdata, ctypeid)); + emit_d(as, A64I_MOVZw | A64F_U16(~LJ_TCDATA), RID_TMP); + if (id < 65536) emit_d(as, A64I_MOVZw | A64F_U16(id), RID_X1); + } + args[0] = ASMREF_L; /* lua_State *L */ + args[1] = ASMREF_TMP1; /* MSize size */ + asm_gencall(as, ci, args); + ra_allockreg(as, (int32_t)(sz+sizeof(GCcdata)), + ra_releasetmp(as, ASMREF_TMP1)); +} +#else +#define asm_cnew(as, ir) ((void)0) +#endif + +/* -- Write barriers ------------------------------------------------------ */ + +static void asm_tbar(ASMState *as, IRIns *ir) +{ + Reg tab = ra_alloc1(as, ir->op1, RSET_GPR); + Reg link = ra_scratch(as, rset_exclude(RSET_GPR, tab)); + Reg gr = ra_allock(as, i64ptr(J2G(as->J)), + rset_exclude(rset_exclude(RSET_GPR, tab), link)); + Reg mark = RID_TMP; + MCLabel l_end = emit_label(as); + emit_lso(as, A64I_STRx, link, tab, (int32_t)offsetof(GCtab, gclist)); + emit_lso(as, A64I_STRB, mark, tab, (int32_t)offsetof(GCtab, marked)); + emit_lso(as, A64I_STRx, tab, gr, + (int32_t)offsetof(global_State, gc.grayagain)); + emit_dn(as, A64I_ANDw^emit_isk13(~LJ_GC_BLACK, 0), mark, mark); + emit_lso(as, A64I_LDRx, link, gr, + (int32_t)offsetof(global_State, gc.grayagain)); + emit_cond_branch(as, CC_EQ, l_end); + emit_n(as, A64I_TSTw^emit_isk13(LJ_GC_BLACK, 0), mark); + emit_lso(as, A64I_LDRB, mark, tab, (int32_t)offsetof(GCtab, marked)); +} + +static void asm_obar(ASMState *as, IRIns *ir) +{ + const CCallInfo *ci = &lj_ir_callinfo[IRCALL_lj_gc_barrieruv]; + IRRef args[2]; + MCLabel l_end; + RegSet allow = RSET_GPR; + Reg obj, val, tmp; + /* No need for other object barriers (yet). */ + lua_assert(IR(ir->op1)->o == IR_UREFC); + ra_evictset(as, RSET_SCRATCH); + l_end = emit_label(as); + args[0] = ASMREF_TMP1; /* global_State *g */ + args[1] = ir->op1; /* TValue *tv */ + asm_gencall(as, ci, args); + ra_allockreg(as, i64ptr(J2G(as->J)), ra_releasetmp(as, ASMREF_TMP1) ); + obj = IR(ir->op1)->r; + tmp = ra_scratch(as, rset_exclude(allow, obj)); + emit_cond_branch(as, CC_EQ, l_end); + emit_n(as, A64I_TSTw^emit_isk13(LJ_GC_BLACK, 0), tmp); + emit_cond_branch(as, CC_EQ, l_end); + emit_n(as, A64I_TSTw^emit_isk13(LJ_GC_WHITES, 0), RID_TMP); + val = ra_alloc1(as, ir->op2, rset_exclude(RSET_GPR, obj)); + emit_lso(as, A64I_LDRB, tmp, obj, + (int32_t)offsetof(GCupval, marked)-(int32_t)offsetof(GCupval, tv)); + emit_lso(as, A64I_LDRB, RID_TMP, val, (int32_t)offsetof(GChead, marked)); +} + +/* -- Arithmetic and logic operations ------------------------------------- */ + +static void asm_fparith(ASMState *as, IRIns *ir, A64Ins ai) +{ + Reg dest = ra_dest(as, ir, RSET_FPR); + Reg right, left = ra_alloc2(as, ir, RSET_FPR); + right = (left >> 8); left &= 255; + emit_dnm(as, ai, (dest & 31), (left & 31), (right & 31)); +} + +static void asm_fpunary(ASMState *as, IRIns *ir, A64Ins ai) +{ + Reg dest = ra_dest(as, ir, RSET_FPR); + Reg left = ra_hintalloc(as, ir->op1, dest, RSET_FPR); + emit_dn(as, ai, (dest & 31), (left & 31)); +} + +static void asm_fpmath(ASMState *as, IRIns *ir) +{ + IRFPMathOp fpm = (IRFPMathOp)ir->op2; + if (fpm == IRFPM_SQRT) { + asm_fpunary(as, ir, A64I_FSQRTd); + } else if (fpm <= IRFPM_TRUNC) { + asm_fpunary(as, ir, fpm == IRFPM_FLOOR ? A64I_FRINTMd : + fpm == IRFPM_CEIL ? A64I_FRINTPd : A64I_FRINTZd); + } else if (fpm == IRFPM_EXP2 && asm_fpjoin_pow(as, ir)) { + return; + } else { + asm_callid(as, ir, IRCALL_lj_vm_floor + fpm); + } +} + +static int asm_swapops(ASMState *as, IRRef lref, IRRef rref) +{ + IRIns *ir; + if (irref_isk(rref)) + return 0; /* Don't swap constants to the left. */ + if (irref_isk(lref)) + return 1; /* But swap constants to the right. */ + ir = IR(rref); + if ((ir->o >= IR_BSHL && ir->o <= IR_BSAR) || + (ir->o == IR_ADD && ir->op1 == ir->op2) || + (ir->o == IR_CONV && ir->op2 == ((IRT_I64<o >= IR_BSHL && ir->o <= IR_BSAR) || + (ir->o == IR_ADD && ir->op1 == ir->op2) || + (ir->o == IR_CONV && ir->op2 == ((IRT_I64<op1, rref = ir->op2; + Reg left, dest = ra_dest(as, ir, RSET_GPR); + uint32_t m; + if ((ai & ~A64I_S) != A64I_SUBw && asm_swapops(as, lref, rref)) { + IRRef tmp = lref; lref = rref; rref = tmp; + } + left = ra_hintalloc(as, lref, dest, RSET_GPR); + if (irt_is64(ir->t)) ai |= A64I_X; + m = asm_fuseopm(as, ai, rref, rset_exclude(RSET_GPR, left)); + if (irt_isguard(ir->t)) { /* For IR_ADDOV etc. */ + asm_guardcc(as, CC_VS); + ai |= A64I_S; + } + emit_dn(as, ai^m, dest, left); +} + +static void asm_intop_s(ASMState *as, IRIns *ir, A64Ins ai) +{ + if (as->flagmcp == as->mcp) { /* Drop cmp r, #0. */ + as->flagmcp = NULL; + as->mcp++; + ai |= A64I_S; + } + asm_intop(as, ir, ai); +} + +static void asm_intneg(ASMState *as, IRIns *ir) +{ + Reg dest = ra_dest(as, ir, RSET_GPR); + Reg left = ra_hintalloc(as, ir->op1, dest, RSET_GPR); + emit_dm(as, irt_is64(ir->t) ? A64I_NEGx : A64I_NEGw, dest, left); +} + +/* NYI: use add/shift for MUL(OV) with constants. FOLD only does 2^k. */ +static void asm_intmul(ASMState *as, IRIns *ir) +{ + Reg dest = ra_dest(as, ir, RSET_GPR); + Reg left = ra_alloc1(as, ir->op1, rset_exclude(RSET_GPR, dest)); + Reg right = ra_alloc1(as, ir->op2, rset_exclude(RSET_GPR, left)); + if (irt_isguard(ir->t)) { /* IR_MULOV */ + asm_guardcc(as, CC_NE); + emit_dm(as, A64I_MOVw, dest, dest); /* Zero-extend. */ + emit_nm(as, A64I_CMPw | A64F_SH(A64SH_ASR, 31), RID_TMP, dest); + emit_dn(as, A64I_ASRx | A64F_IMMR(32), RID_TMP, dest); + emit_dnm(as, A64I_SMULL, dest, right, left); + } else { + emit_dnm(as, irt_is64(ir->t) ? A64I_MULx : A64I_MULw, dest, left, right); + } +} + +static void asm_add(ASMState *as, IRIns *ir) +{ + if (irt_isnum(ir->t)) { + if (!asm_fusemadd(as, ir, A64I_FMADDd, A64I_FMADDd)) + asm_fparith(as, ir, A64I_FADDd); + return; + } + asm_intop_s(as, ir, A64I_ADDw); +} + +static void asm_sub(ASMState *as, IRIns *ir) +{ + if (irt_isnum(ir->t)) { + if (!asm_fusemadd(as, ir, A64I_FNMSUBd, A64I_FMSUBd)) + asm_fparith(as, ir, A64I_FSUBd); + return; + } + asm_intop_s(as, ir, A64I_SUBw); +} + +static void asm_mul(ASMState *as, IRIns *ir) +{ + if (irt_isnum(ir->t)) { + asm_fparith(as, ir, A64I_FMULd); + return; + } + asm_intmul(as, ir); +} + +static void asm_div(ASMState *as, IRIns *ir) +{ +#if LJ_HASFFI + if (!irt_isnum(ir->t)) + asm_callid(as, ir, irt_isi64(ir->t) ? IRCALL_lj_carith_divi64 : + IRCALL_lj_carith_divu64); + else +#endif + asm_fparith(as, ir, A64I_FDIVd); +} + +static void asm_pow(ASMState *as, IRIns *ir) +{ +#if LJ_HASFFI + if (!irt_isnum(ir->t)) + asm_callid(as, ir, irt_isi64(ir->t) ? IRCALL_lj_carith_powi64 : + IRCALL_lj_carith_powu64); + else +#endif + asm_callid(as, ir, IRCALL_lj_vm_powi); +} + +#define asm_addov(as, ir) asm_add(as, ir) +#define asm_subov(as, ir) asm_sub(as, ir) +#define asm_mulov(as, ir) asm_mul(as, ir) + +#define asm_abs(as, ir) asm_fpunary(as, ir, A64I_FABS) +#define asm_atan2(as, ir) asm_callid(as, ir, IRCALL_atan2) +#define asm_ldexp(as, ir) asm_callid(as, ir, IRCALL_ldexp) + +static void asm_mod(ASMState *as, IRIns *ir) +{ +#if LJ_HASFFI + if (!irt_isint(ir->t)) + asm_callid(as, ir, irt_isi64(ir->t) ? IRCALL_lj_carith_modi64 : + IRCALL_lj_carith_modu64); + else +#endif + asm_callid(as, ir, IRCALL_lj_vm_modi); +} + +static void asm_neg(ASMState *as, IRIns *ir) +{ + if (irt_isnum(ir->t)) { + asm_fpunary(as, ir, A64I_FNEGd); + return; + } + asm_intneg(as, ir); +} + +static void asm_band(ASMState *as, IRIns *ir) +{ + A64Ins ai = A64I_ANDw; + if (asm_fuseandshift(as, ir)) + return; + if (as->flagmcp == as->mcp) { + /* Try to drop cmp r, #0. */ + as->flagmcp = NULL; + as->mcp++; + ai = A64I_ANDSw; + } + asm_intop(as, ir, ai); +} + +static void asm_borbxor(ASMState *as, IRIns *ir, A64Ins ai) +{ + IRRef lref = ir->op1, rref = ir->op2; + IRIns *irl = IR(lref), *irr = IR(rref); + if ((canfuse(as, irl) && irl->o == IR_BNOT && !irref_isk(rref)) || + (canfuse(as, irr) && irr->o == IR_BNOT && !irref_isk(lref))) { + Reg left, dest = ra_dest(as, ir, RSET_GPR); + uint32_t m; + if (irl->o == IR_BNOT) { + IRRef tmp = lref; lref = rref; rref = tmp; + } + left = ra_alloc1(as, lref, RSET_GPR); + ai |= A64I_ON; + if (irt_is64(ir->t)) ai |= A64I_X; + m = asm_fuseopm(as, ai, IR(rref)->op1, rset_exclude(RSET_GPR, left)); + emit_dn(as, ai^m, dest, left); + } else { + asm_intop(as, ir, ai); + } +} + +static void asm_bor(ASMState *as, IRIns *ir) +{ + if (asm_fuseorshift(as, ir)) + return; + asm_borbxor(as, ir, A64I_ORRw); +} + +#define asm_bxor(as, ir) asm_borbxor(as, ir, A64I_EORw) + +static void asm_bnot(ASMState *as, IRIns *ir) +{ + A64Ins ai = A64I_MVNw; + Reg dest = ra_dest(as, ir, RSET_GPR); + uint32_t m = asm_fuseopm(as, ai, ir->op1, RSET_GPR); + if (irt_is64(ir->t)) ai |= A64I_X; + emit_d(as, ai^m, dest); +} + +static void asm_bswap(ASMState *as, IRIns *ir) +{ + Reg dest = ra_dest(as, ir, RSET_GPR); + Reg left = ra_alloc1(as, ir->op1, RSET_GPR); + emit_dn(as, irt_is64(ir->t) ? A64I_REVx : A64I_REVw, dest, left); +} + +static void asm_bitshift(ASMState *as, IRIns *ir, A64Ins ai, A64Shift sh) +{ + int32_t shmask = irt_is64(ir->t) ? 63 : 31; + if (irref_isk(ir->op2)) { /* Constant shifts. */ + Reg left, dest = ra_dest(as, ir, RSET_GPR); + int32_t shift = (IR(ir->op2)->i & shmask); + IRIns *irl = IR(ir->op1); + if (shmask == 63) ai += A64I_UBFMx - A64I_UBFMw; + + /* Fuse BSHL + BSHR/BSAR into UBFM/SBFM aka UBFX/SBFX/UBFIZ/SBFIZ. */ + if ((sh == A64SH_LSR || sh == A64SH_ASR) && canfuse(as, irl)) { + if (irl->o == IR_BSHL && irref_isk(irl->op2)) { + int32_t shift2 = (IR(irl->op2)->i & shmask); + shift = ((shift - shift2) & shmask); + shmask -= shift2; + ir = irl; + } + } + + left = ra_alloc1(as, ir->op1, RSET_GPR); + switch (sh) { + case A64SH_LSL: + emit_dn(as, ai | A64F_IMMS(shmask-shift) | + A64F_IMMR((shmask-shift+1)&shmask), dest, left); + break; + case A64SH_LSR: case A64SH_ASR: + emit_dn(as, ai | A64F_IMMS(shmask) | A64F_IMMR(shift), dest, left); + break; + case A64SH_ROR: + emit_dnm(as, ai | A64F_IMMS(shift), dest, left, left); + break; + } + } else { /* Variable-length shifts. */ + Reg dest = ra_dest(as, ir, RSET_GPR); + Reg left = ra_alloc1(as, ir->op1, RSET_GPR); + Reg right = ra_alloc1(as, ir->op2, rset_exclude(RSET_GPR, left)); + emit_dnm(as, (shmask == 63 ? A64I_SHRx : A64I_SHRw) | A64F_BSH(sh), dest, left, right); + } +} + +#define asm_bshl(as, ir) asm_bitshift(as, ir, A64I_UBFMw, A64SH_LSL) +#define asm_bshr(as, ir) asm_bitshift(as, ir, A64I_UBFMw, A64SH_LSR) +#define asm_bsar(as, ir) asm_bitshift(as, ir, A64I_SBFMw, A64SH_ASR) +#define asm_bror(as, ir) asm_bitshift(as, ir, A64I_EXTRw, A64SH_ROR) +#define asm_brol(as, ir) lua_assert(0) + +static void asm_intmin_max(ASMState *as, IRIns *ir, A64CC cc) +{ + Reg dest = ra_dest(as, ir, RSET_GPR); + Reg left = ra_hintalloc(as, ir->op1, dest, RSET_GPR); + Reg right = ra_alloc1(as, ir->op2, rset_exclude(RSET_GPR, left)); + emit_dnm(as, A64I_CSELw|A64F_CC(cc), dest, left, right); + emit_nm(as, A64I_CMPw, left, right); +} + +static void asm_fpmin_max(ASMState *as, IRIns *ir, A64CC fcc) +{ + Reg dest = (ra_dest(as, ir, RSET_FPR) & 31); + Reg right, left = ra_alloc2(as, ir, RSET_FPR); + right = ((left >> 8) & 31); left &= 31; + emit_dnm(as, A64I_FCSELd | A64F_CC(fcc), dest, left, right); + emit_nm(as, A64I_FCMPd, left, right); +} + +static void asm_min_max(ASMState *as, IRIns *ir, A64CC cc, A64CC fcc) +{ + if (irt_isnum(ir->t)) + asm_fpmin_max(as, ir, fcc); + else + asm_intmin_max(as, ir, cc); +} + +#define asm_max(as, ir) asm_min_max(as, ir, CC_GT, CC_HI) +#define asm_min(as, ir) asm_min_max(as, ir, CC_LT, CC_LO) + +/* -- Comparisons --------------------------------------------------------- */ + +/* Map of comparisons to flags. ORDER IR. */ +static const uint8_t asm_compmap[IR_ABC+1] = { + /* op FP swp int cc FP cc */ + /* LT */ CC_GE + (CC_HS << 4), + /* GE x */ CC_LT + (CC_HI << 4), + /* LE */ CC_GT + (CC_HI << 4), + /* GT x */ CC_LE + (CC_HS << 4), + /* ULT x */ CC_HS + (CC_LS << 4), + /* UGE */ CC_LO + (CC_LO << 4), + /* ULE x */ CC_HI + (CC_LO << 4), + /* UGT */ CC_LS + (CC_LS << 4), + /* EQ */ CC_NE + (CC_NE << 4), + /* NE */ CC_EQ + (CC_EQ << 4), + /* ABC */ CC_LS + (CC_LS << 4) /* Same as UGT. */ +}; + +/* FP comparisons. */ +static void asm_fpcomp(ASMState *as, IRIns *ir) +{ + Reg left, right; + A64Ins ai; + int swp = ((ir->o ^ (ir->o >> 2)) & ~(ir->o >> 3) & 1); + if (!swp && irref_isk(ir->op2) && ir_knum(IR(ir->op2))->u64 == 0) { + left = (ra_alloc1(as, ir->op1, RSET_FPR) & 31); + right = 0; + ai = A64I_FCMPZd; + } else { + left = ra_alloc2(as, ir, RSET_FPR); + if (swp) { + right = (left & 31); left = ((left >> 8) & 31); + } else { + right = ((left >> 8) & 31); left &= 31; + } + ai = A64I_FCMPd; + } + asm_guardcc(as, (asm_compmap[ir->o] >> 4)); + emit_nm(as, ai, left, right); +} + +/* Integer comparisons. */ +static void asm_intcomp(ASMState *as, IRIns *ir) +{ + A64CC oldcc, cc = (asm_compmap[ir->o] & 15); + A64Ins ai = irt_is64(ir->t) ? A64I_CMPx : A64I_CMPw; + IRRef lref = ir->op1, rref = ir->op2; + Reg left; + uint32_t m; + int cmpprev0 = 0; + lua_assert(irt_is64(ir->t) || irt_isint(ir->t) || + irt_isu32(ir->t) || irt_isaddr(ir->t) || irt_isu8(ir->t)); + if (asm_swapops(as, lref, rref)) { + IRRef tmp = lref; lref = rref; rref = tmp; + if (cc >= CC_GE) cc ^= 7; /* LT <-> GT, LE <-> GE */ + else if (cc > CC_NE) cc ^= 11; /* LO <-> HI, LS <-> HS */ + } + oldcc = cc; + if (irref_isk(rref) && get_k64val(IR(rref)) == 0) { + IRIns *irl = IR(lref); + if (cc == CC_GE) cc = CC_PL; + else if (cc == CC_LT) cc = CC_MI; + else if (cc > CC_NE) goto nocombine; /* Other conds don't work with tst. */ + cmpprev0 = (irl+1 == ir); + /* Combine and-cmp-bcc into tbz/tbnz or and-cmp into tst. */ + if (cmpprev0 && irl->o == IR_BAND && !ra_used(irl)) { + IRRef blref = irl->op1, brref = irl->op2; + uint32_t m2 = 0; + Reg bleft; + if (asm_swapops(as, blref, brref)) { + Reg tmp = blref; blref = brref; brref = tmp; + } + if (irref_isk(brref)) { + uint64_t k = get_k64val(IR(brref)); + if (k && !(k & (k-1)) && (cc == CC_EQ || cc == CC_NE)) { + asm_guardtnb(as, cc == CC_EQ ? A64I_TBZ : A64I_TBNZ, + ra_alloc1(as, blref, RSET_GPR), emit_ctz64(k)); + return; + } + m2 = emit_isk13(k, irt_is64(irl->t)); + } + bleft = ra_alloc1(as, blref, RSET_GPR); + ai = (irt_is64(irl->t) ? A64I_TSTx : A64I_TSTw); + if (!m2) + m2 = asm_fuseopm(as, ai, brref, rset_exclude(RSET_GPR, bleft)); + asm_guardcc(as, cc); + emit_n(as, ai^m2, bleft); + return; + } + if (cc == CC_EQ || cc == CC_NE) { + /* Combine cmp-bcc into cbz/cbnz. */ + ai = cc == CC_EQ ? A64I_CBZ : A64I_CBNZ; + if (irt_is64(ir->t)) ai |= A64I_X; + asm_guardcnb(as, ai, ra_alloc1(as, lref, RSET_GPR)); + return; + } + } +nocombine: + left = ra_alloc1(as, lref, RSET_GPR); + m = asm_fuseopm(as, ai, rref, rset_exclude(RSET_GPR, left)); + asm_guardcc(as, cc); + emit_n(as, ai^m, left); + /* Signed comparison with zero and referencing previous ins? */ + if (cmpprev0 && (oldcc <= CC_NE || oldcc >= CC_GE)) + as->flagmcp = as->mcp; /* Allow elimination of the compare. */ +} + +static void asm_comp(ASMState *as, IRIns *ir) +{ + if (irt_isnum(ir->t)) + asm_fpcomp(as, ir); + else + asm_intcomp(as, ir); +} + +#define asm_equal(as, ir) asm_comp(as, ir) + +/* -- Support for 64 bit ops in 32 bit mode ------------------------------- */ + +/* Hiword op of a split 64 bit op. Previous op must be the loword op. */ +static void asm_hiop(ASMState *as, IRIns *ir) +{ + UNUSED(as); UNUSED(ir); lua_assert(0); /* Unused on 64 bit. */ +} + +/* -- Profiling ----------------------------------------------------------- */ + +static void asm_prof(ASMState *as, IRIns *ir) +{ + uint32_t k = emit_isk13(HOOK_PROFILE, 0); + lua_assert(k != 0); + UNUSED(ir); + asm_guardcc(as, CC_NE); + emit_n(as, A64I_TSTw^k, RID_TMP); + emit_lsptr(as, A64I_LDRB, RID_TMP, (void *)&J2G(as->J)->hookmask); +} + +/* -- Stack handling ------------------------------------------------------ */ + +/* Check Lua stack size for overflow. Use exit handler as fallback. */ +static void asm_stack_check(ASMState *as, BCReg topslot, + IRIns *irp, RegSet allow, ExitNo exitno) +{ + Reg pbase; + uint32_t k; + if (irp) { + if (!ra_hasspill(irp->s)) { + pbase = irp->r; + lua_assert(ra_hasreg(pbase)); + } else if (allow) { + pbase = rset_pickbot(allow); + } else { + pbase = RID_RET; + emit_lso(as, A64I_LDRx, RID_RET, RID_SP, 0); /* Restore temp register. */ + } + } else { + pbase = RID_BASE; + } + emit_cond_branch(as, CC_LS, asm_exitstub_addr(as, exitno)); + k = emit_isk12((8*topslot)); + lua_assert(k); + emit_n(as, A64I_CMPx^k, RID_TMP); + emit_dnm(as, A64I_SUBx, RID_TMP, RID_TMP, pbase); + emit_lso(as, A64I_LDRx, RID_TMP, RID_TMP, + (int32_t)offsetof(lua_State, maxstack)); + if (irp) { /* Must not spill arbitrary registers in head of side trace. */ + if (ra_hasspill(irp->s)) + emit_lso(as, A64I_LDRx, pbase, RID_SP, sps_scale(irp->s)); + emit_lso(as, A64I_LDRx, RID_TMP, RID_GL, glofs(as, &J2G(as->J)->cur_L)); + if (ra_hasspill(irp->s) && !allow) + emit_lso(as, A64I_STRx, RID_RET, RID_SP, 0); /* Save temp register. */ + } else { + emit_getgl(as, RID_TMP, cur_L); + } +} + +/* Restore Lua stack from on-trace state. */ +static void asm_stack_restore(ASMState *as, SnapShot *snap) +{ + SnapEntry *map = &as->T->snapmap[snap->mapofs]; +#ifdef LUA_USE_ASSERT + SnapEntry *flinks = &as->T->snapmap[snap_nextofs(as->T, snap)-1-LJ_FR2]; +#endif + MSize n, nent = snap->nent; + /* Store the value of all modified slots to the Lua stack. */ + for (n = 0; n < nent; n++) { + SnapEntry sn = map[n]; + BCReg s = snap_slot(sn); + int32_t ofs = 8*((int32_t)s-1-LJ_FR2); + IRRef ref = snap_ref(sn); + IRIns *ir = IR(ref); + if ((sn & SNAP_NORESTORE)) + continue; + if (irt_isnum(ir->t)) { + Reg src = ra_alloc1(as, ref, RSET_FPR); + emit_lso(as, A64I_STRd, (src & 31), RID_BASE, ofs); + } else { + asm_tvstore64(as, RID_BASE, ofs, ref); + } + checkmclim(as); + } + lua_assert(map + nent == flinks); +} + +/* -- GC handling --------------------------------------------------------- */ + +/* Check GC threshold and do one or more GC steps. */ +static void asm_gc_check(ASMState *as) +{ + const CCallInfo *ci = &lj_ir_callinfo[IRCALL_lj_gc_step_jit]; + IRRef args[2]; + MCLabel l_end; + Reg tmp1, tmp2; + ra_evictset(as, RSET_SCRATCH); + l_end = emit_label(as); + /* Exit trace if in GCSatomic or GCSfinalize. Avoids syncing GC objects. */ + asm_guardcnb(as, A64I_CBNZ, RID_RET); /* Assumes asm_snap_prep() is done. */ + args[0] = ASMREF_TMP1; /* global_State *g */ + args[1] = ASMREF_TMP2; /* MSize steps */ + asm_gencall(as, ci, args); + tmp1 = ra_releasetmp(as, ASMREF_TMP1); + tmp2 = ra_releasetmp(as, ASMREF_TMP2); + emit_loadi(as, tmp2, as->gcsteps); + /* Jump around GC step if GC total < GC threshold. */ + emit_cond_branch(as, CC_LS, l_end); + emit_nm(as, A64I_CMPx, RID_TMP, tmp2); + emit_lso(as, A64I_LDRx, tmp2, tmp1, + (int32_t)offsetof(global_State, gc.threshold)); + emit_lso(as, A64I_LDRx, RID_TMP, tmp1, + (int32_t)offsetof(global_State, gc.total)); + ra_allockreg(as, i64ptr(J2G(as->J)), tmp1); + as->gcsteps = 0; + checkmclim(as); +} + +/* -- Loop handling ------------------------------------------------------- */ + +/* Fixup the loop branch. */ +static void asm_loop_fixup(ASMState *as) +{ + MCode *p = as->mctop; + MCode *target = as->mcp; + if (as->loopinv) { /* Inverted loop branch? */ + uint32_t mask = (p[-2] & 0x7e000000) == 0x36000000 ? 0x3fffu : 0x7ffffu; + ptrdiff_t delta = target - (p - 2); + /* asm_guard* already inverted the bcc/tnb/cnb and patched the final b. */ + p[-2] |= ((uint32_t)delta & mask) << 5; + } else { + ptrdiff_t delta = target - (p - 1); + p[-1] = A64I_B | A64F_S26(delta); + } +} + +/* -- Head of trace ------------------------------------------------------- */ + +/* Reload L register from g->cur_L. */ +static void asm_head_lreg(ASMState *as) +{ + IRIns *ir = IR(ASMREF_L); + if (ra_used(ir)) { + Reg r = ra_dest(as, ir, RSET_GPR); + emit_getgl(as, r, cur_L); + ra_evictk(as); + } +} + +/* Coalesce BASE register for a root trace. */ +static void asm_head_root_base(ASMState *as) +{ + IRIns *ir; + asm_head_lreg(as); + ir = IR(REF_BASE); + if (ra_hasreg(ir->r) && (rset_test(as->modset, ir->r) || irt_ismarked(ir->t))) + ra_spill(as, ir); + ra_destreg(as, ir, RID_BASE); +} + +/* Coalesce BASE register for a side trace. */ +static RegSet asm_head_side_base(ASMState *as, IRIns *irp, RegSet allow) +{ + IRIns *ir; + asm_head_lreg(as); + ir = IR(REF_BASE); + if (ra_hasreg(ir->r) && (rset_test(as->modset, ir->r) || irt_ismarked(ir->t))) + ra_spill(as, ir); + if (ra_hasspill(irp->s)) { + rset_clear(allow, ra_dest(as, ir, allow)); + } else { + Reg r = irp->r; + lua_assert(ra_hasreg(r)); + rset_clear(allow, r); + if (r != ir->r && !rset_test(as->freeset, r)) + ra_restore(as, regcost_ref(as->cost[r])); + ra_destreg(as, ir, r); + } + return allow; +} + +/* -- Tail of trace ------------------------------------------------------- */ + +/* Fixup the tail code. */ +static void asm_tail_fixup(ASMState *as, TraceNo lnk) +{ + MCode *p = as->mctop; + MCode *target; + /* Undo the sp adjustment in BC_JLOOP when exiting to the interpreter. */ + int32_t spadj = as->T->spadjust + (lnk ? 0 : sps_scale(SPS_FIXED)); + if (spadj == 0) { + *--p = A64I_LE(A64I_NOP); + as->mctop = p; + } else { + /* Patch stack adjustment. */ + uint32_t k = emit_isk12(spadj); + lua_assert(k); + p[-2] = (A64I_ADDx^k) | A64F_D(RID_SP) | A64F_N(RID_SP); + } + /* Patch exit branch. */ + target = lnk ? traceref(as->J, lnk)->mcode : (MCode *)lj_vm_exit_interp; + p[-1] = A64I_B | A64F_S26((target-p)+1); +} + +/* Prepare tail of code. */ +static void asm_tail_prep(ASMState *as) +{ + MCode *p = as->mctop - 1; /* Leave room for exit branch. */ + if (as->loopref) { + as->invmcp = as->mcp = p; + } else { + as->mcp = p-1; /* Leave room for stack pointer adjustment. */ + as->invmcp = NULL; + } + *p = 0; /* Prevent load/store merging. */ +} + +/* -- Trace setup --------------------------------------------------------- */ + +/* Ensure there are enough stack slots for call arguments. */ +static Reg asm_setup_call_slots(ASMState *as, IRIns *ir, const CCallInfo *ci) +{ + IRRef args[CCI_NARGS_MAX*2]; + uint32_t i, nargs = CCI_XNARGS(ci); + int nslots = 0, ngpr = REGARG_NUMGPR, nfpr = REGARG_NUMFPR; + asm_collectargs(as, ir, ci, args); + for (i = 0; i < nargs; i++) { + if (args[i] && irt_isfp(IR(args[i])->t)) { + if (nfpr > 0) nfpr--; else nslots += 2; + } else { + if (ngpr > 0) ngpr--; else nslots += 2; + } + } + if (nslots > as->evenspill) /* Leave room for args in stack slots. */ + as->evenspill = nslots; + return REGSP_HINT(RID_RET); +} + +static void asm_setup_target(ASMState *as) +{ + /* May need extra exit for asm_stack_check on side traces. */ + asm_exitstub_setup(as, as->T->nsnap + (as->parent ? 1 : 0)); +} + +#if LJ_BE +/* ARM64 instructions are always little-endian. Swap for ARM64BE. */ +static void asm_mcode_fixup(MCode *mcode, MSize size) +{ + MCode *pe = (MCode *)((char *)mcode + size); + while (mcode < pe) { + MCode ins = *mcode; + *mcode++ = lj_bswap(ins); + } +} +#define LJ_TARGET_MCODE_FIXUP 1 +#endif + +/* -- Trace patching ------------------------------------------------------ */ + +/* Patch exit jumps of existing machine code to a new target. */ +void lj_asm_patchexit(jit_State *J, GCtrace *T, ExitNo exitno, MCode *target) +{ + MCode *p = T->mcode; + MCode *pe = (MCode *)((char *)p + T->szmcode); + MCode *cstart = NULL; + MCode *mcarea = lj_mcode_patch(J, p, 0); + MCode *px = exitstub_trace_addr(T, exitno); + /* Note: this assumes a trace exit is only ever patched once. */ + for (; p < pe; p++) { + /* Look for exitstub branch, replace with branch to target. */ + ptrdiff_t delta = target - p; + MCode ins = A64I_LE(*p); + if ((ins & 0xff000000u) == 0x54000000u && + ((ins ^ ((px-p)<<5)) & 0x00ffffe0u) == 0) { + /* Patch bcc, if within range. */ + if (A64F_S_OK(delta, 19)) { + *p = A64I_LE((ins & 0xff00001fu) | A64F_S19(delta)); + if (!cstart) cstart = p; + } + } else if ((ins & 0xfc000000u) == 0x14000000u && + ((ins ^ (px-p)) & 0x03ffffffu) == 0) { + /* Patch b. */ + lua_assert(A64F_S_OK(delta, 26)); + *p = A64I_LE((ins & 0xfc000000u) | A64F_S26(delta)); + if (!cstart) cstart = p; + } else if ((ins & 0x7e000000u) == 0x34000000u && + ((ins ^ ((px-p)<<5)) & 0x00ffffe0u) == 0) { + /* Patch cbz/cbnz, if within range. */ + if (A64F_S_OK(delta, 19)) { + *p = A64I_LE((ins & 0xff00001fu) | A64F_S19(delta)); + if (!cstart) cstart = p; + } + } else if ((ins & 0x7e000000u) == 0x36000000u && + ((ins ^ ((px-p)<<5)) & 0x0007ffe0u) == 0) { + /* Patch tbz/tbnz, if within range. */ + if (A64F_S_OK(delta, 14)) { + *p = A64I_LE((ins & 0xfff8001fu) | A64F_S14(delta)); + if (!cstart) cstart = p; + } + } + } + { /* Always patch long-range branch in exit stub itself. */ + ptrdiff_t delta = target - px; + lua_assert(A64F_S_OK(delta, 26)); + *px = A64I_B | A64F_S26(delta); + if (!cstart) cstart = px; + } + lj_mcode_sync(cstart, px+1); + lj_mcode_patch(J, mcarea, 1); +} + diff --git a/lib/LuaJIT/lj_asm_dyn.o b/lib/LuaJIT/lj_asm_dyn.o new file mode 100644 index 0000000000000000000000000000000000000000..efdff7f15561738cd88fe53466b02ecc1e921ebc GIT binary patch literal 57712 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 literal 0 HcmV?d00001 diff --git a/lib/LuaJIT/lj_asm_mips.h b/lib/LuaJIT/lj_asm_mips.h new file mode 100644 index 0000000..3a4679b --- /dev/null +++ b/lib/LuaJIT/lj_asm_mips.h @@ -0,0 +1,2659 @@ +/* +** MIPS IR assembler (SSA IR -> machine code). +** Copyright (C) 2005-2017 Mike Pall. See Copyright Notice in luajit.h +*/ + +/* -- Register allocator extensions --------------------------------------- */ + +/* Allocate a register with a hint. */ +static Reg ra_hintalloc(ASMState *as, IRRef ref, Reg hint, RegSet allow) +{ + Reg r = IR(ref)->r; + if (ra_noreg(r)) { + if (!ra_hashint(r) && !iscrossref(as, ref)) + ra_sethint(IR(ref)->r, hint); /* Propagate register hint. */ + r = ra_allocref(as, ref, allow); + } + ra_noweak(as, r); + return r; +} + +/* Allocate a register or RID_ZERO. */ +static Reg ra_alloc1z(ASMState *as, IRRef ref, RegSet allow) +{ + Reg r = IR(ref)->r; + if (ra_noreg(r)) { + if (!(allow & RSET_FPR) && irref_isk(ref) && get_kval(IR(ref)) == 0) + return RID_ZERO; + r = ra_allocref(as, ref, allow); + } else { + ra_noweak(as, r); + } + return r; +} + +/* Allocate two source registers for three-operand instructions. */ +static Reg ra_alloc2(ASMState *as, IRIns *ir, RegSet allow) +{ + IRIns *irl = IR(ir->op1), *irr = IR(ir->op2); + Reg left = irl->r, right = irr->r; + if (ra_hasreg(left)) { + ra_noweak(as, left); + if (ra_noreg(right)) + right = ra_alloc1z(as, ir->op2, rset_exclude(allow, left)); + else + ra_noweak(as, right); + } else if (ra_hasreg(right)) { + ra_noweak(as, right); + left = ra_alloc1z(as, ir->op1, rset_exclude(allow, right)); + } else if (ra_hashint(right)) { + right = ra_alloc1z(as, ir->op2, allow); + left = ra_alloc1z(as, ir->op1, rset_exclude(allow, right)); + } else { + left = ra_alloc1z(as, ir->op1, allow); + right = ra_alloc1z(as, ir->op2, rset_exclude(allow, left)); + } + return left | (right << 8); +} + +/* -- Guard handling ------------------------------------------------------ */ + +/* Need some spare long-range jump slots, for out-of-range branches. */ +#define MIPS_SPAREJUMP 4 + +/* Setup spare long-range jump slots per mcarea. */ +static void asm_sparejump_setup(ASMState *as) +{ + MCode *mxp = as->mcbot; + if (((uintptr_t)mxp & (LJ_PAGESIZE-1)) == sizeof(MCLink)) { + lua_assert(MIPSI_NOP == 0); + memset(mxp, 0, MIPS_SPAREJUMP*2*sizeof(MCode)); + mxp += MIPS_SPAREJUMP*2; + lua_assert(mxp < as->mctop); + lj_mcode_sync(as->mcbot, mxp); + lj_mcode_commitbot(as->J, mxp); + as->mcbot = mxp; + as->mclim = as->mcbot + MCLIM_REDZONE; + } +} + +/* Setup exit stub after the end of each trace. */ +static void asm_exitstub_setup(ASMState *as) +{ + MCode *mxp = as->mctop; + /* sw TMP, 0(sp); j ->vm_exit_handler; li TMP, traceno */ + *--mxp = MIPSI_LI|MIPSF_T(RID_TMP)|as->T->traceno; + *--mxp = MIPSI_J|((((uintptr_t)(void *)lj_vm_exit_handler)>>2)&0x03ffffffu); + lua_assert(((uintptr_t)mxp ^ (uintptr_t)(void *)lj_vm_exit_handler)>>28 == 0); + *--mxp = MIPSI_SW|MIPSF_T(RID_TMP)|MIPSF_S(RID_SP)|0; + as->mctop = mxp; +} + +/* Keep this in-sync with exitstub_trace_addr(). */ +#define asm_exitstub_addr(as) ((as)->mctop) + +/* Emit conditional branch to exit for guard. */ +static void asm_guard(ASMState *as, MIPSIns mi, Reg rs, Reg rt) +{ + MCode *target = asm_exitstub_addr(as); + MCode *p = as->mcp; + if (LJ_UNLIKELY(p == as->invmcp)) { + as->invmcp = NULL; + as->loopinv = 1; + as->mcp = p+1; + mi = mi ^ ((mi>>28) == 1 ? 0x04000000u : 0x00010000u); /* Invert cond. */ + target = p; /* Patch target later in asm_loop_fixup. */ + } + emit_ti(as, MIPSI_LI, RID_TMP, as->snapno); + emit_branch(as, mi, rs, rt, target); +} + +/* -- Operand fusion ------------------------------------------------------ */ + +/* Limit linear search to this distance. Avoids O(n^2) behavior. */ +#define CONFLICT_SEARCH_LIM 31 + +/* Check if there's no conflicting instruction between curins and ref. */ +static int noconflict(ASMState *as, IRRef ref, IROp conflict) +{ + IRIns *ir = as->ir; + IRRef i = as->curins; + if (i > ref + CONFLICT_SEARCH_LIM) + return 0; /* Give up, ref is too far away. */ + while (--i > ref) + if (ir[i].o == conflict) + return 0; /* Conflict found. */ + return 1; /* Ok, no conflict. */ +} + +/* Fuse the array base of colocated arrays. */ +static int32_t asm_fuseabase(ASMState *as, IRRef ref) +{ + IRIns *ir = IR(ref); + if (ir->o == IR_TNEW && ir->op1 <= LJ_MAX_COLOSIZE && + !neverfuse(as) && noconflict(as, ref, IR_NEWREF)) + return (int32_t)sizeof(GCtab); + return 0; +} + +/* Fuse array/hash/upvalue reference into register+offset operand. */ +static Reg asm_fuseahuref(ASMState *as, IRRef ref, int32_t *ofsp, RegSet allow) +{ + IRIns *ir = IR(ref); + if (ra_noreg(ir->r)) { + if (ir->o == IR_AREF) { + if (mayfuse(as, ref)) { + if (irref_isk(ir->op2)) { + IRRef tab = IR(ir->op1)->op1; + int32_t ofs = asm_fuseabase(as, tab); + IRRef refa = ofs ? tab : ir->op1; + ofs += 8*IR(ir->op2)->i; + if (checki16(ofs)) { + *ofsp = ofs; + return ra_alloc1(as, refa, allow); + } + } + } + } else if (ir->o == IR_HREFK) { + if (mayfuse(as, ref)) { + int32_t ofs = (int32_t)(IR(ir->op2)->op2 * sizeof(Node)); + if (checki16(ofs)) { + *ofsp = ofs; + return ra_alloc1(as, ir->op1, allow); + } + } + } else if (ir->o == IR_UREFC) { + if (irref_isk(ir->op1)) { + GCfunc *fn = ir_kfunc(IR(ir->op1)); + intptr_t ofs = (intptr_t)&gcref(fn->l.uvptr[(ir->op2 >> 8)])->uv.tv; + intptr_t jgl = (intptr_t)J2G(as->J); + if ((uintptr_t)(ofs-jgl) < 65536) { + *ofsp = ofs-jgl-32768; + return RID_JGL; + } else { + *ofsp = (int16_t)ofs; + return ra_allock(as, ofs-(int16_t)ofs, allow); + } + } + } + } + *ofsp = 0; + return ra_alloc1(as, ref, allow); +} + +/* Fuse XLOAD/XSTORE reference into load/store operand. */ +static void asm_fusexref(ASMState *as, MIPSIns mi, Reg rt, IRRef ref, + RegSet allow, int32_t ofs) +{ + IRIns *ir = IR(ref); + Reg base; + if (ra_noreg(ir->r) && canfuse(as, ir)) { + if (ir->o == IR_ADD) { + intptr_t ofs2; + if (irref_isk(ir->op2) && (ofs2 = ofs + get_kval(IR(ir->op2)), + checki16(ofs2))) { + ref = ir->op1; + ofs = (int32_t)ofs2; + } + } else if (ir->o == IR_STRREF) { + intptr_t ofs2 = 65536; + lua_assert(ofs == 0); + ofs = (int32_t)sizeof(GCstr); + if (irref_isk(ir->op2)) { + ofs2 = ofs + get_kval(IR(ir->op2)); + ref = ir->op1; + } else if (irref_isk(ir->op1)) { + ofs2 = ofs + get_kval(IR(ir->op1)); + ref = ir->op2; + } + if (!checki16(ofs2)) { + /* NYI: Fuse ADD with constant. */ + Reg right, left = ra_alloc2(as, ir, allow); + right = (left >> 8); left &= 255; + emit_hsi(as, mi, rt, RID_TMP, ofs); + emit_dst(as, MIPSI_AADDU, RID_TMP, left, right); + return; + } + ofs = ofs2; + } + } + base = ra_alloc1(as, ref, allow); + emit_hsi(as, mi, rt, base, ofs); +} + +/* -- Calls --------------------------------------------------------------- */ + +/* Generate a call to a C function. */ +static void asm_gencall(ASMState *as, const CCallInfo *ci, IRRef *args) +{ + uint32_t n, nargs = CCI_XNARGS(ci); + int32_t ofs = LJ_32 ? 16 : 0; +#if LJ_SOFTFP + Reg gpr = REGARG_FIRSTGPR; +#else + Reg gpr, fpr = REGARG_FIRSTFPR; +#endif + if ((void *)ci->func) + emit_call(as, (void *)ci->func, 1); +#if !LJ_SOFTFP + for (gpr = REGARG_FIRSTGPR; gpr <= REGARG_LASTGPR; gpr++) + as->cost[gpr] = REGCOST(~0u, ASMREF_L); + gpr = REGARG_FIRSTGPR; +#endif + for (n = 0; n < nargs; n++) { /* Setup args. */ + IRRef ref = args[n]; + if (ref) { + IRIns *ir = IR(ref); +#if !LJ_SOFTFP + if (irt_isfp(ir->t) && fpr <= REGARG_LASTFPR && + !(ci->flags & CCI_VARARG)) { + lua_assert(rset_test(as->freeset, fpr)); /* Already evicted. */ + ra_leftov(as, fpr, ref); + fpr += LJ_32 ? 2 : 1; + gpr += (LJ_32 && irt_isnum(ir->t)) ? 2 : 1; + } else +#endif + { +#if LJ_32 && !LJ_SOFTFP + fpr = REGARG_LASTFPR+1; +#endif + if (LJ_32 && irt_isnum(ir->t)) gpr = (gpr+1) & ~1; + if (gpr <= REGARG_LASTGPR) { + lua_assert(rset_test(as->freeset, gpr)); /* Already evicted. */ +#if !LJ_SOFTFP + if (irt_isfp(ir->t)) { + RegSet of = as->freeset; + Reg r; + /* Workaround to protect argument GPRs from being used for remat. */ + as->freeset &= ~RSET_RANGE(REGARG_FIRSTGPR, REGARG_LASTGPR+1); + r = ra_alloc1(as, ref, RSET_FPR); + as->freeset |= (of & RSET_RANGE(REGARG_FIRSTGPR, REGARG_LASTGPR+1)); + if (irt_isnum(ir->t)) { +#if LJ_32 + emit_tg(as, MIPSI_MFC1, gpr+(LJ_BE?0:1), r+1); + emit_tg(as, MIPSI_MFC1, gpr+(LJ_BE?1:0), r); + lua_assert(rset_test(as->freeset, gpr+1)); /* Already evicted. */ + gpr += 2; +#else + emit_tg(as, MIPSI_DMFC1, gpr, r); + gpr++; fpr++; +#endif + } else if (irt_isfloat(ir->t)) { + emit_tg(as, MIPSI_MFC1, gpr, r); + gpr++; +#if LJ_64 + fpr++; +#endif + } + } else +#endif + { + ra_leftov(as, gpr, ref); + gpr++; +#if LJ_64 && !LJ_SOFTFP + fpr++; +#endif + } + } else { + Reg r = ra_alloc1z(as, ref, !LJ_SOFTFP && irt_isfp(ir->t) ? RSET_FPR : RSET_GPR); +#if LJ_32 + if (irt_isnum(ir->t)) ofs = (ofs + 4) & ~4; + emit_spstore(as, ir, r, ofs); + ofs += irt_isnum(ir->t) ? 8 : 4; +#else + emit_spstore(as, ir, r, ofs + ((LJ_BE && !irt_isfp(ir->t) && !irt_is64(ir->t)) ? 4 : 0)); + ofs += 8; +#endif + } + } + } else { +#if !LJ_SOFTFP + fpr = REGARG_LASTFPR+1; +#endif + if (gpr <= REGARG_LASTGPR) { + gpr++; +#if LJ_64 && !LJ_SOFTFP + fpr++; +#endif + } else { + ofs += LJ_32 ? 4 : 8; + } + } + checkmclim(as); + } +} + +/* Setup result reg/sp for call. Evict scratch regs. */ +static void asm_setupresult(ASMState *as, IRIns *ir, const CCallInfo *ci) +{ + RegSet drop = RSET_SCRATCH; +#if LJ_32 + int hiop = ((ir+1)->o == IR_HIOP && !irt_isnil((ir+1)->t)); +#endif +#if !LJ_SOFTFP + if ((ci->flags & CCI_NOFPRCLOBBER)) + drop &= ~RSET_FPR; +#endif + if (ra_hasreg(ir->r)) + rset_clear(drop, ir->r); /* Dest reg handled below. */ +#if LJ_32 + if (hiop && ra_hasreg((ir+1)->r)) + rset_clear(drop, (ir+1)->r); /* Dest reg handled below. */ +#endif + ra_evictset(as, drop); /* Evictions must be performed first. */ + if (ra_used(ir)) { + lua_assert(!irt_ispri(ir->t)); + if (!LJ_SOFTFP && irt_isfp(ir->t)) { + if ((ci->flags & CCI_CASTU64)) { + int32_t ofs = sps_scale(ir->s); + Reg dest = ir->r; + if (ra_hasreg(dest)) { + ra_free(as, dest); + ra_modified(as, dest); +#if LJ_32 + emit_tg(as, MIPSI_MTC1, RID_RETHI, dest+1); + emit_tg(as, MIPSI_MTC1, RID_RETLO, dest); +#else + emit_tg(as, MIPSI_DMTC1, RID_RET, dest); +#endif + } + if (ofs) { +#if LJ_32 + emit_tsi(as, MIPSI_SW, RID_RETLO, RID_SP, ofs+(LJ_BE?4:0)); + emit_tsi(as, MIPSI_SW, RID_RETHI, RID_SP, ofs+(LJ_BE?0:4)); +#else + emit_tsi(as, MIPSI_SD, RID_RET, RID_SP, ofs); +#endif + } + } else { + ra_destreg(as, ir, RID_FPRET); + } +#if LJ_32 + } else if (hiop) { + ra_destpair(as, ir); +#endif + } else { + ra_destreg(as, ir, RID_RET); + } + } +} + +static void asm_callx(ASMState *as, IRIns *ir) +{ + IRRef args[CCI_NARGS_MAX*2]; + CCallInfo ci; + IRRef func; + IRIns *irf; + ci.flags = asm_callx_flags(as, ir); + asm_collectargs(as, ir, &ci, args); + asm_setupresult(as, ir, &ci); + func = ir->op2; irf = IR(func); + if (irf->o == IR_CARG) { func = irf->op1; irf = IR(func); } + if (irref_isk(func)) { /* Call to constant address. */ + ci.func = (ASMFunction)(void *)get_kval(irf); + } else { /* Need specific register for indirect calls. */ + Reg r = ra_alloc1(as, func, RID2RSET(RID_CFUNCADDR)); + MCode *p = as->mcp; + if (r == RID_CFUNCADDR) + *--p = MIPSI_NOP; + else + *--p = MIPSI_MOVE | MIPSF_D(RID_CFUNCADDR) | MIPSF_S(r); + *--p = MIPSI_JALR | MIPSF_S(r); + as->mcp = p; + ci.func = (ASMFunction)(void *)0; + } + asm_gencall(as, &ci, args); +} + +#if !LJ_SOFTFP +static void asm_callround(ASMState *as, IRIns *ir, IRCallID id) +{ + /* The modified regs must match with the *.dasc implementation. */ + RegSet drop = RID2RSET(RID_R1)|RID2RSET(RID_R12)|RID2RSET(RID_FPRET)| + RID2RSET(RID_F2)|RID2RSET(RID_F4)|RID2RSET(REGARG_FIRSTFPR); + if (ra_hasreg(ir->r)) rset_clear(drop, ir->r); + ra_evictset(as, drop); + ra_destreg(as, ir, RID_FPRET); + emit_call(as, (void *)lj_ir_callinfo[id].func, 0); + ra_leftov(as, REGARG_FIRSTFPR, ir->op1); +} +#endif + +/* -- Returns ------------------------------------------------------------- */ + +/* Return to lower frame. Guard that it goes to the right spot. */ +static void asm_retf(ASMState *as, IRIns *ir) +{ + Reg base = ra_alloc1(as, REF_BASE, RSET_GPR); + void *pc = ir_kptr(IR(ir->op2)); + int32_t delta = 1+LJ_FR2+bc_a(*((const BCIns *)pc - 1)); + as->topslot -= (BCReg)delta; + if ((int32_t)as->topslot < 0) as->topslot = 0; + irt_setmark(IR(REF_BASE)->t); /* Children must not coalesce with BASE reg. */ + emit_setgl(as, base, jit_base); + emit_addptr(as, base, -8*delta); + asm_guard(as, MIPSI_BNE, RID_TMP, + ra_allock(as, igcptr(pc), rset_exclude(RSET_GPR, base))); + emit_tsi(as, MIPSI_AL, RID_TMP, base, -8); +} + +/* -- Type conversions ---------------------------------------------------- */ + +#if !LJ_SOFTFP +static void asm_tointg(ASMState *as, IRIns *ir, Reg left) +{ + Reg tmp = ra_scratch(as, rset_exclude(RSET_FPR, left)); + Reg dest = ra_dest(as, ir, RSET_GPR); + asm_guard(as, MIPSI_BC1F, 0, 0); + emit_fgh(as, MIPSI_C_EQ_D, 0, tmp, left); + emit_fg(as, MIPSI_CVT_D_W, tmp, tmp); + emit_tg(as, MIPSI_MFC1, dest, tmp); + emit_fg(as, MIPSI_CVT_W_D, tmp, left); +} + +static void asm_tobit(ASMState *as, IRIns *ir) +{ + RegSet allow = RSET_FPR; + Reg dest = ra_dest(as, ir, RSET_GPR); + Reg left = ra_alloc1(as, ir->op1, allow); + Reg right = ra_alloc1(as, ir->op2, rset_clear(allow, left)); + Reg tmp = ra_scratch(as, rset_clear(allow, right)); + emit_tg(as, MIPSI_MFC1, dest, tmp); + emit_fgh(as, MIPSI_ADD_D, tmp, left, right); +} +#elif LJ_64 /* && LJ_SOFTFP */ +static void asm_tointg(ASMState *as, IRIns *ir, Reg r) +{ + /* The modified regs must match with the *.dasc implementation. */ + RegSet drop = RID2RSET(REGARG_FIRSTGPR)|RID2RSET(RID_RET)|RID2RSET(RID_RET+1)| + RID2RSET(RID_R1)|RID2RSET(RID_R12); + if (ra_hasreg(ir->r)) rset_clear(drop, ir->r); + ra_evictset(as, drop); + /* Return values are in RID_RET (converted value) and RID_RET+1 (status). */ + ra_destreg(as, ir, RID_RET); + asm_guard(as, MIPSI_BNE, RID_RET+1, RID_ZERO); + emit_call(as, (void *)lj_ir_callinfo[IRCALL_lj_vm_tointg].func, 0); + if (r == RID_NONE) + ra_leftov(as, REGARG_FIRSTGPR, ir->op1); + else if (r != REGARG_FIRSTGPR) + emit_move(as, REGARG_FIRSTGPR, r); +} + +static void asm_tobit(ASMState *as, IRIns *ir) +{ + Reg dest = ra_dest(as, ir, RSET_GPR); + emit_dta(as, MIPSI_SLL, dest, dest, 0); + asm_callid(as, ir, IRCALL_lj_vm_tobit); +} +#endif + +static void asm_conv(ASMState *as, IRIns *ir) +{ + IRType st = (IRType)(ir->op2 & IRCONV_SRCMASK); +#if !LJ_SOFTFP32 + int stfp = (st == IRT_NUM || st == IRT_FLOAT); +#endif +#if LJ_64 + int st64 = (st == IRT_I64 || st == IRT_U64 || st == IRT_P64); +#endif + IRRef lref = ir->op1; +#if LJ_32 + lua_assert(!(irt_isint64(ir->t) || + (st == IRT_I64 || st == IRT_U64))); /* Handled by SPLIT. */ +#endif +#if LJ_SOFTFP32 + /* FP conversions are handled by SPLIT. */ + lua_assert(!irt_isfp(ir->t) && !(st == IRT_NUM || st == IRT_FLOAT)); + /* Can't check for same types: SPLIT uses CONV int.int + BXOR for sfp NEG. */ +#else + lua_assert(irt_type(ir->t) != st); +#if !LJ_SOFTFP + if (irt_isfp(ir->t)) { + Reg dest = ra_dest(as, ir, RSET_FPR); + if (stfp) { /* FP to FP conversion. */ + emit_fg(as, st == IRT_NUM ? MIPSI_CVT_S_D : MIPSI_CVT_D_S, + dest, ra_alloc1(as, lref, RSET_FPR)); + } else if (st == IRT_U32) { /* U32 to FP conversion. */ + /* y = (x ^ 0x8000000) + 2147483648.0 */ + Reg left = ra_alloc1(as, lref, RSET_GPR); + Reg tmp = ra_scratch(as, rset_exclude(RSET_FPR, dest)); + if (irt_isfloat(ir->t)) + emit_fg(as, MIPSI_CVT_S_D, dest, dest); + /* Must perform arithmetic with doubles to keep the precision. */ + emit_fgh(as, MIPSI_ADD_D, dest, dest, tmp); + emit_fg(as, MIPSI_CVT_D_W, dest, dest); + emit_lsptr(as, MIPSI_LDC1, (tmp & 31), + (void *)&as->J->k64[LJ_K64_2P31], RSET_GPR); + emit_tg(as, MIPSI_MTC1, RID_TMP, dest); + emit_dst(as, MIPSI_XOR, RID_TMP, RID_TMP, left); + emit_ti(as, MIPSI_LUI, RID_TMP, 0x8000); +#if LJ_64 + } else if(st == IRT_U64) { /* U64 to FP conversion. */ + /* if (x >= 1u<<63) y = (double)(int64_t)(x&(1u<<63)-1) + pow(2.0, 63) */ + Reg left = ra_alloc1(as, lref, RSET_GPR); + Reg tmp = ra_scratch(as, rset_exclude(RSET_FPR, dest)); + MCLabel l_end = emit_label(as); + if (irt_isfloat(ir->t)) { + emit_fgh(as, MIPSI_ADD_S, dest, dest, tmp); + emit_lsptr(as, MIPSI_LWC1, (tmp & 31), (void *)&as->J->k32[LJ_K32_2P63], + rset_exclude(RSET_GPR, left)); + emit_fg(as, MIPSI_CVT_S_L, dest, dest); + } else { + emit_fgh(as, MIPSI_ADD_D, dest, dest, tmp); + emit_lsptr(as, MIPSI_LDC1, (tmp & 31), (void *)&as->J->k64[LJ_K64_2P63], + rset_exclude(RSET_GPR, left)); + emit_fg(as, MIPSI_CVT_D_L, dest, dest); + } + emit_branch(as, MIPSI_BGEZ, left, RID_ZERO, l_end); + emit_tg(as, MIPSI_DMTC1, RID_TMP, dest); + emit_tsml(as, MIPSI_DEXTM, RID_TMP, left, 30, 0); +#endif + } else { /* Integer to FP conversion. */ + Reg left = ra_alloc1(as, lref, RSET_GPR); +#if LJ_32 + emit_fg(as, irt_isfloat(ir->t) ? MIPSI_CVT_S_W : MIPSI_CVT_D_W, + dest, dest); + emit_tg(as, MIPSI_MTC1, left, dest); +#else + MIPSIns mi = irt_isfloat(ir->t) ? + (st64 ? MIPSI_CVT_S_L : MIPSI_CVT_S_W) : + (st64 ? MIPSI_CVT_D_L : MIPSI_CVT_D_W); + emit_fg(as, mi, dest, dest); + emit_tg(as, st64 ? MIPSI_DMTC1 : MIPSI_MTC1, left, dest); +#endif + } + } else if (stfp) { /* FP to integer conversion. */ + if (irt_isguard(ir->t)) { + /* Checked conversions are only supported from number to int. */ + lua_assert(irt_isint(ir->t) && st == IRT_NUM); + asm_tointg(as, ir, ra_alloc1(as, lref, RSET_FPR)); + } else { + Reg dest = ra_dest(as, ir, RSET_GPR); + Reg left = ra_alloc1(as, lref, RSET_FPR); + Reg tmp = ra_scratch(as, rset_exclude(RSET_FPR, left)); + if (irt_isu32(ir->t)) { /* FP to U32 conversion. */ + /* y = (int)floor(x - 2147483648.0) ^ 0x80000000 */ + emit_dst(as, MIPSI_XOR, dest, dest, RID_TMP); + emit_ti(as, MIPSI_LUI, RID_TMP, 0x8000); + emit_tg(as, MIPSI_MFC1, dest, tmp); + emit_fg(as, st == IRT_FLOAT ? MIPSI_FLOOR_W_S : MIPSI_FLOOR_W_D, + tmp, tmp); + emit_fgh(as, st == IRT_FLOAT ? MIPSI_SUB_S : MIPSI_SUB_D, + tmp, left, tmp); + if (st == IRT_FLOAT) + emit_lsptr(as, MIPSI_LWC1, (tmp & 31), + (void *)&as->J->k32[LJ_K32_2P31], RSET_GPR); + else + emit_lsptr(as, MIPSI_LDC1, (tmp & 31), + (void *)&as->J->k64[LJ_K64_2P31], RSET_GPR); +#if LJ_64 + } else if (irt_isu64(ir->t)) { /* FP to U64 conversion. */ + MCLabel l_end; + emit_tg(as, MIPSI_DMFC1, dest, tmp); + l_end = emit_label(as); + /* For inputs >= 2^63 add -2^64 and convert again. */ + if (st == IRT_NUM) { + emit_fg(as, MIPSI_TRUNC_L_D, tmp, tmp); + emit_fgh(as, MIPSI_ADD_D, tmp, left, tmp); + emit_lsptr(as, MIPSI_LDC1, (tmp & 31), + (void *)&as->J->k64[LJ_K64_M2P64], + rset_exclude(RSET_GPR, dest)); + emit_fg(as, MIPSI_TRUNC_L_D, tmp, left); /* Delay slot. */ + emit_branch(as, MIPSI_BC1T, 0, 0, l_end); + emit_fgh(as, MIPSI_C_OLT_D, 0, left, tmp); + emit_lsptr(as, MIPSI_LDC1, (tmp & 31), + (void *)&as->J->k64[LJ_K64_2P63], + rset_exclude(RSET_GPR, dest)); + } else { + emit_fg(as, MIPSI_TRUNC_L_S, tmp, tmp); + emit_fgh(as, MIPSI_ADD_S, tmp, left, tmp); + emit_lsptr(as, MIPSI_LWC1, (tmp & 31), + (void *)&as->J->k32[LJ_K32_M2P64], + rset_exclude(RSET_GPR, dest)); + emit_fg(as, MIPSI_TRUNC_L_S, tmp, left); /* Delay slot. */ + emit_branch(as, MIPSI_BC1T, 0, 0, l_end); + emit_fgh(as, MIPSI_C_OLT_S, 0, left, tmp); + emit_lsptr(as, MIPSI_LWC1, (tmp & 31), + (void *)&as->J->k32[LJ_K32_2P63], + rset_exclude(RSET_GPR, dest)); + } +#endif + } else { +#if LJ_32 + emit_tg(as, MIPSI_MFC1, dest, tmp); + emit_fg(as, st == IRT_FLOAT ? MIPSI_TRUNC_W_S : MIPSI_TRUNC_W_D, + tmp, left); +#else + MIPSIns mi = irt_is64(ir->t) ? + (st == IRT_NUM ? MIPSI_TRUNC_L_D : MIPSI_TRUNC_L_S) : + (st == IRT_NUM ? MIPSI_TRUNC_W_D : MIPSI_TRUNC_W_S); + emit_tg(as, irt_is64(ir->t) ? MIPSI_DMFC1 : MIPSI_MFC1, dest, left); + emit_fg(as, mi, left, left); +#endif + } + } + } else +#else + if (irt_isfp(ir->t)) { +#if LJ_64 && LJ_HASFFI + if (stfp) { /* FP to FP conversion. */ + asm_callid(as, ir, irt_isnum(ir->t) ? IRCALL_softfp_f2d : + IRCALL_softfp_d2f); + } else { /* Integer to FP conversion. */ + IRCallID cid = ((IRT_IS64 >> st) & 1) ? + (irt_isnum(ir->t) ? + (st == IRT_I64 ? IRCALL_fp64_l2d : IRCALL_fp64_ul2d) : + (st == IRT_I64 ? IRCALL_fp64_l2f : IRCALL_fp64_ul2f)) : + (irt_isnum(ir->t) ? + (st == IRT_INT ? IRCALL_softfp_i2d : IRCALL_softfp_ui2d) : + (st == IRT_INT ? IRCALL_softfp_i2f : IRCALL_softfp_ui2f)); + asm_callid(as, ir, cid); + } +#else + asm_callid(as, ir, IRCALL_softfp_i2d); +#endif + } else if (stfp) { /* FP to integer conversion. */ + if (irt_isguard(ir->t)) { + /* Checked conversions are only supported from number to int. */ + lua_assert(irt_isint(ir->t) && st == IRT_NUM); + asm_tointg(as, ir, RID_NONE); + } else { + IRCallID cid = irt_is64(ir->t) ? + ((st == IRT_NUM) ? + (irt_isi64(ir->t) ? IRCALL_fp64_d2l : IRCALL_fp64_d2ul) : + (irt_isi64(ir->t) ? IRCALL_fp64_f2l : IRCALL_fp64_f2ul)) : + ((st == IRT_NUM) ? + (irt_isint(ir->t) ? IRCALL_softfp_d2i : IRCALL_softfp_d2ui) : + (irt_isint(ir->t) ? IRCALL_softfp_f2i : IRCALL_softfp_f2ui)); + asm_callid(as, ir, cid); + } + } else +#endif +#endif + { + Reg dest = ra_dest(as, ir, RSET_GPR); + if (st >= IRT_I8 && st <= IRT_U16) { /* Extend to 32 bit integer. */ + Reg left = ra_alloc1(as, ir->op1, RSET_GPR); + lua_assert(irt_isint(ir->t) || irt_isu32(ir->t)); + if ((ir->op2 & IRCONV_SEXT)) { + if (LJ_64 || (as->flags & JIT_F_MIPSXXR2)) { + emit_dst(as, st == IRT_I8 ? MIPSI_SEB : MIPSI_SEH, dest, 0, left); + } else { + uint32_t shift = st == IRT_I8 ? 24 : 16; + emit_dta(as, MIPSI_SRA, dest, dest, shift); + emit_dta(as, MIPSI_SLL, dest, left, shift); + } + } else { + emit_tsi(as, MIPSI_ANDI, dest, left, + (int32_t)(st == IRT_U8 ? 0xff : 0xffff)); + } + } else { /* 32/64 bit integer conversions. */ +#if LJ_32 + /* Only need to handle 32/32 bit no-op (cast) on 32 bit archs. */ + ra_leftov(as, dest, lref); /* Do nothing, but may need to move regs. */ +#else + if (irt_is64(ir->t)) { + if (st64) { + /* 64/64 bit no-op (cast)*/ + ra_leftov(as, dest, lref); + } else { + Reg left = ra_alloc1(as, lref, RSET_GPR); + if ((ir->op2 & IRCONV_SEXT)) { /* 32 to 64 bit sign extension. */ + emit_dta(as, MIPSI_SLL, dest, left, 0); + } else { /* 32 to 64 bit zero extension. */ + emit_tsml(as, MIPSI_DEXT, dest, left, 31, 0); + } + } + } else { + if (st64) { + /* This is either a 32 bit reg/reg mov which zeroes the hiword + ** or a load of the loword from a 64 bit address. + */ + Reg left = ra_alloc1(as, lref, RSET_GPR); + emit_tsml(as, MIPSI_DEXT, dest, left, 31, 0); + } else { /* 32/32 bit no-op (cast). */ + /* Do nothing, but may need to move regs. */ + ra_leftov(as, dest, lref); + } + } +#endif + } + } +} + +static void asm_strto(ASMState *as, IRIns *ir) +{ + const CCallInfo *ci = &lj_ir_callinfo[IRCALL_lj_strscan_num]; + IRRef args[2]; + int32_t ofs = 0; +#if LJ_SOFTFP32 + ra_evictset(as, RSET_SCRATCH); + if (ra_used(ir)) { + if (ra_hasspill(ir->s) && ra_hasspill((ir+1)->s) && + (ir->s & 1) == LJ_BE && (ir->s ^ 1) == (ir+1)->s) { + int i; + for (i = 0; i < 2; i++) { + Reg r = (ir+i)->r; + if (ra_hasreg(r)) { + ra_free(as, r); + ra_modified(as, r); + emit_spload(as, ir+i, r, sps_scale((ir+i)->s)); + } + } + ofs = sps_scale(ir->s & ~1); + } else { + Reg rhi = ra_dest(as, ir+1, RSET_GPR); + Reg rlo = ra_dest(as, ir, rset_exclude(RSET_GPR, rhi)); + emit_tsi(as, MIPSI_LW, rhi, RID_SP, ofs+(LJ_BE?0:4)); + emit_tsi(as, MIPSI_LW, rlo, RID_SP, ofs+(LJ_BE?4:0)); + } + } +#else + RegSet drop = RSET_SCRATCH; + if (ra_hasreg(ir->r)) rset_set(drop, ir->r); /* Spill dest reg (if any). */ + ra_evictset(as, drop); + ofs = sps_scale(ir->s); +#endif + asm_guard(as, MIPSI_BEQ, RID_RET, RID_ZERO); /* Test return status. */ + args[0] = ir->op1; /* GCstr *str */ + args[1] = ASMREF_TMP1; /* TValue *n */ + asm_gencall(as, ci, args); + /* Store the result to the spill slot or temp slots. */ + emit_tsi(as, MIPSI_AADDIU, ra_releasetmp(as, ASMREF_TMP1), + RID_SP, ofs); +} + +/* -- Memory references --------------------------------------------------- */ + +#if LJ_64 +/* Store tagged value for ref at base+ofs. */ +static void asm_tvstore64(ASMState *as, Reg base, int32_t ofs, IRRef ref) +{ + RegSet allow = rset_exclude(RSET_GPR, base); + IRIns *ir = IR(ref); + lua_assert(irt_ispri(ir->t) || irt_isaddr(ir->t) || irt_isinteger(ir->t)); + if (irref_isk(ref)) { + TValue k; + lj_ir_kvalue(as->J->L, &k, ir); + emit_tsi(as, MIPSI_SD, ra_allock(as, (int64_t)k.u64, allow), base, ofs); + } else { + Reg src = ra_alloc1(as, ref, allow); + Reg type = ra_allock(as, (int64_t)irt_toitype(ir->t) << 47, + rset_exclude(allow, src)); + emit_tsi(as, MIPSI_SD, RID_TMP, base, ofs); + if (irt_isinteger(ir->t)) { + emit_dst(as, MIPSI_DADDU, RID_TMP, RID_TMP, type); + emit_tsml(as, MIPSI_DEXT, RID_TMP, src, 31, 0); + } else { + emit_dst(as, MIPSI_DADDU, RID_TMP, src, type); + } + } +} +#endif + +/* Get pointer to TValue. */ +static void asm_tvptr(ASMState *as, Reg dest, IRRef ref) +{ + IRIns *ir = IR(ref); + if (irt_isnum(ir->t)) { + if (irref_isk(ref)) /* Use the number constant itself as a TValue. */ + ra_allockreg(as, igcptr(ir_knum(ir)), dest); + else /* Otherwise force a spill and use the spill slot. */ + emit_tsi(as, MIPSI_AADDIU, dest, RID_SP, ra_spill(as, ir)); + } else { + /* Otherwise use g->tmptv to hold the TValue. */ +#if LJ_32 + RegSet allow = rset_exclude(RSET_GPR, dest); + Reg type; + emit_tsi(as, MIPSI_ADDIU, dest, RID_JGL, (int32_t)(offsetof(global_State, tmptv)-32768)); + if (!irt_ispri(ir->t)) { + Reg src = ra_alloc1(as, ref, allow); + emit_setgl(as, src, tmptv.gcr); + } + if (LJ_SOFTFP && (ir+1)->o == IR_HIOP) + type = ra_alloc1(as, ref+1, allow); + else + type = ra_allock(as, (int32_t)irt_toitype(ir->t), allow); + emit_setgl(as, type, tmptv.it); +#else + asm_tvstore64(as, dest, 0, ref); + emit_tsi(as, MIPSI_DADDIU, dest, RID_JGL, + (int32_t)(offsetof(global_State, tmptv)-32768)); +#endif + } +} + +static void asm_aref(ASMState *as, IRIns *ir) +{ + Reg dest = ra_dest(as, ir, RSET_GPR); + Reg idx, base; + if (irref_isk(ir->op2)) { + IRRef tab = IR(ir->op1)->op1; + int32_t ofs = asm_fuseabase(as, tab); + IRRef refa = ofs ? tab : ir->op1; + ofs += 8*IR(ir->op2)->i; + if (checki16(ofs)) { + base = ra_alloc1(as, refa, RSET_GPR); + emit_tsi(as, MIPSI_AADDIU, dest, base, ofs); + return; + } + } + base = ra_alloc1(as, ir->op1, RSET_GPR); + idx = ra_alloc1(as, ir->op2, rset_exclude(RSET_GPR, base)); + emit_dst(as, MIPSI_AADDU, dest, RID_TMP, base); + emit_dta(as, MIPSI_SLL, RID_TMP, idx, 3); +} + +/* Inlined hash lookup. Specialized for key type and for const keys. +** The equivalent C code is: +** Node *n = hashkey(t, key); +** do { +** if (lj_obj_equal(&n->key, key)) return &n->val; +** } while ((n = nextnode(n))); +** return niltv(L); +*/ +static void asm_href(ASMState *as, IRIns *ir, IROp merge) +{ + RegSet allow = RSET_GPR; + int destused = ra_used(ir); + Reg dest = ra_dest(as, ir, allow); + Reg tab = ra_alloc1(as, ir->op1, rset_clear(allow, dest)); + Reg key = RID_NONE, type = RID_NONE, tmpnum = RID_NONE, tmp1 = RID_TMP, tmp2; +#if LJ_64 + Reg cmp64 = RID_NONE; +#endif + IRRef refkey = ir->op2; + IRIns *irkey = IR(refkey); + int isk = irref_isk(refkey); + IRType1 kt = irkey->t; + uint32_t khash; + MCLabel l_end, l_loop, l_next; + + rset_clear(allow, tab); +#if LJ_SOFTFP32 + if (!isk) { + key = ra_alloc1(as, refkey, allow); + rset_clear(allow, key); + if (irkey[1].o == IR_HIOP) { + if (ra_hasreg((irkey+1)->r)) { + type = tmpnum = (irkey+1)->r; + tmp1 = ra_scratch(as, allow); + rset_clear(allow, tmp1); + ra_noweak(as, tmpnum); + } else { + type = tmpnum = ra_allocref(as, refkey+1, allow); + } + rset_clear(allow, tmpnum); + } else { + type = ra_allock(as, (int32_t)irt_toitype(irkey->t), allow); + rset_clear(allow, type); + } + } +#else + if (!LJ_SOFTFP && irt_isnum(kt)) { + key = ra_alloc1(as, refkey, RSET_FPR); + tmpnum = ra_scratch(as, rset_exclude(RSET_FPR, key)); + } else if (!irt_ispri(kt)) { + key = ra_alloc1(as, refkey, allow); + rset_clear(allow, key); +#if LJ_32 + type = ra_allock(as, (int32_t)irt_toitype(irkey->t), allow); + rset_clear(allow, type); +#endif + } +#endif + tmp2 = ra_scratch(as, allow); + rset_clear(allow, tmp2); +#if LJ_64 + if (LJ_SOFTFP || !irt_isnum(kt)) { + /* Allocate cmp64 register used for 64-bit comparisons */ + if (LJ_SOFTFP && irt_isnum(kt)) { + cmp64 = key; + } else if (!isk && irt_isaddr(kt)) { + cmp64 = tmp2; + } else { + int64_t k; + if (isk && irt_isaddr(kt)) { + k = ((int64_t)irt_toitype(irkey->t) << 47) | irkey[1].tv.u64; + } else { + lua_assert(irt_ispri(kt) && !irt_isnil(kt)); + k = ~((int64_t)~irt_toitype(ir->t) << 47); + } + cmp64 = ra_allock(as, k, allow); + rset_clear(allow, cmp64); + } + } +#endif + + /* Key not found in chain: jump to exit (if merged) or load niltv. */ + l_end = emit_label(as); + as->invmcp = NULL; + if (merge == IR_NE) + asm_guard(as, MIPSI_B, RID_ZERO, RID_ZERO); + else if (destused) + emit_loada(as, dest, niltvg(J2G(as->J))); + /* Follow hash chain until the end. */ + emit_move(as, dest, tmp1); + l_loop = --as->mcp; + emit_tsi(as, MIPSI_AL, tmp1, dest, (int32_t)offsetof(Node, next)); + l_next = emit_label(as); + + /* Type and value comparison. */ + if (merge == IR_EQ) { /* Must match asm_guard(). */ + emit_ti(as, MIPSI_LI, RID_TMP, as->snapno); + l_end = asm_exitstub_addr(as); + } + if (!LJ_SOFTFP && irt_isnum(kt)) { + emit_branch(as, MIPSI_BC1T, 0, 0, l_end); + emit_fgh(as, MIPSI_C_EQ_D, 0, tmpnum, key); + *--as->mcp = MIPSI_NOP; /* Avoid NaN comparison overhead. */ + emit_branch(as, MIPSI_BEQ, tmp1, RID_ZERO, l_next); + emit_tsi(as, MIPSI_SLTIU, tmp1, tmp1, (int32_t)LJ_TISNUM); +#if LJ_32 + emit_hsi(as, MIPSI_LDC1, tmpnum, dest, (int32_t)offsetof(Node, key.n)); + } else { + if (irt_ispri(kt)) { + emit_branch(as, MIPSI_BEQ, tmp1, type, l_end); + } else { + emit_branch(as, MIPSI_BEQ, tmp2, key, l_end); + emit_tsi(as, MIPSI_LW, tmp2, dest, (int32_t)offsetof(Node, key.gcr)); + emit_branch(as, MIPSI_BNE, tmp1, type, l_next); + } + } + emit_tsi(as, MIPSI_LW, tmp1, dest, (int32_t)offsetof(Node, key.it)); + *l_loop = MIPSI_BNE | MIPSF_S(tmp1) | ((as->mcp-l_loop-1) & 0xffffu); +#else + emit_dta(as, MIPSI_DSRA32, tmp1, tmp1, 15); + emit_tg(as, MIPSI_DMTC1, tmp1, tmpnum); + emit_tsi(as, MIPSI_LD, tmp1, dest, (int32_t)offsetof(Node, key.u64)); + } else { + emit_branch(as, MIPSI_BEQ, tmp1, cmp64, l_end); + emit_tsi(as, MIPSI_LD, tmp1, dest, (int32_t)offsetof(Node, key.u64)); + } + *l_loop = MIPSI_BNE | MIPSF_S(tmp1) | ((as->mcp-l_loop-1) & 0xffffu); + if (!isk && irt_isaddr(kt)) { + type = ra_allock(as, (int64_t)irt_toitype(kt) << 47, allow); + emit_dst(as, MIPSI_DADDU, tmp2, key, type); + rset_clear(allow, type); + } +#endif + + /* Load main position relative to tab->node into dest. */ + khash = isk ? ir_khash(irkey) : 1; + if (khash == 0) { + emit_tsi(as, MIPSI_AL, dest, tab, (int32_t)offsetof(GCtab, node)); + } else { + Reg tmphash = tmp1; + if (isk) + tmphash = ra_allock(as, khash, allow); + emit_dst(as, MIPSI_AADDU, dest, dest, tmp1); + lua_assert(sizeof(Node) == 24); + emit_dst(as, MIPSI_SUBU, tmp1, tmp2, tmp1); + emit_dta(as, MIPSI_SLL, tmp1, tmp1, 3); + emit_dta(as, MIPSI_SLL, tmp2, tmp1, 5); + emit_dst(as, MIPSI_AND, tmp1, tmp2, tmphash); + emit_tsi(as, MIPSI_AL, dest, tab, (int32_t)offsetof(GCtab, node)); + emit_tsi(as, MIPSI_LW, tmp2, tab, (int32_t)offsetof(GCtab, hmask)); + if (isk) { + /* Nothing to do. */ + } else if (irt_isstr(kt)) { + emit_tsi(as, MIPSI_LW, tmp1, key, (int32_t)offsetof(GCstr, hash)); + } else { /* Must match with hash*() in lj_tab.c. */ + emit_dst(as, MIPSI_SUBU, tmp1, tmp1, tmp2); + emit_rotr(as, tmp2, tmp2, dest, (-HASH_ROT3)&31); + emit_dst(as, MIPSI_XOR, tmp1, tmp1, tmp2); + emit_rotr(as, tmp1, tmp1, dest, (-HASH_ROT2-HASH_ROT1)&31); + emit_dst(as, MIPSI_SUBU, tmp2, tmp2, dest); +#if LJ_32 + if (LJ_SOFTFP ? (irkey[1].o == IR_HIOP) : irt_isnum(kt)) { + emit_dst(as, MIPSI_XOR, tmp2, tmp2, tmp1); + if ((as->flags & JIT_F_MIPSXXR2)) { + emit_dta(as, MIPSI_ROTR, dest, tmp1, (-HASH_ROT1)&31); + } else { + emit_dst(as, MIPSI_OR, dest, dest, tmp1); + emit_dta(as, MIPSI_SLL, tmp1, tmp1, HASH_ROT1); + emit_dta(as, MIPSI_SRL, dest, tmp1, (-HASH_ROT1)&31); + } + emit_dst(as, MIPSI_ADDU, tmp1, tmp1, tmp1); +#if LJ_SOFTFP + emit_ds(as, MIPSI_MOVE, tmp1, type); + emit_ds(as, MIPSI_MOVE, tmp2, key); +#else + emit_tg(as, MIPSI_MFC1, tmp2, key); + emit_tg(as, MIPSI_MFC1, tmp1, key+1); +#endif + } else { + emit_dst(as, MIPSI_XOR, tmp2, key, tmp1); + emit_rotr(as, dest, tmp1, tmp2, (-HASH_ROT1)&31); + emit_dst(as, MIPSI_ADDU, tmp1, key, ra_allock(as, HASH_BIAS, allow)); + } +#else + emit_dst(as, MIPSI_XOR, tmp2, tmp2, tmp1); + emit_dta(as, MIPSI_ROTR, dest, tmp1, (-HASH_ROT1)&31); + if (irt_isnum(kt)) { + emit_dst(as, MIPSI_ADDU, tmp1, tmp1, tmp1); + emit_dta(as, MIPSI_DSRA32, tmp1, LJ_SOFTFP ? key : tmp1, 0); + emit_dta(as, MIPSI_SLL, tmp2, LJ_SOFTFP ? key : tmp1, 0); +#if !LJ_SOFTFP + emit_tg(as, MIPSI_DMFC1, tmp1, key); +#endif + } else { + checkmclim(as); + emit_dta(as, MIPSI_DSRA32, tmp1, tmp1, 0); + emit_dta(as, MIPSI_SLL, tmp2, key, 0); + emit_dst(as, MIPSI_DADDU, tmp1, key, type); + } +#endif + } + } +} + +static void asm_hrefk(ASMState *as, IRIns *ir) +{ + IRIns *kslot = IR(ir->op2); + IRIns *irkey = IR(kslot->op1); + int32_t ofs = (int32_t)(kslot->op2 * sizeof(Node)); + int32_t kofs = ofs + (int32_t)offsetof(Node, key); + Reg dest = (ra_used(ir)||ofs > 32736) ? ra_dest(as, ir, RSET_GPR) : RID_NONE; + Reg node = ra_alloc1(as, ir->op1, RSET_GPR); + RegSet allow = rset_exclude(RSET_GPR, node); + Reg idx = node; +#if LJ_32 + Reg key = RID_NONE, type = RID_TMP; + int32_t lo, hi; +#else + Reg key = ra_scratch(as, allow); + int64_t k; +#endif + lua_assert(ofs % sizeof(Node) == 0); + if (ofs > 32736) { + idx = dest; + rset_clear(allow, dest); + kofs = (int32_t)offsetof(Node, key); + } else if (ra_hasreg(dest)) { + emit_tsi(as, MIPSI_AADDIU, dest, node, ofs); + } +#if LJ_32 + if (!irt_ispri(irkey->t)) { + key = ra_scratch(as, allow); + rset_clear(allow, key); + } + if (irt_isnum(irkey->t)) { + lo = (int32_t)ir_knum(irkey)->u32.lo; + hi = (int32_t)ir_knum(irkey)->u32.hi; + } else { + lo = irkey->i; + hi = irt_toitype(irkey->t); + if (!ra_hasreg(key)) + goto nolo; + } + asm_guard(as, MIPSI_BNE, key, lo ? ra_allock(as, lo, allow) : RID_ZERO); +nolo: + asm_guard(as, MIPSI_BNE, type, hi ? ra_allock(as, hi, allow) : RID_ZERO); + if (ra_hasreg(key)) emit_tsi(as, MIPSI_LW, key, idx, kofs+(LJ_BE?4:0)); + emit_tsi(as, MIPSI_LW, type, idx, kofs+(LJ_BE?0:4)); +#else + if (irt_ispri(irkey->t)) { + lua_assert(!irt_isnil(irkey->t)); + k = ~((int64_t)~irt_toitype(irkey->t) << 47); + } else if (irt_isnum(irkey->t)) { + k = (int64_t)ir_knum(irkey)->u64; + } else { + k = ((int64_t)irt_toitype(irkey->t) << 47) | (int64_t)ir_kgc(irkey); + } + asm_guard(as, MIPSI_BNE, key, ra_allock(as, k, allow)); + emit_tsi(as, MIPSI_LD, key, idx, kofs); +#endif + if (ofs > 32736) + emit_tsi(as, MIPSI_AADDU, dest, node, ra_allock(as, ofs, allow)); +} + +static void asm_uref(ASMState *as, IRIns *ir) +{ + Reg dest = ra_dest(as, ir, RSET_GPR); + if (irref_isk(ir->op1)) { + GCfunc *fn = ir_kfunc(IR(ir->op1)); + MRef *v = &gcref(fn->l.uvptr[(ir->op2 >> 8)])->uv.v; + emit_lsptr(as, MIPSI_AL, dest, v, RSET_GPR); + } else { + Reg uv = ra_scratch(as, RSET_GPR); + Reg func = ra_alloc1(as, ir->op1, RSET_GPR); + if (ir->o == IR_UREFC) { + asm_guard(as, MIPSI_BEQ, RID_TMP, RID_ZERO); + emit_tsi(as, MIPSI_AADDIU, dest, uv, (int32_t)offsetof(GCupval, tv)); + emit_tsi(as, MIPSI_LBU, RID_TMP, uv, (int32_t)offsetof(GCupval, closed)); + } else { + emit_tsi(as, MIPSI_AL, dest, uv, (int32_t)offsetof(GCupval, v)); + } + emit_tsi(as, MIPSI_AL, uv, func, (int32_t)offsetof(GCfuncL, uvptr) + + (int32_t)sizeof(MRef) * (int32_t)(ir->op2 >> 8)); + } +} + +static void asm_fref(ASMState *as, IRIns *ir) +{ + UNUSED(as); UNUSED(ir); + lua_assert(!ra_used(ir)); +} + +static void asm_strref(ASMState *as, IRIns *ir) +{ +#if LJ_32 + Reg dest = ra_dest(as, ir, RSET_GPR); + IRRef ref = ir->op2, refk = ir->op1; + int32_t ofs = (int32_t)sizeof(GCstr); + Reg r; + if (irref_isk(ref)) { + IRRef tmp = refk; refk = ref; ref = tmp; + } else if (!irref_isk(refk)) { + Reg right, left = ra_alloc1(as, ir->op1, RSET_GPR); + IRIns *irr = IR(ir->op2); + if (ra_hasreg(irr->r)) { + ra_noweak(as, irr->r); + right = irr->r; + } else if (mayfuse(as, irr->op2) && + irr->o == IR_ADD && irref_isk(irr->op2) && + checki16(ofs + IR(irr->op2)->i)) { + ofs += IR(irr->op2)->i; + right = ra_alloc1(as, irr->op1, rset_exclude(RSET_GPR, left)); + } else { + right = ra_allocref(as, ir->op2, rset_exclude(RSET_GPR, left)); + } + emit_tsi(as, MIPSI_ADDIU, dest, dest, ofs); + emit_dst(as, MIPSI_ADDU, dest, left, right); + return; + } + r = ra_alloc1(as, ref, RSET_GPR); + ofs += IR(refk)->i; + if (checki16(ofs)) + emit_tsi(as, MIPSI_ADDIU, dest, r, ofs); + else + emit_dst(as, MIPSI_ADDU, dest, r, + ra_allock(as, ofs, rset_exclude(RSET_GPR, r))); +#else + RegSet allow = RSET_GPR; + Reg dest = ra_dest(as, ir, allow); + Reg base = ra_alloc1(as, ir->op1, allow); + IRIns *irr = IR(ir->op2); + int32_t ofs = sizeof(GCstr); + rset_clear(allow, base); + if (irref_isk(ir->op2) && checki16(ofs + irr->i)) { + emit_tsi(as, MIPSI_DADDIU, dest, base, ofs + irr->i); + } else { + emit_tsi(as, MIPSI_DADDIU, dest, dest, ofs); + emit_dst(as, MIPSI_DADDU, dest, base, ra_alloc1(as, ir->op2, allow)); + } +#endif +} + +/* -- Loads and stores ---------------------------------------------------- */ + +static MIPSIns asm_fxloadins(IRIns *ir) +{ + switch (irt_type(ir->t)) { + case IRT_I8: return MIPSI_LB; + case IRT_U8: return MIPSI_LBU; + case IRT_I16: return MIPSI_LH; + case IRT_U16: return MIPSI_LHU; + case IRT_NUM: lua_assert(!LJ_SOFTFP32); if (!LJ_SOFTFP) return MIPSI_LDC1; + case IRT_FLOAT: if (!LJ_SOFTFP) return MIPSI_LWC1; + default: return (LJ_64 && irt_is64(ir->t)) ? MIPSI_LD : MIPSI_LW; + } +} + +static MIPSIns asm_fxstoreins(IRIns *ir) +{ + switch (irt_type(ir->t)) { + case IRT_I8: case IRT_U8: return MIPSI_SB; + case IRT_I16: case IRT_U16: return MIPSI_SH; + case IRT_NUM: lua_assert(!LJ_SOFTFP32); if (!LJ_SOFTFP) return MIPSI_SDC1; + case IRT_FLOAT: if (!LJ_SOFTFP) return MIPSI_SWC1; + default: return (LJ_64 && irt_is64(ir->t)) ? MIPSI_SD : MIPSI_SW; + } +} + +static void asm_fload(ASMState *as, IRIns *ir) +{ + Reg dest = ra_dest(as, ir, RSET_GPR); + MIPSIns mi = asm_fxloadins(ir); + Reg idx; + int32_t ofs; + if (ir->op1 == REF_NIL) { + idx = RID_JGL; + ofs = (ir->op2 << 2) - 32768 - GG_OFS(g); + } else { + idx = ra_alloc1(as, ir->op1, RSET_GPR); + if (ir->op2 == IRFL_TAB_ARRAY) { + ofs = asm_fuseabase(as, ir->op1); + if (ofs) { /* Turn the t->array load into an add for colocated arrays. */ + emit_tsi(as, MIPSI_AADDIU, dest, idx, ofs); + return; + } + } + ofs = field_ofs[ir->op2]; + } + lua_assert(!irt_isfp(ir->t)); + emit_tsi(as, mi, dest, idx, ofs); +} + +static void asm_fstore(ASMState *as, IRIns *ir) +{ + if (ir->r != RID_SINK) { + Reg src = ra_alloc1z(as, ir->op2, RSET_GPR); + IRIns *irf = IR(ir->op1); + Reg idx = ra_alloc1(as, irf->op1, rset_exclude(RSET_GPR, src)); + int32_t ofs = field_ofs[irf->op2]; + MIPSIns mi = asm_fxstoreins(ir); + lua_assert(!irt_isfp(ir->t)); + emit_tsi(as, mi, src, idx, ofs); + } +} + +static void asm_xload(ASMState *as, IRIns *ir) +{ + Reg dest = ra_dest(as, ir, + (!LJ_SOFTFP && irt_isfp(ir->t)) ? RSET_FPR : RSET_GPR); + lua_assert(!(ir->op2 & IRXLOAD_UNALIGNED)); + asm_fusexref(as, asm_fxloadins(ir), dest, ir->op1, RSET_GPR, 0); +} + +static void asm_xstore_(ASMState *as, IRIns *ir, int32_t ofs) +{ + if (ir->r != RID_SINK) { + Reg src = ra_alloc1z(as, ir->op2, + (!LJ_SOFTFP && irt_isfp(ir->t)) ? RSET_FPR : RSET_GPR); + asm_fusexref(as, asm_fxstoreins(ir), src, ir->op1, + rset_exclude(RSET_GPR, src), ofs); + } +} + +#define asm_xstore(as, ir) asm_xstore_(as, ir, 0) + +static void asm_ahuvload(ASMState *as, IRIns *ir) +{ + int hiop = (LJ_SOFTFP32 && (ir+1)->o == IR_HIOP); + Reg dest = RID_NONE, type = RID_TMP, idx; + RegSet allow = RSET_GPR; + int32_t ofs = 0; + IRType1 t = ir->t; + if (hiop) { + t.irt = IRT_NUM; + if (ra_used(ir+1)) { + type = ra_dest(as, ir+1, allow); + rset_clear(allow, type); + } + } + if (ra_used(ir)) { + lua_assert((LJ_SOFTFP32 ? 0 : irt_isnum(ir->t)) || + irt_isint(ir->t) || irt_isaddr(ir->t)); + dest = ra_dest(as, ir, (!LJ_SOFTFP && irt_isnum(t)) ? RSET_FPR : allow); + rset_clear(allow, dest); +#if LJ_64 + if (irt_isaddr(t)) + emit_tsml(as, MIPSI_DEXTM, dest, dest, 14, 0); + else if (irt_isint(t)) + emit_dta(as, MIPSI_SLL, dest, dest, 0); +#endif + } + idx = asm_fuseahuref(as, ir->op1, &ofs, allow); + rset_clear(allow, idx); + if (irt_isnum(t)) { + asm_guard(as, MIPSI_BEQ, RID_TMP, RID_ZERO); + emit_tsi(as, MIPSI_SLTIU, RID_TMP, type, (int32_t)LJ_TISNUM); + } else { + asm_guard(as, MIPSI_BNE, type, + ra_allock(as, (int32_t)irt_toitype(t), allow)); + } +#if LJ_32 + if (ra_hasreg(dest)) { + if (!LJ_SOFTFP && irt_isnum(t)) + emit_hsi(as, MIPSI_LDC1, dest, idx, ofs); + else + emit_tsi(as, MIPSI_LW, dest, idx, ofs+(LJ_BE?4:0)); + } + emit_tsi(as, MIPSI_LW, type, idx, ofs+(LJ_BE?0:4)); +#else + if (ra_hasreg(dest)) { + if (!LJ_SOFTFP && irt_isnum(t)) { + emit_hsi(as, MIPSI_LDC1, dest, idx, ofs); + dest = type; + } + } else { + dest = type; + } + emit_dta(as, MIPSI_DSRA32, type, dest, 15); + emit_tsi(as, MIPSI_LD, dest, idx, ofs); +#endif +} + +static void asm_ahustore(ASMState *as, IRIns *ir) +{ + RegSet allow = RSET_GPR; + Reg idx, src = RID_NONE, type = RID_NONE; + int32_t ofs = 0; + if (ir->r == RID_SINK) + return; + if (!LJ_SOFTFP32 && irt_isnum(ir->t)) { + src = ra_alloc1(as, ir->op2, LJ_SOFTFP ? RSET_GPR : RSET_FPR); + idx = asm_fuseahuref(as, ir->op1, &ofs, allow); + emit_hsi(as, LJ_SOFTFP ? MIPSI_SD : MIPSI_SDC1, src, idx, ofs); + } else { +#if LJ_32 + if (!irt_ispri(ir->t)) { + src = ra_alloc1(as, ir->op2, allow); + rset_clear(allow, src); + } + if (LJ_SOFTFP && (ir+1)->o == IR_HIOP) + type = ra_alloc1(as, (ir+1)->op2, allow); + else + type = ra_allock(as, (int32_t)irt_toitype(ir->t), allow); + rset_clear(allow, type); + idx = asm_fuseahuref(as, ir->op1, &ofs, allow); + if (ra_hasreg(src)) + emit_tsi(as, MIPSI_SW, src, idx, ofs+(LJ_BE?4:0)); + emit_tsi(as, MIPSI_SW, type, idx, ofs+(LJ_BE?0:4)); +#else + Reg tmp = RID_TMP; + if (irt_ispri(ir->t)) { + tmp = ra_allock(as, ~((int64_t)~irt_toitype(ir->t) << 47), allow); + rset_clear(allow, tmp); + } else { + src = ra_alloc1(as, ir->op2, allow); + rset_clear(allow, src); + type = ra_allock(as, (int64_t)irt_toitype(ir->t) << 47, allow); + rset_clear(allow, type); + } + idx = asm_fuseahuref(as, ir->op1, &ofs, allow); + emit_tsi(as, MIPSI_SD, tmp, idx, ofs); + if (ra_hasreg(src)) { + if (irt_isinteger(ir->t)) { + emit_dst(as, MIPSI_DADDU, tmp, tmp, type); + emit_tsml(as, MIPSI_DEXT, tmp, src, 31, 0); + } else { + emit_dst(as, MIPSI_DADDU, tmp, src, type); + } + } +#endif + } +} + +static void asm_sload(ASMState *as, IRIns *ir) +{ + Reg dest = RID_NONE, type = RID_NONE, base; + RegSet allow = RSET_GPR; + IRType1 t = ir->t; +#if LJ_32 + int32_t ofs = 8*((int32_t)ir->op1-1) + ((ir->op2 & IRSLOAD_FRAME) ? 4 : 0); + int hiop = (LJ_SOFTFP32 && (ir+1)->o == IR_HIOP); + if (hiop) + t.irt = IRT_NUM; +#else + int32_t ofs = 8*((int32_t)ir->op1-2); +#endif + lua_assert(!(ir->op2 & IRSLOAD_PARENT)); /* Handled by asm_head_side(). */ + lua_assert(irt_isguard(ir->t) || !(ir->op2 & IRSLOAD_TYPECHECK)); +#if LJ_SOFTFP32 + lua_assert(!(ir->op2 & IRSLOAD_CONVERT)); /* Handled by LJ_SOFTFP SPLIT. */ + if (hiop && ra_used(ir+1)) { + type = ra_dest(as, ir+1, allow); + rset_clear(allow, type); + } +#else + if ((ir->op2 & IRSLOAD_CONVERT) && irt_isguard(t) && irt_isint(t)) { + dest = ra_scratch(as, LJ_SOFTFP ? allow : RSET_FPR); + asm_tointg(as, ir, dest); + t.irt = IRT_NUM; /* Continue with a regular number type check. */ + } else +#endif + if (ra_used(ir)) { + lua_assert((LJ_SOFTFP32 ? 0 : irt_isnum(ir->t)) || + irt_isint(ir->t) || irt_isaddr(ir->t)); + dest = ra_dest(as, ir, (!LJ_SOFTFP && irt_isnum(t)) ? RSET_FPR : allow); + rset_clear(allow, dest); + base = ra_alloc1(as, REF_BASE, allow); + rset_clear(allow, base); + if (!LJ_SOFTFP32 && (ir->op2 & IRSLOAD_CONVERT)) { + if (irt_isint(t)) { + Reg tmp = ra_scratch(as, LJ_SOFTFP ? RSET_GPR : RSET_FPR); +#if LJ_SOFTFP + ra_evictset(as, rset_exclude(RSET_SCRATCH, dest)); + ra_destreg(as, ir, RID_RET); + emit_call(as, (void *)lj_ir_callinfo[IRCALL_softfp_d2i].func, 0); + if (tmp != REGARG_FIRSTGPR) + emit_move(as, REGARG_FIRSTGPR, tmp); +#else + emit_tg(as, MIPSI_MFC1, dest, tmp); + emit_fg(as, MIPSI_TRUNC_W_D, tmp, tmp); +#endif + dest = tmp; + t.irt = IRT_NUM; /* Check for original type. */ + } else { + Reg tmp = ra_scratch(as, RSET_GPR); +#if LJ_SOFTFP + ra_evictset(as, rset_exclude(RSET_SCRATCH, dest)); + ra_destreg(as, ir, RID_RET); + emit_call(as, (void *)lj_ir_callinfo[IRCALL_softfp_i2d].func, 0); + emit_dta(as, MIPSI_SLL, REGARG_FIRSTGPR, tmp, 0); +#else + emit_fg(as, MIPSI_CVT_D_W, dest, dest); + emit_tg(as, MIPSI_MTC1, tmp, dest); +#endif + dest = tmp; + t.irt = IRT_INT; /* Check for original type. */ + } + } +#if LJ_64 + else if (irt_isaddr(t)) { + /* Clear type from pointers. */ + emit_tsml(as, MIPSI_DEXTM, dest, dest, 14, 0); + } else if (irt_isint(t) && (ir->op2 & IRSLOAD_TYPECHECK)) { + /* Sign-extend integers. */ + emit_dta(as, MIPSI_SLL, dest, dest, 0); + } +#endif + goto dotypecheck; + } + base = ra_alloc1(as, REF_BASE, allow); + rset_clear(allow, base); +dotypecheck: +#if LJ_32 + if ((ir->op2 & IRSLOAD_TYPECHECK)) { + if (ra_noreg(type)) + type = RID_TMP; + if (irt_isnum(t)) { + asm_guard(as, MIPSI_BEQ, RID_TMP, RID_ZERO); + emit_tsi(as, MIPSI_SLTIU, RID_TMP, type, (int32_t)LJ_TISNUM); + } else { + Reg ktype = ra_allock(as, irt_toitype(t), allow); + asm_guard(as, MIPSI_BNE, type, ktype); + } + } + if (ra_hasreg(dest)) { + if (!LJ_SOFTFP && irt_isnum(t)) + emit_hsi(as, MIPSI_LDC1, dest, base, ofs); + else + emit_tsi(as, MIPSI_LW, dest, base, ofs ^ (LJ_BE?4:0)); + } + if (ra_hasreg(type)) + emit_tsi(as, MIPSI_LW, type, base, ofs ^ (LJ_BE?0:4)); +#else + if ((ir->op2 & IRSLOAD_TYPECHECK)) { + type = dest < RID_MAX_GPR ? dest : RID_TMP; + if (irt_ispri(t)) { + asm_guard(as, MIPSI_BNE, type, + ra_allock(as, ~((int64_t)~irt_toitype(t) << 47) , allow)); + } else { + if (irt_isnum(t)) { + asm_guard(as, MIPSI_BEQ, RID_TMP, RID_ZERO); + emit_tsi(as, MIPSI_SLTIU, RID_TMP, RID_TMP, (int32_t)LJ_TISNUM); + if (!LJ_SOFTFP && ra_hasreg(dest)) + emit_hsi(as, MIPSI_LDC1, dest, base, ofs); + } else { + asm_guard(as, MIPSI_BNE, RID_TMP, + ra_allock(as, (int32_t)irt_toitype(t), allow)); + } + emit_dta(as, MIPSI_DSRA32, RID_TMP, type, 15); + } + emit_tsi(as, MIPSI_LD, type, base, ofs); + } else if (ra_hasreg(dest)) { + if (!LJ_SOFTFP && irt_isnum(t)) + emit_hsi(as, MIPSI_LDC1, dest, base, ofs); + else + emit_tsi(as, irt_isint(t) ? MIPSI_LW : MIPSI_LD, dest, base, + ofs ^ ((LJ_BE && irt_isint(t)) ? 4 : 0)); + } +#endif +} + +/* -- Allocations --------------------------------------------------------- */ + +#if LJ_HASFFI +static void asm_cnew(ASMState *as, IRIns *ir) +{ + CTState *cts = ctype_ctsG(J2G(as->J)); + CTypeID id = (CTypeID)IR(ir->op1)->i; + CTSize sz; + CTInfo info = lj_ctype_info(cts, id, &sz); + const CCallInfo *ci = &lj_ir_callinfo[IRCALL_lj_mem_newgco]; + IRRef args[4]; + RegSet drop = RSET_SCRATCH; + lua_assert(sz != CTSIZE_INVALID || (ir->o == IR_CNEW && ir->op2 != REF_NIL)); + + as->gcsteps++; + if (ra_hasreg(ir->r)) + rset_clear(drop, ir->r); /* Dest reg handled below. */ + ra_evictset(as, drop); + if (ra_used(ir)) + ra_destreg(as, ir, RID_RET); /* GCcdata * */ + + /* Initialize immutable cdata object. */ + if (ir->o == IR_CNEWI) { + RegSet allow = (RSET_GPR & ~RSET_SCRATCH); +#if LJ_32 + int32_t ofs = sizeof(GCcdata); + if (sz == 8) { + ofs += 4; + lua_assert((ir+1)->o == IR_HIOP); + if (LJ_LE) ir++; + } + for (;;) { + Reg r = ra_alloc1z(as, ir->op2, allow); + emit_tsi(as, MIPSI_SW, r, RID_RET, ofs); + rset_clear(allow, r); + if (ofs == sizeof(GCcdata)) break; + ofs -= 4; if (LJ_BE) ir++; else ir--; + } +#else + emit_tsi(as, MIPSI_SD, ra_alloc1(as, ir->op2, allow), + RID_RET, sizeof(GCcdata)); +#endif + lua_assert(sz == 4 || sz == 8); + } else if (ir->op2 != REF_NIL) { /* Create VLA/VLS/aligned cdata. */ + ci = &lj_ir_callinfo[IRCALL_lj_cdata_newv]; + args[0] = ASMREF_L; /* lua_State *L */ + args[1] = ir->op1; /* CTypeID id */ + args[2] = ir->op2; /* CTSize sz */ + args[3] = ASMREF_TMP1; /* CTSize align */ + asm_gencall(as, ci, args); + emit_loadi(as, ra_releasetmp(as, ASMREF_TMP1), (int32_t)ctype_align(info)); + return; + } + + /* Initialize gct and ctypeid. lj_mem_newgco() already sets marked. */ + emit_tsi(as, MIPSI_SB, RID_RET+1, RID_RET, offsetof(GCcdata, gct)); + emit_tsi(as, MIPSI_SH, RID_TMP, RID_RET, offsetof(GCcdata, ctypeid)); + emit_ti(as, MIPSI_LI, RID_RET+1, ~LJ_TCDATA); + emit_ti(as, MIPSI_LI, RID_TMP, id); /* Lower 16 bit used. Sign-ext ok. */ + args[0] = ASMREF_L; /* lua_State *L */ + args[1] = ASMREF_TMP1; /* MSize size */ + asm_gencall(as, ci, args); + ra_allockreg(as, (int32_t)(sz+sizeof(GCcdata)), + ra_releasetmp(as, ASMREF_TMP1)); +} +#else +#define asm_cnew(as, ir) ((void)0) +#endif + +/* -- Write barriers ------------------------------------------------------ */ + +static void asm_tbar(ASMState *as, IRIns *ir) +{ + Reg tab = ra_alloc1(as, ir->op1, RSET_GPR); + Reg mark = ra_scratch(as, rset_exclude(RSET_GPR, tab)); + Reg link = RID_TMP; + MCLabel l_end = emit_label(as); + emit_tsi(as, MIPSI_AS, link, tab, (int32_t)offsetof(GCtab, gclist)); + emit_tsi(as, MIPSI_SB, mark, tab, (int32_t)offsetof(GCtab, marked)); + emit_setgl(as, tab, gc.grayagain); + emit_getgl(as, link, gc.grayagain); + emit_dst(as, MIPSI_XOR, mark, mark, RID_TMP); /* Clear black bit. */ + emit_branch(as, MIPSI_BEQ, RID_TMP, RID_ZERO, l_end); + emit_tsi(as, MIPSI_ANDI, RID_TMP, mark, LJ_GC_BLACK); + emit_tsi(as, MIPSI_LBU, mark, tab, (int32_t)offsetof(GCtab, marked)); +} + +static void asm_obar(ASMState *as, IRIns *ir) +{ + const CCallInfo *ci = &lj_ir_callinfo[IRCALL_lj_gc_barrieruv]; + IRRef args[2]; + MCLabel l_end; + Reg obj, val, tmp; + /* No need for other object barriers (yet). */ + lua_assert(IR(ir->op1)->o == IR_UREFC); + ra_evictset(as, RSET_SCRATCH); + l_end = emit_label(as); + args[0] = ASMREF_TMP1; /* global_State *g */ + args[1] = ir->op1; /* TValue *tv */ + asm_gencall(as, ci, args); + emit_tsi(as, MIPSI_AADDIU, ra_releasetmp(as, ASMREF_TMP1), RID_JGL, -32768); + obj = IR(ir->op1)->r; + tmp = ra_scratch(as, rset_exclude(RSET_GPR, obj)); + emit_branch(as, MIPSI_BEQ, RID_TMP, RID_ZERO, l_end); + emit_tsi(as, MIPSI_ANDI, tmp, tmp, LJ_GC_BLACK); + emit_branch(as, MIPSI_BEQ, RID_TMP, RID_ZERO, l_end); + emit_tsi(as, MIPSI_ANDI, RID_TMP, RID_TMP, LJ_GC_WHITES); + val = ra_alloc1(as, ir->op2, rset_exclude(RSET_GPR, obj)); + emit_tsi(as, MIPSI_LBU, tmp, obj, + (int32_t)offsetof(GCupval, marked)-(int32_t)offsetof(GCupval, tv)); + emit_tsi(as, MIPSI_LBU, RID_TMP, val, (int32_t)offsetof(GChead, marked)); +} + +/* -- Arithmetic and logic operations ------------------------------------- */ + +#if !LJ_SOFTFP +static void asm_fparith(ASMState *as, IRIns *ir, MIPSIns mi) +{ + Reg dest = ra_dest(as, ir, RSET_FPR); + Reg right, left = ra_alloc2(as, ir, RSET_FPR); + right = (left >> 8); left &= 255; + emit_fgh(as, mi, dest, left, right); +} + +static void asm_fpunary(ASMState *as, IRIns *ir, MIPSIns mi) +{ + Reg dest = ra_dest(as, ir, RSET_FPR); + Reg left = ra_hintalloc(as, ir->op1, dest, RSET_FPR); + emit_fg(as, mi, dest, left); +} +#endif + +#if !LJ_SOFTFP32 +static void asm_fpmath(ASMState *as, IRIns *ir) +{ + if (ir->op2 == IRFPM_EXP2 && asm_fpjoin_pow(as, ir)) + return; +#if !LJ_SOFTFP + if (ir->op2 <= IRFPM_TRUNC) + asm_callround(as, ir, IRCALL_lj_vm_floor + ir->op2); + else if (ir->op2 == IRFPM_SQRT) + asm_fpunary(as, ir, MIPSI_SQRT_D); + else +#endif + asm_callid(as, ir, IRCALL_lj_vm_floor + ir->op2); +} +#endif + +#if !LJ_SOFTFP +#define asm_fpadd(as, ir) asm_fparith(as, ir, MIPSI_ADD_D) +#define asm_fpsub(as, ir) asm_fparith(as, ir, MIPSI_SUB_D) +#define asm_fpmul(as, ir) asm_fparith(as, ir, MIPSI_MUL_D) +#elif LJ_64 /* && LJ_SOFTFP */ +#define asm_fpadd(as, ir) asm_callid(as, ir, IRCALL_softfp_add) +#define asm_fpsub(as, ir) asm_callid(as, ir, IRCALL_softfp_sub) +#define asm_fpmul(as, ir) asm_callid(as, ir, IRCALL_softfp_mul) +#endif + +static void asm_add(ASMState *as, IRIns *ir) +{ + IRType1 t = ir->t; +#if !LJ_SOFTFP32 + if (irt_isnum(t)) { + asm_fpadd(as, ir); + } else +#endif + { + Reg dest = ra_dest(as, ir, RSET_GPR); + Reg right, left = ra_hintalloc(as, ir->op1, dest, RSET_GPR); + if (irref_isk(ir->op2)) { + intptr_t k = get_kval(IR(ir->op2)); + if (checki16(k)) { + emit_tsi(as, (LJ_64 && irt_is64(t)) ? MIPSI_DADDIU : MIPSI_ADDIU, dest, + left, k); + return; + } + } + right = ra_alloc1(as, ir->op2, rset_exclude(RSET_GPR, left)); + emit_dst(as, (LJ_64 && irt_is64(t)) ? MIPSI_DADDU : MIPSI_ADDU, dest, + left, right); + } +} + +static void asm_sub(ASMState *as, IRIns *ir) +{ +#if !LJ_SOFTFP32 + if (irt_isnum(ir->t)) { + asm_fpsub(as, ir); + } else +#endif + { + Reg dest = ra_dest(as, ir, RSET_GPR); + Reg right, left = ra_alloc2(as, ir, RSET_GPR); + right = (left >> 8); left &= 255; + emit_dst(as, (LJ_64 && irt_is64(ir->t)) ? MIPSI_DSUBU : MIPSI_SUBU, dest, + left, right); + } +} + +static void asm_mul(ASMState *as, IRIns *ir) +{ +#if !LJ_SOFTFP32 + if (irt_isnum(ir->t)) { + asm_fpmul(as, ir); + } else +#endif + { + Reg dest = ra_dest(as, ir, RSET_GPR); + Reg right, left = ra_alloc2(as, ir, RSET_GPR); + right = (left >> 8); left &= 255; + if (LJ_64 && irt_is64(ir->t)) { + emit_dst(as, MIPSI_MFLO, dest, 0, 0); + emit_dst(as, MIPSI_DMULT, 0, left, right); + } else { + emit_dst(as, MIPSI_MUL, dest, left, right); + } + } +} + +static void asm_mod(ASMState *as, IRIns *ir) +{ +#if LJ_64 && LJ_HASFFI + if (!irt_isint(ir->t)) + asm_callid(as, ir, irt_isi64(ir->t) ? IRCALL_lj_carith_modi64 : + IRCALL_lj_carith_modu64); + else +#endif + asm_callid(as, ir, IRCALL_lj_vm_modi); +} + +#if !LJ_SOFTFP32 +static void asm_pow(ASMState *as, IRIns *ir) +{ +#if LJ_64 && LJ_HASFFI + if (!irt_isnum(ir->t)) + asm_callid(as, ir, irt_isi64(ir->t) ? IRCALL_lj_carith_powi64 : + IRCALL_lj_carith_powu64); + else +#endif + asm_callid(as, ir, IRCALL_lj_vm_powi); +} + +static void asm_div(ASMState *as, IRIns *ir) +{ +#if LJ_64 && LJ_HASFFI + if (!irt_isnum(ir->t)) + asm_callid(as, ir, irt_isi64(ir->t) ? IRCALL_lj_carith_divi64 : + IRCALL_lj_carith_divu64); + else +#endif +#if !LJ_SOFTFP + asm_fparith(as, ir, MIPSI_DIV_D); +#else + asm_callid(as, ir, IRCALL_softfp_div); +#endif +} +#endif + +static void asm_neg(ASMState *as, IRIns *ir) +{ +#if !LJ_SOFTFP + if (irt_isnum(ir->t)) { + asm_fpunary(as, ir, MIPSI_NEG_D); + } else +#elif LJ_64 /* && LJ_SOFTFP */ + if (irt_isnum(ir->t)) { + Reg dest = ra_dest(as, ir, RSET_GPR); + Reg left = ra_hintalloc(as, ir->op1, dest, RSET_GPR); + emit_dst(as, MIPSI_XOR, dest, left, + ra_allock(as, 0x8000000000000000ll, rset_exclude(RSET_GPR, dest))); + } else +#endif + { + Reg dest = ra_dest(as, ir, RSET_GPR); + Reg left = ra_hintalloc(as, ir->op1, dest, RSET_GPR); + emit_dst(as, (LJ_64 && irt_is64(ir->t)) ? MIPSI_DSUBU : MIPSI_SUBU, dest, + RID_ZERO, left); + } +} + +#if !LJ_SOFTFP +#define asm_abs(as, ir) asm_fpunary(as, ir, MIPSI_ABS_D) +#elif LJ_64 /* && LJ_SOFTFP */ +static void asm_abs(ASMState *as, IRIns *ir) +{ + Reg dest = ra_dest(as, ir, RSET_GPR); + Reg left = ra_alloc1(as, ir->op1, RSET_GPR); + emit_tsml(as, MIPSI_DEXTM, dest, left, 30, 0); +} +#endif + +#define asm_atan2(as, ir) asm_callid(as, ir, IRCALL_atan2) +#define asm_ldexp(as, ir) asm_callid(as, ir, IRCALL_ldexp) + +static void asm_arithov(ASMState *as, IRIns *ir) +{ + Reg right, left, tmp, dest = ra_dest(as, ir, RSET_GPR); + lua_assert(!irt_is64(ir->t)); + if (irref_isk(ir->op2)) { + int k = IR(ir->op2)->i; + if (ir->o == IR_SUBOV) k = -k; + if (checki16(k)) { /* (dest < left) == (k >= 0 ? 1 : 0) */ + left = ra_alloc1(as, ir->op1, RSET_GPR); + asm_guard(as, k >= 0 ? MIPSI_BNE : MIPSI_BEQ, RID_TMP, RID_ZERO); + emit_dst(as, MIPSI_SLT, RID_TMP, dest, dest == left ? RID_TMP : left); + emit_tsi(as, MIPSI_ADDIU, dest, left, k); + if (dest == left) emit_move(as, RID_TMP, left); + return; + } + } + left = ra_alloc2(as, ir, RSET_GPR); + right = (left >> 8); left &= 255; + tmp = ra_scratch(as, rset_exclude(rset_exclude(rset_exclude(RSET_GPR, left), + right), dest)); + asm_guard(as, MIPSI_BLTZ, RID_TMP, 0); + emit_dst(as, MIPSI_AND, RID_TMP, RID_TMP, tmp); + if (ir->o == IR_ADDOV) { /* ((dest^left) & (dest^right)) < 0 */ + emit_dst(as, MIPSI_XOR, RID_TMP, dest, dest == right ? RID_TMP : right); + } else { /* ((dest^left) & (dest^~right)) < 0 */ + emit_dst(as, MIPSI_XOR, RID_TMP, RID_TMP, dest); + emit_dst(as, MIPSI_NOR, RID_TMP, dest == right ? RID_TMP : right, RID_ZERO); + } + emit_dst(as, MIPSI_XOR, tmp, dest, dest == left ? RID_TMP : left); + emit_dst(as, ir->o == IR_ADDOV ? MIPSI_ADDU : MIPSI_SUBU, dest, left, right); + if (dest == left || dest == right) + emit_move(as, RID_TMP, dest == left ? left : right); +} + +#define asm_addov(as, ir) asm_arithov(as, ir) +#define asm_subov(as, ir) asm_arithov(as, ir) + +static void asm_mulov(ASMState *as, IRIns *ir) +{ + Reg dest = ra_dest(as, ir, RSET_GPR); + Reg tmp, right, left = ra_alloc2(as, ir, RSET_GPR); + right = (left >> 8); left &= 255; + tmp = ra_scratch(as, rset_exclude(rset_exclude(rset_exclude(RSET_GPR, left), + right), dest)); + asm_guard(as, MIPSI_BNE, RID_TMP, tmp); + emit_dta(as, MIPSI_SRA, RID_TMP, dest, 31); + emit_dst(as, MIPSI_MFHI, tmp, 0, 0); + emit_dst(as, MIPSI_MFLO, dest, 0, 0); + emit_dst(as, MIPSI_MULT, 0, left, right); +} + +#if LJ_32 && LJ_HASFFI +static void asm_add64(ASMState *as, IRIns *ir) +{ + Reg dest = ra_dest(as, ir, RSET_GPR); + Reg right, left = ra_alloc1(as, ir->op1, RSET_GPR); + if (irref_isk(ir->op2)) { + int32_t k = IR(ir->op2)->i; + if (k == 0) { + emit_dst(as, MIPSI_ADDU, dest, left, RID_TMP); + goto loarith; + } else if (checki16(k)) { + emit_dst(as, MIPSI_ADDU, dest, dest, RID_TMP); + emit_tsi(as, MIPSI_ADDIU, dest, left, k); + goto loarith; + } + } + emit_dst(as, MIPSI_ADDU, dest, dest, RID_TMP); + right = ra_alloc1(as, ir->op2, rset_exclude(RSET_GPR, left)); + emit_dst(as, MIPSI_ADDU, dest, left, right); +loarith: + ir--; + dest = ra_dest(as, ir, RSET_GPR); + left = ra_alloc1(as, ir->op1, RSET_GPR); + if (irref_isk(ir->op2)) { + int32_t k = IR(ir->op2)->i; + if (k == 0) { + if (dest != left) + emit_move(as, dest, left); + return; + } else if (checki16(k)) { + if (dest == left) { + Reg tmp = ra_scratch(as, rset_exclude(RSET_GPR, left)); + emit_move(as, dest, tmp); + dest = tmp; + } + emit_dst(as, MIPSI_SLTU, RID_TMP, dest, left); + emit_tsi(as, MIPSI_ADDIU, dest, left, k); + return; + } + } + right = ra_alloc1(as, ir->op2, rset_exclude(RSET_GPR, left)); + if (dest == left && dest == right) { + Reg tmp = ra_scratch(as, rset_exclude(rset_exclude(RSET_GPR, left), right)); + emit_move(as, dest, tmp); + dest = tmp; + } + emit_dst(as, MIPSI_SLTU, RID_TMP, dest, dest == left ? right : left); + emit_dst(as, MIPSI_ADDU, dest, left, right); +} + +static void asm_sub64(ASMState *as, IRIns *ir) +{ + Reg dest = ra_dest(as, ir, RSET_GPR); + Reg right, left = ra_alloc2(as, ir, RSET_GPR); + right = (left >> 8); left &= 255; + emit_dst(as, MIPSI_SUBU, dest, dest, RID_TMP); + emit_dst(as, MIPSI_SUBU, dest, left, right); + ir--; + dest = ra_dest(as, ir, RSET_GPR); + left = ra_alloc2(as, ir, RSET_GPR); + right = (left >> 8); left &= 255; + if (dest == left) { + Reg tmp = ra_scratch(as, rset_exclude(rset_exclude(RSET_GPR, left), right)); + emit_move(as, dest, tmp); + dest = tmp; + } + emit_dst(as, MIPSI_SLTU, RID_TMP, left, dest); + emit_dst(as, MIPSI_SUBU, dest, left, right); +} + +static void asm_neg64(ASMState *as, IRIns *ir) +{ + Reg dest = ra_dest(as, ir, RSET_GPR); + Reg left = ra_alloc1(as, ir->op1, RSET_GPR); + emit_dst(as, MIPSI_SUBU, dest, dest, RID_TMP); + emit_dst(as, MIPSI_SUBU, dest, RID_ZERO, left); + ir--; + dest = ra_dest(as, ir, RSET_GPR); + left = ra_alloc1(as, ir->op1, RSET_GPR); + emit_dst(as, MIPSI_SLTU, RID_TMP, RID_ZERO, dest); + emit_dst(as, MIPSI_SUBU, dest, RID_ZERO, left); +} +#endif + +static void asm_bnot(ASMState *as, IRIns *ir) +{ + Reg left, right, dest = ra_dest(as, ir, RSET_GPR); + IRIns *irl = IR(ir->op1); + if (mayfuse(as, ir->op1) && irl->o == IR_BOR) { + left = ra_alloc2(as, irl, RSET_GPR); + right = (left >> 8); left &= 255; + } else { + left = ra_hintalloc(as, ir->op1, dest, RSET_GPR); + right = RID_ZERO; + } + emit_dst(as, MIPSI_NOR, dest, left, right); +} + +static void asm_bswap(ASMState *as, IRIns *ir) +{ + Reg dest = ra_dest(as, ir, RSET_GPR); + Reg left = ra_alloc1(as, ir->op1, RSET_GPR); +#if LJ_32 + if ((as->flags & JIT_F_MIPSXXR2)) { + emit_dta(as, MIPSI_ROTR, dest, RID_TMP, 16); + emit_dst(as, MIPSI_WSBH, RID_TMP, 0, left); + } else { + Reg tmp = ra_scratch(as, rset_exclude(rset_exclude(RSET_GPR, left), dest)); + emit_dst(as, MIPSI_OR, dest, dest, tmp); + emit_dst(as, MIPSI_OR, dest, dest, RID_TMP); + emit_tsi(as, MIPSI_ANDI, dest, dest, 0xff00); + emit_dta(as, MIPSI_SLL, RID_TMP, RID_TMP, 8); + emit_dta(as, MIPSI_SRL, dest, left, 8); + emit_tsi(as, MIPSI_ANDI, RID_TMP, left, 0xff00); + emit_dst(as, MIPSI_OR, tmp, tmp, RID_TMP); + emit_dta(as, MIPSI_SRL, tmp, left, 24); + emit_dta(as, MIPSI_SLL, RID_TMP, left, 24); + } +#else + if (irt_is64(ir->t)) { + emit_dst(as, MIPSI_DSHD, dest, 0, RID_TMP); + emit_dst(as, MIPSI_DSBH, RID_TMP, 0, left); + } else { + emit_dta(as, MIPSI_ROTR, dest, RID_TMP, 16); + emit_dst(as, MIPSI_WSBH, RID_TMP, 0, left); + } +#endif +} + +static void asm_bitop(ASMState *as, IRIns *ir, MIPSIns mi, MIPSIns mik) +{ + Reg dest = ra_dest(as, ir, RSET_GPR); + Reg right, left = ra_hintalloc(as, ir->op1, dest, RSET_GPR); + if (irref_isk(ir->op2)) { + intptr_t k = get_kval(IR(ir->op2)); + if (checku16(k)) { + emit_tsi(as, mik, dest, left, k); + return; + } + } + right = ra_alloc1(as, ir->op2, rset_exclude(RSET_GPR, left)); + emit_dst(as, mi, dest, left, right); +} + +#define asm_band(as, ir) asm_bitop(as, ir, MIPSI_AND, MIPSI_ANDI) +#define asm_bor(as, ir) asm_bitop(as, ir, MIPSI_OR, MIPSI_ORI) +#define asm_bxor(as, ir) asm_bitop(as, ir, MIPSI_XOR, MIPSI_XORI) + +static void asm_bitshift(ASMState *as, IRIns *ir, MIPSIns mi, MIPSIns mik) +{ + Reg dest = ra_dest(as, ir, RSET_GPR); + if (irref_isk(ir->op2)) { /* Constant shifts. */ + uint32_t shift = (uint32_t)IR(ir->op2)->i; + if (LJ_64 && irt_is64(ir->t)) mik |= (shift & 32) ? MIPSI_D32 : MIPSI_D; + emit_dta(as, mik, dest, ra_hintalloc(as, ir->op1, dest, RSET_GPR), + (shift & 31)); + } else { + Reg right, left = ra_alloc2(as, ir, RSET_GPR); + right = (left >> 8); left &= 255; + if (LJ_64 && irt_is64(ir->t)) mi |= MIPSI_DV; + emit_dst(as, mi, dest, right, left); /* Shift amount is in rs. */ + } +} + +#define asm_bshl(as, ir) asm_bitshift(as, ir, MIPSI_SLLV, MIPSI_SLL) +#define asm_bshr(as, ir) asm_bitshift(as, ir, MIPSI_SRLV, MIPSI_SRL) +#define asm_bsar(as, ir) asm_bitshift(as, ir, MIPSI_SRAV, MIPSI_SRA) +#define asm_brol(as, ir) lua_assert(0) + +static void asm_bror(ASMState *as, IRIns *ir) +{ + if (LJ_64 || (as->flags & JIT_F_MIPSXXR2)) { + asm_bitshift(as, ir, MIPSI_ROTRV, MIPSI_ROTR); + } else { + Reg dest = ra_dest(as, ir, RSET_GPR); + if (irref_isk(ir->op2)) { /* Constant shifts. */ + uint32_t shift = (uint32_t)(IR(ir->op2)->i & 31); + Reg left = ra_hintalloc(as, ir->op1, dest, RSET_GPR); + emit_rotr(as, dest, left, RID_TMP, shift); + } else { + Reg right, left = ra_alloc2(as, ir, RSET_GPR); + right = (left >> 8); left &= 255; + emit_dst(as, MIPSI_OR, dest, dest, RID_TMP); + emit_dst(as, MIPSI_SRLV, dest, right, left); + emit_dst(as, MIPSI_SLLV, RID_TMP, RID_TMP, left); + emit_dst(as, MIPSI_SUBU, RID_TMP, ra_allock(as, 32, RSET_GPR), right); + } + } +} + +#if LJ_SOFTFP +static void asm_sfpmin_max(ASMState *as, IRIns *ir) +{ + CCallInfo ci = lj_ir_callinfo[(IROp)ir->o == IR_MIN ? IRCALL_lj_vm_sfmin : IRCALL_lj_vm_sfmax]; +#if LJ_64 + IRRef args[2]; + args[0] = ir->op1; + args[1] = ir->op2; +#else + IRRef args[4]; + args[0^LJ_BE] = ir->op1; + args[1^LJ_BE] = (ir+1)->op1; + args[2^LJ_BE] = ir->op2; + args[3^LJ_BE] = (ir+1)->op2; +#endif + asm_setupresult(as, ir, &ci); + emit_call(as, (void *)ci.func, 0); + ci.func = NULL; + asm_gencall(as, &ci, args); +} +#endif + +static void asm_min_max(ASMState *as, IRIns *ir, int ismax) +{ + if (!LJ_SOFTFP32 && irt_isnum(ir->t)) { +#if LJ_SOFTFP + asm_sfpmin_max(as, ir); +#else + Reg dest = ra_dest(as, ir, RSET_FPR); + Reg right, left = ra_alloc2(as, ir, RSET_FPR); + right = (left >> 8); left &= 255; + if (dest == left) { + emit_fg(as, MIPSI_MOVT_D, dest, right); + } else { + emit_fg(as, MIPSI_MOVF_D, dest, left); + if (dest != right) emit_fg(as, MIPSI_MOV_D, dest, right); + } + emit_fgh(as, MIPSI_C_OLT_D, 0, ismax ? left : right, ismax ? right : left); +#endif + } else { + Reg dest = ra_dest(as, ir, RSET_GPR); + Reg right, left = ra_alloc2(as, ir, RSET_GPR); + right = (left >> 8); left &= 255; + if (dest == left) { + emit_dst(as, MIPSI_MOVN, dest, right, RID_TMP); + } else { + emit_dst(as, MIPSI_MOVZ, dest, left, RID_TMP); + if (dest != right) emit_move(as, dest, right); + } + emit_dst(as, MIPSI_SLT, RID_TMP, + ismax ? left : right, ismax ? right : left); + } +} + +#define asm_min(as, ir) asm_min_max(as, ir, 0) +#define asm_max(as, ir) asm_min_max(as, ir, 1) + +/* -- Comparisons --------------------------------------------------------- */ + +#if LJ_SOFTFP +/* SFP comparisons. */ +static void asm_sfpcomp(ASMState *as, IRIns *ir) +{ + const CCallInfo *ci = &lj_ir_callinfo[IRCALL_softfp_cmp]; + RegSet drop = RSET_SCRATCH; + Reg r; +#if LJ_64 + IRRef args[2]; + args[0] = ir->op1; + args[1] = ir->op2; +#else + IRRef args[4]; + args[LJ_LE ? 0 : 1] = ir->op1; args[LJ_LE ? 1 : 0] = (ir+1)->op1; + args[LJ_LE ? 2 : 3] = ir->op2; args[LJ_LE ? 3 : 2] = (ir+1)->op2; +#endif + + for (r = REGARG_FIRSTGPR; r <= REGARG_FIRSTGPR+(LJ_64?1:3); r++) { + if (!rset_test(as->freeset, r) && + regcost_ref(as->cost[r]) == args[r-REGARG_FIRSTGPR]) + rset_clear(drop, r); + } + ra_evictset(as, drop); + + asm_setupresult(as, ir, ci); + + switch ((IROp)ir->o) { + case IR_LT: + asm_guard(as, MIPSI_BGEZ, RID_RET, 0); + break; + case IR_ULT: + asm_guard(as, MIPSI_BEQ, RID_RET, RID_TMP); + emit_loadi(as, RID_TMP, 1); + asm_guard(as, MIPSI_BEQ, RID_RET, RID_ZERO); + break; + case IR_GE: + asm_guard(as, MIPSI_BEQ, RID_RET, RID_TMP); + emit_loadi(as, RID_TMP, 2); + asm_guard(as, MIPSI_BLTZ, RID_RET, 0); + break; + case IR_LE: + asm_guard(as, MIPSI_BGTZ, RID_RET, 0); + break; + case IR_GT: + asm_guard(as, MIPSI_BEQ, RID_RET, RID_TMP); + emit_loadi(as, RID_TMP, 2); + asm_guard(as, MIPSI_BLEZ, RID_RET, 0); + break; + case IR_UGE: + asm_guard(as, MIPSI_BLTZ, RID_RET, 0); + break; + case IR_ULE: + asm_guard(as, MIPSI_BEQ, RID_RET, RID_TMP); + emit_loadi(as, RID_TMP, 1); + break; + case IR_UGT: case IR_ABC: + asm_guard(as, MIPSI_BLEZ, RID_RET, 0); + break; + case IR_EQ: case IR_NE: + asm_guard(as, (ir->o & 1) ? MIPSI_BEQ : MIPSI_BNE, RID_RET, RID_ZERO); + default: + break; + } + asm_gencall(as, ci, args); +} +#endif + +static void asm_comp(ASMState *as, IRIns *ir) +{ + /* ORDER IR: LT GE LE GT ULT UGE ULE UGT. */ + IROp op = ir->o; + if (!LJ_SOFTFP32 && irt_isnum(ir->t)) { +#if LJ_SOFTFP + asm_sfpcomp(as, ir); +#else + Reg right, left = ra_alloc2(as, ir, RSET_FPR); + right = (left >> 8); left &= 255; + asm_guard(as, (op&1) ? MIPSI_BC1T : MIPSI_BC1F, 0, 0); + emit_fgh(as, MIPSI_C_OLT_D + ((op&3) ^ ((op>>2)&1)), 0, left, right); +#endif + } else { + Reg right, left = ra_alloc1(as, ir->op1, RSET_GPR); + if (op == IR_ABC) op = IR_UGT; + if ((op&4) == 0 && irref_isk(ir->op2) && get_kval(IR(ir->op2)) == 0) { + MIPSIns mi = (op&2) ? ((op&1) ? MIPSI_BLEZ : MIPSI_BGTZ) : + ((op&1) ? MIPSI_BLTZ : MIPSI_BGEZ); + asm_guard(as, mi, left, 0); + } else { + if (irref_isk(ir->op2)) { + intptr_t k = get_kval(IR(ir->op2)); + if ((op&2)) k++; + if (checki16(k)) { + asm_guard(as, (op&1) ? MIPSI_BNE : MIPSI_BEQ, RID_TMP, RID_ZERO); + emit_tsi(as, (op&4) ? MIPSI_SLTIU : MIPSI_SLTI, + RID_TMP, left, k); + return; + } + } + right = ra_alloc1(as, ir->op2, rset_exclude(RSET_GPR, left)); + asm_guard(as, ((op^(op>>1))&1) ? MIPSI_BNE : MIPSI_BEQ, RID_TMP, RID_ZERO); + emit_dst(as, (op&4) ? MIPSI_SLTU : MIPSI_SLT, + RID_TMP, (op&2) ? right : left, (op&2) ? left : right); + } + } +} + +static void asm_equal(ASMState *as, IRIns *ir) +{ + Reg right, left = ra_alloc2(as, ir, (!LJ_SOFTFP && irt_isnum(ir->t)) ? + RSET_FPR : RSET_GPR); + right = (left >> 8); left &= 255; + if (!LJ_SOFTFP32 && irt_isnum(ir->t)) { +#if LJ_SOFTFP + asm_sfpcomp(as, ir); +#else + asm_guard(as, (ir->o & 1) ? MIPSI_BC1T : MIPSI_BC1F, 0, 0); + emit_fgh(as, MIPSI_C_EQ_D, 0, left, right); +#endif + } else { + asm_guard(as, (ir->o & 1) ? MIPSI_BEQ : MIPSI_BNE, left, right); + } +} + +#if LJ_32 && LJ_HASFFI +/* 64 bit integer comparisons. */ +static void asm_comp64(ASMState *as, IRIns *ir) +{ + /* ORDER IR: LT GE LE GT ULT UGE ULE UGT. */ + IROp op = (ir-1)->o; + MCLabel l_end; + Reg rightlo, leftlo, righthi, lefthi = ra_alloc2(as, ir, RSET_GPR); + righthi = (lefthi >> 8); lefthi &= 255; + leftlo = ra_alloc2(as, ir-1, + rset_exclude(rset_exclude(RSET_GPR, lefthi), righthi)); + rightlo = (leftlo >> 8); leftlo &= 255; + asm_guard(as, ((op^(op>>1))&1) ? MIPSI_BNE : MIPSI_BEQ, RID_TMP, RID_ZERO); + l_end = emit_label(as); + if (lefthi != righthi) + emit_dst(as, (op&4) ? MIPSI_SLTU : MIPSI_SLT, RID_TMP, + (op&2) ? righthi : lefthi, (op&2) ? lefthi : righthi); + emit_dst(as, MIPSI_SLTU, RID_TMP, + (op&2) ? rightlo : leftlo, (op&2) ? leftlo : rightlo); + if (lefthi != righthi) + emit_branch(as, MIPSI_BEQ, lefthi, righthi, l_end); +} + +static void asm_comp64eq(ASMState *as, IRIns *ir) +{ + Reg tmp, right, left = ra_alloc2(as, ir, RSET_GPR); + right = (left >> 8); left &= 255; + asm_guard(as, ((ir-1)->o & 1) ? MIPSI_BEQ : MIPSI_BNE, RID_TMP, RID_ZERO); + tmp = ra_scratch(as, rset_exclude(rset_exclude(RSET_GPR, left), right)); + emit_dst(as, MIPSI_OR, RID_TMP, RID_TMP, tmp); + emit_dst(as, MIPSI_XOR, tmp, left, right); + left = ra_alloc2(as, ir-1, RSET_GPR); + right = (left >> 8); left &= 255; + emit_dst(as, MIPSI_XOR, RID_TMP, left, right); +} +#endif + +/* -- Support for 64 bit ops in 32 bit mode ------------------------------- */ + +/* Hiword op of a split 64 bit op. Previous op must be the loword op. */ +static void asm_hiop(ASMState *as, IRIns *ir) +{ +#if LJ_32 && (LJ_HASFFI || LJ_SOFTFP) + /* HIOP is marked as a store because it needs its own DCE logic. */ + int uselo = ra_used(ir-1), usehi = ra_used(ir); /* Loword/hiword used? */ + if (LJ_UNLIKELY(!(as->flags & JIT_F_OPT_DCE))) uselo = usehi = 1; + if ((ir-1)->o == IR_CONV) { /* Conversions to/from 64 bit. */ + as->curins--; /* Always skip the CONV. */ +#if LJ_HASFFI && !LJ_SOFTFP + if (usehi || uselo) + asm_conv64(as, ir); + return; +#endif + } else if ((ir-1)->o < IR_EQ) { /* 64 bit integer comparisons. ORDER IR. */ + as->curins--; /* Always skip the loword comparison. */ +#if LJ_SOFTFP + if (!irt_isint(ir->t)) { + asm_sfpcomp(as, ir-1); + return; + } +#endif +#if LJ_HASFFI + asm_comp64(as, ir); +#endif + return; + } else if ((ir-1)->o <= IR_NE) { /* 64 bit integer comparisons. ORDER IR. */ + as->curins--; /* Always skip the loword comparison. */ +#if LJ_SOFTFP + if (!irt_isint(ir->t)) { + asm_sfpcomp(as, ir-1); + return; + } +#endif +#if LJ_HASFFI + asm_comp64eq(as, ir); +#endif + return; +#if LJ_SOFTFP + } else if ((ir-1)->o == IR_MIN || (ir-1)->o == IR_MAX) { + as->curins--; /* Always skip the loword min/max. */ + if (uselo || usehi) + asm_sfpmin_max(as, ir-1); + return; +#endif + } else if ((ir-1)->o == IR_XSTORE) { + as->curins--; /* Handle both stores here. */ + if ((ir-1)->r != RID_SINK) { + asm_xstore_(as, ir, LJ_LE ? 4 : 0); + asm_xstore_(as, ir-1, LJ_LE ? 0 : 4); + } + return; + } + if (!usehi) return; /* Skip unused hiword op for all remaining ops. */ + switch ((ir-1)->o) { +#if LJ_HASFFI + case IR_ADD: as->curins--; asm_add64(as, ir); break; + case IR_SUB: as->curins--; asm_sub64(as, ir); break; + case IR_NEG: as->curins--; asm_neg64(as, ir); break; +#endif +#if LJ_SOFTFP + case IR_SLOAD: case IR_ALOAD: case IR_HLOAD: case IR_ULOAD: case IR_VLOAD: + case IR_STRTO: + if (!uselo) + ra_allocref(as, ir->op1, RSET_GPR); /* Mark lo op as used. */ + break; +#endif + case IR_CALLN: + case IR_CALLS: + case IR_CALLXS: + if (!uselo) + ra_allocref(as, ir->op1, RID2RSET(RID_RETLO)); /* Mark lo op as used. */ + break; +#if LJ_SOFTFP + case IR_ASTORE: case IR_HSTORE: case IR_USTORE: case IR_TOSTR: +#endif + case IR_CNEWI: + /* Nothing to do here. Handled by lo op itself. */ + break; + default: lua_assert(0); break; + } +#else + UNUSED(as); UNUSED(ir); lua_assert(0); /* Unused without FFI. */ +#endif +} + +/* -- Profiling ----------------------------------------------------------- */ + +static void asm_prof(ASMState *as, IRIns *ir) +{ + UNUSED(ir); + asm_guard(as, MIPSI_BNE, RID_TMP, RID_ZERO); + emit_tsi(as, MIPSI_ANDI, RID_TMP, RID_TMP, HOOK_PROFILE); + emit_lsglptr(as, MIPSI_LBU, RID_TMP, + (int32_t)offsetof(global_State, hookmask)); +} + +/* -- Stack handling ------------------------------------------------------ */ + +/* Check Lua stack size for overflow. Use exit handler as fallback. */ +static void asm_stack_check(ASMState *as, BCReg topslot, + IRIns *irp, RegSet allow, ExitNo exitno) +{ + /* Try to get an unused temp. register, otherwise spill/restore RID_RET*. */ + Reg tmp, pbase = irp ? (ra_hasreg(irp->r) ? irp->r : RID_TMP) : RID_BASE; + ExitNo oldsnap = as->snapno; + rset_clear(allow, pbase); +#if LJ_32 + tmp = allow ? rset_pickbot(allow) : + (pbase == RID_RETHI ? RID_RETLO : RID_RETHI); +#else + tmp = allow ? rset_pickbot(allow) : RID_RET; +#endif + as->snapno = exitno; + asm_guard(as, MIPSI_BNE, RID_TMP, RID_ZERO); + as->snapno = oldsnap; + if (allow == RSET_EMPTY) /* Restore temp. register. */ + emit_tsi(as, MIPSI_AL, tmp, RID_SP, 0); + else + ra_modified(as, tmp); + emit_tsi(as, MIPSI_SLTIU, RID_TMP, RID_TMP, (int32_t)(8*topslot)); + emit_dst(as, MIPSI_ASUBU, RID_TMP, tmp, pbase); + emit_tsi(as, MIPSI_AL, tmp, tmp, offsetof(lua_State, maxstack)); + if (pbase == RID_TMP) + emit_getgl(as, RID_TMP, jit_base); + emit_getgl(as, tmp, cur_L); + if (allow == RSET_EMPTY) /* Spill temp. register. */ + emit_tsi(as, MIPSI_AS, tmp, RID_SP, 0); +} + +/* Restore Lua stack from on-trace state. */ +static void asm_stack_restore(ASMState *as, SnapShot *snap) +{ + SnapEntry *map = &as->T->snapmap[snap->mapofs]; +#if LJ_32 || defined(LUA_USE_ASSERT) + SnapEntry *flinks = &as->T->snapmap[snap_nextofs(as->T, snap)-1-LJ_FR2]; +#endif + MSize n, nent = snap->nent; + /* Store the value of all modified slots to the Lua stack. */ + for (n = 0; n < nent; n++) { + SnapEntry sn = map[n]; + BCReg s = snap_slot(sn); + int32_t ofs = 8*((int32_t)s-1-LJ_FR2); + IRRef ref = snap_ref(sn); + IRIns *ir = IR(ref); + if ((sn & SNAP_NORESTORE)) + continue; + if (irt_isnum(ir->t)) { +#if LJ_SOFTFP32 + Reg tmp; + RegSet allow = rset_exclude(RSET_GPR, RID_BASE); + lua_assert(irref_isk(ref)); /* LJ_SOFTFP: must be a number constant. */ + tmp = ra_allock(as, (int32_t)ir_knum(ir)->u32.lo, allow); + emit_tsi(as, MIPSI_SW, tmp, RID_BASE, ofs+(LJ_BE?4:0)); + if (rset_test(as->freeset, tmp+1)) allow = RID2RSET(tmp+1); + tmp = ra_allock(as, (int32_t)ir_knum(ir)->u32.hi, allow); + emit_tsi(as, MIPSI_SW, tmp, RID_BASE, ofs+(LJ_BE?0:4)); +#elif LJ_SOFTFP /* && LJ_64 */ + Reg src = ra_alloc1(as, ref, rset_exclude(RSET_GPR, RID_BASE)); + emit_tsi(as, MIPSI_SD, src, RID_BASE, ofs); +#else + Reg src = ra_alloc1(as, ref, RSET_FPR); + emit_hsi(as, MIPSI_SDC1, src, RID_BASE, ofs); +#endif + } else { +#if LJ_32 + RegSet allow = rset_exclude(RSET_GPR, RID_BASE); + Reg type; + lua_assert(irt_ispri(ir->t) || irt_isaddr(ir->t) || irt_isinteger(ir->t)); + if (!irt_ispri(ir->t)) { + Reg src = ra_alloc1(as, ref, allow); + rset_clear(allow, src); + emit_tsi(as, MIPSI_SW, src, RID_BASE, ofs+(LJ_BE?4:0)); + } + if ((sn & (SNAP_CONT|SNAP_FRAME))) { + if (s == 0) continue; /* Do not overwrite link to previous frame. */ + type = ra_allock(as, (int32_t)(*flinks--), allow); +#if LJ_SOFTFP + } else if ((sn & SNAP_SOFTFPNUM)) { + type = ra_alloc1(as, ref+1, rset_exclude(RSET_GPR, RID_BASE)); +#endif + } else { + type = ra_allock(as, (int32_t)irt_toitype(ir->t), allow); + } + emit_tsi(as, MIPSI_SW, type, RID_BASE, ofs+(LJ_BE?0:4)); +#else + asm_tvstore64(as, RID_BASE, ofs, ref); +#endif + } + checkmclim(as); + } + lua_assert(map + nent == flinks); +} + +/* -- GC handling --------------------------------------------------------- */ + +/* Check GC threshold and do one or more GC steps. */ +static void asm_gc_check(ASMState *as) +{ + const CCallInfo *ci = &lj_ir_callinfo[IRCALL_lj_gc_step_jit]; + IRRef args[2]; + MCLabel l_end; + Reg tmp; + ra_evictset(as, RSET_SCRATCH); + l_end = emit_label(as); + /* Exit trace if in GCSatomic or GCSfinalize. Avoids syncing GC objects. */ + /* Assumes asm_snap_prep() already done. */ + asm_guard(as, MIPSI_BNE, RID_RET, RID_ZERO); + args[0] = ASMREF_TMP1; /* global_State *g */ + args[1] = ASMREF_TMP2; /* MSize steps */ + asm_gencall(as, ci, args); + emit_tsi(as, MIPSI_AADDIU, ra_releasetmp(as, ASMREF_TMP1), RID_JGL, -32768); + tmp = ra_releasetmp(as, ASMREF_TMP2); + emit_loadi(as, tmp, as->gcsteps); + /* Jump around GC step if GC total < GC threshold. */ + emit_branch(as, MIPSI_BNE, RID_TMP, RID_ZERO, l_end); + emit_dst(as, MIPSI_SLTU, RID_TMP, RID_TMP, tmp); + emit_getgl(as, tmp, gc.threshold); + emit_getgl(as, RID_TMP, gc.total); + as->gcsteps = 0; + checkmclim(as); +} + +/* -- Loop handling ------------------------------------------------------- */ + +/* Fixup the loop branch. */ +static void asm_loop_fixup(ASMState *as) +{ + MCode *p = as->mctop; + MCode *target = as->mcp; + p[-1] = MIPSI_NOP; + if (as->loopinv) { /* Inverted loop branch? */ + /* asm_guard already inverted the cond branch. Only patch the target. */ + p[-3] |= ((target-p+2) & 0x0000ffffu); + } else { + p[-2] = MIPSI_J|(((uintptr_t)target>>2)&0x03ffffffu); + } +} + +/* -- Head of trace ------------------------------------------------------- */ + +/* Coalesce BASE register for a root trace. */ +static void asm_head_root_base(ASMState *as) +{ + IRIns *ir = IR(REF_BASE); + Reg r = ir->r; + if (as->loopinv) as->mctop--; + if (ra_hasreg(r)) { + ra_free(as, r); + if (rset_test(as->modset, r) || irt_ismarked(ir->t)) + ir->r = RID_INIT; /* No inheritance for modified BASE register. */ + if (r != RID_BASE) + emit_move(as, r, RID_BASE); + } +} + +/* Coalesce BASE register for a side trace. */ +static RegSet asm_head_side_base(ASMState *as, IRIns *irp, RegSet allow) +{ + IRIns *ir = IR(REF_BASE); + Reg r = ir->r; + if (as->loopinv) as->mctop--; + if (ra_hasreg(r)) { + ra_free(as, r); + if (rset_test(as->modset, r) || irt_ismarked(ir->t)) + ir->r = RID_INIT; /* No inheritance for modified BASE register. */ + if (irp->r == r) { + rset_clear(allow, r); /* Mark same BASE register as coalesced. */ + } else if (ra_hasreg(irp->r) && rset_test(as->freeset, irp->r)) { + rset_clear(allow, irp->r); + emit_move(as, r, irp->r); /* Move from coalesced parent reg. */ + } else { + emit_getgl(as, r, jit_base); /* Otherwise reload BASE. */ + } + } + return allow; +} + +/* -- Tail of trace ------------------------------------------------------- */ + +/* Fixup the tail code. */ +static void asm_tail_fixup(ASMState *as, TraceNo lnk) +{ + MCode *target = lnk ? traceref(as->J,lnk)->mcode : (MCode *)lj_vm_exit_interp; + int32_t spadj = as->T->spadjust; + MCode *p = as->mctop-1; + *p = spadj ? (MIPSI_AADDIU|MIPSF_T(RID_SP)|MIPSF_S(RID_SP)|spadj) : MIPSI_NOP; + p[-1] = MIPSI_J|(((uintptr_t)target>>2)&0x03ffffffu); +} + +/* Prepare tail of code. */ +static void asm_tail_prep(ASMState *as) +{ + as->mcp = as->mctop-2; /* Leave room for branch plus nop or stack adj. */ + as->invmcp = as->loopref ? as->mcp : NULL; +} + +/* -- Trace setup --------------------------------------------------------- */ + +/* Ensure there are enough stack slots for call arguments. */ +static Reg asm_setup_call_slots(ASMState *as, IRIns *ir, const CCallInfo *ci) +{ + IRRef args[CCI_NARGS_MAX*2]; + uint32_t i, nargs = CCI_XNARGS(ci); +#if LJ_32 + int nslots = 4, ngpr = REGARG_NUMGPR, nfpr = REGARG_NUMFPR; +#else + int nslots = 0, ngpr = REGARG_NUMGPR; +#endif + asm_collectargs(as, ir, ci, args); + for (i = 0; i < nargs; i++) { +#if LJ_32 + if (!LJ_SOFTFP && args[i] && irt_isfp(IR(args[i])->t) && + nfpr > 0 && !(ci->flags & CCI_VARARG)) { + nfpr--; + ngpr -= irt_isnum(IR(args[i])->t) ? 2 : 1; + } else if (!LJ_SOFTFP && args[i] && irt_isnum(IR(args[i])->t)) { + nfpr = 0; + ngpr = ngpr & ~1; + if (ngpr > 0) ngpr -= 2; else nslots = (nslots+3) & ~1; + } else { + nfpr = 0; + if (ngpr > 0) ngpr--; else nslots++; + } +#else + if (ngpr > 0) ngpr--; else nslots += 2; +#endif + } + if (nslots > as->evenspill) /* Leave room for args in stack slots. */ + as->evenspill = nslots; + return irt_isfp(ir->t) ? REGSP_HINT(RID_FPRET) : REGSP_HINT(RID_RET); +} + +static void asm_setup_target(ASMState *as) +{ + asm_sparejump_setup(as); + asm_exitstub_setup(as); +} + +/* -- Trace patching ------------------------------------------------------ */ + +/* Patch exit jumps of existing machine code to a new target. */ +void lj_asm_patchexit(jit_State *J, GCtrace *T, ExitNo exitno, MCode *target) +{ + MCode *p = T->mcode; + MCode *pe = (MCode *)((char *)p + T->szmcode); + MCode *px = exitstub_trace_addr(T, exitno); + MCode *cstart = NULL, *cstop = NULL; + MCode *mcarea = lj_mcode_patch(J, p, 0); + MCode exitload = MIPSI_LI | MIPSF_T(RID_TMP) | exitno; + MCode tjump = MIPSI_J|(((uintptr_t)target>>2)&0x03ffffffu); + for (p++; p < pe; p++) { + if (*p == exitload) { /* Look for load of exit number. */ + /* Look for exitstub branch. Yes, this covers all used branch variants. */ + if (((p[-1] ^ (px-p)) & 0xffffu) == 0 && + ((p[-1] & 0xf0000000u) == MIPSI_BEQ || + (p[-1] & 0xfc1e0000u) == MIPSI_BLTZ || + (p[-1] & 0xffe00000u) == MIPSI_BC1F)) { + ptrdiff_t delta = target - p; + if (((delta + 0x8000) >> 16) == 0) { /* Patch in-range branch. */ + patchbranch: + p[-1] = (p[-1] & 0xffff0000u) | (delta & 0xffffu); + *p = MIPSI_NOP; /* Replace the load of the exit number. */ + cstop = p; + if (!cstart) cstart = p-1; + } else { /* Branch out of range. Use spare jump slot in mcarea. */ + int i; + for (i = (int)(sizeof(MCLink)/sizeof(MCode)); + i < (int)(sizeof(MCLink)/sizeof(MCode)+MIPS_SPAREJUMP*2); + i += 2) { + if (mcarea[i] == tjump) { + delta = mcarea+i - p; + goto patchbranch; + } else if (mcarea[i] == MIPSI_NOP) { + mcarea[i] = tjump; + cstart = mcarea+i; + delta = mcarea+i - p; + goto patchbranch; + } + } + /* Ignore jump slot overflow. Child trace is simply not attached. */ + } + } else if (p+1 == pe) { + /* Patch NOP after code for inverted loop branch. Use of J is ok. */ + lua_assert(p[1] == MIPSI_NOP); + p[1] = tjump; + *p = MIPSI_NOP; /* Replace the load of the exit number. */ + cstop = p+2; + if (!cstart) cstart = p+1; + } + } + } + if (cstart) lj_mcode_sync(cstart, cstop); + lj_mcode_patch(J, mcarea, 1); +} + diff --git a/lib/LuaJIT/lj_asm_ppc.h b/lib/LuaJIT/lj_asm_ppc.h new file mode 100644 index 0000000..1955429 --- /dev/null +++ b/lib/LuaJIT/lj_asm_ppc.h @@ -0,0 +1,2251 @@ +/* +** PPC IR assembler (SSA IR -> machine code). +** Copyright (C) 2005-2017 Mike Pall. See Copyright Notice in luajit.h +*/ + +/* -- Register allocator extensions --------------------------------------- */ + +/* Allocate a register with a hint. */ +static Reg ra_hintalloc(ASMState *as, IRRef ref, Reg hint, RegSet allow) +{ + Reg r = IR(ref)->r; + if (ra_noreg(r)) { + if (!ra_hashint(r) && !iscrossref(as, ref)) + ra_sethint(IR(ref)->r, hint); /* Propagate register hint. */ + r = ra_allocref(as, ref, allow); + } + ra_noweak(as, r); + return r; +} + +/* Allocate two source registers for three-operand instructions. */ +static Reg ra_alloc2(ASMState *as, IRIns *ir, RegSet allow) +{ + IRIns *irl = IR(ir->op1), *irr = IR(ir->op2); + Reg left = irl->r, right = irr->r; + if (ra_hasreg(left)) { + ra_noweak(as, left); + if (ra_noreg(right)) + right = ra_allocref(as, ir->op2, rset_exclude(allow, left)); + else + ra_noweak(as, right); + } else if (ra_hasreg(right)) { + ra_noweak(as, right); + left = ra_allocref(as, ir->op1, rset_exclude(allow, right)); + } else if (ra_hashint(right)) { + right = ra_allocref(as, ir->op2, allow); + left = ra_alloc1(as, ir->op1, rset_exclude(allow, right)); + } else { + left = ra_allocref(as, ir->op1, allow); + right = ra_alloc1(as, ir->op2, rset_exclude(allow, left)); + } + return left | (right << 8); +} + +/* -- Guard handling ------------------------------------------------------ */ + +/* Setup exit stubs after the end of each trace. */ +static void asm_exitstub_setup(ASMState *as, ExitNo nexits) +{ + ExitNo i; + MCode *mxp = as->mctop; + if (mxp - (nexits + 3 + MCLIM_REDZONE) < as->mclim) + asm_mclimit(as); + /* 1: mflr r0; bl ->vm_exit_handler; li r0, traceno; bl <1; bl <1; ... */ + for (i = nexits-1; (int32_t)i >= 0; i--) + *--mxp = PPCI_BL|(((-3-i)&0x00ffffffu)<<2); + *--mxp = PPCI_LI|PPCF_T(RID_TMP)|as->T->traceno; /* Read by exit handler. */ + mxp--; + *mxp = PPCI_BL|((((MCode *)(void *)lj_vm_exit_handler-mxp)&0x00ffffffu)<<2); + *--mxp = PPCI_MFLR|PPCF_T(RID_TMP); + as->mctop = mxp; +} + +static MCode *asm_exitstub_addr(ASMState *as, ExitNo exitno) +{ + /* Keep this in-sync with exitstub_trace_addr(). */ + return as->mctop + exitno + 3; +} + +/* Emit conditional branch to exit for guard. */ +static void asm_guardcc(ASMState *as, PPCCC cc) +{ + MCode *target = asm_exitstub_addr(as, as->snapno); + MCode *p = as->mcp; + if (LJ_UNLIKELY(p == as->invmcp)) { + as->loopinv = 1; + *p = PPCI_B | (((target-p) & 0x00ffffffu) << 2); + emit_condbranch(as, PPCI_BC, cc^4, p); + return; + } + emit_condbranch(as, PPCI_BC, cc, target); +} + +/* -- Operand fusion ------------------------------------------------------ */ + +/* Limit linear search to this distance. Avoids O(n^2) behavior. */ +#define CONFLICT_SEARCH_LIM 31 + +/* Check if there's no conflicting instruction between curins and ref. */ +static int noconflict(ASMState *as, IRRef ref, IROp conflict) +{ + IRIns *ir = as->ir; + IRRef i = as->curins; + if (i > ref + CONFLICT_SEARCH_LIM) + return 0; /* Give up, ref is too far away. */ + while (--i > ref) + if (ir[i].o == conflict) + return 0; /* Conflict found. */ + return 1; /* Ok, no conflict. */ +} + +/* Fuse the array base of colocated arrays. */ +static int32_t asm_fuseabase(ASMState *as, IRRef ref) +{ + IRIns *ir = IR(ref); + if (ir->o == IR_TNEW && ir->op1 <= LJ_MAX_COLOSIZE && + !neverfuse(as) && noconflict(as, ref, IR_NEWREF)) + return (int32_t)sizeof(GCtab); + return 0; +} + +/* Indicates load/store indexed is ok. */ +#define AHUREF_LSX ((int32_t)0x80000000) + +/* Fuse array/hash/upvalue reference into register+offset operand. */ +static Reg asm_fuseahuref(ASMState *as, IRRef ref, int32_t *ofsp, RegSet allow) +{ + IRIns *ir = IR(ref); + if (ra_noreg(ir->r)) { + if (ir->o == IR_AREF) { + if (mayfuse(as, ref)) { + if (irref_isk(ir->op2)) { + IRRef tab = IR(ir->op1)->op1; + int32_t ofs = asm_fuseabase(as, tab); + IRRef refa = ofs ? tab : ir->op1; + ofs += 8*IR(ir->op2)->i; + if (checki16(ofs)) { + *ofsp = ofs; + return ra_alloc1(as, refa, allow); + } + } + if (*ofsp == AHUREF_LSX) { + Reg base = ra_alloc1(as, ir->op1, allow); + Reg idx = ra_alloc1(as, ir->op2, rset_exclude(RSET_GPR, base)); + return base | (idx << 8); + } + } + } else if (ir->o == IR_HREFK) { + if (mayfuse(as, ref)) { + int32_t ofs = (int32_t)(IR(ir->op2)->op2 * sizeof(Node)); + if (checki16(ofs)) { + *ofsp = ofs; + return ra_alloc1(as, ir->op1, allow); + } + } + } else if (ir->o == IR_UREFC) { + if (irref_isk(ir->op1)) { + GCfunc *fn = ir_kfunc(IR(ir->op1)); + int32_t ofs = i32ptr(&gcref(fn->l.uvptr[(ir->op2 >> 8)])->uv.tv); + int32_t jgl = (intptr_t)J2G(as->J); + if ((uint32_t)(ofs-jgl) < 65536) { + *ofsp = ofs-jgl-32768; + return RID_JGL; + } else { + *ofsp = (int16_t)ofs; + return ra_allock(as, ofs-(int16_t)ofs, allow); + } + } + } + } + *ofsp = 0; + return ra_alloc1(as, ref, allow); +} + +/* Fuse XLOAD/XSTORE reference into load/store operand. */ +static void asm_fusexref(ASMState *as, PPCIns pi, Reg rt, IRRef ref, + RegSet allow, int32_t ofs) +{ + IRIns *ir = IR(ref); + Reg base; + if (ra_noreg(ir->r) && canfuse(as, ir)) { + if (ir->o == IR_ADD) { + int32_t ofs2; + if (irref_isk(ir->op2) && (ofs2 = ofs + IR(ir->op2)->i, checki16(ofs2))) { + ofs = ofs2; + ref = ir->op1; + } else if (ofs == 0) { + Reg right, left = ra_alloc2(as, ir, allow); + right = (left >> 8); left &= 255; + emit_fab(as, PPCI_LWZX | ((pi >> 20) & 0x780), rt, left, right); + return; + } + } else if (ir->o == IR_STRREF) { + lua_assert(ofs == 0); + ofs = (int32_t)sizeof(GCstr); + if (irref_isk(ir->op2)) { + ofs += IR(ir->op2)->i; + ref = ir->op1; + } else if (irref_isk(ir->op1)) { + ofs += IR(ir->op1)->i; + ref = ir->op2; + } else { + /* NYI: Fuse ADD with constant. */ + Reg tmp, right, left = ra_alloc2(as, ir, allow); + right = (left >> 8); left &= 255; + tmp = ra_scratch(as, rset_exclude(rset_exclude(allow, left), right)); + emit_fai(as, pi, rt, tmp, ofs); + emit_tab(as, PPCI_ADD, tmp, left, right); + return; + } + if (!checki16(ofs)) { + Reg left = ra_alloc1(as, ref, allow); + Reg right = ra_allock(as, ofs, rset_exclude(allow, left)); + emit_fab(as, PPCI_LWZX | ((pi >> 20) & 0x780), rt, left, right); + return; + } + } + } + base = ra_alloc1(as, ref, allow); + emit_fai(as, pi, rt, base, ofs); +} + +/* Fuse XLOAD/XSTORE reference into indexed-only load/store operand. */ +static void asm_fusexrefx(ASMState *as, PPCIns pi, Reg rt, IRRef ref, + RegSet allow) +{ + IRIns *ira = IR(ref); + Reg right, left; + if (canfuse(as, ira) && ira->o == IR_ADD && ra_noreg(ira->r)) { + left = ra_alloc2(as, ira, allow); + right = (left >> 8); left &= 255; + } else { + right = ra_alloc1(as, ref, allow); + left = RID_R0; + } + emit_tab(as, pi, rt, left, right); +} + +#if !LJ_SOFTFP +/* Fuse to multiply-add/sub instruction. */ +static int asm_fusemadd(ASMState *as, IRIns *ir, PPCIns pi, PPCIns pir) +{ + IRRef lref = ir->op1, rref = ir->op2; + IRIns *irm; + if (lref != rref && + ((mayfuse(as, lref) && (irm = IR(lref), irm->o == IR_MUL) && + ra_noreg(irm->r)) || + (mayfuse(as, rref) && (irm = IR(rref), irm->o == IR_MUL) && + (rref = lref, pi = pir, ra_noreg(irm->r))))) { + Reg dest = ra_dest(as, ir, RSET_FPR); + Reg add = ra_alloc1(as, rref, RSET_FPR); + Reg right, left = ra_alloc2(as, irm, rset_exclude(RSET_FPR, add)); + right = (left >> 8); left &= 255; + emit_facb(as, pi, dest, left, right, add); + return 1; + } + return 0; +} +#endif + +/* -- Calls --------------------------------------------------------------- */ + +/* Generate a call to a C function. */ +static void asm_gencall(ASMState *as, const CCallInfo *ci, IRRef *args) +{ + uint32_t n, nargs = CCI_XNARGS(ci); + int32_t ofs = 8; + Reg gpr = REGARG_FIRSTGPR; +#if !LJ_SOFTFP + Reg fpr = REGARG_FIRSTFPR; +#endif + if ((void *)ci->func) + emit_call(as, (void *)ci->func); + for (n = 0; n < nargs; n++) { /* Setup args. */ + IRRef ref = args[n]; + if (ref) { + IRIns *ir = IR(ref); +#if !LJ_SOFTFP + if (irt_isfp(ir->t)) { + if (fpr <= REGARG_LASTFPR) { + lua_assert(rset_test(as->freeset, fpr)); /* Already evicted. */ + ra_leftov(as, fpr, ref); + fpr++; + } else { + Reg r = ra_alloc1(as, ref, RSET_FPR); + if (irt_isnum(ir->t)) ofs = (ofs + 4) & ~4; + emit_spstore(as, ir, r, ofs); + ofs += irt_isnum(ir->t) ? 8 : 4; + } + } else +#endif + { + if (gpr <= REGARG_LASTGPR) { + lua_assert(rset_test(as->freeset, gpr)); /* Already evicted. */ + ra_leftov(as, gpr, ref); + gpr++; + } else { + Reg r = ra_alloc1(as, ref, RSET_GPR); + emit_spstore(as, ir, r, ofs); + ofs += 4; + } + } + } else { + if (gpr <= REGARG_LASTGPR) + gpr++; + else + ofs += 4; + } + checkmclim(as); + } +#if !LJ_SOFTFP + if ((ci->flags & CCI_VARARG)) /* Vararg calls need to know about FPR use. */ + emit_tab(as, fpr == REGARG_FIRSTFPR ? PPCI_CRXOR : PPCI_CREQV, 6, 6, 6); +#endif +} + +/* Setup result reg/sp for call. Evict scratch regs. */ +static void asm_setupresult(ASMState *as, IRIns *ir, const CCallInfo *ci) +{ + RegSet drop = RSET_SCRATCH; + int hiop = ((ir+1)->o == IR_HIOP && !irt_isnil((ir+1)->t)); +#if !LJ_SOFTFP + if ((ci->flags & CCI_NOFPRCLOBBER)) + drop &= ~RSET_FPR; +#endif + if (ra_hasreg(ir->r)) + rset_clear(drop, ir->r); /* Dest reg handled below. */ + if (hiop && ra_hasreg((ir+1)->r)) + rset_clear(drop, (ir+1)->r); /* Dest reg handled below. */ + ra_evictset(as, drop); /* Evictions must be performed first. */ + if (ra_used(ir)) { + lua_assert(!irt_ispri(ir->t)); + if (!LJ_SOFTFP && irt_isfp(ir->t)) { + if ((ci->flags & CCI_CASTU64)) { + /* Use spill slot or temp slots. */ + int32_t ofs = ir->s ? sps_scale(ir->s) : SPOFS_TMP; + Reg dest = ir->r; + if (ra_hasreg(dest)) { + ra_free(as, dest); + ra_modified(as, dest); + emit_fai(as, PPCI_LFD, dest, RID_SP, ofs); + } + emit_tai(as, PPCI_STW, RID_RETHI, RID_SP, ofs); + emit_tai(as, PPCI_STW, RID_RETLO, RID_SP, ofs+4); + } else { + ra_destreg(as, ir, RID_FPRET); + } +#if LJ_32 + } else if (hiop) { + ra_destpair(as, ir); +#endif + } else { + ra_destreg(as, ir, RID_RET); + } + } +} + +static void asm_callx(ASMState *as, IRIns *ir) +{ + IRRef args[CCI_NARGS_MAX*2]; + CCallInfo ci; + IRRef func; + IRIns *irf; + ci.flags = asm_callx_flags(as, ir); + asm_collectargs(as, ir, &ci, args); + asm_setupresult(as, ir, &ci); + func = ir->op2; irf = IR(func); + if (irf->o == IR_CARG) { func = irf->op1; irf = IR(func); } + if (irref_isk(func)) { /* Call to constant address. */ + ci.func = (ASMFunction)(void *)(intptr_t)(irf->i); + } else { /* Need a non-argument register for indirect calls. */ + RegSet allow = RSET_GPR & ~RSET_RANGE(RID_R0, REGARG_LASTGPR+1); + Reg freg = ra_alloc1(as, func, allow); + *--as->mcp = PPCI_BCTRL; + *--as->mcp = PPCI_MTCTR | PPCF_T(freg); + ci.func = (ASMFunction)(void *)0; + } + asm_gencall(as, &ci, args); +} + +/* -- Returns ------------------------------------------------------------- */ + +/* Return to lower frame. Guard that it goes to the right spot. */ +static void asm_retf(ASMState *as, IRIns *ir) +{ + Reg base = ra_alloc1(as, REF_BASE, RSET_GPR); + void *pc = ir_kptr(IR(ir->op2)); + int32_t delta = 1+LJ_FR2+bc_a(*((const BCIns *)pc - 1)); + as->topslot -= (BCReg)delta; + if ((int32_t)as->topslot < 0) as->topslot = 0; + irt_setmark(IR(REF_BASE)->t); /* Children must not coalesce with BASE reg. */ + emit_setgl(as, base, jit_base); + emit_addptr(as, base, -8*delta); + asm_guardcc(as, CC_NE); + emit_ab(as, PPCI_CMPW, RID_TMP, + ra_allock(as, i32ptr(pc), rset_exclude(RSET_GPR, base))); + emit_tai(as, PPCI_LWZ, RID_TMP, base, -8); +} + +/* -- Type conversions ---------------------------------------------------- */ + +#if !LJ_SOFTFP +static void asm_tointg(ASMState *as, IRIns *ir, Reg left) +{ + RegSet allow = RSET_FPR; + Reg tmp = ra_scratch(as, rset_clear(allow, left)); + Reg fbias = ra_scratch(as, rset_clear(allow, tmp)); + Reg dest = ra_dest(as, ir, RSET_GPR); + Reg hibias = ra_allock(as, 0x43300000, rset_exclude(RSET_GPR, dest)); + asm_guardcc(as, CC_NE); + emit_fab(as, PPCI_FCMPU, 0, tmp, left); + emit_fab(as, PPCI_FSUB, tmp, tmp, fbias); + emit_fai(as, PPCI_LFD, tmp, RID_SP, SPOFS_TMP); + emit_tai(as, PPCI_STW, RID_TMP, RID_SP, SPOFS_TMPLO); + emit_tai(as, PPCI_STW, hibias, RID_SP, SPOFS_TMPHI); + emit_asi(as, PPCI_XORIS, RID_TMP, dest, 0x8000); + emit_tai(as, PPCI_LWZ, dest, RID_SP, SPOFS_TMPLO); + emit_lsptr(as, PPCI_LFS, (fbias & 31), + (void *)&as->J->k32[LJ_K32_2P52_2P31], RSET_GPR); + emit_fai(as, PPCI_STFD, tmp, RID_SP, SPOFS_TMP); + emit_fb(as, PPCI_FCTIWZ, tmp, left); +} + +static void asm_tobit(ASMState *as, IRIns *ir) +{ + RegSet allow = RSET_FPR; + Reg dest = ra_dest(as, ir, RSET_GPR); + Reg left = ra_alloc1(as, ir->op1, allow); + Reg right = ra_alloc1(as, ir->op2, rset_clear(allow, left)); + Reg tmp = ra_scratch(as, rset_clear(allow, right)); + emit_tai(as, PPCI_LWZ, dest, RID_SP, SPOFS_TMPLO); + emit_fai(as, PPCI_STFD, tmp, RID_SP, SPOFS_TMP); + emit_fab(as, PPCI_FADD, tmp, left, right); +} +#endif + +static void asm_conv(ASMState *as, IRIns *ir) +{ + IRType st = (IRType)(ir->op2 & IRCONV_SRCMASK); +#if !LJ_SOFTFP + int stfp = (st == IRT_NUM || st == IRT_FLOAT); +#endif + IRRef lref = ir->op1; + lua_assert(!(irt_isint64(ir->t) || + (st == IRT_I64 || st == IRT_U64))); /* Handled by SPLIT. */ +#if LJ_SOFTFP + /* FP conversions are handled by SPLIT. */ + lua_assert(!irt_isfp(ir->t) && !(st == IRT_NUM || st == IRT_FLOAT)); + /* Can't check for same types: SPLIT uses CONV int.int + BXOR for sfp NEG. */ +#else + lua_assert(irt_type(ir->t) != st); + if (irt_isfp(ir->t)) { + Reg dest = ra_dest(as, ir, RSET_FPR); + if (stfp) { /* FP to FP conversion. */ + if (st == IRT_NUM) /* double -> float conversion. */ + emit_fb(as, PPCI_FRSP, dest, ra_alloc1(as, lref, RSET_FPR)); + else /* float -> double conversion is a no-op on PPC. */ + ra_leftov(as, dest, lref); /* Do nothing, but may need to move regs. */ + } else { /* Integer to FP conversion. */ + /* IRT_INT: Flip hibit, bias with 2^52, subtract 2^52+2^31. */ + /* IRT_U32: Bias with 2^52, subtract 2^52. */ + RegSet allow = RSET_GPR; + Reg left = ra_alloc1(as, lref, allow); + Reg hibias = ra_allock(as, 0x43300000, rset_clear(allow, left)); + Reg fbias = ra_scratch(as, rset_exclude(RSET_FPR, dest)); + if (irt_isfloat(ir->t)) emit_fb(as, PPCI_FRSP, dest, dest); + emit_fab(as, PPCI_FSUB, dest, dest, fbias); + emit_fai(as, PPCI_LFD, dest, RID_SP, SPOFS_TMP); + emit_lsptr(as, PPCI_LFS, (fbias & 31), + &as->J->k32[st == IRT_U32 ? LJ_K32_2P52 : LJ_K32_2P52_2P31], + rset_clear(allow, hibias)); + emit_tai(as, PPCI_STW, st == IRT_U32 ? left : RID_TMP, + RID_SP, SPOFS_TMPLO); + emit_tai(as, PPCI_STW, hibias, RID_SP, SPOFS_TMPHI); + if (st != IRT_U32) emit_asi(as, PPCI_XORIS, RID_TMP, left, 0x8000); + } + } else if (stfp) { /* FP to integer conversion. */ + if (irt_isguard(ir->t)) { + /* Checked conversions are only supported from number to int. */ + lua_assert(irt_isint(ir->t) && st == IRT_NUM); + asm_tointg(as, ir, ra_alloc1(as, lref, RSET_FPR)); + } else { + Reg dest = ra_dest(as, ir, RSET_GPR); + Reg left = ra_alloc1(as, lref, RSET_FPR); + Reg tmp = ra_scratch(as, rset_exclude(RSET_FPR, left)); + if (irt_isu32(ir->t)) { + /* Convert both x and x-2^31 to int and merge results. */ + Reg tmpi = ra_scratch(as, rset_exclude(RSET_GPR, dest)); + emit_asb(as, PPCI_OR, dest, dest, tmpi); /* Select with mask idiom. */ + emit_asb(as, PPCI_AND, tmpi, tmpi, RID_TMP); + emit_asb(as, PPCI_ANDC, dest, dest, RID_TMP); + emit_tai(as, PPCI_LWZ, tmpi, RID_SP, SPOFS_TMPLO); /* tmp = (int)(x) */ + emit_tai(as, PPCI_ADDIS, dest, dest, 0x8000); /* dest += 2^31 */ + emit_asb(as, PPCI_SRAWI, RID_TMP, dest, 31); /* mask = -(dest < 0) */ + emit_fai(as, PPCI_STFD, tmp, RID_SP, SPOFS_TMP); + emit_tai(as, PPCI_LWZ, dest, + RID_SP, SPOFS_TMPLO); /* dest = (int)(x-2^31) */ + emit_fb(as, PPCI_FCTIWZ, tmp, left); + emit_fai(as, PPCI_STFD, tmp, RID_SP, SPOFS_TMP); + emit_fb(as, PPCI_FCTIWZ, tmp, tmp); + emit_fab(as, PPCI_FSUB, tmp, left, tmp); + emit_lsptr(as, PPCI_LFS, (tmp & 31), + (void *)&as->J->k32[LJ_K32_2P31], RSET_GPR); + } else { + emit_tai(as, PPCI_LWZ, dest, RID_SP, SPOFS_TMPLO); + emit_fai(as, PPCI_STFD, tmp, RID_SP, SPOFS_TMP); + emit_fb(as, PPCI_FCTIWZ, tmp, left); + } + } + } else +#endif + { + Reg dest = ra_dest(as, ir, RSET_GPR); + if (st >= IRT_I8 && st <= IRT_U16) { /* Extend to 32 bit integer. */ + Reg left = ra_alloc1(as, ir->op1, RSET_GPR); + lua_assert(irt_isint(ir->t) || irt_isu32(ir->t)); + if ((ir->op2 & IRCONV_SEXT)) + emit_as(as, st == IRT_I8 ? PPCI_EXTSB : PPCI_EXTSH, dest, left); + else + emit_rot(as, PPCI_RLWINM, dest, left, 0, st == IRT_U8 ? 24 : 16, 31); + } else { /* 32/64 bit integer conversions. */ + /* Only need to handle 32/32 bit no-op (cast) on 32 bit archs. */ + ra_leftov(as, dest, lref); /* Do nothing, but may need to move regs. */ + } + } +} + +static void asm_strto(ASMState *as, IRIns *ir) +{ + const CCallInfo *ci = &lj_ir_callinfo[IRCALL_lj_strscan_num]; + IRRef args[2]; + int32_t ofs = SPOFS_TMP; +#if LJ_SOFTFP + ra_evictset(as, RSET_SCRATCH); + if (ra_used(ir)) { + if (ra_hasspill(ir->s) && ra_hasspill((ir+1)->s) && + (ir->s & 1) == LJ_BE && (ir->s ^ 1) == (ir+1)->s) { + int i; + for (i = 0; i < 2; i++) { + Reg r = (ir+i)->r; + if (ra_hasreg(r)) { + ra_free(as, r); + ra_modified(as, r); + emit_spload(as, ir+i, r, sps_scale((ir+i)->s)); + } + } + ofs = sps_scale(ir->s & ~1); + } else { + Reg rhi = ra_dest(as, ir+1, RSET_GPR); + Reg rlo = ra_dest(as, ir, rset_exclude(RSET_GPR, rhi)); + emit_tai(as, PPCI_LWZ, rhi, RID_SP, ofs); + emit_tai(as, PPCI_LWZ, rlo, RID_SP, ofs+4); + } + } +#else + RegSet drop = RSET_SCRATCH; + if (ra_hasreg(ir->r)) rset_set(drop, ir->r); /* Spill dest reg (if any). */ + ra_evictset(as, drop); + if (ir->s) ofs = sps_scale(ir->s); +#endif + asm_guardcc(as, CC_EQ); + emit_ai(as, PPCI_CMPWI, RID_RET, 0); /* Test return status. */ + args[0] = ir->op1; /* GCstr *str */ + args[1] = ASMREF_TMP1; /* TValue *n */ + asm_gencall(as, ci, args); + /* Store the result to the spill slot or temp slots. */ + emit_tai(as, PPCI_ADDI, ra_releasetmp(as, ASMREF_TMP1), RID_SP, ofs); +} + +/* -- Memory references --------------------------------------------------- */ + +/* Get pointer to TValue. */ +static void asm_tvptr(ASMState *as, Reg dest, IRRef ref) +{ + IRIns *ir = IR(ref); + if (irt_isnum(ir->t)) { + if (irref_isk(ref)) /* Use the number constant itself as a TValue. */ + ra_allockreg(as, i32ptr(ir_knum(ir)), dest); + else /* Otherwise force a spill and use the spill slot. */ + emit_tai(as, PPCI_ADDI, dest, RID_SP, ra_spill(as, ir)); + } else { + /* Otherwise use g->tmptv to hold the TValue. */ + RegSet allow = rset_exclude(RSET_GPR, dest); + Reg type; + emit_tai(as, PPCI_ADDI, dest, RID_JGL, (int32_t)offsetof(global_State, tmptv)-32768); + if (!irt_ispri(ir->t)) { + Reg src = ra_alloc1(as, ref, allow); + emit_setgl(as, src, tmptv.gcr); + } + if (LJ_SOFTFP && (ir+1)->o == IR_HIOP) + type = ra_alloc1(as, ref+1, allow); + else + type = ra_allock(as, irt_toitype(ir->t), allow); + emit_setgl(as, type, tmptv.it); + } +} + +static void asm_aref(ASMState *as, IRIns *ir) +{ + Reg dest = ra_dest(as, ir, RSET_GPR); + Reg idx, base; + if (irref_isk(ir->op2)) { + IRRef tab = IR(ir->op1)->op1; + int32_t ofs = asm_fuseabase(as, tab); + IRRef refa = ofs ? tab : ir->op1; + ofs += 8*IR(ir->op2)->i; + if (checki16(ofs)) { + base = ra_alloc1(as, refa, RSET_GPR); + emit_tai(as, PPCI_ADDI, dest, base, ofs); + return; + } + } + base = ra_alloc1(as, ir->op1, RSET_GPR); + idx = ra_alloc1(as, ir->op2, rset_exclude(RSET_GPR, base)); + emit_tab(as, PPCI_ADD, dest, RID_TMP, base); + emit_slwi(as, RID_TMP, idx, 3); +} + +/* Inlined hash lookup. Specialized for key type and for const keys. +** The equivalent C code is: +** Node *n = hashkey(t, key); +** do { +** if (lj_obj_equal(&n->key, key)) return &n->val; +** } while ((n = nextnode(n))); +** return niltv(L); +*/ +static void asm_href(ASMState *as, IRIns *ir, IROp merge) +{ + RegSet allow = RSET_GPR; + int destused = ra_used(ir); + Reg dest = ra_dest(as, ir, allow); + Reg tab = ra_alloc1(as, ir->op1, rset_clear(allow, dest)); + Reg key = RID_NONE, tmp1 = RID_TMP, tmp2; + Reg tisnum = RID_NONE, tmpnum = RID_NONE; + IRRef refkey = ir->op2; + IRIns *irkey = IR(refkey); + int isk = irref_isk(refkey); + IRType1 kt = irkey->t; + uint32_t khash; + MCLabel l_end, l_loop, l_next; + + rset_clear(allow, tab); +#if LJ_SOFTFP + if (!isk) { + key = ra_alloc1(as, refkey, allow); + rset_clear(allow, key); + if (irkey[1].o == IR_HIOP) { + if (ra_hasreg((irkey+1)->r)) { + tmpnum = (irkey+1)->r; + ra_noweak(as, tmpnum); + } else { + tmpnum = ra_allocref(as, refkey+1, allow); + } + rset_clear(allow, tmpnum); + } + } +#else + if (irt_isnum(kt)) { + key = ra_alloc1(as, refkey, RSET_FPR); + tmpnum = ra_scratch(as, rset_exclude(RSET_FPR, key)); + tisnum = ra_allock(as, (int32_t)LJ_TISNUM, allow); + rset_clear(allow, tisnum); + } else if (!irt_ispri(kt)) { + key = ra_alloc1(as, refkey, allow); + rset_clear(allow, key); + } +#endif + tmp2 = ra_scratch(as, allow); + rset_clear(allow, tmp2); + + /* Key not found in chain: jump to exit (if merged) or load niltv. */ + l_end = emit_label(as); + as->invmcp = NULL; + if (merge == IR_NE) + asm_guardcc(as, CC_EQ); + else if (destused) + emit_loada(as, dest, niltvg(J2G(as->J))); + + /* Follow hash chain until the end. */ + l_loop = --as->mcp; + emit_ai(as, PPCI_CMPWI, dest, 0); + emit_tai(as, PPCI_LWZ, dest, dest, (int32_t)offsetof(Node, next)); + l_next = emit_label(as); + + /* Type and value comparison. */ + if (merge == IR_EQ) + asm_guardcc(as, CC_EQ); + else + emit_condbranch(as, PPCI_BC|PPCF_Y, CC_EQ, l_end); + if (!LJ_SOFTFP && irt_isnum(kt)) { + emit_fab(as, PPCI_FCMPU, 0, tmpnum, key); + emit_condbranch(as, PPCI_BC, CC_GE, l_next); + emit_ab(as, PPCI_CMPLW, tmp1, tisnum); + emit_fai(as, PPCI_LFD, tmpnum, dest, (int32_t)offsetof(Node, key.n)); + } else { + if (!irt_ispri(kt)) { + emit_ab(as, PPCI_CMPW, tmp2, key); + emit_condbranch(as, PPCI_BC, CC_NE, l_next); + } + if (LJ_SOFTFP && ra_hasreg(tmpnum)) + emit_ab(as, PPCI_CMPW, tmp1, tmpnum); + else + emit_ai(as, PPCI_CMPWI, tmp1, irt_toitype(irkey->t)); + if (!irt_ispri(kt)) + emit_tai(as, PPCI_LWZ, tmp2, dest, (int32_t)offsetof(Node, key.gcr)); + } + emit_tai(as, PPCI_LWZ, tmp1, dest, (int32_t)offsetof(Node, key.it)); + *l_loop = PPCI_BC | PPCF_Y | PPCF_CC(CC_NE) | + (((char *)as->mcp-(char *)l_loop) & 0xffffu); + + /* Load main position relative to tab->node into dest. */ + khash = isk ? ir_khash(irkey) : 1; + if (khash == 0) { + emit_tai(as, PPCI_LWZ, dest, tab, (int32_t)offsetof(GCtab, node)); + } else { + Reg tmphash = tmp1; + if (isk) + tmphash = ra_allock(as, khash, allow); + emit_tab(as, PPCI_ADD, dest, dest, tmp1); + emit_tai(as, PPCI_MULLI, tmp1, tmp1, sizeof(Node)); + emit_asb(as, PPCI_AND, tmp1, tmp2, tmphash); + emit_tai(as, PPCI_LWZ, dest, tab, (int32_t)offsetof(GCtab, node)); + emit_tai(as, PPCI_LWZ, tmp2, tab, (int32_t)offsetof(GCtab, hmask)); + if (isk) { + /* Nothing to do. */ + } else if (irt_isstr(kt)) { + emit_tai(as, PPCI_LWZ, tmp1, key, (int32_t)offsetof(GCstr, hash)); + } else { /* Must match with hash*() in lj_tab.c. */ + emit_tab(as, PPCI_SUBF, tmp1, tmp2, tmp1); + emit_rotlwi(as, tmp2, tmp2, HASH_ROT3); + emit_asb(as, PPCI_XOR, tmp1, tmp1, tmp2); + emit_rotlwi(as, tmp1, tmp1, (HASH_ROT2+HASH_ROT1)&31); + emit_tab(as, PPCI_SUBF, tmp2, dest, tmp2); + if (LJ_SOFTFP ? (irkey[1].o == IR_HIOP) : irt_isnum(kt)) { +#if LJ_SOFTFP + emit_asb(as, PPCI_XOR, tmp2, key, tmp1); + emit_rotlwi(as, dest, tmp1, HASH_ROT1); + emit_tab(as, PPCI_ADD, tmp1, tmpnum, tmpnum); +#else + int32_t ofs = ra_spill(as, irkey); + emit_asb(as, PPCI_XOR, tmp2, tmp2, tmp1); + emit_rotlwi(as, dest, tmp1, HASH_ROT1); + emit_tab(as, PPCI_ADD, tmp1, tmp1, tmp1); + emit_tai(as, PPCI_LWZ, tmp2, RID_SP, ofs+4); + emit_tai(as, PPCI_LWZ, tmp1, RID_SP, ofs); +#endif + } else { + emit_asb(as, PPCI_XOR, tmp2, key, tmp1); + emit_rotlwi(as, dest, tmp1, HASH_ROT1); + emit_tai(as, PPCI_ADDI, tmp1, tmp2, HASH_BIAS); + emit_tai(as, PPCI_ADDIS, tmp2, key, (HASH_BIAS + 32768)>>16); + } + } + } +} + +static void asm_hrefk(ASMState *as, IRIns *ir) +{ + IRIns *kslot = IR(ir->op2); + IRIns *irkey = IR(kslot->op1); + int32_t ofs = (int32_t)(kslot->op2 * sizeof(Node)); + int32_t kofs = ofs + (int32_t)offsetof(Node, key); + Reg dest = (ra_used(ir)||ofs > 32736) ? ra_dest(as, ir, RSET_GPR) : RID_NONE; + Reg node = ra_alloc1(as, ir->op1, RSET_GPR); + Reg key = RID_NONE, type = RID_TMP, idx = node; + RegSet allow = rset_exclude(RSET_GPR, node); + lua_assert(ofs % sizeof(Node) == 0); + if (ofs > 32736) { + idx = dest; + rset_clear(allow, dest); + kofs = (int32_t)offsetof(Node, key); + } else if (ra_hasreg(dest)) { + emit_tai(as, PPCI_ADDI, dest, node, ofs); + } + asm_guardcc(as, CC_NE); + if (!irt_ispri(irkey->t)) { + key = ra_scratch(as, allow); + rset_clear(allow, key); + } + rset_clear(allow, type); + if (irt_isnum(irkey->t)) { + emit_cmpi(as, key, (int32_t)ir_knum(irkey)->u32.lo); + asm_guardcc(as, CC_NE); + emit_cmpi(as, type, (int32_t)ir_knum(irkey)->u32.hi); + } else { + if (ra_hasreg(key)) { + emit_cmpi(as, key, irkey->i); /* May use RID_TMP, i.e. type. */ + asm_guardcc(as, CC_NE); + } + emit_ai(as, PPCI_CMPWI, type, irt_toitype(irkey->t)); + } + if (ra_hasreg(key)) emit_tai(as, PPCI_LWZ, key, idx, kofs+4); + emit_tai(as, PPCI_LWZ, type, idx, kofs); + if (ofs > 32736) { + emit_tai(as, PPCI_ADDIS, dest, dest, (ofs + 32768) >> 16); + emit_tai(as, PPCI_ADDI, dest, node, ofs); + } +} + +static void asm_uref(ASMState *as, IRIns *ir) +{ + Reg dest = ra_dest(as, ir, RSET_GPR); + if (irref_isk(ir->op1)) { + GCfunc *fn = ir_kfunc(IR(ir->op1)); + MRef *v = &gcref(fn->l.uvptr[(ir->op2 >> 8)])->uv.v; + emit_lsptr(as, PPCI_LWZ, dest, v, RSET_GPR); + } else { + Reg uv = ra_scratch(as, RSET_GPR); + Reg func = ra_alloc1(as, ir->op1, RSET_GPR); + if (ir->o == IR_UREFC) { + asm_guardcc(as, CC_NE); + emit_ai(as, PPCI_CMPWI, RID_TMP, 1); + emit_tai(as, PPCI_ADDI, dest, uv, (int32_t)offsetof(GCupval, tv)); + emit_tai(as, PPCI_LBZ, RID_TMP, uv, (int32_t)offsetof(GCupval, closed)); + } else { + emit_tai(as, PPCI_LWZ, dest, uv, (int32_t)offsetof(GCupval, v)); + } + emit_tai(as, PPCI_LWZ, uv, func, + (int32_t)offsetof(GCfuncL, uvptr) + 4*(int32_t)(ir->op2 >> 8)); + } +} + +static void asm_fref(ASMState *as, IRIns *ir) +{ + UNUSED(as); UNUSED(ir); + lua_assert(!ra_used(ir)); +} + +static void asm_strref(ASMState *as, IRIns *ir) +{ + Reg dest = ra_dest(as, ir, RSET_GPR); + IRRef ref = ir->op2, refk = ir->op1; + int32_t ofs = (int32_t)sizeof(GCstr); + Reg r; + if (irref_isk(ref)) { + IRRef tmp = refk; refk = ref; ref = tmp; + } else if (!irref_isk(refk)) { + Reg right, left = ra_alloc1(as, ir->op1, RSET_GPR); + IRIns *irr = IR(ir->op2); + if (ra_hasreg(irr->r)) { + ra_noweak(as, irr->r); + right = irr->r; + } else if (mayfuse(as, irr->op2) && + irr->o == IR_ADD && irref_isk(irr->op2) && + checki16(ofs + IR(irr->op2)->i)) { + ofs += IR(irr->op2)->i; + right = ra_alloc1(as, irr->op1, rset_exclude(RSET_GPR, left)); + } else { + right = ra_allocref(as, ir->op2, rset_exclude(RSET_GPR, left)); + } + emit_tai(as, PPCI_ADDI, dest, dest, ofs); + emit_tab(as, PPCI_ADD, dest, left, right); + return; + } + r = ra_alloc1(as, ref, RSET_GPR); + ofs += IR(refk)->i; + if (checki16(ofs)) + emit_tai(as, PPCI_ADDI, dest, r, ofs); + else + emit_tab(as, PPCI_ADD, dest, r, + ra_allock(as, ofs, rset_exclude(RSET_GPR, r))); +} + +/* -- Loads and stores ---------------------------------------------------- */ + +static PPCIns asm_fxloadins(IRIns *ir) +{ + switch (irt_type(ir->t)) { + case IRT_I8: return PPCI_LBZ; /* Needs sign-extension. */ + case IRT_U8: return PPCI_LBZ; + case IRT_I16: return PPCI_LHA; + case IRT_U16: return PPCI_LHZ; + case IRT_NUM: lua_assert(!LJ_SOFTFP); return PPCI_LFD; + case IRT_FLOAT: if (!LJ_SOFTFP) return PPCI_LFS; + default: return PPCI_LWZ; + } +} + +static PPCIns asm_fxstoreins(IRIns *ir) +{ + switch (irt_type(ir->t)) { + case IRT_I8: case IRT_U8: return PPCI_STB; + case IRT_I16: case IRT_U16: return PPCI_STH; + case IRT_NUM: lua_assert(!LJ_SOFTFP); return PPCI_STFD; + case IRT_FLOAT: if (!LJ_SOFTFP) return PPCI_STFS; + default: return PPCI_STW; + } +} + +static void asm_fload(ASMState *as, IRIns *ir) +{ + Reg dest = ra_dest(as, ir, RSET_GPR); + PPCIns pi = asm_fxloadins(ir); + Reg idx; + int32_t ofs; + if (ir->op1 == REF_NIL) { + idx = RID_JGL; + ofs = (ir->op2 << 2) - 32768; + } else { + idx = ra_alloc1(as, ir->op1, RSET_GPR); + if (ir->op2 == IRFL_TAB_ARRAY) { + ofs = asm_fuseabase(as, ir->op1); + if (ofs) { /* Turn the t->array load into an add for colocated arrays. */ + emit_tai(as, PPCI_ADDI, dest, idx, ofs); + return; + } + } + ofs = field_ofs[ir->op2]; + } + lua_assert(!irt_isi8(ir->t)); + emit_tai(as, pi, dest, idx, ofs); +} + +static void asm_fstore(ASMState *as, IRIns *ir) +{ + if (ir->r != RID_SINK) { + Reg src = ra_alloc1(as, ir->op2, RSET_GPR); + IRIns *irf = IR(ir->op1); + Reg idx = ra_alloc1(as, irf->op1, rset_exclude(RSET_GPR, src)); + int32_t ofs = field_ofs[irf->op2]; + PPCIns pi = asm_fxstoreins(ir); + emit_tai(as, pi, src, idx, ofs); + } +} + +static void asm_xload(ASMState *as, IRIns *ir) +{ + Reg dest = ra_dest(as, ir, + (!LJ_SOFTFP && irt_isfp(ir->t)) ? RSET_FPR : RSET_GPR); + lua_assert(!(ir->op2 & IRXLOAD_UNALIGNED)); + if (irt_isi8(ir->t)) + emit_as(as, PPCI_EXTSB, dest, dest); + asm_fusexref(as, asm_fxloadins(ir), dest, ir->op1, RSET_GPR, 0); +} + +static void asm_xstore_(ASMState *as, IRIns *ir, int32_t ofs) +{ + IRIns *irb; + if (ir->r == RID_SINK) + return; + if (ofs == 0 && mayfuse(as, ir->op2) && (irb = IR(ir->op2))->o == IR_BSWAP && + ra_noreg(irb->r) && (irt_isint(ir->t) || irt_isu32(ir->t))) { + /* Fuse BSWAP with XSTORE to stwbrx. */ + Reg src = ra_alloc1(as, irb->op1, RSET_GPR); + asm_fusexrefx(as, PPCI_STWBRX, src, ir->op1, rset_exclude(RSET_GPR, src)); + } else { + Reg src = ra_alloc1(as, ir->op2, + (!LJ_SOFTFP && irt_isfp(ir->t)) ? RSET_FPR : RSET_GPR); + asm_fusexref(as, asm_fxstoreins(ir), src, ir->op1, + rset_exclude(RSET_GPR, src), ofs); + } +} + +#define asm_xstore(as, ir) asm_xstore_(as, ir, 0) + +static void asm_ahuvload(ASMState *as, IRIns *ir) +{ + IRType1 t = ir->t; + Reg dest = RID_NONE, type = RID_TMP, tmp = RID_TMP, idx; + RegSet allow = RSET_GPR; + int32_t ofs = AHUREF_LSX; + if (LJ_SOFTFP && (ir+1)->o == IR_HIOP) { + t.irt = IRT_NUM; + if (ra_used(ir+1)) { + type = ra_dest(as, ir+1, allow); + rset_clear(allow, type); + } + ofs = 0; + } + if (ra_used(ir)) { + lua_assert((LJ_SOFTFP ? 0 : irt_isnum(ir->t)) || + irt_isint(ir->t) || irt_isaddr(ir->t)); + if (LJ_SOFTFP || !irt_isnum(t)) ofs = 0; + dest = ra_dest(as, ir, (!LJ_SOFTFP && irt_isnum(t)) ? RSET_FPR : allow); + rset_clear(allow, dest); + } + idx = asm_fuseahuref(as, ir->op1, &ofs, allow); + if (irt_isnum(t)) { + Reg tisnum = ra_allock(as, (int32_t)LJ_TISNUM, rset_exclude(allow, idx)); + asm_guardcc(as, CC_GE); + emit_ab(as, PPCI_CMPLW, type, tisnum); + if (ra_hasreg(dest)) { + if (!LJ_SOFTFP && ofs == AHUREF_LSX) { + tmp = ra_scratch(as, rset_exclude(rset_exclude(RSET_GPR, + (idx&255)), (idx>>8))); + emit_fab(as, PPCI_LFDX, dest, (idx&255), tmp); + } else { + emit_fai(as, LJ_SOFTFP ? PPCI_LWZ : PPCI_LFD, dest, idx, + ofs+4*LJ_SOFTFP); + } + } + } else { + asm_guardcc(as, CC_NE); + emit_ai(as, PPCI_CMPWI, type, irt_toitype(t)); + if (ra_hasreg(dest)) emit_tai(as, PPCI_LWZ, dest, idx, ofs+4); + } + if (ofs == AHUREF_LSX) { + emit_tab(as, PPCI_LWZX, type, (idx&255), tmp); + emit_slwi(as, tmp, (idx>>8), 3); + } else { + emit_tai(as, PPCI_LWZ, type, idx, ofs); + } +} + +static void asm_ahustore(ASMState *as, IRIns *ir) +{ + RegSet allow = RSET_GPR; + Reg idx, src = RID_NONE, type = RID_NONE; + int32_t ofs = AHUREF_LSX; + if (ir->r == RID_SINK) + return; + if (!LJ_SOFTFP && irt_isnum(ir->t)) { + src = ra_alloc1(as, ir->op2, RSET_FPR); + } else { + if (!irt_ispri(ir->t)) { + src = ra_alloc1(as, ir->op2, allow); + rset_clear(allow, src); + ofs = 0; + } + if (LJ_SOFTFP && (ir+1)->o == IR_HIOP) + type = ra_alloc1(as, (ir+1)->op2, allow); + else + type = ra_allock(as, (int32_t)irt_toitype(ir->t), allow); + rset_clear(allow, type); + } + idx = asm_fuseahuref(as, ir->op1, &ofs, allow); + if (!LJ_SOFTFP && irt_isnum(ir->t)) { + if (ofs == AHUREF_LSX) { + emit_fab(as, PPCI_STFDX, src, (idx&255), RID_TMP); + emit_slwi(as, RID_TMP, (idx>>8), 3); + } else { + emit_fai(as, PPCI_STFD, src, idx, ofs); + } + } else { + if (ra_hasreg(src)) + emit_tai(as, PPCI_STW, src, idx, ofs+4); + if (ofs == AHUREF_LSX) { + emit_tab(as, PPCI_STWX, type, (idx&255), RID_TMP); + emit_slwi(as, RID_TMP, (idx>>8), 3); + } else { + emit_tai(as, PPCI_STW, type, idx, ofs); + } + } +} + +static void asm_sload(ASMState *as, IRIns *ir) +{ + int32_t ofs = 8*((int32_t)ir->op1-1) + ((ir->op2 & IRSLOAD_FRAME) ? 0 : 4); + IRType1 t = ir->t; + Reg dest = RID_NONE, type = RID_NONE, base; + RegSet allow = RSET_GPR; + int hiop = (LJ_SOFTFP && (ir+1)->o == IR_HIOP); + if (hiop) + t.irt = IRT_NUM; + lua_assert(!(ir->op2 & IRSLOAD_PARENT)); /* Handled by asm_head_side(). */ + lua_assert(irt_isguard(ir->t) || !(ir->op2 & IRSLOAD_TYPECHECK)); + lua_assert(LJ_DUALNUM || + !irt_isint(t) || (ir->op2 & (IRSLOAD_CONVERT|IRSLOAD_FRAME))); +#if LJ_SOFTFP + lua_assert(!(ir->op2 & IRSLOAD_CONVERT)); /* Handled by LJ_SOFTFP SPLIT. */ + if (hiop && ra_used(ir+1)) { + type = ra_dest(as, ir+1, allow); + rset_clear(allow, type); + } +#else + if ((ir->op2 & IRSLOAD_CONVERT) && irt_isguard(t) && irt_isint(t)) { + dest = ra_scratch(as, RSET_FPR); + asm_tointg(as, ir, dest); + t.irt = IRT_NUM; /* Continue with a regular number type check. */ + } else +#endif + if (ra_used(ir)) { + lua_assert(irt_isnum(t) || irt_isint(t) || irt_isaddr(t)); + dest = ra_dest(as, ir, (!LJ_SOFTFP && irt_isnum(t)) ? RSET_FPR : allow); + rset_clear(allow, dest); + base = ra_alloc1(as, REF_BASE, allow); + rset_clear(allow, base); + if (!LJ_SOFTFP && (ir->op2 & IRSLOAD_CONVERT)) { + if (irt_isint(t)) { + emit_tai(as, PPCI_LWZ, dest, RID_SP, SPOFS_TMPLO); + dest = ra_scratch(as, RSET_FPR); + emit_fai(as, PPCI_STFD, dest, RID_SP, SPOFS_TMP); + emit_fb(as, PPCI_FCTIWZ, dest, dest); + t.irt = IRT_NUM; /* Check for original type. */ + } else { + Reg tmp = ra_scratch(as, allow); + Reg hibias = ra_allock(as, 0x43300000, rset_clear(allow, tmp)); + Reg fbias = ra_scratch(as, rset_exclude(RSET_FPR, dest)); + emit_fab(as, PPCI_FSUB, dest, dest, fbias); + emit_fai(as, PPCI_LFD, dest, RID_SP, SPOFS_TMP); + emit_lsptr(as, PPCI_LFS, (fbias & 31), + (void *)&as->J->k32[LJ_K32_2P52_2P31], + rset_clear(allow, hibias)); + emit_tai(as, PPCI_STW, tmp, RID_SP, SPOFS_TMPLO); + emit_tai(as, PPCI_STW, hibias, RID_SP, SPOFS_TMPHI); + emit_asi(as, PPCI_XORIS, tmp, tmp, 0x8000); + dest = tmp; + t.irt = IRT_INT; /* Check for original type. */ + } + } + goto dotypecheck; + } + base = ra_alloc1(as, REF_BASE, allow); + rset_clear(allow, base); +dotypecheck: + if (irt_isnum(t)) { + if ((ir->op2 & IRSLOAD_TYPECHECK)) { + Reg tisnum = ra_allock(as, (int32_t)LJ_TISNUM, allow); + asm_guardcc(as, CC_GE); +#if !LJ_SOFTFP + type = RID_TMP; +#endif + emit_ab(as, PPCI_CMPLW, type, tisnum); + } + if (ra_hasreg(dest)) emit_fai(as, LJ_SOFTFP ? PPCI_LWZ : PPCI_LFD, dest, + base, ofs-(LJ_SOFTFP?0:4)); + } else { + if ((ir->op2 & IRSLOAD_TYPECHECK)) { + asm_guardcc(as, CC_NE); + emit_ai(as, PPCI_CMPWI, RID_TMP, irt_toitype(t)); + type = RID_TMP; + } + if (ra_hasreg(dest)) emit_tai(as, PPCI_LWZ, dest, base, ofs); + } + if (ra_hasreg(type)) emit_tai(as, PPCI_LWZ, type, base, ofs-4); +} + +/* -- Allocations --------------------------------------------------------- */ + +#if LJ_HASFFI +static void asm_cnew(ASMState *as, IRIns *ir) +{ + CTState *cts = ctype_ctsG(J2G(as->J)); + CTypeID id = (CTypeID)IR(ir->op1)->i; + CTSize sz; + CTInfo info = lj_ctype_info(cts, id, &sz); + const CCallInfo *ci = &lj_ir_callinfo[IRCALL_lj_mem_newgco]; + IRRef args[4]; + RegSet drop = RSET_SCRATCH; + lua_assert(sz != CTSIZE_INVALID || (ir->o == IR_CNEW && ir->op2 != REF_NIL)); + + as->gcsteps++; + if (ra_hasreg(ir->r)) + rset_clear(drop, ir->r); /* Dest reg handled below. */ + ra_evictset(as, drop); + if (ra_used(ir)) + ra_destreg(as, ir, RID_RET); /* GCcdata * */ + + /* Initialize immutable cdata object. */ + if (ir->o == IR_CNEWI) { + RegSet allow = (RSET_GPR & ~RSET_SCRATCH); + int32_t ofs = sizeof(GCcdata); + lua_assert(sz == 4 || sz == 8); + if (sz == 8) { + ofs += 4; + lua_assert((ir+1)->o == IR_HIOP); + } + for (;;) { + Reg r = ra_alloc1(as, ir->op2, allow); + emit_tai(as, PPCI_STW, r, RID_RET, ofs); + rset_clear(allow, r); + if (ofs == sizeof(GCcdata)) break; + ofs -= 4; ir++; + } + } else if (ir->op2 != REF_NIL) { /* Create VLA/VLS/aligned cdata. */ + ci = &lj_ir_callinfo[IRCALL_lj_cdata_newv]; + args[0] = ASMREF_L; /* lua_State *L */ + args[1] = ir->op1; /* CTypeID id */ + args[2] = ir->op2; /* CTSize sz */ + args[3] = ASMREF_TMP1; /* CTSize align */ + asm_gencall(as, ci, args); + emit_loadi(as, ra_releasetmp(as, ASMREF_TMP1), (int32_t)ctype_align(info)); + return; + } + + /* Initialize gct and ctypeid. lj_mem_newgco() already sets marked. */ + emit_tai(as, PPCI_STB, RID_RET+1, RID_RET, offsetof(GCcdata, gct)); + emit_tai(as, PPCI_STH, RID_TMP, RID_RET, offsetof(GCcdata, ctypeid)); + emit_ti(as, PPCI_LI, RID_RET+1, ~LJ_TCDATA); + emit_ti(as, PPCI_LI, RID_TMP, id); /* Lower 16 bit used. Sign-ext ok. */ + args[0] = ASMREF_L; /* lua_State *L */ + args[1] = ASMREF_TMP1; /* MSize size */ + asm_gencall(as, ci, args); + ra_allockreg(as, (int32_t)(sz+sizeof(GCcdata)), + ra_releasetmp(as, ASMREF_TMP1)); +} +#else +#define asm_cnew(as, ir) ((void)0) +#endif + +/* -- Write barriers ------------------------------------------------------ */ + +static void asm_tbar(ASMState *as, IRIns *ir) +{ + Reg tab = ra_alloc1(as, ir->op1, RSET_GPR); + Reg mark = ra_scratch(as, rset_exclude(RSET_GPR, tab)); + Reg link = RID_TMP; + MCLabel l_end = emit_label(as); + emit_tai(as, PPCI_STW, link, tab, (int32_t)offsetof(GCtab, gclist)); + emit_tai(as, PPCI_STB, mark, tab, (int32_t)offsetof(GCtab, marked)); + emit_setgl(as, tab, gc.grayagain); + lua_assert(LJ_GC_BLACK == 0x04); + emit_rot(as, PPCI_RLWINM, mark, mark, 0, 30, 28); /* Clear black bit. */ + emit_getgl(as, link, gc.grayagain); + emit_condbranch(as, PPCI_BC|PPCF_Y, CC_EQ, l_end); + emit_asi(as, PPCI_ANDIDOT, RID_TMP, mark, LJ_GC_BLACK); + emit_tai(as, PPCI_LBZ, mark, tab, (int32_t)offsetof(GCtab, marked)); +} + +static void asm_obar(ASMState *as, IRIns *ir) +{ + const CCallInfo *ci = &lj_ir_callinfo[IRCALL_lj_gc_barrieruv]; + IRRef args[2]; + MCLabel l_end; + Reg obj, val, tmp; + /* No need for other object barriers (yet). */ + lua_assert(IR(ir->op1)->o == IR_UREFC); + ra_evictset(as, RSET_SCRATCH); + l_end = emit_label(as); + args[0] = ASMREF_TMP1; /* global_State *g */ + args[1] = ir->op1; /* TValue *tv */ + asm_gencall(as, ci, args); + emit_tai(as, PPCI_ADDI, ra_releasetmp(as, ASMREF_TMP1), RID_JGL, -32768); + obj = IR(ir->op1)->r; + tmp = ra_scratch(as, rset_exclude(RSET_GPR, obj)); + emit_condbranch(as, PPCI_BC|PPCF_Y, CC_EQ, l_end); + emit_asi(as, PPCI_ANDIDOT, tmp, tmp, LJ_GC_BLACK); + emit_condbranch(as, PPCI_BC, CC_EQ, l_end); + emit_asi(as, PPCI_ANDIDOT, RID_TMP, RID_TMP, LJ_GC_WHITES); + val = ra_alloc1(as, ir->op2, rset_exclude(RSET_GPR, obj)); + emit_tai(as, PPCI_LBZ, tmp, obj, + (int32_t)offsetof(GCupval, marked)-(int32_t)offsetof(GCupval, tv)); + emit_tai(as, PPCI_LBZ, RID_TMP, val, (int32_t)offsetof(GChead, marked)); +} + +/* -- Arithmetic and logic operations ------------------------------------- */ + +#if !LJ_SOFTFP +static void asm_fparith(ASMState *as, IRIns *ir, PPCIns pi) +{ + Reg dest = ra_dest(as, ir, RSET_FPR); + Reg right, left = ra_alloc2(as, ir, RSET_FPR); + right = (left >> 8); left &= 255; + if (pi == PPCI_FMUL) + emit_fac(as, pi, dest, left, right); + else + emit_fab(as, pi, dest, left, right); +} + +static void asm_fpunary(ASMState *as, IRIns *ir, PPCIns pi) +{ + Reg dest = ra_dest(as, ir, RSET_FPR); + Reg left = ra_hintalloc(as, ir->op1, dest, RSET_FPR); + emit_fb(as, pi, dest, left); +} + +static void asm_fpmath(ASMState *as, IRIns *ir) +{ + if (ir->op2 == IRFPM_EXP2 && asm_fpjoin_pow(as, ir)) + return; + if (ir->op2 == IRFPM_SQRT && (as->flags & JIT_F_SQRT)) + asm_fpunary(as, ir, PPCI_FSQRT); + else + asm_callid(as, ir, IRCALL_lj_vm_floor + ir->op2); +} +#endif + +static void asm_add(ASMState *as, IRIns *ir) +{ +#if !LJ_SOFTFP + if (irt_isnum(ir->t)) { + if (!asm_fusemadd(as, ir, PPCI_FMADD, PPCI_FMADD)) + asm_fparith(as, ir, PPCI_FADD); + } else +#endif + { + Reg dest = ra_dest(as, ir, RSET_GPR); + Reg right, left = ra_hintalloc(as, ir->op1, dest, RSET_GPR); + PPCIns pi; + if (irref_isk(ir->op2)) { + int32_t k = IR(ir->op2)->i; + if (checki16(k)) { + pi = PPCI_ADDI; + /* May fail due to spills/restores above, but simplifies the logic. */ + if (as->flagmcp == as->mcp) { + as->flagmcp = NULL; + as->mcp++; + pi = PPCI_ADDICDOT; + } + emit_tai(as, pi, dest, left, k); + return; + } else if ((k & 0xffff) == 0) { + emit_tai(as, PPCI_ADDIS, dest, left, (k >> 16)); + return; + } else if (!as->sectref) { + emit_tai(as, PPCI_ADDIS, dest, dest, (k + 32768) >> 16); + emit_tai(as, PPCI_ADDI, dest, left, k); + return; + } + } + pi = PPCI_ADD; + /* May fail due to spills/restores above, but simplifies the logic. */ + if (as->flagmcp == as->mcp) { + as->flagmcp = NULL; + as->mcp++; + pi |= PPCF_DOT; + } + right = ra_alloc1(as, ir->op2, rset_exclude(RSET_GPR, left)); + emit_tab(as, pi, dest, left, right); + } +} + +static void asm_sub(ASMState *as, IRIns *ir) +{ +#if !LJ_SOFTFP + if (irt_isnum(ir->t)) { + if (!asm_fusemadd(as, ir, PPCI_FMSUB, PPCI_FNMSUB)) + asm_fparith(as, ir, PPCI_FSUB); + } else +#endif + { + PPCIns pi = PPCI_SUBF; + Reg dest = ra_dest(as, ir, RSET_GPR); + Reg left, right; + if (irref_isk(ir->op1)) { + int32_t k = IR(ir->op1)->i; + if (checki16(k)) { + right = ra_alloc1(as, ir->op2, RSET_GPR); + emit_tai(as, PPCI_SUBFIC, dest, right, k); + return; + } + } + /* May fail due to spills/restores above, but simplifies the logic. */ + if (as->flagmcp == as->mcp) { + as->flagmcp = NULL; + as->mcp++; + pi |= PPCF_DOT; + } + left = ra_hintalloc(as, ir->op1, dest, RSET_GPR); + right = ra_alloc1(as, ir->op2, rset_exclude(RSET_GPR, left)); + emit_tab(as, pi, dest, right, left); /* Subtract right _from_ left. */ + } +} + +static void asm_mul(ASMState *as, IRIns *ir) +{ +#if !LJ_SOFTFP + if (irt_isnum(ir->t)) { + asm_fparith(as, ir, PPCI_FMUL); + } else +#endif + { + PPCIns pi = PPCI_MULLW; + Reg dest = ra_dest(as, ir, RSET_GPR); + Reg right, left = ra_hintalloc(as, ir->op1, dest, RSET_GPR); + if (irref_isk(ir->op2)) { + int32_t k = IR(ir->op2)->i; + if (checki16(k)) { + emit_tai(as, PPCI_MULLI, dest, left, k); + return; + } + } + /* May fail due to spills/restores above, but simplifies the logic. */ + if (as->flagmcp == as->mcp) { + as->flagmcp = NULL; + as->mcp++; + pi |= PPCF_DOT; + } + right = ra_alloc1(as, ir->op2, rset_exclude(RSET_GPR, left)); + emit_tab(as, pi, dest, left, right); + } +} + +#define asm_div(as, ir) asm_fparith(as, ir, PPCI_FDIV) +#define asm_mod(as, ir) asm_callid(as, ir, IRCALL_lj_vm_modi) +#define asm_pow(as, ir) asm_callid(as, ir, IRCALL_lj_vm_powi) + +static void asm_neg(ASMState *as, IRIns *ir) +{ +#if !LJ_SOFTFP + if (irt_isnum(ir->t)) { + asm_fpunary(as, ir, PPCI_FNEG); + } else +#endif + { + Reg dest, left; + PPCIns pi = PPCI_NEG; + if (as->flagmcp == as->mcp) { + as->flagmcp = NULL; + as->mcp++; + pi |= PPCF_DOT; + } + dest = ra_dest(as, ir, RSET_GPR); + left = ra_hintalloc(as, ir->op1, dest, RSET_GPR); + emit_tab(as, pi, dest, left, 0); + } +} + +#define asm_abs(as, ir) asm_fpunary(as, ir, PPCI_FABS) +#define asm_atan2(as, ir) asm_callid(as, ir, IRCALL_atan2) +#define asm_ldexp(as, ir) asm_callid(as, ir, IRCALL_ldexp) + +static void asm_arithov(ASMState *as, IRIns *ir, PPCIns pi) +{ + Reg dest, left, right; + if (as->flagmcp == as->mcp) { + as->flagmcp = NULL; + as->mcp++; + } + asm_guardcc(as, CC_SO); + dest = ra_dest(as, ir, RSET_GPR); + left = ra_alloc2(as, ir, RSET_GPR); + right = (left >> 8); left &= 255; + if (pi == PPCI_SUBFO) { Reg tmp = left; left = right; right = tmp; } + emit_tab(as, pi|PPCF_DOT, dest, left, right); +} + +#define asm_addov(as, ir) asm_arithov(as, ir, PPCI_ADDO) +#define asm_subov(as, ir) asm_arithov(as, ir, PPCI_SUBFO) +#define asm_mulov(as, ir) asm_arithov(as, ir, PPCI_MULLWO) + +#if LJ_HASFFI +static void asm_add64(ASMState *as, IRIns *ir) +{ + Reg dest = ra_dest(as, ir, RSET_GPR); + Reg right, left = ra_alloc1(as, ir->op1, RSET_GPR); + PPCIns pi = PPCI_ADDE; + if (irref_isk(ir->op2)) { + int32_t k = IR(ir->op2)->i; + if (k == 0) + pi = PPCI_ADDZE; + else if (k == -1) + pi = PPCI_ADDME; + else + goto needright; + right = 0; + } else { + needright: + right = ra_alloc1(as, ir->op2, rset_exclude(RSET_GPR, left)); + } + emit_tab(as, pi, dest, left, right); + ir--; + dest = ra_dest(as, ir, RSET_GPR); + left = ra_alloc1(as, ir->op1, RSET_GPR); + if (irref_isk(ir->op2)) { + int32_t k = IR(ir->op2)->i; + if (checki16(k)) { + emit_tai(as, PPCI_ADDIC, dest, left, k); + return; + } + } + right = ra_alloc1(as, ir->op2, rset_exclude(RSET_GPR, left)); + emit_tab(as, PPCI_ADDC, dest, left, right); +} + +static void asm_sub64(ASMState *as, IRIns *ir) +{ + Reg dest = ra_dest(as, ir, RSET_GPR); + Reg left, right = ra_alloc1(as, ir->op2, RSET_GPR); + PPCIns pi = PPCI_SUBFE; + if (irref_isk(ir->op1)) { + int32_t k = IR(ir->op1)->i; + if (k == 0) + pi = PPCI_SUBFZE; + else if (k == -1) + pi = PPCI_SUBFME; + else + goto needleft; + left = 0; + } else { + needleft: + left = ra_alloc1(as, ir->op1, rset_exclude(RSET_GPR, right)); + } + emit_tab(as, pi, dest, right, left); /* Subtract right _from_ left. */ + ir--; + dest = ra_dest(as, ir, RSET_GPR); + right = ra_alloc1(as, ir->op2, RSET_GPR); + if (irref_isk(ir->op1)) { + int32_t k = IR(ir->op1)->i; + if (checki16(k)) { + emit_tai(as, PPCI_SUBFIC, dest, right, k); + return; + } + } + left = ra_alloc1(as, ir->op1, rset_exclude(RSET_GPR, right)); + emit_tab(as, PPCI_SUBFC, dest, right, left); +} + +static void asm_neg64(ASMState *as, IRIns *ir) +{ + Reg dest = ra_dest(as, ir, RSET_GPR); + Reg left = ra_alloc1(as, ir->op1, RSET_GPR); + emit_tab(as, PPCI_SUBFZE, dest, left, 0); + ir--; + dest = ra_dest(as, ir, RSET_GPR); + left = ra_alloc1(as, ir->op1, RSET_GPR); + emit_tai(as, PPCI_SUBFIC, dest, left, 0); +} +#endif + +static void asm_bnot(ASMState *as, IRIns *ir) +{ + Reg dest, left, right; + PPCIns pi = PPCI_NOR; + if (as->flagmcp == as->mcp) { + as->flagmcp = NULL; + as->mcp++; + pi |= PPCF_DOT; + } + dest = ra_dest(as, ir, RSET_GPR); + if (mayfuse(as, ir->op1)) { + IRIns *irl = IR(ir->op1); + if (irl->o == IR_BAND) + pi ^= (PPCI_NOR ^ PPCI_NAND); + else if (irl->o == IR_BXOR) + pi ^= (PPCI_NOR ^ PPCI_EQV); + else if (irl->o != IR_BOR) + goto nofuse; + left = ra_hintalloc(as, irl->op1, dest, RSET_GPR); + right = ra_alloc1(as, irl->op2, rset_exclude(RSET_GPR, left)); + } else { +nofuse: + left = right = ra_hintalloc(as, ir->op1, dest, RSET_GPR); + } + emit_asb(as, pi, dest, left, right); +} + +static void asm_bswap(ASMState *as, IRIns *ir) +{ + Reg dest = ra_dest(as, ir, RSET_GPR); + IRIns *irx; + if (mayfuse(as, ir->op1) && (irx = IR(ir->op1))->o == IR_XLOAD && + ra_noreg(irx->r) && (irt_isint(irx->t) || irt_isu32(irx->t))) { + /* Fuse BSWAP with XLOAD to lwbrx. */ + asm_fusexrefx(as, PPCI_LWBRX, dest, irx->op1, RSET_GPR); + } else { + Reg left = ra_alloc1(as, ir->op1, RSET_GPR); + Reg tmp = dest; + if (tmp == left) { + tmp = RID_TMP; + emit_mr(as, dest, RID_TMP); + } + emit_rot(as, PPCI_RLWIMI, tmp, left, 24, 16, 23); + emit_rot(as, PPCI_RLWIMI, tmp, left, 24, 0, 7); + emit_rotlwi(as, tmp, left, 8); + } +} + +/* Fuse BAND with contiguous bitmask and a shift to rlwinm. */ +static void asm_fuseandsh(ASMState *as, PPCIns pi, int32_t mask, IRRef ref) +{ + IRIns *ir; + Reg left; + if (mayfuse(as, ref) && (ir = IR(ref), ra_noreg(ir->r)) && + irref_isk(ir->op2) && ir->o >= IR_BSHL && ir->o <= IR_BROR) { + int32_t sh = (IR(ir->op2)->i & 31); + switch (ir->o) { + case IR_BSHL: + if ((mask & ((1u<>sh))) goto nofuse; + sh = ((32-sh)&31); + break; + case IR_BROL: + break; + default: + goto nofuse; + } + left = ra_alloc1(as, ir->op1, RSET_GPR); + *--as->mcp = pi | PPCF_T(left) | PPCF_B(sh); + return; + } +nofuse: + left = ra_alloc1(as, ref, RSET_GPR); + *--as->mcp = pi | PPCF_T(left); +} + +static void asm_band(ASMState *as, IRIns *ir) +{ + Reg dest, left, right; + IRRef lref = ir->op1; + PPCIns dot = 0; + IRRef op2; + if (as->flagmcp == as->mcp) { + as->flagmcp = NULL; + as->mcp++; + dot = PPCF_DOT; + } + dest = ra_dest(as, ir, RSET_GPR); + if (irref_isk(ir->op2)) { + int32_t k = IR(ir->op2)->i; + if (k) { + /* First check for a contiguous bitmask as used by rlwinm. */ + uint32_t s1 = lj_ffs((uint32_t)k); + uint32_t k1 = ((uint32_t)k >> s1); + if ((k1 & (k1+1)) == 0) { + asm_fuseandsh(as, PPCI_RLWINM|dot | PPCF_A(dest) | + PPCF_MB(31-lj_fls((uint32_t)k)) | PPCF_ME(31-s1), + k, lref); + return; + } + if (~(uint32_t)k) { + uint32_t s2 = lj_ffs(~(uint32_t)k); + uint32_t k2 = (~(uint32_t)k >> s2); + if ((k2 & (k2+1)) == 0) { + asm_fuseandsh(as, PPCI_RLWINM|dot | PPCF_A(dest) | + PPCF_MB(32-s2) | PPCF_ME(30-lj_fls(~(uint32_t)k)), + k, lref); + return; + } + } + } + if (checku16(k)) { + left = ra_alloc1(as, lref, RSET_GPR); + emit_asi(as, PPCI_ANDIDOT, dest, left, k); + return; + } else if ((k & 0xffff) == 0) { + left = ra_alloc1(as, lref, RSET_GPR); + emit_asi(as, PPCI_ANDISDOT, dest, left, (k >> 16)); + return; + } + } + op2 = ir->op2; + if (mayfuse(as, op2) && IR(op2)->o == IR_BNOT && ra_noreg(IR(op2)->r)) { + dot ^= (PPCI_AND ^ PPCI_ANDC); + op2 = IR(op2)->op1; + } + left = ra_hintalloc(as, lref, dest, RSET_GPR); + right = ra_alloc1(as, op2, rset_exclude(RSET_GPR, left)); + emit_asb(as, PPCI_AND ^ dot, dest, left, right); +} + +static void asm_bitop(ASMState *as, IRIns *ir, PPCIns pi, PPCIns pik) +{ + Reg dest = ra_dest(as, ir, RSET_GPR); + Reg right, left = ra_hintalloc(as, ir->op1, dest, RSET_GPR); + if (irref_isk(ir->op2)) { + int32_t k = IR(ir->op2)->i; + Reg tmp = left; + if ((checku16(k) || (k & 0xffff) == 0) || (tmp = dest, !as->sectref)) { + if (!checku16(k)) { + emit_asi(as, pik ^ (PPCI_ORI ^ PPCI_ORIS), dest, tmp, (k >> 16)); + if ((k & 0xffff) == 0) return; + } + emit_asi(as, pik, dest, left, k); + return; + } + } + /* May fail due to spills/restores above, but simplifies the logic. */ + if (as->flagmcp == as->mcp) { + as->flagmcp = NULL; + as->mcp++; + pi |= PPCF_DOT; + } + right = ra_alloc1(as, ir->op2, rset_exclude(RSET_GPR, left)); + emit_asb(as, pi, dest, left, right); +} + +#define asm_bor(as, ir) asm_bitop(as, ir, PPCI_OR, PPCI_ORI) +#define asm_bxor(as, ir) asm_bitop(as, ir, PPCI_XOR, PPCI_XORI) + +static void asm_bitshift(ASMState *as, IRIns *ir, PPCIns pi, PPCIns pik) +{ + Reg dest, left; + Reg dot = 0; + if (as->flagmcp == as->mcp) { + as->flagmcp = NULL; + as->mcp++; + dot = PPCF_DOT; + } + dest = ra_dest(as, ir, RSET_GPR); + left = ra_alloc1(as, ir->op1, RSET_GPR); + if (irref_isk(ir->op2)) { /* Constant shifts. */ + int32_t shift = (IR(ir->op2)->i & 31); + if (pik == 0) /* SLWI */ + emit_rot(as, PPCI_RLWINM|dot, dest, left, shift, 0, 31-shift); + else if (pik == 1) /* SRWI */ + emit_rot(as, PPCI_RLWINM|dot, dest, left, (32-shift)&31, shift, 31); + else + emit_asb(as, pik|dot, dest, left, shift); + } else { + Reg right = ra_alloc1(as, ir->op2, rset_exclude(RSET_GPR, left)); + emit_asb(as, pi|dot, dest, left, right); + } +} + +#define asm_bshl(as, ir) asm_bitshift(as, ir, PPCI_SLW, 0) +#define asm_bshr(as, ir) asm_bitshift(as, ir, PPCI_SRW, 1) +#define asm_bsar(as, ir) asm_bitshift(as, ir, PPCI_SRAW, PPCI_SRAWI) +#define asm_brol(as, ir) \ + asm_bitshift(as, ir, PPCI_RLWNM|PPCF_MB(0)|PPCF_ME(31), \ + PPCI_RLWINM|PPCF_MB(0)|PPCF_ME(31)) +#define asm_bror(as, ir) lua_assert(0) + +#if LJ_SOFTFP +static void asm_sfpmin_max(ASMState *as, IRIns *ir) +{ + CCallInfo ci = lj_ir_callinfo[IRCALL_softfp_cmp]; + IRRef args[4]; + MCLabel l_right, l_end; + Reg desthi = ra_dest(as, ir, RSET_GPR), destlo = ra_dest(as, ir+1, RSET_GPR); + Reg righthi, lefthi = ra_alloc2(as, ir, RSET_GPR); + Reg rightlo, leftlo = ra_alloc2(as, ir+1, RSET_GPR); + PPCCC cond = (IROp)ir->o == IR_MIN ? CC_EQ : CC_NE; + righthi = (lefthi >> 8); lefthi &= 255; + rightlo = (leftlo >> 8); leftlo &= 255; + args[0^LJ_BE] = ir->op1; args[1^LJ_BE] = (ir+1)->op1; + args[2^LJ_BE] = ir->op2; args[3^LJ_BE] = (ir+1)->op2; + l_end = emit_label(as); + if (desthi != righthi) emit_mr(as, desthi, righthi); + if (destlo != rightlo) emit_mr(as, destlo, rightlo); + l_right = emit_label(as); + if (l_end != l_right) emit_jmp(as, l_end); + if (desthi != lefthi) emit_mr(as, desthi, lefthi); + if (destlo != leftlo) emit_mr(as, destlo, leftlo); + if (l_right == as->mcp+1) { + cond ^= 4; l_right = l_end; ++as->mcp; + } + emit_condbranch(as, PPCI_BC, cond, l_right); + ra_evictset(as, RSET_SCRATCH); + emit_cmpi(as, RID_RET, 1); + asm_gencall(as, &ci, args); +} +#endif + +static void asm_min_max(ASMState *as, IRIns *ir, int ismax) +{ + if (!LJ_SOFTFP && irt_isnum(ir->t)) { + Reg dest = ra_dest(as, ir, RSET_FPR); + Reg tmp = dest; + Reg right, left = ra_alloc2(as, ir, RSET_FPR); + right = (left >> 8); left &= 255; + if (tmp == left || tmp == right) + tmp = ra_scratch(as, rset_exclude(rset_exclude(rset_exclude(RSET_FPR, + dest), left), right)); + emit_facb(as, PPCI_FSEL, dest, tmp, + ismax ? left : right, ismax ? right : left); + emit_fab(as, PPCI_FSUB, tmp, left, right); + } else { + Reg dest = ra_dest(as, ir, RSET_GPR); + Reg tmp1 = RID_TMP, tmp2 = dest; + Reg right, left = ra_alloc2(as, ir, RSET_GPR); + right = (left >> 8); left &= 255; + if (tmp2 == left || tmp2 == right) + tmp2 = ra_scratch(as, rset_exclude(rset_exclude(rset_exclude(RSET_GPR, + dest), left), right)); + emit_tab(as, PPCI_ADD, dest, tmp2, right); + emit_asb(as, ismax ? PPCI_ANDC : PPCI_AND, tmp2, tmp2, tmp1); + emit_tab(as, PPCI_SUBFE, tmp1, tmp1, tmp1); + emit_tab(as, PPCI_SUBFC, tmp2, tmp2, tmp1); + emit_asi(as, PPCI_XORIS, tmp2, right, 0x8000); + emit_asi(as, PPCI_XORIS, tmp1, left, 0x8000); + } +} + +#define asm_min(as, ir) asm_min_max(as, ir, 0) +#define asm_max(as, ir) asm_min_max(as, ir, 1) + +/* -- Comparisons --------------------------------------------------------- */ + +#define CC_UNSIGNED 0x08 /* Unsigned integer comparison. */ +#define CC_TWO 0x80 /* Check two flags for FP comparison. */ + +/* Map of comparisons to flags. ORDER IR. */ +static const uint8_t asm_compmap[IR_ABC+1] = { + /* op int cc FP cc */ + /* LT */ CC_GE + (CC_GE<<4), + /* GE */ CC_LT + (CC_LE<<4) + CC_TWO, + /* LE */ CC_GT + (CC_GE<<4) + CC_TWO, + /* GT */ CC_LE + (CC_LE<<4), + /* ULT */ CC_GE + CC_UNSIGNED + (CC_GT<<4) + CC_TWO, + /* UGE */ CC_LT + CC_UNSIGNED + (CC_LT<<4), + /* ULE */ CC_GT + CC_UNSIGNED + (CC_GT<<4), + /* UGT */ CC_LE + CC_UNSIGNED + (CC_LT<<4) + CC_TWO, + /* EQ */ CC_NE + (CC_NE<<4), + /* NE */ CC_EQ + (CC_EQ<<4), + /* ABC */ CC_LE + CC_UNSIGNED + (CC_LT<<4) + CC_TWO /* Same as UGT. */ +}; + +static void asm_intcomp_(ASMState *as, IRRef lref, IRRef rref, Reg cr, PPCCC cc) +{ + Reg right, left = ra_alloc1(as, lref, RSET_GPR); + if (irref_isk(rref)) { + int32_t k = IR(rref)->i; + if ((cc & CC_UNSIGNED) == 0) { /* Signed comparison with constant. */ + if (checki16(k)) { + emit_tai(as, PPCI_CMPWI, cr, left, k); + /* Signed comparison with zero and referencing previous ins? */ + if (k == 0 && lref == as->curins-1) + as->flagmcp = as->mcp; /* Allow elimination of the compare. */ + return; + } else if ((cc & 3) == (CC_EQ & 3)) { /* Use CMPLWI for EQ or NE. */ + if (checku16(k)) { + emit_tai(as, PPCI_CMPLWI, cr, left, k); + return; + } else if (!as->sectref && ra_noreg(IR(rref)->r)) { + emit_tai(as, PPCI_CMPLWI, cr, RID_TMP, k); + emit_asi(as, PPCI_XORIS, RID_TMP, left, (k >> 16)); + return; + } + } + } else { /* Unsigned comparison with constant. */ + if (checku16(k)) { + emit_tai(as, PPCI_CMPLWI, cr, left, k); + return; + } + } + } + right = ra_alloc1(as, rref, rset_exclude(RSET_GPR, left)); + emit_tab(as, (cc & CC_UNSIGNED) ? PPCI_CMPLW : PPCI_CMPW, cr, left, right); +} + +static void asm_comp(ASMState *as, IRIns *ir) +{ + PPCCC cc = asm_compmap[ir->o]; + if (!LJ_SOFTFP && irt_isnum(ir->t)) { + Reg right, left = ra_alloc2(as, ir, RSET_FPR); + right = (left >> 8); left &= 255; + asm_guardcc(as, (cc >> 4)); + if ((cc & CC_TWO)) + emit_tab(as, PPCI_CROR, ((cc>>4)&3), ((cc>>4)&3), (CC_EQ&3)); + emit_fab(as, PPCI_FCMPU, 0, left, right); + } else { + IRRef lref = ir->op1, rref = ir->op2; + if (irref_isk(lref) && !irref_isk(rref)) { + /* Swap constants to the right (only for ABC). */ + IRRef tmp = lref; lref = rref; rref = tmp; + if ((cc & 2) == 0) cc ^= 1; /* LT <-> GT, LE <-> GE */ + } + asm_guardcc(as, cc); + asm_intcomp_(as, lref, rref, 0, cc); + } +} + +#define asm_equal(as, ir) asm_comp(as, ir) + +#if LJ_SOFTFP +/* SFP comparisons. */ +static void asm_sfpcomp(ASMState *as, IRIns *ir) +{ + const CCallInfo *ci = &lj_ir_callinfo[IRCALL_softfp_cmp]; + RegSet drop = RSET_SCRATCH; + Reg r; + IRRef args[4]; + args[0^LJ_BE] = ir->op1; args[1^LJ_BE] = (ir+1)->op1; + args[2^LJ_BE] = ir->op2; args[3^LJ_BE] = (ir+1)->op2; + + for (r = REGARG_FIRSTGPR; r <= REGARG_FIRSTGPR+3; r++) { + if (!rset_test(as->freeset, r) && + regcost_ref(as->cost[r]) == args[r-REGARG_FIRSTGPR]) + rset_clear(drop, r); + } + ra_evictset(as, drop); + asm_setupresult(as, ir, ci); + switch ((IROp)ir->o) { + case IR_ULT: + asm_guardcc(as, CC_EQ); + emit_ai(as, PPCI_CMPWI, RID_RET, 0); + case IR_ULE: + asm_guardcc(as, CC_EQ); + emit_ai(as, PPCI_CMPWI, RID_RET, 1); + break; + case IR_GE: case IR_GT: + asm_guardcc(as, CC_EQ); + emit_ai(as, PPCI_CMPWI, RID_RET, 2); + default: + asm_guardcc(as, (asm_compmap[ir->o] & 0xf)); + emit_ai(as, PPCI_CMPWI, RID_RET, 0); + break; + } + asm_gencall(as, ci, args); +} +#endif + +#if LJ_HASFFI +/* 64 bit integer comparisons. */ +static void asm_comp64(ASMState *as, IRIns *ir) +{ + PPCCC cc = asm_compmap[(ir-1)->o]; + if ((cc&3) == (CC_EQ&3)) { + asm_guardcc(as, cc); + emit_tab(as, (cc&4) ? PPCI_CRAND : PPCI_CROR, + (CC_EQ&3), (CC_EQ&3), 4+(CC_EQ&3)); + } else { + asm_guardcc(as, CC_EQ); + emit_tab(as, PPCI_CROR, (CC_EQ&3), (CC_EQ&3), ((cc^~(cc>>2))&1)); + emit_tab(as, (cc&4) ? PPCI_CRAND : PPCI_CRANDC, + (CC_EQ&3), (CC_EQ&3), 4+(cc&3)); + } + /* Loword comparison sets cr1 and is unsigned, except for equality. */ + asm_intcomp_(as, (ir-1)->op1, (ir-1)->op2, 4, + cc | ((cc&3) == (CC_EQ&3) ? 0 : CC_UNSIGNED)); + /* Hiword comparison sets cr0. */ + asm_intcomp_(as, ir->op1, ir->op2, 0, cc); + as->flagmcp = NULL; /* Doesn't work here. */ +} +#endif + +/* -- Support for 64 bit ops in 32 bit mode ------------------------------- */ + +/* Hiword op of a split 64 bit op. Previous op must be the loword op. */ +static void asm_hiop(ASMState *as, IRIns *ir) +{ +#if LJ_HASFFI || LJ_SOFTFP + /* HIOP is marked as a store because it needs its own DCE logic. */ + int uselo = ra_used(ir-1), usehi = ra_used(ir); /* Loword/hiword used? */ + if (LJ_UNLIKELY(!(as->flags & JIT_F_OPT_DCE))) uselo = usehi = 1; + if ((ir-1)->o == IR_CONV) { /* Conversions to/from 64 bit. */ + as->curins--; /* Always skip the CONV. */ +#if LJ_HASFFI && !LJ_SOFTFP + if (usehi || uselo) + asm_conv64(as, ir); + return; +#endif + } else if ((ir-1)->o <= IR_NE) { /* 64 bit integer comparisons. ORDER IR. */ + as->curins--; /* Always skip the loword comparison. */ +#if LJ_SOFTFP + if (!irt_isint(ir->t)) { + asm_sfpcomp(as, ir-1); + return; + } +#endif +#if LJ_HASFFI + asm_comp64(as, ir); +#endif + return; +#if LJ_SOFTFP + } else if ((ir-1)->o == IR_MIN || (ir-1)->o == IR_MAX) { + as->curins--; /* Always skip the loword min/max. */ + if (uselo || usehi) + asm_sfpmin_max(as, ir-1); + return; +#endif + } else if ((ir-1)->o == IR_XSTORE) { + as->curins--; /* Handle both stores here. */ + if ((ir-1)->r != RID_SINK) { + asm_xstore_(as, ir, 0); + asm_xstore_(as, ir-1, 4); + } + return; + } + if (!usehi) return; /* Skip unused hiword op for all remaining ops. */ + switch ((ir-1)->o) { +#if LJ_HASFFI + case IR_ADD: as->curins--; asm_add64(as, ir); break; + case IR_SUB: as->curins--; asm_sub64(as, ir); break; + case IR_NEG: as->curins--; asm_neg64(as, ir); break; +#endif +#if LJ_SOFTFP + case IR_SLOAD: case IR_ALOAD: case IR_HLOAD: case IR_ULOAD: case IR_VLOAD: + case IR_STRTO: + if (!uselo) + ra_allocref(as, ir->op1, RSET_GPR); /* Mark lo op as used. */ + break; +#endif + case IR_CALLN: + case IR_CALLS: + case IR_CALLXS: + if (!uselo) + ra_allocref(as, ir->op1, RID2RSET(RID_RETLO)); /* Mark lo op as used. */ + break; +#if LJ_SOFTFP + case IR_ASTORE: case IR_HSTORE: case IR_USTORE: case IR_TOSTR: +#endif + case IR_CNEWI: + /* Nothing to do here. Handled by lo op itself. */ + break; + default: lua_assert(0); break; + } +#else + UNUSED(as); UNUSED(ir); lua_assert(0); /* Unused without FFI. */ +#endif +} + +/* -- Profiling ----------------------------------------------------------- */ + +static void asm_prof(ASMState *as, IRIns *ir) +{ + UNUSED(ir); + asm_guardcc(as, CC_NE); + emit_asi(as, PPCI_ANDIDOT, RID_TMP, RID_TMP, HOOK_PROFILE); + emit_lsglptr(as, PPCI_LBZ, RID_TMP, + (int32_t)offsetof(global_State, hookmask)); +} + +/* -- Stack handling ------------------------------------------------------ */ + +/* Check Lua stack size for overflow. Use exit handler as fallback. */ +static void asm_stack_check(ASMState *as, BCReg topslot, + IRIns *irp, RegSet allow, ExitNo exitno) +{ + /* Try to get an unused temp. register, otherwise spill/restore RID_RET*. */ + Reg tmp, pbase = irp ? (ra_hasreg(irp->r) ? irp->r : RID_TMP) : RID_BASE; + rset_clear(allow, pbase); + tmp = allow ? rset_pickbot(allow) : + (pbase == RID_RETHI ? RID_RETLO : RID_RETHI); + emit_condbranch(as, PPCI_BC, CC_LT, asm_exitstub_addr(as, exitno)); + if (allow == RSET_EMPTY) /* Restore temp. register. */ + emit_tai(as, PPCI_LWZ, tmp, RID_SP, SPOFS_TMPW); + else + ra_modified(as, tmp); + emit_ai(as, PPCI_CMPLWI, RID_TMP, (int32_t)(8*topslot)); + emit_tab(as, PPCI_SUBF, RID_TMP, pbase, tmp); + emit_tai(as, PPCI_LWZ, tmp, tmp, offsetof(lua_State, maxstack)); + if (pbase == RID_TMP) + emit_getgl(as, RID_TMP, jit_base); + emit_getgl(as, tmp, cur_L); + if (allow == RSET_EMPTY) /* Spill temp. register. */ + emit_tai(as, PPCI_STW, tmp, RID_SP, SPOFS_TMPW); +} + +/* Restore Lua stack from on-trace state. */ +static void asm_stack_restore(ASMState *as, SnapShot *snap) +{ + SnapEntry *map = &as->T->snapmap[snap->mapofs]; + SnapEntry *flinks = &as->T->snapmap[snap_nextofs(as->T, snap)-1]; + MSize n, nent = snap->nent; + /* Store the value of all modified slots to the Lua stack. */ + for (n = 0; n < nent; n++) { + SnapEntry sn = map[n]; + BCReg s = snap_slot(sn); + int32_t ofs = 8*((int32_t)s-1); + IRRef ref = snap_ref(sn); + IRIns *ir = IR(ref); + if ((sn & SNAP_NORESTORE)) + continue; + if (irt_isnum(ir->t)) { +#if LJ_SOFTFP + Reg tmp; + RegSet allow = rset_exclude(RSET_GPR, RID_BASE); + lua_assert(irref_isk(ref)); /* LJ_SOFTFP: must be a number constant. */ + tmp = ra_allock(as, (int32_t)ir_knum(ir)->u32.lo, allow); + emit_tai(as, PPCI_STW, tmp, RID_BASE, ofs+(LJ_BE?4:0)); + if (rset_test(as->freeset, tmp+1)) allow = RID2RSET(tmp+1); + tmp = ra_allock(as, (int32_t)ir_knum(ir)->u32.hi, allow); + emit_tai(as, PPCI_STW, tmp, RID_BASE, ofs+(LJ_BE?0:4)); +#else + Reg src = ra_alloc1(as, ref, RSET_FPR); + emit_fai(as, PPCI_STFD, src, RID_BASE, ofs); +#endif + } else { + Reg type; + RegSet allow = rset_exclude(RSET_GPR, RID_BASE); + lua_assert(irt_ispri(ir->t) || irt_isaddr(ir->t) || irt_isinteger(ir->t)); + if (!irt_ispri(ir->t)) { + Reg src = ra_alloc1(as, ref, allow); + rset_clear(allow, src); + emit_tai(as, PPCI_STW, src, RID_BASE, ofs+4); + } + if ((sn & (SNAP_CONT|SNAP_FRAME))) { + if (s == 0) continue; /* Do not overwrite link to previous frame. */ + type = ra_allock(as, (int32_t)(*flinks--), allow); +#if LJ_SOFTFP + } else if ((sn & SNAP_SOFTFPNUM)) { + type = ra_alloc1(as, ref+1, rset_exclude(RSET_GPR, RID_BASE)); +#endif + } else { + type = ra_allock(as, (int32_t)irt_toitype(ir->t), allow); + } + emit_tai(as, PPCI_STW, type, RID_BASE, ofs); + } + checkmclim(as); + } + lua_assert(map + nent == flinks); +} + +/* -- GC handling --------------------------------------------------------- */ + +/* Check GC threshold and do one or more GC steps. */ +static void asm_gc_check(ASMState *as) +{ + const CCallInfo *ci = &lj_ir_callinfo[IRCALL_lj_gc_step_jit]; + IRRef args[2]; + MCLabel l_end; + Reg tmp; + ra_evictset(as, RSET_SCRATCH); + l_end = emit_label(as); + /* Exit trace if in GCSatomic or GCSfinalize. Avoids syncing GC objects. */ + asm_guardcc(as, CC_NE); /* Assumes asm_snap_prep() already done. */ + emit_ai(as, PPCI_CMPWI, RID_RET, 0); + args[0] = ASMREF_TMP1; /* global_State *g */ + args[1] = ASMREF_TMP2; /* MSize steps */ + asm_gencall(as, ci, args); + emit_tai(as, PPCI_ADDI, ra_releasetmp(as, ASMREF_TMP1), RID_JGL, -32768); + tmp = ra_releasetmp(as, ASMREF_TMP2); + emit_loadi(as, tmp, as->gcsteps); + /* Jump around GC step if GC total < GC threshold. */ + emit_condbranch(as, PPCI_BC|PPCF_Y, CC_LT, l_end); + emit_ab(as, PPCI_CMPLW, RID_TMP, tmp); + emit_getgl(as, tmp, gc.threshold); + emit_getgl(as, RID_TMP, gc.total); + as->gcsteps = 0; + checkmclim(as); +} + +/* -- Loop handling ------------------------------------------------------- */ + +/* Fixup the loop branch. */ +static void asm_loop_fixup(ASMState *as) +{ + MCode *p = as->mctop; + MCode *target = as->mcp; + if (as->loopinv) { /* Inverted loop branch? */ + /* asm_guardcc already inverted the cond branch and patched the final b. */ + p[-2] = (p[-2] & (0xffff0000u & ~PPCF_Y)) | (((target-p+2) & 0x3fffu) << 2); + } else { + p[-1] = PPCI_B|(((target-p+1)&0x00ffffffu)<<2); + } +} + +/* -- Head of trace ------------------------------------------------------- */ + +/* Coalesce BASE register for a root trace. */ +static void asm_head_root_base(ASMState *as) +{ + IRIns *ir = IR(REF_BASE); + Reg r = ir->r; + if (ra_hasreg(r)) { + ra_free(as, r); + if (rset_test(as->modset, r) || irt_ismarked(ir->t)) + ir->r = RID_INIT; /* No inheritance for modified BASE register. */ + if (r != RID_BASE) + emit_mr(as, r, RID_BASE); + } +} + +/* Coalesce BASE register for a side trace. */ +static RegSet asm_head_side_base(ASMState *as, IRIns *irp, RegSet allow) +{ + IRIns *ir = IR(REF_BASE); + Reg r = ir->r; + if (ra_hasreg(r)) { + ra_free(as, r); + if (rset_test(as->modset, r) || irt_ismarked(ir->t)) + ir->r = RID_INIT; /* No inheritance for modified BASE register. */ + if (irp->r == r) { + rset_clear(allow, r); /* Mark same BASE register as coalesced. */ + } else if (ra_hasreg(irp->r) && rset_test(as->freeset, irp->r)) { + rset_clear(allow, irp->r); + emit_mr(as, r, irp->r); /* Move from coalesced parent reg. */ + } else { + emit_getgl(as, r, jit_base); /* Otherwise reload BASE. */ + } + } + return allow; +} + +/* -- Tail of trace ------------------------------------------------------- */ + +/* Fixup the tail code. */ +static void asm_tail_fixup(ASMState *as, TraceNo lnk) +{ + MCode *p = as->mctop; + MCode *target; + int32_t spadj = as->T->spadjust; + if (spadj == 0) { + *--p = PPCI_NOP; + *--p = PPCI_NOP; + as->mctop = p; + } else { + /* Patch stack adjustment. */ + lua_assert(checki16(CFRAME_SIZE+spadj)); + p[-3] = PPCI_ADDI | PPCF_T(RID_TMP) | PPCF_A(RID_SP) | (CFRAME_SIZE+spadj); + p[-2] = PPCI_STWU | PPCF_T(RID_TMP) | PPCF_A(RID_SP) | spadj; + } + /* Patch exit branch. */ + target = lnk ? traceref(as->J, lnk)->mcode : (MCode *)lj_vm_exit_interp; + p[-1] = PPCI_B|(((target-p+1)&0x00ffffffu)<<2); +} + +/* Prepare tail of code. */ +static void asm_tail_prep(ASMState *as) +{ + MCode *p = as->mctop - 1; /* Leave room for exit branch. */ + if (as->loopref) { + as->invmcp = as->mcp = p; + } else { + as->mcp = p-2; /* Leave room for stack pointer adjustment. */ + as->invmcp = NULL; + } +} + +/* -- Trace setup --------------------------------------------------------- */ + +/* Ensure there are enough stack slots for call arguments. */ +static Reg asm_setup_call_slots(ASMState *as, IRIns *ir, const CCallInfo *ci) +{ + IRRef args[CCI_NARGS_MAX*2]; + uint32_t i, nargs = CCI_XNARGS(ci); + int nslots = 2, ngpr = REGARG_NUMGPR, nfpr = REGARG_NUMFPR; + asm_collectargs(as, ir, ci, args); + for (i = 0; i < nargs; i++) + if (!LJ_SOFTFP && args[i] && irt_isfp(IR(args[i])->t)) { + if (nfpr > 0) nfpr--; else nslots = (nslots+3) & ~1; + } else { + if (ngpr > 0) ngpr--; else nslots++; + } + if (nslots > as->evenspill) /* Leave room for args in stack slots. */ + as->evenspill = nslots; + return (!LJ_SOFTFP && irt_isfp(ir->t)) ? REGSP_HINT(RID_FPRET) : + REGSP_HINT(RID_RET); +} + +static void asm_setup_target(ASMState *as) +{ + asm_exitstub_setup(as, as->T->nsnap + (as->parent ? 1 : 0)); +} + +/* -- Trace patching ------------------------------------------------------ */ + +/* Patch exit jumps of existing machine code to a new target. */ +void lj_asm_patchexit(jit_State *J, GCtrace *T, ExitNo exitno, MCode *target) +{ + MCode *p = T->mcode; + MCode *pe = (MCode *)((char *)p + T->szmcode); + MCode *px = exitstub_trace_addr(T, exitno); + MCode *cstart = NULL; + MCode *mcarea = lj_mcode_patch(J, p, 0); + int clearso = 0; + for (; p < pe; p++) { + /* Look for exitstub branch, try to replace with branch to target. */ + uint32_t ins = *p; + if ((ins & 0xfc000000u) == 0x40000000u && + ((ins ^ ((char *)px-(char *)p)) & 0xffffu) == 0) { + ptrdiff_t delta = (char *)target - (char *)p; + if (((ins >> 16) & 3) == (CC_SO&3)) { + clearso = sizeof(MCode); + delta -= sizeof(MCode); + } + /* Many, but not all short-range branches can be patched directly. */ + if (((delta + 0x8000) >> 16) == 0) { + *p = (ins & 0xffdf0000u) | ((uint32_t)delta & 0xffffu) | + ((delta & 0x8000) * (PPCF_Y/0x8000)); + if (!cstart) cstart = p; + } + } else if ((ins & 0xfc000000u) == PPCI_B && + ((ins ^ ((char *)px-(char *)p)) & 0x03ffffffu) == 0) { + ptrdiff_t delta = (char *)target - (char *)p; + lua_assert(((delta + 0x02000000) >> 26) == 0); + *p = PPCI_B | ((uint32_t)delta & 0x03ffffffu); + if (!cstart) cstart = p; + } + } + { /* Always patch long-range branch in exit stub itself. */ + ptrdiff_t delta = (char *)target - (char *)px - clearso; + lua_assert(((delta + 0x02000000) >> 26) == 0); + *px = PPCI_B | ((uint32_t)delta & 0x03ffffffu); + } + if (!cstart) cstart = px; + lj_mcode_sync(cstart, px+1); + if (clearso) { /* Extend the current trace. Ugly workaround. */ + MCode *pp = J->cur.mcode; + J->cur.szmcode += sizeof(MCode); + *--pp = PPCI_MCRXR; /* Clear SO flag. */ + J->cur.mcode = pp; + lj_mcode_sync(pp, pp+1); + } + lj_mcode_patch(J, mcarea, 1); +} + diff --git a/lib/LuaJIT/lj_asm_x86.h b/lib/LuaJIT/lj_asm_x86.h new file mode 100644 index 0000000..af54dc7 --- /dev/null +++ b/lib/LuaJIT/lj_asm_x86.h @@ -0,0 +1,3121 @@ +/* +** x86/x64 IR assembler (SSA IR -> machine code). +** Copyright (C) 2005-2017 Mike Pall. See Copyright Notice in luajit.h +*/ + +/* -- Guard handling ------------------------------------------------------ */ + +/* Generate an exit stub group at the bottom of the reserved MCode memory. */ +static MCode *asm_exitstub_gen(ASMState *as, ExitNo group) +{ + ExitNo i, groupofs = (group*EXITSTUBS_PER_GROUP) & 0xff; + MCode *mxp = as->mcbot; + MCode *mxpstart = mxp; + if (mxp + (2+2)*EXITSTUBS_PER_GROUP+8+5 >= as->mctop) + asm_mclimit(as); + /* Push low byte of exitno for each exit stub. */ + *mxp++ = XI_PUSHi8; *mxp++ = (MCode)groupofs; + for (i = 1; i < EXITSTUBS_PER_GROUP; i++) { + *mxp++ = XI_JMPs; *mxp++ = (MCode)((2+2)*(EXITSTUBS_PER_GROUP - i) - 2); + *mxp++ = XI_PUSHi8; *mxp++ = (MCode)(groupofs + i); + } + /* Push the high byte of the exitno for each exit stub group. */ + *mxp++ = XI_PUSHi8; *mxp++ = (MCode)((group*EXITSTUBS_PER_GROUP)>>8); +#if !LJ_GC64 + /* Store DISPATCH at original stack slot 0. Account for the two push ops. */ + *mxp++ = XI_MOVmi; + *mxp++ = MODRM(XM_OFS8, 0, RID_ESP); + *mxp++ = MODRM(XM_SCALE1, RID_ESP, RID_ESP); + *mxp++ = 2*sizeof(void *); + *(int32_t *)mxp = ptr2addr(J2GG(as->J)->dispatch); mxp += 4; +#endif + /* Jump to exit handler which fills in the ExitState. */ + *mxp++ = XI_JMP; mxp += 4; + *((int32_t *)(mxp-4)) = jmprel(mxp, (MCode *)(void *)lj_vm_exit_handler); + /* Commit the code for this group (even if assembly fails later on). */ + lj_mcode_commitbot(as->J, mxp); + as->mcbot = mxp; + as->mclim = as->mcbot + MCLIM_REDZONE; + return mxpstart; +} + +/* Setup all needed exit stubs. */ +static void asm_exitstub_setup(ASMState *as, ExitNo nexits) +{ + ExitNo i; + if (nexits >= EXITSTUBS_PER_GROUP*LJ_MAX_EXITSTUBGR) + lj_trace_err(as->J, LJ_TRERR_SNAPOV); + for (i = 0; i < (nexits+EXITSTUBS_PER_GROUP-1)/EXITSTUBS_PER_GROUP; i++) + if (as->J->exitstubgroup[i] == NULL) + as->J->exitstubgroup[i] = asm_exitstub_gen(as, i); +} + +/* Emit conditional branch to exit for guard. +** It's important to emit this *after* all registers have been allocated, +** because rematerializations may invalidate the flags. +*/ +static void asm_guardcc(ASMState *as, int cc) +{ + MCode *target = exitstub_addr(as->J, as->snapno); + MCode *p = as->mcp; + if (LJ_UNLIKELY(p == as->invmcp)) { + as->loopinv = 1; + *(int32_t *)(p+1) = jmprel(p+5, target); + target = p; + cc ^= 1; + if (as->realign) { + if (LJ_GC64 && LJ_UNLIKELY(as->mrm.base == RID_RIP)) + as->mrm.ofs += 2; /* Fixup RIP offset for pending fused load. */ + emit_sjcc(as, cc, target); + return; + } + } + if (LJ_GC64 && LJ_UNLIKELY(as->mrm.base == RID_RIP)) + as->mrm.ofs += 6; /* Fixup RIP offset for pending fused load. */ + emit_jcc(as, cc, target); +} + +/* -- Memory operand fusion ----------------------------------------------- */ + +/* Limit linear search to this distance. Avoids O(n^2) behavior. */ +#define CONFLICT_SEARCH_LIM 31 + +/* Check if a reference is a signed 32 bit constant. */ +static int asm_isk32(ASMState *as, IRRef ref, int32_t *k) +{ + if (irref_isk(ref)) { + IRIns *ir = IR(ref); +#if LJ_GC64 + if (ir->o == IR_KNULL || !irt_is64(ir->t)) { + *k = ir->i; + return 1; + } else if (checki32((int64_t)ir_k64(ir)->u64)) { + *k = (int32_t)ir_k64(ir)->u64; + return 1; + } +#else + if (ir->o != IR_KINT64) { + *k = ir->i; + return 1; + } else if (checki32((int64_t)ir_kint64(ir)->u64)) { + *k = (int32_t)ir_kint64(ir)->u64; + return 1; + } +#endif + } + return 0; +} + +/* Check if there's no conflicting instruction between curins and ref. +** Also avoid fusing loads if there are multiple references. +*/ +static int noconflict(ASMState *as, IRRef ref, IROp conflict, int noload) +{ + IRIns *ir = as->ir; + IRRef i = as->curins; + if (i > ref + CONFLICT_SEARCH_LIM) + return 0; /* Give up, ref is too far away. */ + while (--i > ref) { + if (ir[i].o == conflict) + return 0; /* Conflict found. */ + else if (!noload && (ir[i].op1 == ref || ir[i].op2 == ref)) + return 0; + } + return 1; /* Ok, no conflict. */ +} + +/* Fuse array base into memory operand. */ +static IRRef asm_fuseabase(ASMState *as, IRRef ref) +{ + IRIns *irb = IR(ref); + as->mrm.ofs = 0; + if (irb->o == IR_FLOAD) { + IRIns *ira = IR(irb->op1); + lua_assert(irb->op2 == IRFL_TAB_ARRAY); + /* We can avoid the FLOAD of t->array for colocated arrays. */ + if (ira->o == IR_TNEW && ira->op1 <= LJ_MAX_COLOSIZE && + !neverfuse(as) && noconflict(as, irb->op1, IR_NEWREF, 1)) { + as->mrm.ofs = (int32_t)sizeof(GCtab); /* Ofs to colocated array. */ + return irb->op1; /* Table obj. */ + } + } else if (irb->o == IR_ADD && irref_isk(irb->op2)) { + /* Fuse base offset (vararg load). */ + as->mrm.ofs = IR(irb->op2)->i; + return irb->op1; + } + return ref; /* Otherwise use the given array base. */ +} + +/* Fuse array reference into memory operand. */ +static void asm_fusearef(ASMState *as, IRIns *ir, RegSet allow) +{ + IRIns *irx; + lua_assert(ir->o == IR_AREF); + as->mrm.base = (uint8_t)ra_alloc1(as, asm_fuseabase(as, ir->op1), allow); + irx = IR(ir->op2); + if (irref_isk(ir->op2)) { + as->mrm.ofs += 8*irx->i; + as->mrm.idx = RID_NONE; + } else { + rset_clear(allow, as->mrm.base); + as->mrm.scale = XM_SCALE8; + /* Fuse a constant ADD (e.g. t[i+1]) into the offset. + ** Doesn't help much without ABCelim, but reduces register pressure. + */ + if (!LJ_64 && /* Has bad effects with negative index on x64. */ + mayfuse(as, ir->op2) && ra_noreg(irx->r) && + irx->o == IR_ADD && irref_isk(irx->op2)) { + as->mrm.ofs += 8*IR(irx->op2)->i; + as->mrm.idx = (uint8_t)ra_alloc1(as, irx->op1, allow); + } else { + as->mrm.idx = (uint8_t)ra_alloc1(as, ir->op2, allow); + } + } +} + +/* Fuse array/hash/upvalue reference into memory operand. +** Caveat: this may allocate GPRs for the base/idx registers. Be sure to +** pass the final allow mask, excluding any GPRs used for other inputs. +** In particular: 2-operand GPR instructions need to call ra_dest() first! +*/ +static void asm_fuseahuref(ASMState *as, IRRef ref, RegSet allow) +{ + IRIns *ir = IR(ref); + if (ra_noreg(ir->r)) { + switch ((IROp)ir->o) { + case IR_AREF: + if (mayfuse(as, ref)) { + asm_fusearef(as, ir, allow); + return; + } + break; + case IR_HREFK: + if (mayfuse(as, ref)) { + as->mrm.base = (uint8_t)ra_alloc1(as, ir->op1, allow); + as->mrm.ofs = (int32_t)(IR(ir->op2)->op2 * sizeof(Node)); + as->mrm.idx = RID_NONE; + return; + } + break; + case IR_UREFC: + if (irref_isk(ir->op1)) { + GCfunc *fn = ir_kfunc(IR(ir->op1)); + GCupval *uv = &gcref(fn->l.uvptr[(ir->op2 >> 8)])->uv; +#if LJ_GC64 + int64_t ofs = dispofs(as, &uv->tv); + if (checki32(ofs) && checki32(ofs+4)) { + as->mrm.ofs = (int32_t)ofs; + as->mrm.base = RID_DISPATCH; + as->mrm.idx = RID_NONE; + return; + } +#else + as->mrm.ofs = ptr2addr(&uv->tv); + as->mrm.base = as->mrm.idx = RID_NONE; + return; +#endif + } + break; + default: + lua_assert(ir->o == IR_HREF || ir->o == IR_NEWREF || ir->o == IR_UREFO || + ir->o == IR_KKPTR); + break; + } + } + as->mrm.base = (uint8_t)ra_alloc1(as, ref, allow); + as->mrm.ofs = 0; + as->mrm.idx = RID_NONE; +} + +/* Fuse FLOAD/FREF reference into memory operand. */ +static void asm_fusefref(ASMState *as, IRIns *ir, RegSet allow) +{ + lua_assert(ir->o == IR_FLOAD || ir->o == IR_FREF); + as->mrm.idx = RID_NONE; + if (ir->op1 == REF_NIL) { +#if LJ_GC64 + as->mrm.ofs = (int32_t)(ir->op2 << 2) - GG_OFS(dispatch); + as->mrm.base = RID_DISPATCH; +#else + as->mrm.ofs = (int32_t)(ir->op2 << 2) + ptr2addr(J2GG(as->J)); + as->mrm.base = RID_NONE; +#endif + return; + } + as->mrm.ofs = field_ofs[ir->op2]; + if (irref_isk(ir->op1)) { + IRIns *op1 = IR(ir->op1); +#if LJ_GC64 + if (ir->op1 == REF_NIL) { + as->mrm.ofs -= GG_OFS(dispatch); + as->mrm.base = RID_DISPATCH; + return; + } else if (op1->o == IR_KPTR || op1->o == IR_KKPTR) { + intptr_t ofs = dispofs(as, ir_kptr(op1)); + if (checki32(as->mrm.ofs + ofs)) { + as->mrm.ofs += (int32_t)ofs; + as->mrm.base = RID_DISPATCH; + return; + } + } +#else + as->mrm.ofs += op1->i; + as->mrm.base = RID_NONE; + return; +#endif + } + as->mrm.base = (uint8_t)ra_alloc1(as, ir->op1, allow); +} + +/* Fuse string reference into memory operand. */ +static void asm_fusestrref(ASMState *as, IRIns *ir, RegSet allow) +{ + IRIns *irr; + lua_assert(ir->o == IR_STRREF); + as->mrm.base = as->mrm.idx = RID_NONE; + as->mrm.scale = XM_SCALE1; + as->mrm.ofs = sizeof(GCstr); + if (!LJ_GC64 && irref_isk(ir->op1)) { + as->mrm.ofs += IR(ir->op1)->i; + } else { + Reg r = ra_alloc1(as, ir->op1, allow); + rset_clear(allow, r); + as->mrm.base = (uint8_t)r; + } + irr = IR(ir->op2); + if (irref_isk(ir->op2)) { + as->mrm.ofs += irr->i; + } else { + Reg r; + /* Fuse a constant add into the offset, e.g. string.sub(s, i+10). */ + if (!LJ_64 && /* Has bad effects with negative index on x64. */ + mayfuse(as, ir->op2) && irr->o == IR_ADD && irref_isk(irr->op2)) { + as->mrm.ofs += IR(irr->op2)->i; + r = ra_alloc1(as, irr->op1, allow); + } else { + r = ra_alloc1(as, ir->op2, allow); + } + if (as->mrm.base == RID_NONE) + as->mrm.base = (uint8_t)r; + else + as->mrm.idx = (uint8_t)r; + } +} + +static void asm_fusexref(ASMState *as, IRRef ref, RegSet allow) +{ + IRIns *ir = IR(ref); + as->mrm.idx = RID_NONE; + if (ir->o == IR_KPTR || ir->o == IR_KKPTR) { +#if LJ_GC64 + intptr_t ofs = dispofs(as, ir_kptr(ir)); + if (checki32(ofs)) { + as->mrm.ofs = (int32_t)ofs; + as->mrm.base = RID_DISPATCH; + return; + } + } if (0) { +#else + as->mrm.ofs = ir->i; + as->mrm.base = RID_NONE; + } else if (ir->o == IR_STRREF) { + asm_fusestrref(as, ir, allow); +#endif + } else { + as->mrm.ofs = 0; + if (canfuse(as, ir) && ir->o == IR_ADD && ra_noreg(ir->r)) { + /* Gather (base+idx*sz)+ofs as emitted by cdata ptr/array indexing. */ + IRIns *irx; + IRRef idx; + Reg r; + if (asm_isk32(as, ir->op2, &as->mrm.ofs)) { /* Recognize x+ofs. */ + ref = ir->op1; + ir = IR(ref); + if (!(ir->o == IR_ADD && canfuse(as, ir) && ra_noreg(ir->r))) + goto noadd; + } + as->mrm.scale = XM_SCALE1; + idx = ir->op1; + ref = ir->op2; + irx = IR(idx); + if (!(irx->o == IR_BSHL || irx->o == IR_ADD)) { /* Try other operand. */ + idx = ir->op2; + ref = ir->op1; + irx = IR(idx); + } + if (canfuse(as, irx) && ra_noreg(irx->r)) { + if (irx->o == IR_BSHL && irref_isk(irx->op2) && IR(irx->op2)->i <= 3) { + /* Recognize idx<op1; + as->mrm.scale = (uint8_t)(IR(irx->op2)->i << 6); + } else if (irx->o == IR_ADD && irx->op1 == irx->op2) { + /* FOLD does idx*2 ==> idx<<1 ==> idx+idx. */ + idx = irx->op1; + as->mrm.scale = XM_SCALE2; + } + } + r = ra_alloc1(as, idx, allow); + rset_clear(allow, r); + as->mrm.idx = (uint8_t)r; + } + noadd: + as->mrm.base = (uint8_t)ra_alloc1(as, ref, allow); + } +} + +/* Fuse load of 64 bit IR constant into memory operand. */ +static Reg asm_fuseloadk64(ASMState *as, IRIns *ir) +{ + const uint64_t *k = &ir_k64(ir)->u64; + if (!LJ_GC64 || checki32((intptr_t)k)) { + as->mrm.ofs = ptr2addr(k); + as->mrm.base = RID_NONE; +#if LJ_GC64 + } else if (checki32(dispofs(as, k))) { + as->mrm.ofs = (int32_t)dispofs(as, k); + as->mrm.base = RID_DISPATCH; + } else if (checki32(mcpofs(as, k)) && checki32(mcpofs(as, k+1)) && + checki32(mctopofs(as, k)) && checki32(mctopofs(as, k+1))) { + as->mrm.ofs = (int32_t)mcpofs(as, k); + as->mrm.base = RID_RIP; + } else { + if (ir->i) { + lua_assert(*k == *(uint64_t*)(as->mctop - ir->i)); + } else { + while ((uintptr_t)as->mcbot & 7) *as->mcbot++ = XI_INT3; + *(uint64_t*)as->mcbot = *k; + ir->i = (int32_t)(as->mctop - as->mcbot); + as->mcbot += 8; + as->mclim = as->mcbot + MCLIM_REDZONE; + lj_mcode_commitbot(as->J, as->mcbot); + } + as->mrm.ofs = (int32_t)mcpofs(as, as->mctop - ir->i); + as->mrm.base = RID_RIP; +#endif + } + as->mrm.idx = RID_NONE; + return RID_MRM; +} + +/* Fuse load into memory operand. +** +** Important caveat: this may emit RIP-relative loads! So don't place any +** code emitters between this function and the use of its result. +** The only permitted exception is asm_guardcc(). +*/ +static Reg asm_fuseload(ASMState *as, IRRef ref, RegSet allow) +{ + IRIns *ir = IR(ref); + if (ra_hasreg(ir->r)) { + if (allow != RSET_EMPTY) { /* Fast path. */ + ra_noweak(as, ir->r); + return ir->r; + } + fusespill: + /* Force a spill if only memory operands are allowed (asm_x87load). */ + as->mrm.base = RID_ESP; + as->mrm.ofs = ra_spill(as, ir); + as->mrm.idx = RID_NONE; + return RID_MRM; + } + if (ir->o == IR_KNUM) { + RegSet avail = as->freeset & ~as->modset & RSET_FPR; + lua_assert(allow != RSET_EMPTY); + if (!(avail & (avail-1))) /* Fuse if less than two regs available. */ + return asm_fuseloadk64(as, ir); + } else if (ref == REF_BASE || ir->o == IR_KINT64) { + RegSet avail = as->freeset & ~as->modset & RSET_GPR; + lua_assert(allow != RSET_EMPTY); + if (!(avail & (avail-1))) { /* Fuse if less than two regs available. */ + if (ref == REF_BASE) { +#if LJ_GC64 + as->mrm.ofs = (int32_t)dispofs(as, &J2G(as->J)->jit_base); + as->mrm.base = RID_DISPATCH; +#else + as->mrm.ofs = ptr2addr(&J2G(as->J)->jit_base); + as->mrm.base = RID_NONE; +#endif + as->mrm.idx = RID_NONE; + return RID_MRM; + } else { + return asm_fuseloadk64(as, ir); + } + } + } else if (mayfuse(as, ref)) { + RegSet xallow = (allow & RSET_GPR) ? allow : RSET_GPR; + if (ir->o == IR_SLOAD) { + if (!(ir->op2 & (IRSLOAD_PARENT|IRSLOAD_CONVERT)) && + noconflict(as, ref, IR_RETF, 0) && + !(LJ_GC64 && irt_isaddr(ir->t))) { + as->mrm.base = (uint8_t)ra_alloc1(as, REF_BASE, xallow); + as->mrm.ofs = 8*((int32_t)ir->op1-1-LJ_FR2) + + (!LJ_FR2 && (ir->op2 & IRSLOAD_FRAME) ? 4 : 0); + as->mrm.idx = RID_NONE; + return RID_MRM; + } + } else if (ir->o == IR_FLOAD) { + /* Generic fusion is only ok for 32 bit operand (but see asm_comp). */ + if ((irt_isint(ir->t) || irt_isu32(ir->t) || irt_isaddr(ir->t)) && + noconflict(as, ref, IR_FSTORE, 0)) { + asm_fusefref(as, ir, xallow); + return RID_MRM; + } + } else if (ir->o == IR_ALOAD || ir->o == IR_HLOAD || ir->o == IR_ULOAD) { + if (noconflict(as, ref, ir->o + IRDELTA_L2S, 0) && + !(LJ_GC64 && irt_isaddr(ir->t))) { + asm_fuseahuref(as, ir->op1, xallow); + return RID_MRM; + } + } else if (ir->o == IR_XLOAD) { + /* Generic fusion is not ok for 8/16 bit operands (but see asm_comp). + ** Fusing unaligned memory operands is ok on x86 (except for SIMD types). + */ + if ((!irt_typerange(ir->t, IRT_I8, IRT_U16)) && + noconflict(as, ref, IR_XSTORE, 0)) { + asm_fusexref(as, ir->op1, xallow); + return RID_MRM; + } + } else if (ir->o == IR_VLOAD && !(LJ_GC64 && irt_isaddr(ir->t))) { + asm_fuseahuref(as, ir->op1, xallow); + return RID_MRM; + } + } + if (ir->o == IR_FLOAD && ir->op1 == REF_NIL) { + asm_fusefref(as, ir, RSET_EMPTY); + return RID_MRM; + } + if (!(as->freeset & allow) && !emit_canremat(ref) && + (allow == RSET_EMPTY || ra_hasspill(ir->s) || iscrossref(as, ref))) + goto fusespill; + return ra_allocref(as, ref, allow); +} + +#if LJ_64 +/* Don't fuse a 32 bit load into a 64 bit operation. */ +static Reg asm_fuseloadm(ASMState *as, IRRef ref, RegSet allow, int is64) +{ + if (is64 && !irt_is64(IR(ref)->t)) + return ra_alloc1(as, ref, allow); + return asm_fuseload(as, ref, allow); +} +#else +#define asm_fuseloadm(as, ref, allow, is64) asm_fuseload(as, (ref), (allow)) +#endif + +/* -- Calls --------------------------------------------------------------- */ + +/* Count the required number of stack slots for a call. */ +static int asm_count_call_slots(ASMState *as, const CCallInfo *ci, IRRef *args) +{ + uint32_t i, nargs = CCI_XNARGS(ci); + int nslots = 0; +#if LJ_64 + if (LJ_ABI_WIN) { + nslots = (int)(nargs*2); /* Only matters for more than four args. */ + } else { + int ngpr = REGARG_NUMGPR, nfpr = REGARG_NUMFPR; + for (i = 0; i < nargs; i++) + if (args[i] && irt_isfp(IR(args[i])->t)) { + if (nfpr > 0) nfpr--; else nslots += 2; + } else { + if (ngpr > 0) ngpr--; else nslots += 2; + } + } +#else + int ngpr = 0; + if ((ci->flags & CCI_CC_MASK) == CCI_CC_FASTCALL) + ngpr = 2; + else if ((ci->flags & CCI_CC_MASK) == CCI_CC_THISCALL) + ngpr = 1; + for (i = 0; i < nargs; i++) + if (args[i] && irt_isfp(IR(args[i])->t)) { + nslots += irt_isnum(IR(args[i])->t) ? 2 : 1; + } else { + if (ngpr > 0) ngpr--; else nslots++; + } +#endif + return nslots; +} + +/* Generate a call to a C function. */ +static void asm_gencall(ASMState *as, const CCallInfo *ci, IRRef *args) +{ + uint32_t n, nargs = CCI_XNARGS(ci); + int32_t ofs = STACKARG_OFS; +#if LJ_64 + uint32_t gprs = REGARG_GPRS; + Reg fpr = REGARG_FIRSTFPR; +#if !LJ_ABI_WIN + MCode *patchnfpr = NULL; +#endif +#else + uint32_t gprs = 0; + if ((ci->flags & CCI_CC_MASK) != CCI_CC_CDECL) { + if ((ci->flags & CCI_CC_MASK) == CCI_CC_THISCALL) + gprs = (REGARG_GPRS & 31); + else if ((ci->flags & CCI_CC_MASK) == CCI_CC_FASTCALL) + gprs = REGARG_GPRS; + } +#endif + if ((void *)ci->func) + emit_call(as, ci->func); +#if LJ_64 + if ((ci->flags & CCI_VARARG)) { /* Special handling for vararg calls. */ +#if LJ_ABI_WIN + for (n = 0; n < 4 && n < nargs; n++) { + IRIns *ir = IR(args[n]); + if (irt_isfp(ir->t)) /* Duplicate FPRs in GPRs. */ + emit_rr(as, XO_MOVDto, (irt_isnum(ir->t) ? REX_64 : 0) | (fpr+n), + ((gprs >> (n*5)) & 31)); /* Either MOVD or MOVQ. */ + } +#else + patchnfpr = --as->mcp; /* Indicate number of used FPRs in register al. */ + *--as->mcp = XI_MOVrib | RID_EAX; +#endif + } +#endif + for (n = 0; n < nargs; n++) { /* Setup args. */ + IRRef ref = args[n]; + IRIns *ir = IR(ref); + Reg r; +#if LJ_64 && LJ_ABI_WIN + /* Windows/x64 argument registers are strictly positional. */ + r = irt_isfp(ir->t) ? (fpr <= REGARG_LASTFPR ? fpr : 0) : (gprs & 31); + fpr++; gprs >>= 5; +#elif LJ_64 + /* POSIX/x64 argument registers are used in order of appearance. */ + if (irt_isfp(ir->t)) { + r = fpr <= REGARG_LASTFPR ? fpr++ : 0; + } else { + r = gprs & 31; gprs >>= 5; + } +#else + if (ref && irt_isfp(ir->t)) { + r = 0; + } else { + r = gprs & 31; gprs >>= 5; + if (!ref) continue; + } +#endif + if (r) { /* Argument is in a register. */ + if (r < RID_MAX_GPR && ref < ASMREF_TMP1) { +#if LJ_64 + if (LJ_GC64 ? !(ir->o == IR_KINT || ir->o == IR_KNULL) : ir->o == IR_KINT64) + emit_loadu64(as, r, ir_k64(ir)->u64); + else +#endif + emit_loadi(as, r, ir->i); + } else { + lua_assert(rset_test(as->freeset, r)); /* Must have been evicted. */ + if (ra_hasreg(ir->r)) { + ra_noweak(as, ir->r); + emit_movrr(as, ir, r, ir->r); + } else { + ra_allocref(as, ref, RID2RSET(r)); + } + } + } else if (irt_isfp(ir->t)) { /* FP argument is on stack. */ + lua_assert(!(irt_isfloat(ir->t) && irref_isk(ref))); /* No float k. */ + if (LJ_32 && (ofs & 4) && irref_isk(ref)) { + /* Split stores for unaligned FP consts. */ + emit_movmroi(as, RID_ESP, ofs, (int32_t)ir_knum(ir)->u32.lo); + emit_movmroi(as, RID_ESP, ofs+4, (int32_t)ir_knum(ir)->u32.hi); + } else { + r = ra_alloc1(as, ref, RSET_FPR); + emit_rmro(as, irt_isnum(ir->t) ? XO_MOVSDto : XO_MOVSSto, + r, RID_ESP, ofs); + } + ofs += (LJ_32 && irt_isfloat(ir->t)) ? 4 : 8; + } else { /* Non-FP argument is on stack. */ + if (LJ_32 && ref < ASMREF_TMP1) { + emit_movmroi(as, RID_ESP, ofs, ir->i); + } else { + r = ra_alloc1(as, ref, RSET_GPR); + emit_movtomro(as, REX_64 + r, RID_ESP, ofs); + } + ofs += sizeof(intptr_t); + } + checkmclim(as); + } +#if LJ_64 && !LJ_ABI_WIN + if (patchnfpr) *patchnfpr = fpr - REGARG_FIRSTFPR; +#endif +} + +/* Setup result reg/sp for call. Evict scratch regs. */ +static void asm_setupresult(ASMState *as, IRIns *ir, const CCallInfo *ci) +{ + RegSet drop = RSET_SCRATCH; + int hiop = (LJ_32 && (ir+1)->o == IR_HIOP && !irt_isnil((ir+1)->t)); + if ((ci->flags & CCI_NOFPRCLOBBER)) + drop &= ~RSET_FPR; + if (ra_hasreg(ir->r)) + rset_clear(drop, ir->r); /* Dest reg handled below. */ + if (hiop && ra_hasreg((ir+1)->r)) + rset_clear(drop, (ir+1)->r); /* Dest reg handled below. */ + ra_evictset(as, drop); /* Evictions must be performed first. */ + if (ra_used(ir)) { + if (irt_isfp(ir->t)) { + int32_t ofs = sps_scale(ir->s); /* Use spill slot or temp slots. */ +#if LJ_64 + if ((ci->flags & CCI_CASTU64)) { + Reg dest = ir->r; + if (ra_hasreg(dest)) { + ra_free(as, dest); + ra_modified(as, dest); + emit_rr(as, XO_MOVD, dest|REX_64, RID_RET); /* Really MOVQ. */ + } + if (ofs) emit_movtomro(as, RID_RET|REX_64, RID_ESP, ofs); + } else { + ra_destreg(as, ir, RID_FPRET); + } +#else + /* Number result is in x87 st0 for x86 calling convention. */ + Reg dest = ir->r; + if (ra_hasreg(dest)) { + ra_free(as, dest); + ra_modified(as, dest); + emit_rmro(as, irt_isnum(ir->t) ? XO_MOVSD : XO_MOVSS, + dest, RID_ESP, ofs); + } + if ((ci->flags & CCI_CASTU64)) { + emit_movtomro(as, RID_RETLO, RID_ESP, ofs); + emit_movtomro(as, RID_RETHI, RID_ESP, ofs+4); + } else { + emit_rmro(as, irt_isnum(ir->t) ? XO_FSTPq : XO_FSTPd, + irt_isnum(ir->t) ? XOg_FSTPq : XOg_FSTPd, RID_ESP, ofs); + } +#endif +#if LJ_32 + } else if (hiop) { + ra_destpair(as, ir); +#endif + } else { + lua_assert(!irt_ispri(ir->t)); + ra_destreg(as, ir, RID_RET); + } + } else if (LJ_32 && irt_isfp(ir->t) && !(ci->flags & CCI_CASTU64)) { + emit_x87op(as, XI_FPOP); /* Pop unused result from x87 st0. */ + } +} + +/* Return a constant function pointer or NULL for indirect calls. */ +static void *asm_callx_func(ASMState *as, IRIns *irf, IRRef func) +{ +#if LJ_32 + UNUSED(as); + if (irref_isk(func)) + return (void *)irf->i; +#else + if (irref_isk(func)) { + MCode *p; + if (irf->o == IR_KINT64) + p = (MCode *)(void *)ir_k64(irf)->u64; + else + p = (MCode *)(void *)(uintptr_t)(uint32_t)irf->i; + if (p - as->mcp == (int32_t)(p - as->mcp)) + return p; /* Call target is still in +-2GB range. */ + /* Avoid the indirect case of emit_call(). Try to hoist func addr. */ + } +#endif + return NULL; +} + +static void asm_callx(ASMState *as, IRIns *ir) +{ + IRRef args[CCI_NARGS_MAX*2]; + CCallInfo ci; + IRRef func; + IRIns *irf; + int32_t spadj = 0; + ci.flags = asm_callx_flags(as, ir); + asm_collectargs(as, ir, &ci, args); + asm_setupresult(as, ir, &ci); +#if LJ_32 + /* Have to readjust stack after non-cdecl calls due to callee cleanup. */ + if ((ci.flags & CCI_CC_MASK) != CCI_CC_CDECL) + spadj = 4 * asm_count_call_slots(as, &ci, args); +#endif + func = ir->op2; irf = IR(func); + if (irf->o == IR_CARG) { func = irf->op1; irf = IR(func); } + ci.func = (ASMFunction)asm_callx_func(as, irf, func); + if (!(void *)ci.func) { + /* Use a (hoistable) non-scratch register for indirect calls. */ + RegSet allow = (RSET_GPR & ~RSET_SCRATCH); + Reg r = ra_alloc1(as, func, allow); + if (LJ_32) emit_spsub(as, spadj); /* Above code may cause restores! */ + emit_rr(as, XO_GROUP5, XOg_CALL, r); + } else if (LJ_32) { + emit_spsub(as, spadj); + } + asm_gencall(as, &ci, args); +} + +/* -- Returns ------------------------------------------------------------- */ + +/* Return to lower frame. Guard that it goes to the right spot. */ +static void asm_retf(ASMState *as, IRIns *ir) +{ + Reg base = ra_alloc1(as, REF_BASE, RSET_GPR); +#if LJ_FR2 + Reg rpc = ra_scratch(as, rset_exclude(RSET_GPR, base)); +#endif + void *pc = ir_kptr(IR(ir->op2)); + int32_t delta = 1+LJ_FR2+bc_a(*((const BCIns *)pc - 1)); + as->topslot -= (BCReg)delta; + if ((int32_t)as->topslot < 0) as->topslot = 0; + irt_setmark(IR(REF_BASE)->t); /* Children must not coalesce with BASE reg. */ + emit_setgl(as, base, jit_base); + emit_addptr(as, base, -8*delta); + asm_guardcc(as, CC_NE); +#if LJ_FR2 + emit_rmro(as, XO_CMP, rpc|REX_GC64, base, -8); + emit_loadu64(as, rpc, u64ptr(pc)); +#else + emit_gmroi(as, XG_ARITHi(XOg_CMP), base, -4, ptr2addr(pc)); +#endif +} + +/* -- Type conversions ---------------------------------------------------- */ + +static void asm_tointg(ASMState *as, IRIns *ir, Reg left) +{ + Reg tmp = ra_scratch(as, rset_exclude(RSET_FPR, left)); + Reg dest = ra_dest(as, ir, RSET_GPR); + asm_guardcc(as, CC_P); + asm_guardcc(as, CC_NE); + emit_rr(as, XO_UCOMISD, left, tmp); + emit_rr(as, XO_CVTSI2SD, tmp, dest); + emit_rr(as, XO_XORPS, tmp, tmp); /* Avoid partial register stall. */ + emit_rr(as, XO_CVTTSD2SI, dest, left); + /* Can't fuse since left is needed twice. */ +} + +static void asm_tobit(ASMState *as, IRIns *ir) +{ + Reg dest = ra_dest(as, ir, RSET_GPR); + Reg tmp = ra_noreg(IR(ir->op1)->r) ? + ra_alloc1(as, ir->op1, RSET_FPR) : + ra_scratch(as, RSET_FPR); + Reg right; + emit_rr(as, XO_MOVDto, tmp, dest); + right = asm_fuseload(as, ir->op2, rset_exclude(RSET_FPR, tmp)); + emit_mrm(as, XO_ADDSD, tmp, right); + ra_left(as, tmp, ir->op1); +} + +static void asm_conv(ASMState *as, IRIns *ir) +{ + IRType st = (IRType)(ir->op2 & IRCONV_SRCMASK); + int st64 = (st == IRT_I64 || st == IRT_U64 || (LJ_64 && st == IRT_P64)); + int stfp = (st == IRT_NUM || st == IRT_FLOAT); + IRRef lref = ir->op1; + lua_assert(irt_type(ir->t) != st); + lua_assert(!(LJ_32 && (irt_isint64(ir->t) || st64))); /* Handled by SPLIT. */ + if (irt_isfp(ir->t)) { + Reg dest = ra_dest(as, ir, RSET_FPR); + if (stfp) { /* FP to FP conversion. */ + Reg left = asm_fuseload(as, lref, RSET_FPR); + emit_mrm(as, st == IRT_NUM ? XO_CVTSD2SS : XO_CVTSS2SD, dest, left); + if (left == dest) return; /* Avoid the XO_XORPS. */ + } else if (LJ_32 && st == IRT_U32) { /* U32 to FP conversion on x86. */ + /* number = (2^52+2^51 .. u32) - (2^52+2^51) */ + cTValue *k = &as->J->k64[LJ_K64_TOBIT]; + Reg bias = ra_scratch(as, rset_exclude(RSET_FPR, dest)); + if (irt_isfloat(ir->t)) + emit_rr(as, XO_CVTSD2SS, dest, dest); + emit_rr(as, XO_SUBSD, dest, bias); /* Subtract 2^52+2^51 bias. */ + emit_rr(as, XO_XORPS, dest, bias); /* Merge bias and integer. */ + emit_rma(as, XO_MOVSD, bias, k); + emit_mrm(as, XO_MOVD, dest, asm_fuseload(as, lref, RSET_GPR)); + return; + } else { /* Integer to FP conversion. */ + Reg left = (LJ_64 && (st == IRT_U32 || st == IRT_U64)) ? + ra_alloc1(as, lref, RSET_GPR) : + asm_fuseloadm(as, lref, RSET_GPR, st64); + if (LJ_64 && st == IRT_U64) { + MCLabel l_end = emit_label(as); + cTValue *k = &as->J->k64[LJ_K64_2P64]; + emit_rma(as, XO_ADDSD, dest, k); /* Add 2^64 to compensate. */ + emit_sjcc(as, CC_NS, l_end); + emit_rr(as, XO_TEST, left|REX_64, left); /* Check if u64 >= 2^63. */ + } + emit_mrm(as, irt_isnum(ir->t) ? XO_CVTSI2SD : XO_CVTSI2SS, + dest|((LJ_64 && (st64 || st == IRT_U32)) ? REX_64 : 0), left); + } + emit_rr(as, XO_XORPS, dest, dest); /* Avoid partial register stall. */ + } else if (stfp) { /* FP to integer conversion. */ + if (irt_isguard(ir->t)) { + /* Checked conversions are only supported from number to int. */ + lua_assert(irt_isint(ir->t) && st == IRT_NUM); + asm_tointg(as, ir, ra_alloc1(as, lref, RSET_FPR)); + } else { + Reg dest = ra_dest(as, ir, RSET_GPR); + x86Op op = st == IRT_NUM ? XO_CVTTSD2SI : XO_CVTTSS2SI; + if (LJ_64 ? irt_isu64(ir->t) : irt_isu32(ir->t)) { + /* LJ_64: For inputs >= 2^63 add -2^64, convert again. */ + /* LJ_32: For inputs >= 2^31 add -2^31, convert again and add 2^31. */ + Reg tmp = ra_noreg(IR(lref)->r) ? ra_alloc1(as, lref, RSET_FPR) : + ra_scratch(as, RSET_FPR); + MCLabel l_end = emit_label(as); + if (LJ_32) + emit_gri(as, XG_ARITHi(XOg_ADD), dest, (int32_t)0x80000000); + emit_rr(as, op, dest|REX_64, tmp); + if (st == IRT_NUM) + emit_rma(as, XO_ADDSD, tmp, &as->J->k64[LJ_K64_M2P64_31]); + else + emit_rma(as, XO_ADDSS, tmp, &as->J->k32[LJ_K32_M2P64_31]); + emit_sjcc(as, CC_NS, l_end); + emit_rr(as, XO_TEST, dest|REX_64, dest); /* Check if dest negative. */ + emit_rr(as, op, dest|REX_64, tmp); + ra_left(as, tmp, lref); + } else { + if (LJ_64 && irt_isu32(ir->t)) + emit_rr(as, XO_MOV, dest, dest); /* Zero hiword. */ + emit_mrm(as, op, + dest|((LJ_64 && + (irt_is64(ir->t) || irt_isu32(ir->t))) ? REX_64 : 0), + asm_fuseload(as, lref, RSET_FPR)); + } + } + } else if (st >= IRT_I8 && st <= IRT_U16) { /* Extend to 32 bit integer. */ + Reg left, dest = ra_dest(as, ir, RSET_GPR); + RegSet allow = RSET_GPR; + x86Op op; + lua_assert(irt_isint(ir->t) || irt_isu32(ir->t)); + if (st == IRT_I8) { + op = XO_MOVSXb; allow = RSET_GPR8; dest |= FORCE_REX; + } else if (st == IRT_U8) { + op = XO_MOVZXb; allow = RSET_GPR8; dest |= FORCE_REX; + } else if (st == IRT_I16) { + op = XO_MOVSXw; + } else { + op = XO_MOVZXw; + } + left = asm_fuseload(as, lref, allow); + /* Add extra MOV if source is already in wrong register. */ + if (!LJ_64 && left != RID_MRM && !rset_test(allow, left)) { + Reg tmp = ra_scratch(as, allow); + emit_rr(as, op, dest, tmp); + emit_rr(as, XO_MOV, tmp, left); + } else { + emit_mrm(as, op, dest, left); + } + } else { /* 32/64 bit integer conversions. */ + if (LJ_32) { /* Only need to handle 32/32 bit no-op (cast) on x86. */ + Reg dest = ra_dest(as, ir, RSET_GPR); + ra_left(as, dest, lref); /* Do nothing, but may need to move regs. */ + } else if (irt_is64(ir->t)) { + Reg dest = ra_dest(as, ir, RSET_GPR); + if (st64 || !(ir->op2 & IRCONV_SEXT)) { + /* 64/64 bit no-op (cast) or 32 to 64 bit zero extension. */ + ra_left(as, dest, lref); /* Do nothing, but may need to move regs. */ + } else { /* 32 to 64 bit sign extension. */ + Reg left = asm_fuseload(as, lref, RSET_GPR); + emit_mrm(as, XO_MOVSXd, dest|REX_64, left); + } + } else { + Reg dest = ra_dest(as, ir, RSET_GPR); + if (st64) { + Reg left = asm_fuseload(as, lref, RSET_GPR); + /* This is either a 32 bit reg/reg mov which zeroes the hiword + ** or a load of the loword from a 64 bit address. + */ + emit_mrm(as, XO_MOV, dest, left); + } else { /* 32/32 bit no-op (cast). */ + ra_left(as, dest, lref); /* Do nothing, but may need to move regs. */ + } + } + } +} + +#if LJ_32 && LJ_HASFFI +/* No SSE conversions to/from 64 bit on x86, so resort to ugly x87 code. */ + +/* 64 bit integer to FP conversion in 32 bit mode. */ +static void asm_conv_fp_int64(ASMState *as, IRIns *ir) +{ + Reg hi = ra_alloc1(as, ir->op1, RSET_GPR); + Reg lo = ra_alloc1(as, (ir-1)->op1, rset_exclude(RSET_GPR, hi)); + int32_t ofs = sps_scale(ir->s); /* Use spill slot or temp slots. */ + Reg dest = ir->r; + if (ra_hasreg(dest)) { + ra_free(as, dest); + ra_modified(as, dest); + emit_rmro(as, irt_isnum(ir->t) ? XO_MOVSD : XO_MOVSS, dest, RID_ESP, ofs); + } + emit_rmro(as, irt_isnum(ir->t) ? XO_FSTPq : XO_FSTPd, + irt_isnum(ir->t) ? XOg_FSTPq : XOg_FSTPd, RID_ESP, ofs); + if (((ir-1)->op2 & IRCONV_SRCMASK) == IRT_U64) { + /* For inputs in [2^63,2^64-1] add 2^64 to compensate. */ + MCLabel l_end = emit_label(as); + emit_rma(as, XO_FADDq, XOg_FADDq, &as->J->k64[LJ_K64_2P64]); + emit_sjcc(as, CC_NS, l_end); + emit_rr(as, XO_TEST, hi, hi); /* Check if u64 >= 2^63. */ + } else { + lua_assert(((ir-1)->op2 & IRCONV_SRCMASK) == IRT_I64); + } + emit_rmro(as, XO_FILDq, XOg_FILDq, RID_ESP, 0); + /* NYI: Avoid narrow-to-wide store-to-load forwarding stall. */ + emit_rmro(as, XO_MOVto, hi, RID_ESP, 4); + emit_rmro(as, XO_MOVto, lo, RID_ESP, 0); +} + +/* FP to 64 bit integer conversion in 32 bit mode. */ +static void asm_conv_int64_fp(ASMState *as, IRIns *ir) +{ + IRType st = (IRType)((ir-1)->op2 & IRCONV_SRCMASK); + IRType dt = (((ir-1)->op2 & IRCONV_DSTMASK) >> IRCONV_DSH); + Reg lo, hi; + lua_assert(st == IRT_NUM || st == IRT_FLOAT); + lua_assert(dt == IRT_I64 || dt == IRT_U64); + hi = ra_dest(as, ir, RSET_GPR); + lo = ra_dest(as, ir-1, rset_exclude(RSET_GPR, hi)); + if (ra_used(ir-1)) emit_rmro(as, XO_MOV, lo, RID_ESP, 0); + /* NYI: Avoid wide-to-narrow store-to-load forwarding stall. */ + if (!(as->flags & JIT_F_SSE3)) { /* Set FPU rounding mode to default. */ + emit_rmro(as, XO_FLDCW, XOg_FLDCW, RID_ESP, 4); + emit_rmro(as, XO_MOVto, lo, RID_ESP, 4); + emit_gri(as, XG_ARITHi(XOg_AND), lo, 0xf3ff); + } + if (dt == IRT_U64) { + /* For inputs in [2^63,2^64-1] add -2^64 and convert again. */ + MCLabel l_pop, l_end = emit_label(as); + emit_x87op(as, XI_FPOP); + l_pop = emit_label(as); + emit_sjmp(as, l_end); + emit_rmro(as, XO_MOV, hi, RID_ESP, 4); + if ((as->flags & JIT_F_SSE3)) + emit_rmro(as, XO_FISTTPq, XOg_FISTTPq, RID_ESP, 0); + else + emit_rmro(as, XO_FISTPq, XOg_FISTPq, RID_ESP, 0); + emit_rma(as, XO_FADDq, XOg_FADDq, &as->J->k64[LJ_K64_M2P64]); + emit_sjcc(as, CC_NS, l_pop); + emit_rr(as, XO_TEST, hi, hi); /* Check if out-of-range (2^63). */ + } + emit_rmro(as, XO_MOV, hi, RID_ESP, 4); + if ((as->flags & JIT_F_SSE3)) { /* Truncation is easy with SSE3. */ + emit_rmro(as, XO_FISTTPq, XOg_FISTTPq, RID_ESP, 0); + } else { /* Otherwise set FPU rounding mode to truncate before the store. */ + emit_rmro(as, XO_FISTPq, XOg_FISTPq, RID_ESP, 0); + emit_rmro(as, XO_FLDCW, XOg_FLDCW, RID_ESP, 0); + emit_rmro(as, XO_MOVtow, lo, RID_ESP, 0); + emit_rmro(as, XO_ARITHw(XOg_OR), lo, RID_ESP, 0); + emit_loadi(as, lo, 0xc00); + emit_rmro(as, XO_FNSTCW, XOg_FNSTCW, RID_ESP, 0); + } + if (dt == IRT_U64) + emit_x87op(as, XI_FDUP); + emit_mrm(as, st == IRT_NUM ? XO_FLDq : XO_FLDd, + st == IRT_NUM ? XOg_FLDq: XOg_FLDd, + asm_fuseload(as, ir->op1, RSET_EMPTY)); +} + +static void asm_conv64(ASMState *as, IRIns *ir) +{ + if (irt_isfp(ir->t)) + asm_conv_fp_int64(as, ir); + else + asm_conv_int64_fp(as, ir); +} +#endif + +static void asm_strto(ASMState *as, IRIns *ir) +{ + /* Force a spill slot for the destination register (if any). */ + const CCallInfo *ci = &lj_ir_callinfo[IRCALL_lj_strscan_num]; + IRRef args[2]; + RegSet drop = RSET_SCRATCH; + if ((drop & RSET_FPR) != RSET_FPR && ra_hasreg(ir->r)) + rset_set(drop, ir->r); /* WIN64 doesn't spill all FPRs. */ + ra_evictset(as, drop); + asm_guardcc(as, CC_E); + emit_rr(as, XO_TEST, RID_RET, RID_RET); /* Test return status. */ + args[0] = ir->op1; /* GCstr *str */ + args[1] = ASMREF_TMP1; /* TValue *n */ + asm_gencall(as, ci, args); + /* Store the result to the spill slot or temp slots. */ + emit_rmro(as, XO_LEA, ra_releasetmp(as, ASMREF_TMP1)|REX_64, + RID_ESP, sps_scale(ir->s)); +} + +/* -- Memory references --------------------------------------------------- */ + +/* Get pointer to TValue. */ +static void asm_tvptr(ASMState *as, Reg dest, IRRef ref) +{ + IRIns *ir = IR(ref); + if (irt_isnum(ir->t)) { + /* For numbers use the constant itself or a spill slot as a TValue. */ + if (irref_isk(ref)) + emit_loada(as, dest, ir_knum(ir)); + else + emit_rmro(as, XO_LEA, dest|REX_64, RID_ESP, ra_spill(as, ir)); + } else { + /* Otherwise use g->tmptv to hold the TValue. */ +#if LJ_GC64 + if (irref_isk(ref)) { + TValue k; + lj_ir_kvalue(as->J->L, &k, ir); + emit_movmroi(as, dest, 4, k.u32.hi); + emit_movmroi(as, dest, 0, k.u32.lo); + } else { + /* TODO: 64 bit store + 32 bit load-modify-store is suboptimal. */ + Reg src = ra_alloc1(as, ref, rset_exclude(RSET_GPR, dest)); + if (irt_is64(ir->t)) { + emit_u32(as, irt_toitype(ir->t) << 15); + emit_rmro(as, XO_ARITHi, XOg_OR, dest, 4); + } else { + /* Currently, no caller passes integers that might end up here. */ + emit_movmroi(as, dest, 4, (irt_toitype(ir->t) << 15)); + } + emit_movtomro(as, REX_64IR(ir, src), dest, 0); + } +#else + if (!irref_isk(ref)) { + Reg src = ra_alloc1(as, ref, rset_exclude(RSET_GPR, dest)); + emit_movtomro(as, REX_64IR(ir, src), dest, 0); + } else if (!irt_ispri(ir->t)) { + emit_movmroi(as, dest, 0, ir->i); + } + if (!(LJ_64 && irt_islightud(ir->t))) + emit_movmroi(as, dest, 4, irt_toitype(ir->t)); +#endif + emit_loada(as, dest, &J2G(as->J)->tmptv); + } +} + +static void asm_aref(ASMState *as, IRIns *ir) +{ + Reg dest = ra_dest(as, ir, RSET_GPR); + asm_fusearef(as, ir, RSET_GPR); + if (!(as->mrm.idx == RID_NONE && as->mrm.ofs == 0)) + emit_mrm(as, XO_LEA, dest|REX_GC64, RID_MRM); + else if (as->mrm.base != dest) + emit_rr(as, XO_MOV, dest|REX_GC64, as->mrm.base); +} + +/* Inlined hash lookup. Specialized for key type and for const keys. +** The equivalent C code is: +** Node *n = hashkey(t, key); +** do { +** if (lj_obj_equal(&n->key, key)) return &n->val; +** } while ((n = nextnode(n))); +** return niltv(L); +*/ +static void asm_href(ASMState *as, IRIns *ir, IROp merge) +{ + RegSet allow = RSET_GPR; + int destused = ra_used(ir); + Reg dest = ra_dest(as, ir, allow); + Reg tab = ra_alloc1(as, ir->op1, rset_clear(allow, dest)); + Reg key = RID_NONE, tmp = RID_NONE; + IRIns *irkey = IR(ir->op2); + int isk = irref_isk(ir->op2); + IRType1 kt = irkey->t; + uint32_t khash; + MCLabel l_end, l_loop, l_next; + + if (!isk) { + rset_clear(allow, tab); + key = ra_alloc1(as, ir->op2, irt_isnum(kt) ? RSET_FPR : allow); + if (LJ_GC64 || !irt_isstr(kt)) + tmp = ra_scratch(as, rset_exclude(allow, key)); + } + + /* Key not found in chain: jump to exit (if merged) or load niltv. */ + l_end = emit_label(as); + if (merge == IR_NE) + asm_guardcc(as, CC_E); /* XI_JMP is not found by lj_asm_patchexit. */ + else if (destused) + emit_loada(as, dest, niltvg(J2G(as->J))); + + /* Follow hash chain until the end. */ + l_loop = emit_sjcc_label(as, CC_NZ); + emit_rr(as, XO_TEST, dest|REX_GC64, dest); + emit_rmro(as, XO_MOV, dest|REX_GC64, dest, offsetof(Node, next)); + l_next = emit_label(as); + + /* Type and value comparison. */ + if (merge == IR_EQ) + asm_guardcc(as, CC_E); + else + emit_sjcc(as, CC_E, l_end); + if (irt_isnum(kt)) { + if (isk) { + /* Assumes -0.0 is already canonicalized to +0.0. */ + emit_gmroi(as, XG_ARITHi(XOg_CMP), dest, offsetof(Node, key.u32.lo), + (int32_t)ir_knum(irkey)->u32.lo); + emit_sjcc(as, CC_NE, l_next); + emit_gmroi(as, XG_ARITHi(XOg_CMP), dest, offsetof(Node, key.u32.hi), + (int32_t)ir_knum(irkey)->u32.hi); + } else { + emit_sjcc(as, CC_P, l_next); + emit_rmro(as, XO_UCOMISD, key, dest, offsetof(Node, key.n)); + emit_sjcc(as, CC_AE, l_next); + /* The type check avoids NaN penalties and complaints from Valgrind. */ +#if LJ_64 && !LJ_GC64 + emit_u32(as, LJ_TISNUM); + emit_rmro(as, XO_ARITHi, XOg_CMP, dest, offsetof(Node, key.it)); +#else + emit_i8(as, LJ_TISNUM); + emit_rmro(as, XO_ARITHi8, XOg_CMP, dest, offsetof(Node, key.it)); +#endif + } +#if LJ_64 && !LJ_GC64 + } else if (irt_islightud(kt)) { + emit_rmro(as, XO_CMP, key|REX_64, dest, offsetof(Node, key.u64)); +#endif +#if LJ_GC64 + } else if (irt_isaddr(kt)) { + if (isk) { + TValue k; + k.u64 = ((uint64_t)irt_toitype(irkey->t) << 47) | irkey[1].tv.u64; + emit_gmroi(as, XG_ARITHi(XOg_CMP), dest, offsetof(Node, key.u32.lo), + k.u32.lo); + emit_sjcc(as, CC_NE, l_next); + emit_gmroi(as, XG_ARITHi(XOg_CMP), dest, offsetof(Node, key.u32.hi), + k.u32.hi); + } else { + emit_rmro(as, XO_CMP, tmp|REX_64, dest, offsetof(Node, key.u64)); + } + } else { + lua_assert(irt_ispri(kt) && !irt_isnil(kt)); + emit_u32(as, (irt_toitype(kt)<<15)|0x7fff); + emit_rmro(as, XO_ARITHi, XOg_CMP, dest, offsetof(Node, key.it)); +#else + } else { + if (!irt_ispri(kt)) { + lua_assert(irt_isaddr(kt)); + if (isk) + emit_gmroi(as, XG_ARITHi(XOg_CMP), dest, offsetof(Node, key.gcr), + ptr2addr(ir_kgc(irkey))); + else + emit_rmro(as, XO_CMP, key, dest, offsetof(Node, key.gcr)); + emit_sjcc(as, CC_NE, l_next); + } + lua_assert(!irt_isnil(kt)); + emit_i8(as, irt_toitype(kt)); + emit_rmro(as, XO_ARITHi8, XOg_CMP, dest, offsetof(Node, key.it)); +#endif + } + emit_sfixup(as, l_loop); + checkmclim(as); +#if LJ_GC64 + if (!isk && irt_isaddr(kt)) { + emit_rr(as, XO_OR, tmp|REX_64, key); + emit_loadu64(as, tmp, (uint64_t)irt_toitype(kt) << 47); + } +#endif + + /* Load main position relative to tab->node into dest. */ + khash = isk ? ir_khash(irkey) : 1; + if (khash == 0) { + emit_rmro(as, XO_MOV, dest|REX_GC64, tab, offsetof(GCtab, node)); + } else { + emit_rmro(as, XO_ARITH(XOg_ADD), dest|REX_GC64, tab, offsetof(GCtab,node)); + if ((as->flags & JIT_F_PREFER_IMUL)) { + emit_i8(as, sizeof(Node)); + emit_rr(as, XO_IMULi8, dest, dest); + } else { + emit_shifti(as, XOg_SHL, dest, 3); + emit_rmrxo(as, XO_LEA, dest, dest, dest, XM_SCALE2, 0); + } + if (isk) { + emit_gri(as, XG_ARITHi(XOg_AND), dest, (int32_t)khash); + emit_rmro(as, XO_MOV, dest, tab, offsetof(GCtab, hmask)); + } else if (irt_isstr(kt)) { + emit_rmro(as, XO_ARITH(XOg_AND), dest, key, offsetof(GCstr, hash)); + emit_rmro(as, XO_MOV, dest, tab, offsetof(GCtab, hmask)); + } else { /* Must match with hashrot() in lj_tab.c. */ + emit_rmro(as, XO_ARITH(XOg_AND), dest, tab, offsetof(GCtab, hmask)); + emit_rr(as, XO_ARITH(XOg_SUB), dest, tmp); + emit_shifti(as, XOg_ROL, tmp, HASH_ROT3); + emit_rr(as, XO_ARITH(XOg_XOR), dest, tmp); + emit_shifti(as, XOg_ROL, dest, HASH_ROT2); + emit_rr(as, XO_ARITH(XOg_SUB), tmp, dest); + emit_shifti(as, XOg_ROL, dest, HASH_ROT1); + emit_rr(as, XO_ARITH(XOg_XOR), tmp, dest); + if (irt_isnum(kt)) { + emit_rr(as, XO_ARITH(XOg_ADD), dest, dest); +#if LJ_64 + emit_shifti(as, XOg_SHR|REX_64, dest, 32); + emit_rr(as, XO_MOV, tmp, dest); + emit_rr(as, XO_MOVDto, key|REX_64, dest); +#else + emit_rmro(as, XO_MOV, dest, RID_ESP, ra_spill(as, irkey)+4); + emit_rr(as, XO_MOVDto, key, tmp); +#endif + } else { + emit_rr(as, XO_MOV, tmp, key); +#if LJ_GC64 + checkmclim(as); + emit_gri(as, XG_ARITHi(XOg_XOR), dest, irt_toitype(kt) << 15); + if ((as->flags & JIT_F_BMI2)) { + emit_i8(as, 32); + emit_mrm(as, XV_RORX|VEX_64, dest, key); + } else { + emit_shifti(as, XOg_SHR|REX_64, dest, 32); + emit_rr(as, XO_MOV, dest|REX_64, key|REX_64); + } +#else + emit_rmro(as, XO_LEA, dest, key, HASH_BIAS); +#endif + } + } + } +} + +static void asm_hrefk(ASMState *as, IRIns *ir) +{ + IRIns *kslot = IR(ir->op2); + IRIns *irkey = IR(kslot->op1); + int32_t ofs = (int32_t)(kslot->op2 * sizeof(Node)); + Reg dest = ra_used(ir) ? ra_dest(as, ir, RSET_GPR) : RID_NONE; + Reg node = ra_alloc1(as, ir->op1, RSET_GPR); +#if !LJ_64 + MCLabel l_exit; +#endif + lua_assert(ofs % sizeof(Node) == 0); + if (ra_hasreg(dest)) { + if (ofs != 0) { + if (dest == node && !(as->flags & JIT_F_LEA_AGU)) + emit_gri(as, XG_ARITHi(XOg_ADD), dest|REX_GC64, ofs); + else + emit_rmro(as, XO_LEA, dest|REX_GC64, node, ofs); + } else if (dest != node) { + emit_rr(as, XO_MOV, dest|REX_GC64, node); + } + } + asm_guardcc(as, CC_NE); +#if LJ_64 + if (!irt_ispri(irkey->t)) { + Reg key = ra_scratch(as, rset_exclude(RSET_GPR, node)); + emit_rmro(as, XO_CMP, key|REX_64, node, + ofs + (int32_t)offsetof(Node, key.u64)); + lua_assert(irt_isnum(irkey->t) || irt_isgcv(irkey->t)); + /* Assumes -0.0 is already canonicalized to +0.0. */ + emit_loadu64(as, key, irt_isnum(irkey->t) ? ir_knum(irkey)->u64 : +#if LJ_GC64 + ((uint64_t)irt_toitype(irkey->t) << 47) | + (uint64_t)ir_kgc(irkey)); +#else + ((uint64_t)irt_toitype(irkey->t) << 32) | + (uint64_t)(uint32_t)ptr2addr(ir_kgc(irkey))); +#endif + } else { + lua_assert(!irt_isnil(irkey->t)); +#if LJ_GC64 + emit_i32(as, (irt_toitype(irkey->t)<<15)|0x7fff); + emit_rmro(as, XO_ARITHi, XOg_CMP, node, + ofs + (int32_t)offsetof(Node, key.it)); +#else + emit_i8(as, irt_toitype(irkey->t)); + emit_rmro(as, XO_ARITHi8, XOg_CMP, node, + ofs + (int32_t)offsetof(Node, key.it)); +#endif + } +#else + l_exit = emit_label(as); + if (irt_isnum(irkey->t)) { + /* Assumes -0.0 is already canonicalized to +0.0. */ + emit_gmroi(as, XG_ARITHi(XOg_CMP), node, + ofs + (int32_t)offsetof(Node, key.u32.lo), + (int32_t)ir_knum(irkey)->u32.lo); + emit_sjcc(as, CC_NE, l_exit); + emit_gmroi(as, XG_ARITHi(XOg_CMP), node, + ofs + (int32_t)offsetof(Node, key.u32.hi), + (int32_t)ir_knum(irkey)->u32.hi); + } else { + if (!irt_ispri(irkey->t)) { + lua_assert(irt_isgcv(irkey->t)); + emit_gmroi(as, XG_ARITHi(XOg_CMP), node, + ofs + (int32_t)offsetof(Node, key.gcr), + ptr2addr(ir_kgc(irkey))); + emit_sjcc(as, CC_NE, l_exit); + } + lua_assert(!irt_isnil(irkey->t)); + emit_i8(as, irt_toitype(irkey->t)); + emit_rmro(as, XO_ARITHi8, XOg_CMP, node, + ofs + (int32_t)offsetof(Node, key.it)); + } +#endif +} + +static void asm_uref(ASMState *as, IRIns *ir) +{ + Reg dest = ra_dest(as, ir, RSET_GPR); + if (irref_isk(ir->op1)) { + GCfunc *fn = ir_kfunc(IR(ir->op1)); + MRef *v = &gcref(fn->l.uvptr[(ir->op2 >> 8)])->uv.v; + emit_rma(as, XO_MOV, dest|REX_GC64, v); + } else { + Reg uv = ra_scratch(as, RSET_GPR); + Reg func = ra_alloc1(as, ir->op1, RSET_GPR); + if (ir->o == IR_UREFC) { + emit_rmro(as, XO_LEA, dest|REX_GC64, uv, offsetof(GCupval, tv)); + asm_guardcc(as, CC_NE); + emit_i8(as, 1); + emit_rmro(as, XO_ARITHib, XOg_CMP, uv, offsetof(GCupval, closed)); + } else { + emit_rmro(as, XO_MOV, dest|REX_GC64, uv, offsetof(GCupval, v)); + } + emit_rmro(as, XO_MOV, uv|REX_GC64, func, + (int32_t)offsetof(GCfuncL, uvptr) + + (int32_t)sizeof(MRef) * (int32_t)(ir->op2 >> 8)); + } +} + +static void asm_fref(ASMState *as, IRIns *ir) +{ + Reg dest = ra_dest(as, ir, RSET_GPR); + asm_fusefref(as, ir, RSET_GPR); + emit_mrm(as, XO_LEA, dest, RID_MRM); +} + +static void asm_strref(ASMState *as, IRIns *ir) +{ + Reg dest = ra_dest(as, ir, RSET_GPR); + asm_fusestrref(as, ir, RSET_GPR); + if (as->mrm.base == RID_NONE) + emit_loadi(as, dest, as->mrm.ofs); + else if (as->mrm.base == dest && as->mrm.idx == RID_NONE) + emit_gri(as, XG_ARITHi(XOg_ADD), dest|REX_GC64, as->mrm.ofs); + else + emit_mrm(as, XO_LEA, dest|REX_GC64, RID_MRM); +} + +/* -- Loads and stores ---------------------------------------------------- */ + +static void asm_fxload(ASMState *as, IRIns *ir) +{ + Reg dest = ra_dest(as, ir, irt_isfp(ir->t) ? RSET_FPR : RSET_GPR); + x86Op xo; + if (ir->o == IR_FLOAD) + asm_fusefref(as, ir, RSET_GPR); + else + asm_fusexref(as, ir->op1, RSET_GPR); + /* ir->op2 is ignored -- unaligned loads are ok on x86. */ + switch (irt_type(ir->t)) { + case IRT_I8: xo = XO_MOVSXb; break; + case IRT_U8: xo = XO_MOVZXb; break; + case IRT_I16: xo = XO_MOVSXw; break; + case IRT_U16: xo = XO_MOVZXw; break; + case IRT_NUM: xo = XO_MOVSD; break; + case IRT_FLOAT: xo = XO_MOVSS; break; + default: + if (LJ_64 && irt_is64(ir->t)) + dest |= REX_64; + else + lua_assert(irt_isint(ir->t) || irt_isu32(ir->t) || irt_isaddr(ir->t)); + xo = XO_MOV; + break; + } + emit_mrm(as, xo, dest, RID_MRM); +} + +#define asm_fload(as, ir) asm_fxload(as, ir) +#define asm_xload(as, ir) asm_fxload(as, ir) + +static void asm_fxstore(ASMState *as, IRIns *ir) +{ + RegSet allow = RSET_GPR; + Reg src = RID_NONE, osrc = RID_NONE; + int32_t k = 0; + if (ir->r == RID_SINK) + return; + /* The IRT_I16/IRT_U16 stores should never be simplified for constant + ** values since mov word [mem], imm16 has a length-changing prefix. + */ + if (irt_isi16(ir->t) || irt_isu16(ir->t) || irt_isfp(ir->t) || + !asm_isk32(as, ir->op2, &k)) { + RegSet allow8 = irt_isfp(ir->t) ? RSET_FPR : + (irt_isi8(ir->t) || irt_isu8(ir->t)) ? RSET_GPR8 : RSET_GPR; + src = osrc = ra_alloc1(as, ir->op2, allow8); + if (!LJ_64 && !rset_test(allow8, src)) { /* Already in wrong register. */ + rset_clear(allow, osrc); + src = ra_scratch(as, allow8); + } + rset_clear(allow, src); + } + if (ir->o == IR_FSTORE) { + asm_fusefref(as, IR(ir->op1), allow); + } else { + asm_fusexref(as, ir->op1, allow); + if (LJ_32 && ir->o == IR_HIOP) as->mrm.ofs += 4; + } + if (ra_hasreg(src)) { + x86Op xo; + switch (irt_type(ir->t)) { + case IRT_I8: case IRT_U8: xo = XO_MOVtob; src |= FORCE_REX; break; + case IRT_I16: case IRT_U16: xo = XO_MOVtow; break; + case IRT_NUM: xo = XO_MOVSDto; break; + case IRT_FLOAT: xo = XO_MOVSSto; break; +#if LJ_64 && !LJ_GC64 + case IRT_LIGHTUD: lua_assert(0); /* NYI: mask 64 bit lightuserdata. */ +#endif + default: + if (LJ_64 && irt_is64(ir->t)) + src |= REX_64; + else + lua_assert(irt_isint(ir->t) || irt_isu32(ir->t) || irt_isaddr(ir->t)); + xo = XO_MOVto; + break; + } + emit_mrm(as, xo, src, RID_MRM); + if (!LJ_64 && src != osrc) { + ra_noweak(as, osrc); + emit_rr(as, XO_MOV, src, osrc); + } + } else { + if (irt_isi8(ir->t) || irt_isu8(ir->t)) { + emit_i8(as, k); + emit_mrm(as, XO_MOVmib, 0, RID_MRM); + } else { + lua_assert(irt_is64(ir->t) || irt_isint(ir->t) || irt_isu32(ir->t) || + irt_isaddr(ir->t)); + emit_i32(as, k); + emit_mrm(as, XO_MOVmi, REX_64IR(ir, 0), RID_MRM); + } + } +} + +#define asm_fstore(as, ir) asm_fxstore(as, ir) +#define asm_xstore(as, ir) asm_fxstore(as, ir) + +#if LJ_64 && !LJ_GC64 +static Reg asm_load_lightud64(ASMState *as, IRIns *ir, int typecheck) +{ + if (ra_used(ir) || typecheck) { + Reg dest = ra_dest(as, ir, RSET_GPR); + if (typecheck) { + Reg tmp = ra_scratch(as, rset_exclude(RSET_GPR, dest)); + asm_guardcc(as, CC_NE); + emit_i8(as, -2); + emit_rr(as, XO_ARITHi8, XOg_CMP, tmp); + emit_shifti(as, XOg_SAR|REX_64, tmp, 47); + emit_rr(as, XO_MOV, tmp|REX_64, dest); + } + return dest; + } else { + return RID_NONE; + } +} +#endif + +static void asm_ahuvload(ASMState *as, IRIns *ir) +{ +#if LJ_GC64 + Reg tmp = RID_NONE; +#endif + lua_assert(irt_isnum(ir->t) || irt_ispri(ir->t) || irt_isaddr(ir->t) || + (LJ_DUALNUM && irt_isint(ir->t))); +#if LJ_64 && !LJ_GC64 + if (irt_islightud(ir->t)) { + Reg dest = asm_load_lightud64(as, ir, 1); + if (ra_hasreg(dest)) { + asm_fuseahuref(as, ir->op1, RSET_GPR); + emit_mrm(as, XO_MOV, dest|REX_64, RID_MRM); + } + return; + } else +#endif + if (ra_used(ir)) { + RegSet allow = irt_isnum(ir->t) ? RSET_FPR : RSET_GPR; + Reg dest = ra_dest(as, ir, allow); + asm_fuseahuref(as, ir->op1, RSET_GPR); +#if LJ_GC64 + if (irt_isaddr(ir->t)) { + emit_shifti(as, XOg_SHR|REX_64, dest, 17); + asm_guardcc(as, CC_NE); + emit_i8(as, irt_toitype(ir->t)); + emit_rr(as, XO_ARITHi8, XOg_CMP, dest); + emit_i8(as, XI_O16); + if ((as->flags & JIT_F_BMI2)) { + emit_i8(as, 47); + emit_mrm(as, XV_RORX|VEX_64, dest, RID_MRM); + } else { + emit_shifti(as, XOg_ROR|REX_64, dest, 47); + emit_mrm(as, XO_MOV, dest|REX_64, RID_MRM); + } + return; + } else +#endif + emit_mrm(as, dest < RID_MAX_GPR ? XO_MOV : XO_MOVSD, dest, RID_MRM); + } else { + RegSet gpr = RSET_GPR; +#if LJ_GC64 + if (irt_isaddr(ir->t)) { + tmp = ra_scratch(as, RSET_GPR); + gpr = rset_exclude(gpr, tmp); + } +#endif + asm_fuseahuref(as, ir->op1, gpr); + } + /* Always do the type check, even if the load result is unused. */ + as->mrm.ofs += 4; + asm_guardcc(as, irt_isnum(ir->t) ? CC_AE : CC_NE); + if (LJ_64 && irt_type(ir->t) >= IRT_NUM) { + lua_assert(irt_isinteger(ir->t) || irt_isnum(ir->t)); +#if LJ_GC64 + emit_u32(as, LJ_TISNUM << 15); +#else + emit_u32(as, LJ_TISNUM); +#endif + emit_mrm(as, XO_ARITHi, XOg_CMP, RID_MRM); +#if LJ_GC64 + } else if (irt_isaddr(ir->t)) { + as->mrm.ofs -= 4; + emit_i8(as, irt_toitype(ir->t)); + emit_mrm(as, XO_ARITHi8, XOg_CMP, tmp); + emit_shifti(as, XOg_SAR|REX_64, tmp, 47); + emit_mrm(as, XO_MOV, tmp|REX_64, RID_MRM); + } else if (irt_isnil(ir->t)) { + as->mrm.ofs -= 4; + emit_i8(as, -1); + emit_mrm(as, XO_ARITHi8, XOg_CMP|REX_64, RID_MRM); + } else { + emit_u32(as, (irt_toitype(ir->t) << 15) | 0x7fff); + emit_mrm(as, XO_ARITHi, XOg_CMP, RID_MRM); +#else + } else { + emit_i8(as, irt_toitype(ir->t)); + emit_mrm(as, XO_ARITHi8, XOg_CMP, RID_MRM); +#endif + } +} + +static void asm_ahustore(ASMState *as, IRIns *ir) +{ + if (ir->r == RID_SINK) + return; + if (irt_isnum(ir->t)) { + Reg src = ra_alloc1(as, ir->op2, RSET_FPR); + asm_fuseahuref(as, ir->op1, RSET_GPR); + emit_mrm(as, XO_MOVSDto, src, RID_MRM); +#if LJ_64 && !LJ_GC64 + } else if (irt_islightud(ir->t)) { + Reg src = ra_alloc1(as, ir->op2, RSET_GPR); + asm_fuseahuref(as, ir->op1, rset_exclude(RSET_GPR, src)); + emit_mrm(as, XO_MOVto, src|REX_64, RID_MRM); +#endif +#if LJ_GC64 + } else if (irref_isk(ir->op2)) { + TValue k; + lj_ir_kvalue(as->J->L, &k, IR(ir->op2)); + asm_fuseahuref(as, ir->op1, RSET_GPR); + if (tvisnil(&k)) { + emit_i32(as, -1); + emit_mrm(as, XO_MOVmi, REX_64, RID_MRM); + } else { + emit_u32(as, k.u32.lo); + emit_mrm(as, XO_MOVmi, 0, RID_MRM); + as->mrm.ofs += 4; + emit_u32(as, k.u32.hi); + emit_mrm(as, XO_MOVmi, 0, RID_MRM); + } +#endif + } else { + IRIns *irr = IR(ir->op2); + RegSet allow = RSET_GPR; + Reg src = RID_NONE; + if (!irref_isk(ir->op2)) { + src = ra_alloc1(as, ir->op2, allow); + rset_clear(allow, src); + } + asm_fuseahuref(as, ir->op1, allow); + if (ra_hasreg(src)) { +#if LJ_GC64 + if (!(LJ_DUALNUM && irt_isinteger(ir->t))) { + /* TODO: 64 bit store + 32 bit load-modify-store is suboptimal. */ + as->mrm.ofs += 4; + emit_u32(as, irt_toitype(ir->t) << 15); + emit_mrm(as, XO_ARITHi, XOg_OR, RID_MRM); + as->mrm.ofs -= 4; + emit_mrm(as, XO_MOVto, src|REX_64, RID_MRM); + return; + } +#endif + emit_mrm(as, XO_MOVto, src, RID_MRM); + } else if (!irt_ispri(irr->t)) { + lua_assert(irt_isaddr(ir->t) || (LJ_DUALNUM && irt_isinteger(ir->t))); + emit_i32(as, irr->i); + emit_mrm(as, XO_MOVmi, 0, RID_MRM); + } + as->mrm.ofs += 4; +#if LJ_GC64 + lua_assert(LJ_DUALNUM && irt_isinteger(ir->t)); + emit_i32(as, LJ_TNUMX << 15); +#else + emit_i32(as, (int32_t)irt_toitype(ir->t)); +#endif + emit_mrm(as, XO_MOVmi, 0, RID_MRM); + } +} + +static void asm_sload(ASMState *as, IRIns *ir) +{ + int32_t ofs = 8*((int32_t)ir->op1-1-LJ_FR2) + + (!LJ_FR2 && (ir->op2 & IRSLOAD_FRAME) ? 4 : 0); + IRType1 t = ir->t; + Reg base; + lua_assert(!(ir->op2 & IRSLOAD_PARENT)); /* Handled by asm_head_side(). */ + lua_assert(irt_isguard(t) || !(ir->op2 & IRSLOAD_TYPECHECK)); + lua_assert(LJ_DUALNUM || + !irt_isint(t) || (ir->op2 & (IRSLOAD_CONVERT|IRSLOAD_FRAME))); + if ((ir->op2 & IRSLOAD_CONVERT) && irt_isguard(t) && irt_isint(t)) { + Reg left = ra_scratch(as, RSET_FPR); + asm_tointg(as, ir, left); /* Frees dest reg. Do this before base alloc. */ + base = ra_alloc1(as, REF_BASE, RSET_GPR); + emit_rmro(as, XO_MOVSD, left, base, ofs); + t.irt = IRT_NUM; /* Continue with a regular number type check. */ +#if LJ_64 && !LJ_GC64 + } else if (irt_islightud(t)) { + Reg dest = asm_load_lightud64(as, ir, (ir->op2 & IRSLOAD_TYPECHECK)); + if (ra_hasreg(dest)) { + base = ra_alloc1(as, REF_BASE, RSET_GPR); + emit_rmro(as, XO_MOV, dest|REX_64, base, ofs); + } + return; +#endif + } else if (ra_used(ir)) { + RegSet allow = irt_isnum(t) ? RSET_FPR : RSET_GPR; + Reg dest = ra_dest(as, ir, allow); + base = ra_alloc1(as, REF_BASE, RSET_GPR); + lua_assert(irt_isnum(t) || irt_isint(t) || irt_isaddr(t)); + if ((ir->op2 & IRSLOAD_CONVERT)) { + t.irt = irt_isint(t) ? IRT_NUM : IRT_INT; /* Check for original type. */ + emit_rmro(as, irt_isint(t) ? XO_CVTSI2SD : XO_CVTTSD2SI, dest, base, ofs); + } else { +#if LJ_GC64 + if (irt_isaddr(t)) { + /* LJ_GC64 type check + tag removal without BMI2 and with BMI2: + ** + ** mov r64, [addr] rorx r64, [addr], 47 + ** ror r64, 47 + ** cmp r16, itype cmp r16, itype + ** jne ->exit jne ->exit + ** shr r64, 16 shr r64, 16 + */ + emit_shifti(as, XOg_SHR|REX_64, dest, 17); + if ((ir->op2 & IRSLOAD_TYPECHECK)) { + asm_guardcc(as, CC_NE); + emit_i8(as, irt_toitype(t)); + emit_rr(as, XO_ARITHi8, XOg_CMP, dest); + emit_i8(as, XI_O16); + } + if ((as->flags & JIT_F_BMI2)) { + emit_i8(as, 47); + emit_rmro(as, XV_RORX|VEX_64, dest, base, ofs); + } else { + if ((ir->op2 & IRSLOAD_TYPECHECK)) + emit_shifti(as, XOg_ROR|REX_64, dest, 47); + else + emit_shifti(as, XOg_SHL|REX_64, dest, 17); + emit_rmro(as, XO_MOV, dest|REX_64, base, ofs); + } + return; + } else +#endif + emit_rmro(as, irt_isnum(t) ? XO_MOVSD : XO_MOV, dest, base, ofs); + } + } else { + if (!(ir->op2 & IRSLOAD_TYPECHECK)) + return; /* No type check: avoid base alloc. */ + base = ra_alloc1(as, REF_BASE, RSET_GPR); + } + if ((ir->op2 & IRSLOAD_TYPECHECK)) { + /* Need type check, even if the load result is unused. */ + asm_guardcc(as, irt_isnum(t) ? CC_AE : CC_NE); + if (LJ_64 && irt_type(t) >= IRT_NUM) { + lua_assert(irt_isinteger(t) || irt_isnum(t)); +#if LJ_GC64 + emit_u32(as, LJ_TISNUM << 15); +#else + emit_u32(as, LJ_TISNUM); +#endif + emit_rmro(as, XO_ARITHi, XOg_CMP, base, ofs+4); +#if LJ_GC64 + } else if (irt_isnil(t)) { + /* LJ_GC64 type check for nil: + ** + ** cmp qword [addr], -1 + ** jne ->exit + */ + emit_i8(as, -1); + emit_rmro(as, XO_ARITHi8, XOg_CMP|REX_64, base, ofs); + } else if (irt_ispri(t)) { + emit_u32(as, (irt_toitype(t) << 15) | 0x7fff); + emit_rmro(as, XO_ARITHi, XOg_CMP, base, ofs+4); + } else { + /* LJ_GC64 type check only: + ** + ** mov r64, [addr] + ** sar r64, 47 + ** cmp r32, itype + ** jne ->exit + */ + Reg tmp = ra_scratch(as, rset_exclude(RSET_GPR, base)); + emit_i8(as, irt_toitype(t)); + emit_rr(as, XO_ARITHi8, XOg_CMP, tmp); + emit_shifti(as, XOg_SAR|REX_64, tmp, 47); + emit_rmro(as, XO_MOV, tmp|REX_64, base, ofs); +#else + } else { + emit_i8(as, irt_toitype(t)); + emit_rmro(as, XO_ARITHi8, XOg_CMP, base, ofs+4); +#endif + } + } +} + +/* -- Allocations --------------------------------------------------------- */ + +#if LJ_HASFFI +static void asm_cnew(ASMState *as, IRIns *ir) +{ + CTState *cts = ctype_ctsG(J2G(as->J)); + CTypeID id = (CTypeID)IR(ir->op1)->i; + CTSize sz; + CTInfo info = lj_ctype_info(cts, id, &sz); + const CCallInfo *ci = &lj_ir_callinfo[IRCALL_lj_mem_newgco]; + IRRef args[4]; + lua_assert(sz != CTSIZE_INVALID || (ir->o == IR_CNEW && ir->op2 != REF_NIL)); + + as->gcsteps++; + asm_setupresult(as, ir, ci); /* GCcdata * */ + + /* Initialize immutable cdata object. */ + if (ir->o == IR_CNEWI) { + RegSet allow = (RSET_GPR & ~RSET_SCRATCH); +#if LJ_64 + Reg r64 = sz == 8 ? REX_64 : 0; + if (irref_isk(ir->op2)) { + IRIns *irk = IR(ir->op2); + uint64_t k = (irk->o == IR_KINT64 || + (LJ_GC64 && (irk->o == IR_KPTR || irk->o == IR_KKPTR))) ? + ir_k64(irk)->u64 : (uint64_t)(uint32_t)irk->i; + if (sz == 4 || checki32((int64_t)k)) { + emit_i32(as, (int32_t)k); + emit_rmro(as, XO_MOVmi, r64, RID_RET, sizeof(GCcdata)); + } else { + emit_movtomro(as, RID_ECX + r64, RID_RET, sizeof(GCcdata)); + emit_loadu64(as, RID_ECX, k); + } + } else { + Reg r = ra_alloc1(as, ir->op2, allow); + emit_movtomro(as, r + r64, RID_RET, sizeof(GCcdata)); + } +#else + int32_t ofs = sizeof(GCcdata); + if (sz == 8) { + ofs += 4; ir++; + lua_assert(ir->o == IR_HIOP); + } + do { + if (irref_isk(ir->op2)) { + emit_movmroi(as, RID_RET, ofs, IR(ir->op2)->i); + } else { + Reg r = ra_alloc1(as, ir->op2, allow); + emit_movtomro(as, r, RID_RET, ofs); + rset_clear(allow, r); + } + if (ofs == sizeof(GCcdata)) break; + ofs -= 4; ir--; + } while (1); +#endif + lua_assert(sz == 4 || sz == 8); + } else if (ir->op2 != REF_NIL) { /* Create VLA/VLS/aligned cdata. */ + ci = &lj_ir_callinfo[IRCALL_lj_cdata_newv]; + args[0] = ASMREF_L; /* lua_State *L */ + args[1] = ir->op1; /* CTypeID id */ + args[2] = ir->op2; /* CTSize sz */ + args[3] = ASMREF_TMP1; /* CTSize align */ + asm_gencall(as, ci, args); + emit_loadi(as, ra_releasetmp(as, ASMREF_TMP1), (int32_t)ctype_align(info)); + return; + } + + /* Combine initialization of marked, gct and ctypeid. */ + emit_movtomro(as, RID_ECX, RID_RET, offsetof(GCcdata, marked)); + emit_gri(as, XG_ARITHi(XOg_OR), RID_ECX, + (int32_t)((~LJ_TCDATA<<8)+(id<<16))); + emit_gri(as, XG_ARITHi(XOg_AND), RID_ECX, LJ_GC_WHITES); + emit_opgl(as, XO_MOVZXb, RID_ECX, gc.currentwhite); + + args[0] = ASMREF_L; /* lua_State *L */ + args[1] = ASMREF_TMP1; /* MSize size */ + asm_gencall(as, ci, args); + emit_loadi(as, ra_releasetmp(as, ASMREF_TMP1), (int32_t)(sz+sizeof(GCcdata))); +} +#else +#define asm_cnew(as, ir) ((void)0) +#endif + +/* -- Write barriers ------------------------------------------------------ */ + +static void asm_tbar(ASMState *as, IRIns *ir) +{ + Reg tab = ra_alloc1(as, ir->op1, RSET_GPR); + Reg tmp = ra_scratch(as, rset_exclude(RSET_GPR, tab)); + MCLabel l_end = emit_label(as); + emit_movtomro(as, tmp|REX_GC64, tab, offsetof(GCtab, gclist)); + emit_setgl(as, tab, gc.grayagain); + emit_getgl(as, tmp, gc.grayagain); + emit_i8(as, ~LJ_GC_BLACK); + emit_rmro(as, XO_ARITHib, XOg_AND, tab, offsetof(GCtab, marked)); + emit_sjcc(as, CC_Z, l_end); + emit_i8(as, LJ_GC_BLACK); + emit_rmro(as, XO_GROUP3b, XOg_TEST, tab, offsetof(GCtab, marked)); +} + +static void asm_obar(ASMState *as, IRIns *ir) +{ + const CCallInfo *ci = &lj_ir_callinfo[IRCALL_lj_gc_barrieruv]; + IRRef args[2]; + MCLabel l_end; + Reg obj; + /* No need for other object barriers (yet). */ + lua_assert(IR(ir->op1)->o == IR_UREFC); + ra_evictset(as, RSET_SCRATCH); + l_end = emit_label(as); + args[0] = ASMREF_TMP1; /* global_State *g */ + args[1] = ir->op1; /* TValue *tv */ + asm_gencall(as, ci, args); + emit_loada(as, ra_releasetmp(as, ASMREF_TMP1), J2G(as->J)); + obj = IR(ir->op1)->r; + emit_sjcc(as, CC_Z, l_end); + emit_i8(as, LJ_GC_WHITES); + if (irref_isk(ir->op2)) { + GCobj *vp = ir_kgc(IR(ir->op2)); + emit_rma(as, XO_GROUP3b, XOg_TEST, &vp->gch.marked); + } else { + Reg val = ra_alloc1(as, ir->op2, rset_exclude(RSET_SCRATCH&RSET_GPR, obj)); + emit_rmro(as, XO_GROUP3b, XOg_TEST, val, (int32_t)offsetof(GChead, marked)); + } + emit_sjcc(as, CC_Z, l_end); + emit_i8(as, LJ_GC_BLACK); + emit_rmro(as, XO_GROUP3b, XOg_TEST, obj, + (int32_t)offsetof(GCupval, marked)-(int32_t)offsetof(GCupval, tv)); +} + +/* -- FP/int arithmetic and logic operations ------------------------------ */ + +/* Load reference onto x87 stack. Force a spill to memory if needed. */ +static void asm_x87load(ASMState *as, IRRef ref) +{ + IRIns *ir = IR(ref); + if (ir->o == IR_KNUM) { + cTValue *tv = ir_knum(ir); + if (tvispzero(tv)) /* Use fldz only for +0. */ + emit_x87op(as, XI_FLDZ); + else if (tvispone(tv)) + emit_x87op(as, XI_FLD1); + else + emit_rma(as, XO_FLDq, XOg_FLDq, tv); + } else if (ir->o == IR_CONV && ir->op2 == IRCONV_NUM_INT && !ra_used(ir) && + !irref_isk(ir->op1) && mayfuse(as, ir->op1)) { + IRIns *iri = IR(ir->op1); + emit_rmro(as, XO_FILDd, XOg_FILDd, RID_ESP, ra_spill(as, iri)); + } else { + emit_mrm(as, XO_FLDq, XOg_FLDq, asm_fuseload(as, ref, RSET_EMPTY)); + } +} + +static void asm_fpmath(ASMState *as, IRIns *ir) +{ + IRFPMathOp fpm = (IRFPMathOp)ir->op2; + if (fpm == IRFPM_SQRT) { + Reg dest = ra_dest(as, ir, RSET_FPR); + Reg left = asm_fuseload(as, ir->op1, RSET_FPR); + emit_mrm(as, XO_SQRTSD, dest, left); + } else if (fpm <= IRFPM_TRUNC) { + if (as->flags & JIT_F_SSE4_1) { /* SSE4.1 has a rounding instruction. */ + Reg dest = ra_dest(as, ir, RSET_FPR); + Reg left = asm_fuseload(as, ir->op1, RSET_FPR); + /* ROUNDSD has a 4-byte opcode which doesn't fit in x86Op. + ** Let's pretend it's a 3-byte opcode, and compensate afterwards. + ** This is atrocious, but the alternatives are much worse. + */ + /* Round down/up/trunc == 1001/1010/1011. */ + emit_i8(as, 0x09 + fpm); + emit_mrm(as, XO_ROUNDSD, dest, left); + if (LJ_64 && as->mcp[1] != (MCode)(XO_ROUNDSD >> 16)) { + as->mcp[0] = as->mcp[1]; as->mcp[1] = 0x0f; /* Swap 0F and REX. */ + } + *--as->mcp = 0x66; /* 1st byte of ROUNDSD opcode. */ + } else { /* Call helper functions for SSE2 variant. */ + /* The modified regs must match with the *.dasc implementation. */ + RegSet drop = RSET_RANGE(RID_XMM0, RID_XMM3+1)|RID2RSET(RID_EAX); + if (ra_hasreg(ir->r)) + rset_clear(drop, ir->r); /* Dest reg handled below. */ + ra_evictset(as, drop); + ra_destreg(as, ir, RID_XMM0); + emit_call(as, fpm == IRFPM_FLOOR ? lj_vm_floor_sse : + fpm == IRFPM_CEIL ? lj_vm_ceil_sse : lj_vm_trunc_sse); + ra_left(as, RID_XMM0, ir->op1); + } + } else if (fpm == IRFPM_EXP2 && asm_fpjoin_pow(as, ir)) { + /* Rejoined to pow(). */ + } else { + asm_callid(as, ir, IRCALL_lj_vm_floor + fpm); + } +} + +#define asm_atan2(as, ir) asm_callid(as, ir, IRCALL_atan2) + +static void asm_ldexp(ASMState *as, IRIns *ir) +{ + int32_t ofs = sps_scale(ir->s); /* Use spill slot or temp slots. */ + Reg dest = ir->r; + if (ra_hasreg(dest)) { + ra_free(as, dest); + ra_modified(as, dest); + emit_rmro(as, XO_MOVSD, dest, RID_ESP, ofs); + } + emit_rmro(as, XO_FSTPq, XOg_FSTPq, RID_ESP, ofs); + emit_x87op(as, XI_FPOP1); + emit_x87op(as, XI_FSCALE); + asm_x87load(as, ir->op1); + asm_x87load(as, ir->op2); +} + +static void asm_fppowi(ASMState *as, IRIns *ir) +{ + /* The modified regs must match with the *.dasc implementation. */ + RegSet drop = RSET_RANGE(RID_XMM0, RID_XMM1+1)|RID2RSET(RID_EAX); + if (ra_hasreg(ir->r)) + rset_clear(drop, ir->r); /* Dest reg handled below. */ + ra_evictset(as, drop); + ra_destreg(as, ir, RID_XMM0); + emit_call(as, lj_vm_powi_sse); + ra_left(as, RID_XMM0, ir->op1); + ra_left(as, RID_EAX, ir->op2); +} + +static void asm_pow(ASMState *as, IRIns *ir) +{ +#if LJ_64 && LJ_HASFFI + if (!irt_isnum(ir->t)) + asm_callid(as, ir, irt_isi64(ir->t) ? IRCALL_lj_carith_powi64 : + IRCALL_lj_carith_powu64); + else +#endif + asm_fppowi(as, ir); +} + +static int asm_swapops(ASMState *as, IRIns *ir) +{ + IRIns *irl = IR(ir->op1); + IRIns *irr = IR(ir->op2); + lua_assert(ra_noreg(irr->r)); + if (!irm_iscomm(lj_ir_mode[ir->o])) + return 0; /* Can't swap non-commutative operations. */ + if (irref_isk(ir->op2)) + return 0; /* Don't swap constants to the left. */ + if (ra_hasreg(irl->r)) + return 1; /* Swap if left already has a register. */ + if (ra_samehint(ir->r, irr->r)) + return 1; /* Swap if dest and right have matching hints. */ + if (as->curins > as->loopref) { /* In variant part? */ + if (ir->op2 < as->loopref && !irt_isphi(irr->t)) + return 0; /* Keep invariants on the right. */ + if (ir->op1 < as->loopref && !irt_isphi(irl->t)) + return 1; /* Swap invariants to the right. */ + } + if (opisfusableload(irl->o)) + return 1; /* Swap fusable loads to the right. */ + return 0; /* Otherwise don't swap. */ +} + +static void asm_fparith(ASMState *as, IRIns *ir, x86Op xo) +{ + IRRef lref = ir->op1; + IRRef rref = ir->op2; + RegSet allow = RSET_FPR; + Reg dest; + Reg right = IR(rref)->r; + if (ra_hasreg(right)) { + rset_clear(allow, right); + ra_noweak(as, right); + } + dest = ra_dest(as, ir, allow); + if (lref == rref) { + right = dest; + } else if (ra_noreg(right)) { + if (asm_swapops(as, ir)) { + IRRef tmp = lref; lref = rref; rref = tmp; + } + right = asm_fuseload(as, rref, rset_clear(allow, dest)); + } + emit_mrm(as, xo, dest, right); + ra_left(as, dest, lref); +} + +static void asm_intarith(ASMState *as, IRIns *ir, x86Arith xa) +{ + IRRef lref = ir->op1; + IRRef rref = ir->op2; + RegSet allow = RSET_GPR; + Reg dest, right; + int32_t k = 0; + if (as->flagmcp == as->mcp) { /* Drop test r,r instruction. */ + MCode *p = as->mcp + ((LJ_64 && *as->mcp < XI_TESTb) ? 3 : 2); + if ((p[1] & 15) < 14) { + if ((p[1] & 15) >= 12) p[1] -= 4; /* L <->S, NL <-> NS */ + as->flagmcp = NULL; + as->mcp = p; + } /* else: cannot transform LE/NLE to cc without use of OF. */ + } + right = IR(rref)->r; + if (ra_hasreg(right)) { + rset_clear(allow, right); + ra_noweak(as, right); + } + dest = ra_dest(as, ir, allow); + if (lref == rref) { + right = dest; + } else if (ra_noreg(right) && !asm_isk32(as, rref, &k)) { + if (asm_swapops(as, ir)) { + IRRef tmp = lref; lref = rref; rref = tmp; + } + right = asm_fuseloadm(as, rref, rset_clear(allow, dest), irt_is64(ir->t)); + } + if (irt_isguard(ir->t)) /* For IR_ADDOV etc. */ + asm_guardcc(as, CC_O); + if (xa != XOg_X_IMUL) { + if (ra_hasreg(right)) + emit_mrm(as, XO_ARITH(xa), REX_64IR(ir, dest), right); + else + emit_gri(as, XG_ARITHi(xa), REX_64IR(ir, dest), k); + } else if (ra_hasreg(right)) { /* IMUL r, mrm. */ + emit_mrm(as, XO_IMUL, REX_64IR(ir, dest), right); + } else { /* IMUL r, r, k. */ + /* NYI: use lea/shl/add/sub (FOLD only does 2^k) depending on CPU. */ + Reg left = asm_fuseloadm(as, lref, RSET_GPR, irt_is64(ir->t)); + x86Op xo; + if (checki8(k)) { emit_i8(as, k); xo = XO_IMULi8; + } else { emit_i32(as, k); xo = XO_IMULi; } + emit_mrm(as, xo, REX_64IR(ir, dest), left); + return; + } + ra_left(as, dest, lref); +} + +/* LEA is really a 4-operand ADD with an independent destination register, +** up to two source registers and an immediate. One register can be scaled +** by 1, 2, 4 or 8. This can be used to avoid moves or to fuse several +** instructions. +** +** Currently only a few common cases are supported: +** - 3-operand ADD: y = a+b; y = a+k with a and b already allocated +** - Left ADD fusion: y = (a+b)+k; y = (a+k)+b +** - Right ADD fusion: y = a+(b+k) +** The ommited variants have already been reduced by FOLD. +** +** There are more fusion opportunities, like gathering shifts or joining +** common references. But these are probably not worth the trouble, since +** array indexing is not decomposed and already makes use of all fields +** of the ModRM operand. +*/ +static int asm_lea(ASMState *as, IRIns *ir) +{ + IRIns *irl = IR(ir->op1); + IRIns *irr = IR(ir->op2); + RegSet allow = RSET_GPR; + Reg dest; + as->mrm.base = as->mrm.idx = RID_NONE; + as->mrm.scale = XM_SCALE1; + as->mrm.ofs = 0; + if (ra_hasreg(irl->r)) { + rset_clear(allow, irl->r); + ra_noweak(as, irl->r); + as->mrm.base = irl->r; + if (irref_isk(ir->op2) || ra_hasreg(irr->r)) { + /* The PHI renaming logic does a better job in some cases. */ + if (ra_hasreg(ir->r) && + ((irt_isphi(irl->t) && as->phireg[ir->r] == ir->op1) || + (irt_isphi(irr->t) && as->phireg[ir->r] == ir->op2))) + return 0; + if (irref_isk(ir->op2)) { + as->mrm.ofs = irr->i; + } else { + rset_clear(allow, irr->r); + ra_noweak(as, irr->r); + as->mrm.idx = irr->r; + } + } else if (irr->o == IR_ADD && mayfuse(as, ir->op2) && + irref_isk(irr->op2)) { + Reg idx = ra_alloc1(as, irr->op1, allow); + rset_clear(allow, idx); + as->mrm.idx = (uint8_t)idx; + as->mrm.ofs = IR(irr->op2)->i; + } else { + return 0; + } + } else if (ir->op1 != ir->op2 && irl->o == IR_ADD && mayfuse(as, ir->op1) && + (irref_isk(ir->op2) || irref_isk(irl->op2))) { + Reg idx, base = ra_alloc1(as, irl->op1, allow); + rset_clear(allow, base); + as->mrm.base = (uint8_t)base; + if (irref_isk(ir->op2)) { + as->mrm.ofs = irr->i; + idx = ra_alloc1(as, irl->op2, allow); + } else { + as->mrm.ofs = IR(irl->op2)->i; + idx = ra_alloc1(as, ir->op2, allow); + } + rset_clear(allow, idx); + as->mrm.idx = (uint8_t)idx; + } else { + return 0; + } + dest = ra_dest(as, ir, allow); + emit_mrm(as, XO_LEA, dest, RID_MRM); + return 1; /* Success. */ +} + +static void asm_add(ASMState *as, IRIns *ir) +{ + if (irt_isnum(ir->t)) + asm_fparith(as, ir, XO_ADDSD); + else if ((as->flags & JIT_F_LEA_AGU) || as->flagmcp == as->mcp || + irt_is64(ir->t) || !asm_lea(as, ir)) + asm_intarith(as, ir, XOg_ADD); +} + +static void asm_sub(ASMState *as, IRIns *ir) +{ + if (irt_isnum(ir->t)) + asm_fparith(as, ir, XO_SUBSD); + else /* Note: no need for LEA trick here. i-k is encoded as i+(-k). */ + asm_intarith(as, ir, XOg_SUB); +} + +static void asm_mul(ASMState *as, IRIns *ir) +{ + if (irt_isnum(ir->t)) + asm_fparith(as, ir, XO_MULSD); + else + asm_intarith(as, ir, XOg_X_IMUL); +} + +static void asm_div(ASMState *as, IRIns *ir) +{ +#if LJ_64 && LJ_HASFFI + if (!irt_isnum(ir->t)) + asm_callid(as, ir, irt_isi64(ir->t) ? IRCALL_lj_carith_divi64 : + IRCALL_lj_carith_divu64); + else +#endif + asm_fparith(as, ir, XO_DIVSD); +} + +static void asm_mod(ASMState *as, IRIns *ir) +{ +#if LJ_64 && LJ_HASFFI + if (!irt_isint(ir->t)) + asm_callid(as, ir, irt_isi64(ir->t) ? IRCALL_lj_carith_modi64 : + IRCALL_lj_carith_modu64); + else +#endif + asm_callid(as, ir, IRCALL_lj_vm_modi); +} + +static void asm_neg_not(ASMState *as, IRIns *ir, x86Group3 xg) +{ + Reg dest = ra_dest(as, ir, RSET_GPR); + emit_rr(as, XO_GROUP3, REX_64IR(ir, xg), dest); + ra_left(as, dest, ir->op1); +} + +static void asm_neg(ASMState *as, IRIns *ir) +{ + if (irt_isnum(ir->t)) + asm_fparith(as, ir, XO_XORPS); + else + asm_neg_not(as, ir, XOg_NEG); +} + +#define asm_abs(as, ir) asm_fparith(as, ir, XO_ANDPS) + +static void asm_intmin_max(ASMState *as, IRIns *ir, int cc) +{ + Reg right, dest = ra_dest(as, ir, RSET_GPR); + IRRef lref = ir->op1, rref = ir->op2; + if (irref_isk(rref)) { lref = rref; rref = ir->op1; } + right = ra_alloc1(as, rref, rset_exclude(RSET_GPR, dest)); + emit_rr(as, XO_CMOV + (cc<<24), REX_64IR(ir, dest), right); + emit_rr(as, XO_CMP, REX_64IR(ir, dest), right); + ra_left(as, dest, lref); +} + +static void asm_min(ASMState *as, IRIns *ir) +{ + if (irt_isnum(ir->t)) + asm_fparith(as, ir, XO_MINSD); + else + asm_intmin_max(as, ir, CC_G); +} + +static void asm_max(ASMState *as, IRIns *ir) +{ + if (irt_isnum(ir->t)) + asm_fparith(as, ir, XO_MAXSD); + else + asm_intmin_max(as, ir, CC_L); +} + +/* Note: don't use LEA for overflow-checking arithmetic! */ +#define asm_addov(as, ir) asm_intarith(as, ir, XOg_ADD) +#define asm_subov(as, ir) asm_intarith(as, ir, XOg_SUB) +#define asm_mulov(as, ir) asm_intarith(as, ir, XOg_X_IMUL) + +#define asm_bnot(as, ir) asm_neg_not(as, ir, XOg_NOT) + +static void asm_bswap(ASMState *as, IRIns *ir) +{ + Reg dest = ra_dest(as, ir, RSET_GPR); + as->mcp = emit_op(XO_BSWAP + ((dest&7) << 24), + REX_64IR(ir, 0), dest, 0, as->mcp, 1); + ra_left(as, dest, ir->op1); +} + +#define asm_band(as, ir) asm_intarith(as, ir, XOg_AND) +#define asm_bor(as, ir) asm_intarith(as, ir, XOg_OR) +#define asm_bxor(as, ir) asm_intarith(as, ir, XOg_XOR) + +static void asm_bitshift(ASMState *as, IRIns *ir, x86Shift xs, x86Op xv) +{ + IRRef rref = ir->op2; + IRIns *irr = IR(rref); + Reg dest; + if (irref_isk(rref)) { /* Constant shifts. */ + int shift; + dest = ra_dest(as, ir, RSET_GPR); + shift = irr->i & (irt_is64(ir->t) ? 63 : 31); + if (!xv && shift && (as->flags & JIT_F_BMI2)) { + Reg left = asm_fuseloadm(as, ir->op1, RSET_GPR, irt_is64(ir->t)); + if (left != dest) { /* BMI2 rotate right by constant. */ + emit_i8(as, xs == XOg_ROL ? -shift : shift); + emit_mrm(as, VEX_64IR(ir, XV_RORX), dest, left); + return; + } + } + switch (shift) { + case 0: break; + case 1: emit_rr(as, XO_SHIFT1, REX_64IR(ir, xs), dest); break; + default: emit_shifti(as, REX_64IR(ir, xs), dest, shift); break; + } + } else if ((as->flags & JIT_F_BMI2) && xv) { /* BMI2 variable shifts. */ + Reg left, right; + dest = ra_dest(as, ir, RSET_GPR); + right = ra_alloc1(as, rref, RSET_GPR); + left = asm_fuseloadm(as, ir->op1, rset_exclude(RSET_GPR, right), + irt_is64(ir->t)); + emit_mrm(as, VEX_64IR(ir, xv) ^ (right << 19), dest, left); + return; + } else { /* Variable shifts implicitly use register cl (i.e. ecx). */ + Reg right; + dest = ra_dest(as, ir, rset_exclude(RSET_GPR, RID_ECX)); + if (dest == RID_ECX) { + dest = ra_scratch(as, rset_exclude(RSET_GPR, RID_ECX)); + emit_rr(as, XO_MOV, RID_ECX, dest); + } + right = irr->r; + if (ra_noreg(right)) + right = ra_allocref(as, rref, RID2RSET(RID_ECX)); + else if (right != RID_ECX) + ra_scratch(as, RID2RSET(RID_ECX)); + emit_rr(as, XO_SHIFTcl, REX_64IR(ir, xs), dest); + ra_noweak(as, right); + if (right != RID_ECX) + emit_rr(as, XO_MOV, RID_ECX, right); + } + ra_left(as, dest, ir->op1); + /* + ** Note: avoid using the flags resulting from a shift or rotate! + ** All of them cause a partial flag stall, except for r,1 shifts + ** (but not rotates). And a shift count of 0 leaves the flags unmodified. + */ +} + +#define asm_bshl(as, ir) asm_bitshift(as, ir, XOg_SHL, XV_SHLX) +#define asm_bshr(as, ir) asm_bitshift(as, ir, XOg_SHR, XV_SHRX) +#define asm_bsar(as, ir) asm_bitshift(as, ir, XOg_SAR, XV_SARX) +#define asm_brol(as, ir) asm_bitshift(as, ir, XOg_ROL, 0) +#define asm_bror(as, ir) asm_bitshift(as, ir, XOg_ROR, 0) + +/* -- Comparisons --------------------------------------------------------- */ + +/* Virtual flags for unordered FP comparisons. */ +#define VCC_U 0x1000 /* Unordered. */ +#define VCC_P 0x2000 /* Needs extra CC_P branch. */ +#define VCC_S 0x4000 /* Swap avoids CC_P branch. */ +#define VCC_PS (VCC_P|VCC_S) + +/* Map of comparisons to flags. ORDER IR. */ +#define COMPFLAGS(ci, cin, cu, cf) ((ci)+((cu)<<4)+((cin)<<8)+(cf)) +static const uint16_t asm_compmap[IR_ABC+1] = { + /* signed non-eq unsigned flags */ + /* LT */ COMPFLAGS(CC_GE, CC_G, CC_AE, VCC_PS), + /* GE */ COMPFLAGS(CC_L, CC_L, CC_B, 0), + /* LE */ COMPFLAGS(CC_G, CC_G, CC_A, VCC_PS), + /* GT */ COMPFLAGS(CC_LE, CC_L, CC_BE, 0), + /* ULT */ COMPFLAGS(CC_AE, CC_A, CC_AE, VCC_U), + /* UGE */ COMPFLAGS(CC_B, CC_B, CC_B, VCC_U|VCC_PS), + /* ULE */ COMPFLAGS(CC_A, CC_A, CC_A, VCC_U), + /* UGT */ COMPFLAGS(CC_BE, CC_B, CC_BE, VCC_U|VCC_PS), + /* EQ */ COMPFLAGS(CC_NE, CC_NE, CC_NE, VCC_P), + /* NE */ COMPFLAGS(CC_E, CC_E, CC_E, VCC_U|VCC_P), + /* ABC */ COMPFLAGS(CC_BE, CC_B, CC_BE, VCC_U|VCC_PS) /* Same as UGT. */ +}; + +/* FP and integer comparisons. */ +static void asm_comp(ASMState *as, IRIns *ir) +{ + uint32_t cc = asm_compmap[ir->o]; + if (irt_isnum(ir->t)) { + IRRef lref = ir->op1; + IRRef rref = ir->op2; + Reg left, right; + MCLabel l_around; + /* + ** An extra CC_P branch is required to preserve ordered/unordered + ** semantics for FP comparisons. This can be avoided by swapping + ** the operands and inverting the condition (except for EQ and UNE). + ** So always try to swap if possible. + ** + ** Another option would be to swap operands to achieve better memory + ** operand fusion. But it's unlikely that this outweighs the cost + ** of the extra branches. + */ + if (cc & VCC_S) { /* Swap? */ + IRRef tmp = lref; lref = rref; rref = tmp; + cc ^= (VCC_PS|(5<<4)); /* A <-> B, AE <-> BE, PS <-> none */ + } + left = ra_alloc1(as, lref, RSET_FPR); + l_around = emit_label(as); + asm_guardcc(as, cc >> 4); + if (cc & VCC_P) { /* Extra CC_P branch required? */ + if (!(cc & VCC_U)) { + asm_guardcc(as, CC_P); /* Branch to exit for ordered comparisons. */ + } else if (l_around != as->invmcp) { + emit_sjcc(as, CC_P, l_around); /* Branch around for unordered. */ + } else { + /* Patched to mcloop by asm_loop_fixup. */ + as->loopinv = 2; + if (as->realign) + emit_sjcc(as, CC_P, as->mcp); + else + emit_jcc(as, CC_P, as->mcp); + } + } + right = asm_fuseload(as, rref, rset_exclude(RSET_FPR, left)); + emit_mrm(as, XO_UCOMISD, left, right); + } else { + IRRef lref = ir->op1, rref = ir->op2; + IROp leftop = (IROp)(IR(lref)->o); + Reg r64 = REX_64IR(ir, 0); + int32_t imm = 0; + lua_assert(irt_is64(ir->t) || irt_isint(ir->t) || + irt_isu32(ir->t) || irt_isaddr(ir->t) || irt_isu8(ir->t)); + /* Swap constants (only for ABC) and fusable loads to the right. */ + if (irref_isk(lref) || (!irref_isk(rref) && opisfusableload(leftop))) { + if ((cc & 0xc) == 0xc) cc ^= 0x53; /* L <-> G, LE <-> GE */ + else if ((cc & 0xa) == 0x2) cc ^= 0x55; /* A <-> B, AE <-> BE */ + lref = ir->op2; rref = ir->op1; + } + if (asm_isk32(as, rref, &imm)) { + IRIns *irl = IR(lref); + /* Check wether we can use test ins. Not for unsigned, since CF=0. */ + int usetest = (imm == 0 && (cc & 0xa) != 0x2); + if (usetest && irl->o == IR_BAND && irl+1 == ir && !ra_used(irl)) { + /* Combine comp(BAND(ref, r/imm), 0) into test mrm, r/imm. */ + Reg right, left = RID_NONE; + RegSet allow = RSET_GPR; + if (!asm_isk32(as, irl->op2, &imm)) { + left = ra_alloc1(as, irl->op2, allow); + rset_clear(allow, left); + } else { /* Try to Fuse IRT_I8/IRT_U8 loads, too. See below. */ + IRIns *irll = IR(irl->op1); + if (opisfusableload((IROp)irll->o) && + (irt_isi8(irll->t) || irt_isu8(irll->t))) { + IRType1 origt = irll->t; /* Temporarily flip types. */ + irll->t.irt = (irll->t.irt & ~IRT_TYPE) | IRT_INT; + as->curins--; /* Skip to BAND to avoid failing in noconflict(). */ + right = asm_fuseload(as, irl->op1, RSET_GPR); + as->curins++; + irll->t = origt; + if (right != RID_MRM) goto test_nofuse; + /* Fusion succeeded, emit test byte mrm, imm8. */ + asm_guardcc(as, cc); + emit_i8(as, (imm & 0xff)); + emit_mrm(as, XO_GROUP3b, XOg_TEST, RID_MRM); + return; + } + } + as->curins--; /* Skip to BAND to avoid failing in noconflict(). */ + right = asm_fuseloadm(as, irl->op1, allow, r64); + as->curins++; /* Undo the above. */ + test_nofuse: + asm_guardcc(as, cc); + if (ra_noreg(left)) { + emit_i32(as, imm); + emit_mrm(as, XO_GROUP3, r64 + XOg_TEST, right); + } else { + emit_mrm(as, XO_TEST, r64 + left, right); + } + } else { + Reg left; + if (opisfusableload((IROp)irl->o) && + ((irt_isu8(irl->t) && checku8(imm)) || + ((irt_isi8(irl->t) || irt_isi16(irl->t)) && checki8(imm)) || + (irt_isu16(irl->t) && checku16(imm) && checki8((int16_t)imm)))) { + /* Only the IRT_INT case is fused by asm_fuseload. + ** The IRT_I8/IRT_U8 loads and some IRT_I16/IRT_U16 loads + ** are handled here. + ** Note that cmp word [mem], imm16 should not be generated, + ** since it has a length-changing prefix. Compares of a word + ** against a sign-extended imm8 are ok, however. + */ + IRType1 origt = irl->t; /* Temporarily flip types. */ + irl->t.irt = (irl->t.irt & ~IRT_TYPE) | IRT_INT; + left = asm_fuseload(as, lref, RSET_GPR); + irl->t = origt; + if (left == RID_MRM) { /* Fusion succeeded? */ + if (irt_isu8(irl->t) || irt_isu16(irl->t)) + cc >>= 4; /* Need unsigned compare. */ + asm_guardcc(as, cc); + emit_i8(as, imm); + emit_mrm(as, (irt_isi8(origt) || irt_isu8(origt)) ? + XO_ARITHib : XO_ARITHiw8, r64 + XOg_CMP, RID_MRM); + return; + } /* Otherwise handle register case as usual. */ + } else { + left = asm_fuseloadm(as, lref, + irt_isu8(ir->t) ? RSET_GPR8 : RSET_GPR, r64); + } + asm_guardcc(as, cc); + if (usetest && left != RID_MRM) { + /* Use test r,r instead of cmp r,0. */ + x86Op xo = XO_TEST; + if (irt_isu8(ir->t)) { + lua_assert(ir->o == IR_EQ || ir->o == IR_NE); + xo = XO_TESTb; + if (!rset_test(RSET_RANGE(RID_EAX, RID_EBX+1), left)) { + if (LJ_64) { + left |= FORCE_REX; + } else { + emit_i32(as, 0xff); + emit_mrm(as, XO_GROUP3, XOg_TEST, left); + return; + } + } + } + emit_rr(as, xo, r64 + left, left); + if (irl+1 == ir) /* Referencing previous ins? */ + as->flagmcp = as->mcp; /* Set flag to drop test r,r if possible. */ + } else { + emit_gmrmi(as, XG_ARITHi(XOg_CMP), r64 + left, imm); + } + } + } else { + Reg left = ra_alloc1(as, lref, RSET_GPR); + Reg right = asm_fuseloadm(as, rref, rset_exclude(RSET_GPR, left), r64); + asm_guardcc(as, cc); + emit_mrm(as, XO_CMP, r64 + left, right); + } + } +} + +#define asm_equal(as, ir) asm_comp(as, ir) + +#if LJ_32 && LJ_HASFFI +/* 64 bit integer comparisons in 32 bit mode. */ +static void asm_comp_int64(ASMState *as, IRIns *ir) +{ + uint32_t cc = asm_compmap[(ir-1)->o]; + RegSet allow = RSET_GPR; + Reg lefthi = RID_NONE, leftlo = RID_NONE; + Reg righthi = RID_NONE, rightlo = RID_NONE; + MCLabel l_around; + x86ModRM mrm; + + as->curins--; /* Skip loword ins. Avoids failing in noconflict(), too. */ + + /* Allocate/fuse hiword operands. */ + if (irref_isk(ir->op2)) { + lefthi = asm_fuseload(as, ir->op1, allow); + } else { + lefthi = ra_alloc1(as, ir->op1, allow); + rset_clear(allow, lefthi); + righthi = asm_fuseload(as, ir->op2, allow); + if (righthi == RID_MRM) { + if (as->mrm.base != RID_NONE) rset_clear(allow, as->mrm.base); + if (as->mrm.idx != RID_NONE) rset_clear(allow, as->mrm.idx); + } else { + rset_clear(allow, righthi); + } + } + mrm = as->mrm; /* Save state for hiword instruction. */ + + /* Allocate/fuse loword operands. */ + if (irref_isk((ir-1)->op2)) { + leftlo = asm_fuseload(as, (ir-1)->op1, allow); + } else { + leftlo = ra_alloc1(as, (ir-1)->op1, allow); + rset_clear(allow, leftlo); + rightlo = asm_fuseload(as, (ir-1)->op2, allow); + } + + /* All register allocations must be performed _before_ this point. */ + l_around = emit_label(as); + as->invmcp = as->flagmcp = NULL; /* Cannot use these optimizations. */ + + /* Loword comparison and branch. */ + asm_guardcc(as, cc >> 4); /* Always use unsigned compare for loword. */ + if (ra_noreg(rightlo)) { + int32_t imm = IR((ir-1)->op2)->i; + if (imm == 0 && ((cc >> 4) & 0xa) != 0x2 && leftlo != RID_MRM) + emit_rr(as, XO_TEST, leftlo, leftlo); + else + emit_gmrmi(as, XG_ARITHi(XOg_CMP), leftlo, imm); + } else { + emit_mrm(as, XO_CMP, leftlo, rightlo); + } + + /* Hiword comparison and branches. */ + if ((cc & 15) != CC_NE) + emit_sjcc(as, CC_NE, l_around); /* Hiword unequal: skip loword compare. */ + if ((cc & 15) != CC_E) + asm_guardcc(as, cc >> 8); /* Hiword compare without equality check. */ + as->mrm = mrm; /* Restore state. */ + if (ra_noreg(righthi)) { + int32_t imm = IR(ir->op2)->i; + if (imm == 0 && (cc & 0xa) != 0x2 && lefthi != RID_MRM) + emit_rr(as, XO_TEST, lefthi, lefthi); + else + emit_gmrmi(as, XG_ARITHi(XOg_CMP), lefthi, imm); + } else { + emit_mrm(as, XO_CMP, lefthi, righthi); + } +} +#endif + +/* -- Support for 64 bit ops in 32 bit mode ------------------------------- */ + +/* Hiword op of a split 64 bit op. Previous op must be the loword op. */ +static void asm_hiop(ASMState *as, IRIns *ir) +{ +#if LJ_32 && LJ_HASFFI + /* HIOP is marked as a store because it needs its own DCE logic. */ + int uselo = ra_used(ir-1), usehi = ra_used(ir); /* Loword/hiword used? */ + if (LJ_UNLIKELY(!(as->flags & JIT_F_OPT_DCE))) uselo = usehi = 1; + if ((ir-1)->o == IR_CONV) { /* Conversions to/from 64 bit. */ + as->curins--; /* Always skip the CONV. */ + if (usehi || uselo) + asm_conv64(as, ir); + return; + } else if ((ir-1)->o <= IR_NE) { /* 64 bit integer comparisons. ORDER IR. */ + asm_comp_int64(as, ir); + return; + } else if ((ir-1)->o == IR_XSTORE) { + if ((ir-1)->r != RID_SINK) + asm_fxstore(as, ir); + return; + } + if (!usehi) return; /* Skip unused hiword op for all remaining ops. */ + switch ((ir-1)->o) { + case IR_ADD: + as->flagmcp = NULL; + as->curins--; + asm_intarith(as, ir, XOg_ADC); + asm_intarith(as, ir-1, XOg_ADD); + break; + case IR_SUB: + as->flagmcp = NULL; + as->curins--; + asm_intarith(as, ir, XOg_SBB); + asm_intarith(as, ir-1, XOg_SUB); + break; + case IR_NEG: { + Reg dest = ra_dest(as, ir, RSET_GPR); + emit_rr(as, XO_GROUP3, XOg_NEG, dest); + emit_i8(as, 0); + emit_rr(as, XO_ARITHi8, XOg_ADC, dest); + ra_left(as, dest, ir->op1); + as->curins--; + asm_neg_not(as, ir-1, XOg_NEG); + break; + } + case IR_CALLN: + case IR_CALLXS: + if (!uselo) + ra_allocref(as, ir->op1, RID2RSET(RID_RETLO)); /* Mark lo op as used. */ + break; + case IR_CNEWI: + /* Nothing to do here. Handled by CNEWI itself. */ + break; + default: lua_assert(0); break; + } +#else + UNUSED(as); UNUSED(ir); lua_assert(0); /* Unused on x64 or without FFI. */ +#endif +} + +/* -- Profiling ----------------------------------------------------------- */ + +static void asm_prof(ASMState *as, IRIns *ir) +{ + UNUSED(ir); + asm_guardcc(as, CC_NE); + emit_i8(as, HOOK_PROFILE); + emit_rma(as, XO_GROUP3b, XOg_TEST, &J2G(as->J)->hookmask); +} + +/* -- Stack handling ------------------------------------------------------ */ + +/* Check Lua stack size for overflow. Use exit handler as fallback. */ +static void asm_stack_check(ASMState *as, BCReg topslot, + IRIns *irp, RegSet allow, ExitNo exitno) +{ + /* Try to get an unused temp. register, otherwise spill/restore eax. */ + Reg pbase = irp ? irp->r : RID_BASE; + Reg r = allow ? rset_pickbot(allow) : RID_EAX; + emit_jcc(as, CC_B, exitstub_addr(as->J, exitno)); + if (allow == RSET_EMPTY) /* Restore temp. register. */ + emit_rmro(as, XO_MOV, r|REX_64, RID_ESP, 0); + else + ra_modified(as, r); + emit_gri(as, XG_ARITHi(XOg_CMP), r|REX_GC64, (int32_t)(8*topslot)); + if (ra_hasreg(pbase) && pbase != r) + emit_rr(as, XO_ARITH(XOg_SUB), r|REX_GC64, pbase); + else +#if LJ_GC64 + emit_rmro(as, XO_ARITH(XOg_SUB), r|REX_64, RID_DISPATCH, + (int32_t)dispofs(as, &J2G(as->J)->jit_base)); +#else + emit_rmro(as, XO_ARITH(XOg_SUB), r, RID_NONE, + ptr2addr(&J2G(as->J)->jit_base)); +#endif + emit_rmro(as, XO_MOV, r|REX_GC64, r, offsetof(lua_State, maxstack)); + emit_getgl(as, r, cur_L); + if (allow == RSET_EMPTY) /* Spill temp. register. */ + emit_rmro(as, XO_MOVto, r|REX_64, RID_ESP, 0); +} + +/* Restore Lua stack from on-trace state. */ +static void asm_stack_restore(ASMState *as, SnapShot *snap) +{ + SnapEntry *map = &as->T->snapmap[snap->mapofs]; +#if !LJ_FR2 || defined(LUA_USE_ASSERT) + SnapEntry *flinks = &as->T->snapmap[snap_nextofs(as->T, snap)-1-LJ_FR2]; +#endif + MSize n, nent = snap->nent; + /* Store the value of all modified slots to the Lua stack. */ + for (n = 0; n < nent; n++) { + SnapEntry sn = map[n]; + BCReg s = snap_slot(sn); + int32_t ofs = 8*((int32_t)s-1-LJ_FR2); + IRRef ref = snap_ref(sn); + IRIns *ir = IR(ref); + if ((sn & SNAP_NORESTORE)) + continue; + if (irt_isnum(ir->t)) { + Reg src = ra_alloc1(as, ref, RSET_FPR); + emit_rmro(as, XO_MOVSDto, src, RID_BASE, ofs); + } else { + lua_assert(irt_ispri(ir->t) || irt_isaddr(ir->t) || + (LJ_DUALNUM && irt_isinteger(ir->t))); + if (!irref_isk(ref)) { + Reg src = ra_alloc1(as, ref, rset_exclude(RSET_GPR, RID_BASE)); +#if LJ_GC64 + if (irt_is64(ir->t)) { + /* TODO: 64 bit store + 32 bit load-modify-store is suboptimal. */ + emit_u32(as, irt_toitype(ir->t) << 15); + emit_rmro(as, XO_ARITHi, XOg_OR, RID_BASE, ofs+4); + } else if (LJ_DUALNUM && irt_isinteger(ir->t)) { + emit_movmroi(as, RID_BASE, ofs+4, LJ_TISNUM << 15); + } else { + emit_movmroi(as, RID_BASE, ofs+4, (irt_toitype(ir->t)<<15)|0x7fff); + } +#endif + emit_movtomro(as, REX_64IR(ir, src), RID_BASE, ofs); +#if LJ_GC64 + } else { + TValue k; + lj_ir_kvalue(as->J->L, &k, ir); + if (tvisnil(&k)) { + emit_i32(as, -1); + emit_rmro(as, XO_MOVmi, REX_64, RID_BASE, ofs); + } else { + emit_movmroi(as, RID_BASE, ofs+4, k.u32.hi); + emit_movmroi(as, RID_BASE, ofs, k.u32.lo); + } +#else + } else if (!irt_ispri(ir->t)) { + emit_movmroi(as, RID_BASE, ofs, ir->i); +#endif + } + if ((sn & (SNAP_CONT|SNAP_FRAME))) { +#if !LJ_FR2 + if (s != 0) /* Do not overwrite link to previous frame. */ + emit_movmroi(as, RID_BASE, ofs+4, (int32_t)(*flinks--)); +#endif +#if !LJ_GC64 + } else { + if (!(LJ_64 && irt_islightud(ir->t))) + emit_movmroi(as, RID_BASE, ofs+4, irt_toitype(ir->t)); +#endif + } + } + checkmclim(as); + } + lua_assert(map + nent == flinks); +} + +/* -- GC handling --------------------------------------------------------- */ + +/* Check GC threshold and do one or more GC steps. */ +static void asm_gc_check(ASMState *as) +{ + const CCallInfo *ci = &lj_ir_callinfo[IRCALL_lj_gc_step_jit]; + IRRef args[2]; + MCLabel l_end; + Reg tmp; + ra_evictset(as, RSET_SCRATCH); + l_end = emit_label(as); + /* Exit trace if in GCSatomic or GCSfinalize. Avoids syncing GC objects. */ + asm_guardcc(as, CC_NE); /* Assumes asm_snap_prep() already done. */ + emit_rr(as, XO_TEST, RID_RET, RID_RET); + args[0] = ASMREF_TMP1; /* global_State *g */ + args[1] = ASMREF_TMP2; /* MSize steps */ + asm_gencall(as, ci, args); + tmp = ra_releasetmp(as, ASMREF_TMP1); +#if LJ_GC64 + emit_rmro(as, XO_LEA, tmp|REX_64, RID_DISPATCH, GG_DISP2G); +#else + emit_loada(as, tmp, J2G(as->J)); +#endif + emit_loadi(as, ra_releasetmp(as, ASMREF_TMP2), as->gcsteps); + /* Jump around GC step if GC total < GC threshold. */ + emit_sjcc(as, CC_B, l_end); + emit_opgl(as, XO_ARITH(XOg_CMP), tmp|REX_GC64, gc.threshold); + emit_getgl(as, tmp, gc.total); + as->gcsteps = 0; + checkmclim(as); +} + +/* -- Loop handling ------------------------------------------------------- */ + +/* Fixup the loop branch. */ +static void asm_loop_fixup(ASMState *as) +{ + MCode *p = as->mctop; + MCode *target = as->mcp; + if (as->realign) { /* Realigned loops use short jumps. */ + as->realign = NULL; /* Stop another retry. */ + lua_assert(((intptr_t)target & 15) == 0); + if (as->loopinv) { /* Inverted loop branch? */ + p -= 5; + p[0] = XI_JMP; + lua_assert(target - p >= -128); + p[-1] = (MCode)(target - p); /* Patch sjcc. */ + if (as->loopinv == 2) + p[-3] = (MCode)(target - p + 2); /* Patch opt. short jp. */ + } else { + lua_assert(target - p >= -128); + p[-1] = (MCode)(int8_t)(target - p); /* Patch short jmp. */ + p[-2] = XI_JMPs; + } + } else { + MCode *newloop; + p[-5] = XI_JMP; + if (as->loopinv) { /* Inverted loop branch? */ + /* asm_guardcc already inverted the jcc and patched the jmp. */ + p -= 5; + newloop = target+4; + *(int32_t *)(p-4) = (int32_t)(target - p); /* Patch jcc. */ + if (as->loopinv == 2) { + *(int32_t *)(p-10) = (int32_t)(target - p + 6); /* Patch opt. jp. */ + newloop = target+8; + } + } else { /* Otherwise just patch jmp. */ + *(int32_t *)(p-4) = (int32_t)(target - p); + newloop = target+3; + } + /* Realign small loops and shorten the loop branch. */ + if (newloop >= p - 128) { + as->realign = newloop; /* Force a retry and remember alignment. */ + as->curins = as->stopins; /* Abort asm_trace now. */ + as->T->nins = as->orignins; /* Remove any added renames. */ + } + } +} + +/* -- Head of trace ------------------------------------------------------- */ + +/* Coalesce BASE register for a root trace. */ +static void asm_head_root_base(ASMState *as) +{ + IRIns *ir = IR(REF_BASE); + Reg r = ir->r; + if (ra_hasreg(r)) { + ra_free(as, r); + if (rset_test(as->modset, r) || irt_ismarked(ir->t)) + ir->r = RID_INIT; /* No inheritance for modified BASE register. */ + if (r != RID_BASE) + emit_rr(as, XO_MOV, r|REX_GC64, RID_BASE); + } +} + +/* Coalesce or reload BASE register for a side trace. */ +static RegSet asm_head_side_base(ASMState *as, IRIns *irp, RegSet allow) +{ + IRIns *ir = IR(REF_BASE); + Reg r = ir->r; + if (ra_hasreg(r)) { + ra_free(as, r); + if (rset_test(as->modset, r) || irt_ismarked(ir->t)) + ir->r = RID_INIT; /* No inheritance for modified BASE register. */ + if (irp->r == r) { + rset_clear(allow, r); /* Mark same BASE register as coalesced. */ + } else if (ra_hasreg(irp->r) && rset_test(as->freeset, irp->r)) { + /* Move from coalesced parent reg. */ + rset_clear(allow, irp->r); + emit_rr(as, XO_MOV, r|REX_GC64, irp->r); + } else { + emit_getgl(as, r, jit_base); /* Otherwise reload BASE. */ + } + } + return allow; +} + +/* -- Tail of trace ------------------------------------------------------- */ + +/* Fixup the tail code. */ +static void asm_tail_fixup(ASMState *as, TraceNo lnk) +{ + /* Note: don't use as->mcp swap + emit_*: emit_op overwrites more bytes. */ + MCode *p = as->mctop; + MCode *target, *q; + int32_t spadj = as->T->spadjust; + if (spadj == 0) { + p -= ((as->flags & JIT_F_LEA_AGU) ? 7 : 6) + (LJ_64 ? 1 : 0); + } else { + MCode *p1; + /* Patch stack adjustment. */ + if (checki8(spadj)) { + p -= 3; + p1 = p-6; + *p1 = (MCode)spadj; + } else { + p1 = p-9; + *(int32_t *)p1 = spadj; + } + if ((as->flags & JIT_F_LEA_AGU)) { +#if LJ_64 + p1[-4] = 0x48; +#endif + p1[-3] = (MCode)XI_LEA; + p1[-2] = MODRM(checki8(spadj) ? XM_OFS8 : XM_OFS32, RID_ESP, RID_ESP); + p1[-1] = MODRM(XM_SCALE1, RID_ESP, RID_ESP); + } else { +#if LJ_64 + p1[-3] = 0x48; +#endif + p1[-2] = (MCode)(checki8(spadj) ? XI_ARITHi8 : XI_ARITHi); + p1[-1] = MODRM(XM_REG, XOg_ADD, RID_ESP); + } + } + /* Patch exit branch. */ + target = lnk ? traceref(as->J, lnk)->mcode : (MCode *)lj_vm_exit_interp; + *(int32_t *)(p-4) = jmprel(p, target); + p[-5] = XI_JMP; + /* Drop unused mcode tail. Fill with NOPs to make the prefetcher happy. */ + for (q = as->mctop-1; q >= p; q--) + *q = XI_NOP; + as->mctop = p; +} + +/* Prepare tail of code. */ +static void asm_tail_prep(ASMState *as) +{ + MCode *p = as->mctop; + /* Realign and leave room for backwards loop branch or exit branch. */ + if (as->realign) { + int i = ((int)(intptr_t)as->realign) & 15; + /* Fill unused mcode tail with NOPs to make the prefetcher happy. */ + while (i-- > 0) + *--p = XI_NOP; + as->mctop = p; + p -= (as->loopinv ? 5 : 2); /* Space for short/near jmp. */ + } else { + p -= 5; /* Space for exit branch (near jmp). */ + } + if (as->loopref) { + as->invmcp = as->mcp = p; + } else { + /* Leave room for ESP adjustment: add esp, imm or lea esp, [esp+imm] */ + as->mcp = p - (((as->flags & JIT_F_LEA_AGU) ? 7 : 6) + (LJ_64 ? 1 : 0)); + as->invmcp = NULL; + } +} + +/* -- Trace setup --------------------------------------------------------- */ + +/* Ensure there are enough stack slots for call arguments. */ +static Reg asm_setup_call_slots(ASMState *as, IRIns *ir, const CCallInfo *ci) +{ + IRRef args[CCI_NARGS_MAX*2]; + int nslots; + asm_collectargs(as, ir, ci, args); + nslots = asm_count_call_slots(as, ci, args); + if (nslots > as->evenspill) /* Leave room for args in stack slots. */ + as->evenspill = nslots; +#if LJ_64 + return irt_isfp(ir->t) ? REGSP_HINT(RID_FPRET) : REGSP_HINT(RID_RET); +#else + return irt_isfp(ir->t) ? REGSP_INIT : REGSP_HINT(RID_RET); +#endif +} + +/* Target-specific setup. */ +static void asm_setup_target(ASMState *as) +{ + asm_exitstub_setup(as, as->T->nsnap); + as->mrm.base = 0; +} + +/* -- Trace patching ------------------------------------------------------ */ + +static const uint8_t map_op1[256] = { +0x92,0x92,0x92,0x92,0x52,0x45,0x51,0x51,0x92,0x92,0x92,0x92,0x52,0x45,0x51,0x20, +0x92,0x92,0x92,0x92,0x52,0x45,0x51,0x51,0x92,0x92,0x92,0x92,0x52,0x45,0x51,0x51, +0x92,0x92,0x92,0x92,0x52,0x45,0x10,0x51,0x92,0x92,0x92,0x92,0x52,0x45,0x10,0x51, +0x92,0x92,0x92,0x92,0x52,0x45,0x10,0x51,0x92,0x92,0x92,0x92,0x52,0x45,0x10,0x51, +#if LJ_64 +0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x14,0x14,0x14,0x14,0x14,0x14,0x14,0x14, +#else +0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51, +#endif +0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51, +0x51,0x51,0x92,0x92,0x10,0x10,0x12,0x11,0x45,0x86,0x52,0x93,0x51,0x51,0x51,0x51, +0x52,0x52,0x52,0x52,0x52,0x52,0x52,0x52,0x52,0x52,0x52,0x52,0x52,0x52,0x52,0x52, +0x93,0x86,0x93,0x93,0x92,0x92,0x92,0x92,0x92,0x92,0x92,0x92,0x92,0x92,0x92,0x92, +0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x47,0x51,0x51,0x51,0x51,0x51, +#if LJ_64 +0x59,0x59,0x59,0x59,0x51,0x51,0x51,0x51,0x52,0x45,0x51,0x51,0x51,0x51,0x51,0x51, +#else +0x55,0x55,0x55,0x55,0x51,0x51,0x51,0x51,0x52,0x45,0x51,0x51,0x51,0x51,0x51,0x51, +#endif +0x52,0x52,0x52,0x52,0x52,0x52,0x52,0x52,0x05,0x05,0x05,0x05,0x05,0x05,0x05,0x05, +0x93,0x93,0x53,0x51,0x70,0x71,0x93,0x86,0x54,0x51,0x53,0x51,0x51,0x52,0x51,0x51, +0x92,0x92,0x92,0x92,0x52,0x52,0x51,0x51,0x92,0x92,0x92,0x92,0x92,0x92,0x92,0x92, +0x52,0x52,0x52,0x52,0x52,0x52,0x52,0x52,0x45,0x45,0x47,0x52,0x51,0x51,0x51,0x51, +0x10,0x51,0x10,0x10,0x51,0x51,0x63,0x66,0x51,0x51,0x51,0x51,0x51,0x51,0x92,0x92 +}; + +static const uint8_t map_op2[256] = { +0x93,0x93,0x93,0x93,0x52,0x52,0x52,0x52,0x52,0x52,0x51,0x52,0x51,0x93,0x52,0x94, +0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93, +0x53,0x53,0x53,0x53,0x53,0x53,0x53,0x53,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93, +0x52,0x52,0x52,0x52,0x52,0x52,0x52,0x52,0x34,0x51,0x35,0x51,0x51,0x51,0x51,0x51, +0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93, +0x53,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93, +0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93, +0x94,0x54,0x54,0x54,0x93,0x93,0x93,0x52,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93, +0x46,0x46,0x46,0x46,0x46,0x46,0x46,0x46,0x46,0x46,0x46,0x46,0x46,0x46,0x46,0x46, +0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93, +0x52,0x52,0x52,0x93,0x94,0x93,0x51,0x51,0x52,0x52,0x52,0x93,0x94,0x93,0x93,0x93, +0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x94,0x93,0x93,0x93,0x93,0x93, +0x93,0x93,0x94,0x93,0x94,0x94,0x94,0x93,0x52,0x52,0x52,0x52,0x52,0x52,0x52,0x52, +0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93, +0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93, +0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x52 +}; + +static uint32_t asm_x86_inslen(const uint8_t* p) +{ + uint32_t result = 0; + uint32_t prefixes = 0; + uint32_t x = map_op1[*p]; + for (;;) { + switch (x >> 4) { + case 0: return result + x + (prefixes & 4); + case 1: prefixes |= x; x = map_op1[*++p]; result++; break; + case 2: x = map_op2[*++p]; break; + case 3: p++; goto mrm; + case 4: result -= (prefixes & 2); /* fallthrough */ + case 5: return result + (x & 15); + case 6: /* Group 3. */ + if (p[1] & 0x38) x = 2; + else if ((prefixes & 2) && (x == 0x66)) x = 4; + goto mrm; + case 7: /* VEX c4/c5. */ + if (LJ_32 && p[1] < 0xc0) { + x = 2; + goto mrm; + } + if (x == 0x70) { + x = *++p & 0x1f; + result++; + if (x >= 2) { + p += 2; + result += 2; + goto mrm; + } + } + p++; + result++; + x = map_op2[*++p]; + break; + case 8: result -= (prefixes & 2); /* fallthrough */ + case 9: mrm: /* ModR/M and possibly SIB. */ + result += (x & 15); + x = *++p; + switch (x >> 6) { + case 0: if ((x & 7) == 5) return result + 4; break; + case 1: result++; break; + case 2: result += 4; break; + case 3: return result; + } + if ((x & 7) == 4) { + result++; + if (x < 0x40 && (p[1] & 7) == 5) result += 4; + } + return result; + } + } +} + +/* Patch exit jumps of existing machine code to a new target. */ +void lj_asm_patchexit(jit_State *J, GCtrace *T, ExitNo exitno, MCode *target) +{ + MCode *p = T->mcode; + MCode *mcarea = lj_mcode_patch(J, p, 0); + MSize len = T->szmcode; + MCode *px = exitstub_addr(J, exitno) - 6; + MCode *pe = p+len-6; +#if LJ_GC64 + uint32_t statei = (uint32_t)(GG_OFS(g.vmstate) - GG_OFS(dispatch)); +#else + uint32_t statei = u32ptr(&J2G(J)->vmstate); +#endif + if (len > 5 && p[len-5] == XI_JMP && p+len-6 + *(int32_t *)(p+len-4) == px) + *(int32_t *)(p+len-4) = jmprel(p+len, target); + /* Do not patch parent exit for a stack check. Skip beyond vmstate update. */ + for (; p < pe; p += asm_x86_inslen(p)) { + intptr_t ofs = LJ_GC64 ? (p[0] & 0xf0) == 0x40 : LJ_64; + if (*(uint32_t *)(p+2+ofs) == statei && p[ofs+LJ_GC64-LJ_64] == XI_MOVmi) + break; + } + lua_assert(p < pe); + for (; p < pe; p += asm_x86_inslen(p)) + if ((*(uint16_t *)p & 0xf0ff) == 0x800f && p + *(int32_t *)(p+2) == px) + *(int32_t *)(p+2) = jmprel(p+6, target); + lj_mcode_sync(T->mcode, T->mcode + T->szmcode); + lj_mcode_patch(J, mcarea, 1); +} + diff --git a/lib/LuaJIT/lj_bc.c b/lib/LuaJIT/lj_bc.c new file mode 100644 index 0000000..a597692 --- /dev/null +++ b/lib/LuaJIT/lj_bc.c @@ -0,0 +1,14 @@ +/* +** Bytecode instruction modes. +** Copyright (C) 2005-2017 Mike Pall. See Copyright Notice in luajit.h +*/ + +#define lj_bc_c +#define LUA_CORE + +#include "lj_obj.h" +#include "lj_bc.h" + +/* Bytecode offsets and bytecode instruction modes. */ +#include "lj_bcdef.h" + diff --git a/lib/LuaJIT/lj_bc.h b/lib/LuaJIT/lj_bc.h new file mode 100644 index 0000000..69a45f2 --- /dev/null +++ b/lib/LuaJIT/lj_bc.h @@ -0,0 +1,265 @@ +/* +** Bytecode instruction format. +** Copyright (C) 2005-2017 Mike Pall. See Copyright Notice in luajit.h +*/ + +#ifndef _LJ_BC_H +#define _LJ_BC_H + +#include "lj_def.h" +#include "lj_arch.h" + +/* Bytecode instruction format, 32 bit wide, fields of 8 or 16 bit: +** +** +----+----+----+----+ +** | B | C | A | OP | Format ABC +** +----+----+----+----+ +** | D | A | OP | Format AD +** +-------------------- +** MSB LSB +** +** In-memory instructions are always stored in host byte order. +*/ + +/* Operand ranges and related constants. */ +#define BCMAX_A 0xff +#define BCMAX_B 0xff +#define BCMAX_C 0xff +#define BCMAX_D 0xffff +#define BCBIAS_J 0x8000 +#define NO_REG BCMAX_A +#define NO_JMP (~(BCPos)0) + +/* Macros to get instruction fields. */ +#define bc_op(i) ((BCOp)((i)&0xff)) +#define bc_a(i) ((BCReg)(((i)>>8)&0xff)) +#define bc_b(i) ((BCReg)((i)>>24)) +#define bc_c(i) ((BCReg)(((i)>>16)&0xff)) +#define bc_d(i) ((BCReg)((i)>>16)) +#define bc_j(i) ((ptrdiff_t)bc_d(i)-BCBIAS_J) + +/* Macros to set instruction fields. */ +#define setbc_byte(p, x, ofs) \ + ((uint8_t *)(p))[LJ_ENDIAN_SELECT(ofs, 3-ofs)] = (uint8_t)(x) +#define setbc_op(p, x) setbc_byte(p, (x), 0) +#define setbc_a(p, x) setbc_byte(p, (x), 1) +#define setbc_b(p, x) setbc_byte(p, (x), 3) +#define setbc_c(p, x) setbc_byte(p, (x), 2) +#define setbc_d(p, x) \ + ((uint16_t *)(p))[LJ_ENDIAN_SELECT(1, 0)] = (uint16_t)(x) +#define setbc_j(p, x) setbc_d(p, (BCPos)((int32_t)(x)+BCBIAS_J)) + +/* Macros to compose instructions. */ +#define BCINS_ABC(o, a, b, c) \ + (((BCIns)(o))|((BCIns)(a)<<8)|((BCIns)(b)<<24)|((BCIns)(c)<<16)) +#define BCINS_AD(o, a, d) \ + (((BCIns)(o))|((BCIns)(a)<<8)|((BCIns)(d)<<16)) +#define BCINS_AJ(o, a, j) BCINS_AD(o, a, (BCPos)((int32_t)(j)+BCBIAS_J)) + +/* Bytecode instruction definition. Order matters, see below. +** +** (name, filler, Amode, Bmode, Cmode or Dmode, metamethod) +** +** The opcode name suffixes specify the type for RB/RC or RD: +** V = variable slot +** S = string const +** N = number const +** P = primitive type (~itype) +** B = unsigned byte literal +** M = multiple args/results +*/ +#define BCDEF(_) \ + /* Comparison ops. ORDER OPR. */ \ + _(ISLT, var, ___, var, lt) \ + _(ISGE, var, ___, var, lt) \ + _(ISLE, var, ___, var, le) \ + _(ISGT, var, ___, var, le) \ + \ + _(ISEQV, var, ___, var, eq) \ + _(ISNEV, var, ___, var, eq) \ + _(ISEQS, var, ___, str, eq) \ + _(ISNES, var, ___, str, eq) \ + _(ISEQN, var, ___, num, eq) \ + _(ISNEN, var, ___, num, eq) \ + _(ISEQP, var, ___, pri, eq) \ + _(ISNEP, var, ___, pri, eq) \ + \ + /* Unary test and copy ops. */ \ + _(ISTC, dst, ___, var, ___) \ + _(ISFC, dst, ___, var, ___) \ + _(IST, ___, ___, var, ___) \ + _(ISF, ___, ___, var, ___) \ + _(ISTYPE, var, ___, lit, ___) \ + _(ISNUM, var, ___, lit, ___) \ + \ + /* Unary ops. */ \ + _(MOV, dst, ___, var, ___) \ + _(NOT, dst, ___, var, ___) \ + _(UNM, dst, ___, var, unm) \ + _(LEN, dst, ___, var, len) \ + \ + /* Binary ops. ORDER OPR. VV last, POW must be next. */ \ + _(ADDVN, dst, var, num, add) \ + _(SUBVN, dst, var, num, sub) \ + _(MULVN, dst, var, num, mul) \ + _(DIVVN, dst, var, num, div) \ + _(MODVN, dst, var, num, mod) \ + \ + _(ADDNV, dst, var, num, add) \ + _(SUBNV, dst, var, num, sub) \ + _(MULNV, dst, var, num, mul) \ + _(DIVNV, dst, var, num, div) \ + _(MODNV, dst, var, num, mod) \ + \ + _(ADDVV, dst, var, var, add) \ + _(SUBVV, dst, var, var, sub) \ + _(MULVV, dst, var, var, mul) \ + _(DIVVV, dst, var, var, div) \ + _(MODVV, dst, var, var, mod) \ + \ + _(POW, dst, var, var, pow) \ + _(CAT, dst, rbase, rbase, concat) \ + \ + /* Constant ops. */ \ + _(KSTR, dst, ___, str, ___) \ + _(KCDATA, dst, ___, cdata, ___) \ + _(KSHORT, dst, ___, lits, ___) \ + _(KNUM, dst, ___, num, ___) \ + _(KPRI, dst, ___, pri, ___) \ + _(KNIL, base, ___, base, ___) \ + \ + /* Upvalue and function ops. */ \ + _(UGET, dst, ___, uv, ___) \ + _(USETV, uv, ___, var, ___) \ + _(USETS, uv, ___, str, ___) \ + _(USETN, uv, ___, num, ___) \ + _(USETP, uv, ___, pri, ___) \ + _(UCLO, rbase, ___, jump, ___) \ + _(FNEW, dst, ___, func, gc) \ + \ + /* Table ops. */ \ + _(TNEW, dst, ___, lit, gc) \ + _(TDUP, dst, ___, tab, gc) \ + _(GGET, dst, ___, str, index) \ + _(GSET, var, ___, str, newindex) \ + _(TGETV, dst, var, var, index) \ + _(TGETS, dst, var, str, index) \ + _(TGETB, dst, var, lit, index) \ + _(TGETR, dst, var, var, index) \ + _(TSETV, var, var, var, newindex) \ + _(TSETS, var, var, str, newindex) \ + _(TSETB, var, var, lit, newindex) \ + _(TSETM, base, ___, num, newindex) \ + _(TSETR, var, var, var, newindex) \ + \ + /* Calls and vararg handling. T = tail call. */ \ + _(CALLM, base, lit, lit, call) \ + _(CALL, base, lit, lit, call) \ + _(CALLMT, base, ___, lit, call) \ + _(CALLT, base, ___, lit, call) \ + _(ITERC, base, lit, lit, call) \ + _(ITERN, base, lit, lit, call) \ + _(VARG, base, lit, lit, ___) \ + _(ISNEXT, base, ___, jump, ___) \ + \ + /* Returns. */ \ + _(RETM, base, ___, lit, ___) \ + _(RET, rbase, ___, lit, ___) \ + _(RET0, rbase, ___, lit, ___) \ + _(RET1, rbase, ___, lit, ___) \ + \ + /* Loops and branches. I/J = interp/JIT, I/C/L = init/call/loop. */ \ + _(FORI, base, ___, jump, ___) \ + _(JFORI, base, ___, jump, ___) \ + \ + _(FORL, base, ___, jump, ___) \ + _(IFORL, base, ___, jump, ___) \ + _(JFORL, base, ___, lit, ___) \ + \ + _(ITERL, base, ___, jump, ___) \ + _(IITERL, base, ___, jump, ___) \ + _(JITERL, base, ___, lit, ___) \ + \ + _(LOOP, rbase, ___, jump, ___) \ + _(ILOOP, rbase, ___, jump, ___) \ + _(JLOOP, rbase, ___, lit, ___) \ + \ + _(JMP, rbase, ___, jump, ___) \ + \ + /* Function headers. I/J = interp/JIT, F/V/C = fixarg/vararg/C func. */ \ + _(FUNCF, rbase, ___, ___, ___) \ + _(IFUNCF, rbase, ___, ___, ___) \ + _(JFUNCF, rbase, ___, lit, ___) \ + _(FUNCV, rbase, ___, ___, ___) \ + _(IFUNCV, rbase, ___, ___, ___) \ + _(JFUNCV, rbase, ___, lit, ___) \ + _(FUNCC, rbase, ___, ___, ___) \ + _(FUNCCW, rbase, ___, ___, ___) + +/* Bytecode opcode numbers. */ +typedef enum { +#define BCENUM(name, ma, mb, mc, mt) BC_##name, +BCDEF(BCENUM) +#undef BCENUM + BC__MAX +} BCOp; + +LJ_STATIC_ASSERT((int)BC_ISEQV+1 == (int)BC_ISNEV); +LJ_STATIC_ASSERT(((int)BC_ISEQV^1) == (int)BC_ISNEV); +LJ_STATIC_ASSERT(((int)BC_ISEQS^1) == (int)BC_ISNES); +LJ_STATIC_ASSERT(((int)BC_ISEQN^1) == (int)BC_ISNEN); +LJ_STATIC_ASSERT(((int)BC_ISEQP^1) == (int)BC_ISNEP); +LJ_STATIC_ASSERT(((int)BC_ISLT^1) == (int)BC_ISGE); +LJ_STATIC_ASSERT(((int)BC_ISLE^1) == (int)BC_ISGT); +LJ_STATIC_ASSERT(((int)BC_ISLT^3) == (int)BC_ISGT); +LJ_STATIC_ASSERT((int)BC_IST-(int)BC_ISTC == (int)BC_ISF-(int)BC_ISFC); +LJ_STATIC_ASSERT((int)BC_CALLT-(int)BC_CALL == (int)BC_CALLMT-(int)BC_CALLM); +LJ_STATIC_ASSERT((int)BC_CALLMT + 1 == (int)BC_CALLT); +LJ_STATIC_ASSERT((int)BC_RETM + 1 == (int)BC_RET); +LJ_STATIC_ASSERT((int)BC_FORL + 1 == (int)BC_IFORL); +LJ_STATIC_ASSERT((int)BC_FORL + 2 == (int)BC_JFORL); +LJ_STATIC_ASSERT((int)BC_ITERL + 1 == (int)BC_IITERL); +LJ_STATIC_ASSERT((int)BC_ITERL + 2 == (int)BC_JITERL); +LJ_STATIC_ASSERT((int)BC_LOOP + 1 == (int)BC_ILOOP); +LJ_STATIC_ASSERT((int)BC_LOOP + 2 == (int)BC_JLOOP); +LJ_STATIC_ASSERT((int)BC_FUNCF + 1 == (int)BC_IFUNCF); +LJ_STATIC_ASSERT((int)BC_FUNCF + 2 == (int)BC_JFUNCF); +LJ_STATIC_ASSERT((int)BC_FUNCV + 1 == (int)BC_IFUNCV); +LJ_STATIC_ASSERT((int)BC_FUNCV + 2 == (int)BC_JFUNCV); + +/* This solves a circular dependency problem, change as needed. */ +#define FF_next_N 4 + +/* Stack slots used by FORI/FORL, relative to operand A. */ +enum { + FORL_IDX, FORL_STOP, FORL_STEP, FORL_EXT +}; + +/* Bytecode operand modes. ORDER BCMode */ +typedef enum { + BCMnone, BCMdst, BCMbase, BCMvar, BCMrbase, BCMuv, /* Mode A must be <= 7 */ + BCMlit, BCMlits, BCMpri, BCMnum, BCMstr, BCMtab, BCMfunc, BCMjump, BCMcdata, + BCM_max +} BCMode; +#define BCM___ BCMnone + +#define bcmode_a(op) ((BCMode)(lj_bc_mode[op] & 7)) +#define bcmode_b(op) ((BCMode)((lj_bc_mode[op]>>3) & 15)) +#define bcmode_c(op) ((BCMode)((lj_bc_mode[op]>>7) & 15)) +#define bcmode_d(op) bcmode_c(op) +#define bcmode_hasd(op) ((lj_bc_mode[op] & (15<<3)) == (BCMnone<<3)) +#define bcmode_mm(op) ((MMS)(lj_bc_mode[op]>>11)) + +#define BCMODE(name, ma, mb, mc, mm) \ + (BCM##ma|(BCM##mb<<3)|(BCM##mc<<7)|(MM_##mm<<11)), +#define BCMODE_FF 0 + +static LJ_AINLINE int bc_isret(BCOp op) +{ + return (op == BC_RETM || op == BC_RET || op == BC_RET0 || op == BC_RET1); +} + +LJ_DATA const uint16_t lj_bc_mode[]; +LJ_DATA const uint16_t lj_bc_ofs[]; + +#endif diff --git a/lib/LuaJIT/lj_bc.o b/lib/LuaJIT/lj_bc.o new file mode 100644 index 0000000000000000000000000000000000000000..4c96f44b6135461b46c6c3721681ce0e6a4206c2 GIT binary patch literal 1704 zcmb<-^>JfjWMqH=Mg}_u1P><4z@Wi`U^{@B4h&ojoD9u|Kxhesip`45s?4g*D$Od) zK-jpkabp7zGH+(y3}QDnHfqicnHe)PWoFLI5+qjO%*dIEGc#uv&a6~)V%*BOy>WBn z7RD`18yh#ZZe-osx`lO1%Qi+aM&U+LMpkCtnT(RGlFTz1XLCyPN^>{y%;q%mHu7cy zLJ)1VxoI2IW)?7P+6IA4KrxVL%eIznEI^WF0}yUTkO+Sb49kOI0>cdk1;*8kmP{2) z+nN3_IWm_quVTK-EWl#PlEhNSGLK~k%MBJ0);QKFtfyER*v!}p*_N~2WfNxiWY1;a z!2X%tjH8lc6~`|QH_j~1KF&3qCpjN+@^Tq+b#UF{V&GQd4&|Q8&BPPWQ^?cIBhEXS z_ZzPc-%Y-K{8j=d1mp#M1ic7MU&bT_i#Dps1FZomh+5ak1NC z?BaUjPU0cr&Ej*!H;BI%SCvqeSR}Dn;*SK2g1ds7(psfwN}0-?3fhY8in|qWDK1t% zqim(dtYWE}r}kEjRb4?nUAIa%|p z=2tBP?LE3)`l9;%`uFq=44MqC7<|$V*4v_&tvyRyL}#y#kZ!szgS)e{m4b%5U#O;n zrJkXl0iK8hrDYa|kN>eLVaA%$85o!un6N1aiZkO7N6H*XDwrAAka!3tGXpDv17&dn zX+b80IVR9dC;}CCfbwDD3^`fxNy&Q25IR0LKP45)%ug$3V9+bB%q>Yw0@5W#5IO_K zDoL#;3KwTg& zpofS94%49Ch07`eSs*d=7;u2AKxLroR|iUS05Q4xb%1(7W}>GI2cQ@Tl%Po{fJG66 z36KfWkDgYyK=lbBl!93>_rqvgsD3nM49C#yg~`M8A@i{*JpmQ`096PIBNzq=NkGLx j=@UKdKvFxPf^txGFbds%L7)US{nwy^zA%kY8eKmCbxGF` literal 0 HcmV?d00001 diff --git a/lib/LuaJIT/lj_bc_dyn.o b/lib/LuaJIT/lj_bc_dyn.o new file mode 100644 index 0000000000000000000000000000000000000000..4c96f44b6135461b46c6c3721681ce0e6a4206c2 GIT binary patch literal 1704 zcmb<-^>JfjWMqH=Mg}_u1P><4z@Wi`U^{@B4h&ojoD9u|Kxhesip`45s?4g*D$Od) zK-jpkabp7zGH+(y3}QDnHfqicnHe)PWoFLI5+qjO%*dIEGc#uv&a6~)V%*BOy>WBn z7RD`18yh#ZZe-osx`lO1%Qi+aM&U+LMpkCtnT(RGlFTz1XLCyPN^>{y%;q%mHu7cy zLJ)1VxoI2IW)?7P+6IA4KrxVL%eIznEI^WF0}yUTkO+Sb49kOI0>cdk1;*8kmP{2) z+nN3_IWm_quVTK-EWl#PlEhNSGLK~k%MBJ0);QKFtfyER*v!}p*_N~2WfNxiWY1;a z!2X%tjH8lc6~`|QH_j~1KF&3qCpjN+@^Tq+b#UF{V&GQd4&|Q8&BPPWQ^?cIBhEXS z_ZzPc-%Y-K{8j=d1mp#M1ic7MU&bT_i#Dps1FZomh+5ak1NC z?BaUjPU0cr&Ej*!H;BI%SCvqeSR}Dn;*SK2g1ds7(psfwN}0-?3fhY8in|qWDK1t% zqim(dtYWE}r}kEjRb4?nUAIa%|p z=2tBP?LE3)`l9;%`uFq=44MqC7<|$V*4v_&tvyRyL}#y#kZ!szgS)e{m4b%5U#O;n zrJkXl0iK8hrDYa|kN>eLVaA%$85o!un6N1aiZkO7N6H*XDwrAAka!3tGXpDv17&dn zX+b80IVR9dC;}CCfbwDD3^`fxNy&Q25IR0LKP45)%ug$3V9+bB%q>Yw0@5W#5IO_K zDoL#;3KwTg& zpofS94%49Ch07`eSs*d=7;u2AKxLroR|iUS05Q4xb%1(7W}>GI2cQ@Tl%Po{fJG66 z36KfWkDgYyK=lbBl!93>_rqvgsD3nM49C#yg~`M8A@i{*JpmQ`096PIBNzq=NkGLx j=@UKdKvFxPf^txGFbds%L7)US{nwy^zA%kY8eKmCbxGF` literal 0 HcmV?d00001 diff --git a/lib/LuaJIT/lj_bcdump.h b/lib/LuaJIT/lj_bcdump.h new file mode 100644 index 0000000..fdfc6ec --- /dev/null +++ b/lib/LuaJIT/lj_bcdump.h @@ -0,0 +1,68 @@ +/* +** Bytecode dump definitions. +** Copyright (C) 2005-2017 Mike Pall. See Copyright Notice in luajit.h +*/ + +#ifndef _LJ_BCDUMP_H +#define _LJ_BCDUMP_H + +#include "lj_obj.h" +#include "lj_lex.h" + +/* -- Bytecode dump format ------------------------------------------------ */ + +/* +** dump = header proto+ 0U +** header = ESC 'L' 'J' versionB flagsU [namelenU nameB*] +** proto = lengthU pdata +** pdata = phead bcinsW* uvdataH* kgc* knum* [debugB*] +** phead = flagsB numparamsB framesizeB numuvB numkgcU numknU numbcU +** [debuglenU [firstlineU numlineU]] +** kgc = kgctypeU { ktab | (loU hiU) | (rloU rhiU iloU ihiU) | strB* } +** knum = intU0 | (loU1 hiU) +** ktab = narrayU nhashU karray* khash* +** karray = ktabk +** khash = ktabk ktabk +** ktabk = ktabtypeU { intU | (loU hiU) | strB* } +** +** B = 8 bit, H = 16 bit, W = 32 bit, U = ULEB128 of W, U0/U1 = ULEB128 of W+1 +*/ + +/* Bytecode dump header. */ +#define BCDUMP_HEAD1 0x1b +#define BCDUMP_HEAD2 0x4c +#define BCDUMP_HEAD3 0x4a + +/* If you perform *any* kind of private modifications to the bytecode itself +** or to the dump format, you *must* set BCDUMP_VERSION to 0x80 or higher. +*/ +#define BCDUMP_VERSION 2 + +/* Compatibility flags. */ +#define BCDUMP_F_BE 0x01 +#define BCDUMP_F_STRIP 0x02 +#define BCDUMP_F_FFI 0x04 +#define BCDUMP_F_FR2 0x08 + +#define BCDUMP_F_KNOWN (BCDUMP_F_FR2*2-1) + +/* Type codes for the GC constants of a prototype. Plus length for strings. */ +enum { + BCDUMP_KGC_CHILD, BCDUMP_KGC_TAB, BCDUMP_KGC_I64, BCDUMP_KGC_U64, + BCDUMP_KGC_COMPLEX, BCDUMP_KGC_STR +}; + +/* Type codes for the keys/values of a constant table. */ +enum { + BCDUMP_KTAB_NIL, BCDUMP_KTAB_FALSE, BCDUMP_KTAB_TRUE, + BCDUMP_KTAB_INT, BCDUMP_KTAB_NUM, BCDUMP_KTAB_STR +}; + +/* -- Bytecode reader/writer ---------------------------------------------- */ + +LJ_FUNC int lj_bcwrite(lua_State *L, GCproto *pt, lua_Writer writer, + void *data, int strip); +LJ_FUNC GCproto *lj_bcread_proto(LexState *ls); +LJ_FUNC GCproto *lj_bcread(LexState *ls); + +#endif diff --git a/lib/LuaJIT/lj_bcread.c b/lib/LuaJIT/lj_bcread.c new file mode 100644 index 0000000..48c5e7c --- /dev/null +++ b/lib/LuaJIT/lj_bcread.c @@ -0,0 +1,457 @@ +/* +** Bytecode reader. +** Copyright (C) 2005-2017 Mike Pall. See Copyright Notice in luajit.h +*/ + +#define lj_bcread_c +#define LUA_CORE + +#include "lj_obj.h" +#include "lj_gc.h" +#include "lj_err.h" +#include "lj_buf.h" +#include "lj_str.h" +#include "lj_tab.h" +#include "lj_bc.h" +#if LJ_HASFFI +#include "lj_ctype.h" +#include "lj_cdata.h" +#include "lualib.h" +#endif +#include "lj_lex.h" +#include "lj_bcdump.h" +#include "lj_state.h" +#include "lj_strfmt.h" + +/* Reuse some lexer fields for our own purposes. */ +#define bcread_flags(ls) ls->level +#define bcread_swap(ls) \ + ((bcread_flags(ls) & BCDUMP_F_BE) != LJ_BE*BCDUMP_F_BE) +#define bcread_oldtop(L, ls) restorestack(L, ls->lastline) +#define bcread_savetop(L, ls, top) \ + ls->lastline = (BCLine)savestack(L, (top)) + +/* -- Input buffer handling ----------------------------------------------- */ + +/* Throw reader error. */ +static LJ_NOINLINE void bcread_error(LexState *ls, ErrMsg em) +{ + lua_State *L = ls->L; + const char *name = ls->chunkarg; + if (*name == BCDUMP_HEAD1) name = "(binary)"; + else if (*name == '@' || *name == '=') name++; + lj_strfmt_pushf(L, "%s: %s", name, err2msg(em)); + lj_err_throw(L, LUA_ERRSYNTAX); +} + +/* Refill buffer. */ +static LJ_NOINLINE void bcread_fill(LexState *ls, MSize len, int need) +{ + lua_assert(len != 0); + if (len > LJ_MAX_BUF || ls->c < 0) + bcread_error(ls, LJ_ERR_BCBAD); + do { + const char *buf; + size_t sz; + char *p = sbufB(&ls->sb); + MSize n = (MSize)(ls->pe - ls->p); + if (n) { /* Copy remainder to buffer. */ + if (sbuflen(&ls->sb)) { /* Move down in buffer. */ + lua_assert(ls->pe == sbufP(&ls->sb)); + if (ls->p != p) memmove(p, ls->p, n); + } else { /* Copy from buffer provided by reader. */ + p = lj_buf_need(&ls->sb, len); + memcpy(p, ls->p, n); + } + ls->p = p; + ls->pe = p + n; + } + setsbufP(&ls->sb, p + n); + buf = ls->rfunc(ls->L, ls->rdata, &sz); /* Get more data from reader. */ + if (buf == NULL || sz == 0) { /* EOF? */ + if (need) bcread_error(ls, LJ_ERR_BCBAD); + ls->c = -1; /* Only bad if we get called again. */ + break; + } + if (n) { /* Append to buffer. */ + n += (MSize)sz; + p = lj_buf_need(&ls->sb, n < len ? len : n); + memcpy(sbufP(&ls->sb), buf, sz); + setsbufP(&ls->sb, p + n); + ls->p = p; + ls->pe = p + n; + } else { /* Return buffer provided by reader. */ + ls->p = buf; + ls->pe = buf + sz; + } + } while (ls->p + len > ls->pe); +} + +/* Need a certain number of bytes. */ +static LJ_AINLINE void bcread_need(LexState *ls, MSize len) +{ + if (LJ_UNLIKELY(ls->p + len > ls->pe)) + bcread_fill(ls, len, 1); +} + +/* Want to read up to a certain number of bytes, but may need less. */ +static LJ_AINLINE void bcread_want(LexState *ls, MSize len) +{ + if (LJ_UNLIKELY(ls->p + len > ls->pe)) + bcread_fill(ls, len, 0); +} + +/* Return memory block from buffer. */ +static LJ_AINLINE uint8_t *bcread_mem(LexState *ls, MSize len) +{ + uint8_t *p = (uint8_t *)ls->p; + ls->p += len; + lua_assert(ls->p <= ls->pe); + return p; +} + +/* Copy memory block from buffer. */ +static void bcread_block(LexState *ls, void *q, MSize len) +{ + memcpy(q, bcread_mem(ls, len), len); +} + +/* Read byte from buffer. */ +static LJ_AINLINE uint32_t bcread_byte(LexState *ls) +{ + lua_assert(ls->p < ls->pe); + return (uint32_t)(uint8_t)*ls->p++; +} + +/* Read ULEB128 value from buffer. */ +static LJ_AINLINE uint32_t bcread_uleb128(LexState *ls) +{ + uint32_t v = lj_buf_ruleb128(&ls->p); + lua_assert(ls->p <= ls->pe); + return v; +} + +/* Read top 32 bits of 33 bit ULEB128 value from buffer. */ +static uint32_t bcread_uleb128_33(LexState *ls) +{ + const uint8_t *p = (const uint8_t *)ls->p; + uint32_t v = (*p++ >> 1); + if (LJ_UNLIKELY(v >= 0x40)) { + int sh = -1; + v &= 0x3f; + do { + v |= ((*p & 0x7f) << (sh += 7)); + } while (*p++ >= 0x80); + } + ls->p = (char *)p; + lua_assert(ls->p <= ls->pe); + return v; +} + +/* -- Bytecode reader ----------------------------------------------------- */ + +/* Read debug info of a prototype. */ +static void bcread_dbg(LexState *ls, GCproto *pt, MSize sizedbg) +{ + void *lineinfo = (void *)proto_lineinfo(pt); + bcread_block(ls, lineinfo, sizedbg); + /* Swap lineinfo if the endianess differs. */ + if (bcread_swap(ls) && pt->numline >= 256) { + MSize i, n = pt->sizebc-1; + if (pt->numline < 65536) { + uint16_t *p = (uint16_t *)lineinfo; + for (i = 0; i < n; i++) p[i] = (uint16_t)((p[i] >> 8)|(p[i] << 8)); + } else { + uint32_t *p = (uint32_t *)lineinfo; + for (i = 0; i < n; i++) p[i] = lj_bswap(p[i]); + } + } +} + +/* Find pointer to varinfo. */ +static const void *bcread_varinfo(GCproto *pt) +{ + const uint8_t *p = proto_uvinfo(pt); + MSize n = pt->sizeuv; + if (n) while (*p++ || --n) ; + return p; +} + +/* Read a single constant key/value of a template table. */ +static void bcread_ktabk(LexState *ls, TValue *o) +{ + MSize tp = bcread_uleb128(ls); + if (tp >= BCDUMP_KTAB_STR) { + MSize len = tp - BCDUMP_KTAB_STR; + const char *p = (const char *)bcread_mem(ls, len); + setstrV(ls->L, o, lj_str_new(ls->L, p, len)); + } else if (tp == BCDUMP_KTAB_INT) { + setintV(o, (int32_t)bcread_uleb128(ls)); + } else if (tp == BCDUMP_KTAB_NUM) { + o->u32.lo = bcread_uleb128(ls); + o->u32.hi = bcread_uleb128(ls); + } else { + lua_assert(tp <= BCDUMP_KTAB_TRUE); + setpriV(o, ~tp); + } +} + +/* Read a template table. */ +static GCtab *bcread_ktab(LexState *ls) +{ + MSize narray = bcread_uleb128(ls); + MSize nhash = bcread_uleb128(ls); + GCtab *t = lj_tab_new(ls->L, narray, hsize2hbits(nhash)); + if (narray) { /* Read array entries. */ + MSize i; + TValue *o = tvref(t->array); + for (i = 0; i < narray; i++, o++) + bcread_ktabk(ls, o); + } + if (nhash) { /* Read hash entries. */ + MSize i; + for (i = 0; i < nhash; i++) { + TValue key; + bcread_ktabk(ls, &key); + lua_assert(!tvisnil(&key)); + bcread_ktabk(ls, lj_tab_set(ls->L, t, &key)); + } + } + return t; +} + +/* Read GC constants of a prototype. */ +static void bcread_kgc(LexState *ls, GCproto *pt, MSize sizekgc) +{ + MSize i; + GCRef *kr = mref(pt->k, GCRef) - (ptrdiff_t)sizekgc; + for (i = 0; i < sizekgc; i++, kr++) { + MSize tp = bcread_uleb128(ls); + if (tp >= BCDUMP_KGC_STR) { + MSize len = tp - BCDUMP_KGC_STR; + const char *p = (const char *)bcread_mem(ls, len); + setgcref(*kr, obj2gco(lj_str_new(ls->L, p, len))); + } else if (tp == BCDUMP_KGC_TAB) { + setgcref(*kr, obj2gco(bcread_ktab(ls))); +#if LJ_HASFFI + } else if (tp != BCDUMP_KGC_CHILD) { + CTypeID id = tp == BCDUMP_KGC_COMPLEX ? CTID_COMPLEX_DOUBLE : + tp == BCDUMP_KGC_I64 ? CTID_INT64 : CTID_UINT64; + CTSize sz = tp == BCDUMP_KGC_COMPLEX ? 16 : 8; + GCcdata *cd = lj_cdata_new_(ls->L, id, sz); + TValue *p = (TValue *)cdataptr(cd); + setgcref(*kr, obj2gco(cd)); + p[0].u32.lo = bcread_uleb128(ls); + p[0].u32.hi = bcread_uleb128(ls); + if (tp == BCDUMP_KGC_COMPLEX) { + p[1].u32.lo = bcread_uleb128(ls); + p[1].u32.hi = bcread_uleb128(ls); + } +#endif + } else { + lua_State *L = ls->L; + lua_assert(tp == BCDUMP_KGC_CHILD); + if (L->top <= bcread_oldtop(L, ls)) /* Stack underflow? */ + bcread_error(ls, LJ_ERR_BCBAD); + L->top--; + setgcref(*kr, obj2gco(protoV(L->top))); + } + } +} + +/* Read number constants of a prototype. */ +static void bcread_knum(LexState *ls, GCproto *pt, MSize sizekn) +{ + MSize i; + TValue *o = mref(pt->k, TValue); + for (i = 0; i < sizekn; i++, o++) { + int isnum = (ls->p[0] & 1); + uint32_t lo = bcread_uleb128_33(ls); + if (isnum) { + o->u32.lo = lo; + o->u32.hi = bcread_uleb128(ls); + } else { + setintV(o, lo); + } + } +} + +/* Read bytecode instructions. */ +static void bcread_bytecode(LexState *ls, GCproto *pt, MSize sizebc) +{ + BCIns *bc = proto_bc(pt); + bc[0] = BCINS_AD((pt->flags & PROTO_VARARG) ? BC_FUNCV : BC_FUNCF, + pt->framesize, 0); + bcread_block(ls, bc+1, (sizebc-1)*(MSize)sizeof(BCIns)); + /* Swap bytecode instructions if the endianess differs. */ + if (bcread_swap(ls)) { + MSize i; + for (i = 1; i < sizebc; i++) bc[i] = lj_bswap(bc[i]); + } +} + +/* Read upvalue refs. */ +static void bcread_uv(LexState *ls, GCproto *pt, MSize sizeuv) +{ + if (sizeuv) { + uint16_t *uv = proto_uv(pt); + bcread_block(ls, uv, sizeuv*2); + /* Swap upvalue refs if the endianess differs. */ + if (bcread_swap(ls)) { + MSize i; + for (i = 0; i < sizeuv; i++) + uv[i] = (uint16_t)((uv[i] >> 8)|(uv[i] << 8)); + } + } +} + +/* Read a prototype. */ +GCproto *lj_bcread_proto(LexState *ls) +{ + GCproto *pt; + MSize framesize, numparams, flags, sizeuv, sizekgc, sizekn, sizebc, sizept; + MSize ofsk, ofsuv, ofsdbg; + MSize sizedbg = 0; + BCLine firstline = 0, numline = 0; + + /* Read prototype header. */ + flags = bcread_byte(ls); + numparams = bcread_byte(ls); + framesize = bcread_byte(ls); + sizeuv = bcread_byte(ls); + sizekgc = bcread_uleb128(ls); + sizekn = bcread_uleb128(ls); + sizebc = bcread_uleb128(ls) + 1; + if (!(bcread_flags(ls) & BCDUMP_F_STRIP)) { + sizedbg = bcread_uleb128(ls); + if (sizedbg) { + firstline = bcread_uleb128(ls); + numline = bcread_uleb128(ls); + } + } + + /* Calculate total size of prototype including all colocated arrays. */ + sizept = (MSize)sizeof(GCproto) + + sizebc*(MSize)sizeof(BCIns) + + sizekgc*(MSize)sizeof(GCRef); + sizept = (sizept + (MSize)sizeof(TValue)-1) & ~((MSize)sizeof(TValue)-1); + ofsk = sizept; sizept += sizekn*(MSize)sizeof(TValue); + ofsuv = sizept; sizept += ((sizeuv+1)&~1)*2; + ofsdbg = sizept; sizept += sizedbg; + + /* Allocate prototype object and initialize its fields. */ + pt = (GCproto *)lj_mem_newgco(ls->L, (MSize)sizept); + pt->gct = ~LJ_TPROTO; + pt->numparams = (uint8_t)numparams; + pt->framesize = (uint8_t)framesize; + pt->sizebc = sizebc; + setmref(pt->k, (char *)pt + ofsk); + setmref(pt->uv, (char *)pt + ofsuv); + pt->sizekgc = 0; /* Set to zero until fully initialized. */ + pt->sizekn = sizekn; + pt->sizept = sizept; + pt->sizeuv = (uint8_t)sizeuv; + pt->flags = (uint8_t)flags; + pt->trace = 0; + setgcref(pt->chunkname, obj2gco(ls->chunkname)); + + /* Close potentially uninitialized gap between bc and kgc. */ + *(uint32_t *)((char *)pt + ofsk - sizeof(GCRef)*(sizekgc+1)) = 0; + + /* Read bytecode instructions and upvalue refs. */ + bcread_bytecode(ls, pt, sizebc); + bcread_uv(ls, pt, sizeuv); + + /* Read constants. */ + bcread_kgc(ls, pt, sizekgc); + pt->sizekgc = sizekgc; + bcread_knum(ls, pt, sizekn); + + /* Read and initialize debug info. */ + pt->firstline = firstline; + pt->numline = numline; + if (sizedbg) { + MSize sizeli = (sizebc-1) << (numline < 256 ? 0 : numline < 65536 ? 1 : 2); + setmref(pt->lineinfo, (char *)pt + ofsdbg); + setmref(pt->uvinfo, (char *)pt + ofsdbg + sizeli); + bcread_dbg(ls, pt, sizedbg); + setmref(pt->varinfo, bcread_varinfo(pt)); + } else { + setmref(pt->lineinfo, NULL); + setmref(pt->uvinfo, NULL); + setmref(pt->varinfo, NULL); + } + return pt; +} + +/* Read and check header of bytecode dump. */ +static int bcread_header(LexState *ls) +{ + uint32_t flags; + bcread_want(ls, 3+5+5); + if (bcread_byte(ls) != BCDUMP_HEAD2 || + bcread_byte(ls) != BCDUMP_HEAD3 || + bcread_byte(ls) != BCDUMP_VERSION) return 0; + bcread_flags(ls) = flags = bcread_uleb128(ls); + if ((flags & ~(BCDUMP_F_KNOWN)) != 0) return 0; + if ((flags & BCDUMP_F_FR2) != LJ_FR2*BCDUMP_F_FR2) return 0; + if ((flags & BCDUMP_F_FFI)) { +#if LJ_HASFFI + lua_State *L = ls->L; + if (!ctype_ctsG(G(L))) { + ptrdiff_t oldtop = savestack(L, L->top); + luaopen_ffi(L); /* Load FFI library on-demand. */ + L->top = restorestack(L, oldtop); + } +#else + return 0; +#endif + } + if ((flags & BCDUMP_F_STRIP)) { + ls->chunkname = lj_str_newz(ls->L, ls->chunkarg); + } else { + MSize len = bcread_uleb128(ls); + bcread_need(ls, len); + ls->chunkname = lj_str_new(ls->L, (const char *)bcread_mem(ls, len), len); + } + return 1; /* Ok. */ +} + +/* Read a bytecode dump. */ +GCproto *lj_bcread(LexState *ls) +{ + lua_State *L = ls->L; + lua_assert(ls->c == BCDUMP_HEAD1); + bcread_savetop(L, ls, L->top); + lj_buf_reset(&ls->sb); + /* Check for a valid bytecode dump header. */ + if (!bcread_header(ls)) + bcread_error(ls, LJ_ERR_BCFMT); + for (;;) { /* Process all prototypes in the bytecode dump. */ + GCproto *pt; + MSize len; + const char *startp; + /* Read length. */ + if (ls->p < ls->pe && ls->p[0] == 0) { /* Shortcut EOF. */ + ls->p++; + break; + } + bcread_want(ls, 5); + len = bcread_uleb128(ls); + if (!len) break; /* EOF */ + bcread_need(ls, len); + startp = ls->p; + pt = lj_bcread_proto(ls); + if (ls->p != startp + len) + bcread_error(ls, LJ_ERR_BCBAD); + setprotoV(L, L->top, pt); + incr_top(L); + } + if ((int32_t)(2*(uint32_t)(ls->pe - ls->p)) > 0 || + L->top-1 != bcread_oldtop(L, ls)) + bcread_error(ls, LJ_ERR_BCBAD); + /* Pop off last prototype. */ + L->top--; + return protoV(L->top); +} + diff --git a/lib/LuaJIT/lj_bcread.o b/lib/LuaJIT/lj_bcread.o new file mode 100644 index 0000000000000000000000000000000000000000..ad47a6aad8d6213bf93c5a936e5b313c28cf871e GIT binary patch literal 7152 zcmbVQe{dXC9pAmXw%0b?9$Fwsw5M({VK9>*Vgf;KlS}iqY)G{!1t}b9b7_#I$=vOc zqDYH-*Y>d-Mo0fpnXx)L{+vV1i-9~OxWG&;T z)-Zm?;IB;4rd0@xTwoRa=Ixl7*Svk`7TzAXRa?Jzl-a3BZuJ>ve>KQ*feRULiwKTe z_O}Pl6pKaPE)iXyvOFWSxDxTjR*BS+r7Vw0lDprh93Q*oq;lEVN#*==BCx+!*}ahQ z_xRIBZUG5wxx`$m^Ajym^YWH!&03cGETN1aH~2*381tV;OQB|uzqBD1x-}+GA6Ax2 z+B%&VwHvjyTKC#D%zCYfcdJ$Rot28kt;&vv5IK8YL-w$;^P8BmTt^GDCN{9oN3xkrSW)+Ay0Mk zp@`1owXAt>>UN`UTC=-i8h?pIrV)CrxvRyD8_jw1e8!(L_(_AmAp|f!uJf)4naaCr z^9E?o<%i{J!aASSc}Ij#L5CwhxroKFMNMcc6K4{xoOw!fnRdWvOYP(Sw|iri4EOQun93>!-rk@thaiL0PE z3OT9eVhh5#W!Xi*+H$e0eAdyziWwAYuIeBPk>#!oA~aF{HaAtnHr0rHmi)!4L0F*d zHq{!0j5QYbMHej6`Qs!tx^s9T?ZD{%AQp>oGQ;3*(5PggJE}n{+=TCjd_>#1UR5aidvAPLue_$E8rYXB) z>I&}tQT^>*a1p@21{g^RWWOCfL*g*4V$0ArobWluKOvv=$@ogka(lP z+hzny`Y(nJ%Y}2~ho~C}tI+#lquN5jDya7ihd)mCrdKCxv0FKQ!J@ta# zJ6keOre7ENMs1?HNMsX=BzqpbAX8fMuOsh>qrP!nuid4hb5z4z)_h3WekGKkMHK94 z0o|jT`|~T#&h{rKts?iO0)e<1s-%A)E{{Sg%1JgC=T;Y$#|~Q)G9^9cbTkxDd-ioD zdjM3ZHOAMgwJ|;&LqXS|HH&-kw84wkUU}+Eg<_F-MMFO((u5tx_f18yqcy)c@(i;D z|0nYqw8Rqs9y+dqnDWaXr?UM$)icg?OkEh`pL92AUCO@j1r_Jr35x~MEX!XC>10Xc z;W9rm+t9d0if{$Pmj?Kv@kxW9V0=KW?c~Q8|63=Y(fNMH-@uixS(7rOqWjw0#bT^^ zdSnXis>a|2h6sTE74oQ|lfR|&ec97W_BjMTe+iU~@@F5xr)V9!uH+{gwq%VjqC$`H z3G3+AXO1p@wmmbpD*^@4%%{6S5XBnWBee? zwS;uOHDbLJOszRR9#FDBg^@Voin9AE>!>ffvq8!JnCvcHFz8l`@xIb>Hx^m0d$rH4 z_r7rntz^u{nkUk4=8r+4Q$Kdd;6Y|@MeW`bEU?^`APyI^UMaA;cUaw%^3&sa|C{V5 z(?2NDBg&cQW)N8CL{)vEgfl)dtKwk#ME-XmvM=Oed)*B04$kD)BOA)Y>(2IJbvAzk zR&aH=bVFZv8s_xXyS6IJqj-On9$z}25$`XU7!TTQIC!aQR=3aYi1CqLPVX*pg`dZo z*VQUt|ABaot(J8(j>xtqBds7`zS3%oyvn>-NB!VwaE}^%^egl>m^?+1$2kI>-=XvQ z($DF+0qQ!XoN7Fy(&dd({W*?OG5yHT0_H`X5Eazyl|g$Oy`(A0WB6*;%edt4#H&lMjfx53?!G#x` zl5HlB?KRchXSYR|H4)t3Izu)xzNQ+vR;;~X(@{)#4Ia2_@wR3OW&&c?_yQoQ9gT+} zbPv6mr-BHE>oe@gZlRPp?{gtSsKu*J9>exG_K+B;);Gq~UOR5q-$}bT%L?P^7 zB5wIViQY=~Bg=?gen5Cz$`qv+QJY+q3)Jh^!0C}S6zGT({a0s0pSK{#Qw`#!`0$(5 zo>r=z_Px~xe_OBHFBk|iE_JV1X8D*uGE;HCc;dp<#cGyd=~FNDI}1 zq%~9x7TBRLHRr`!my$0cfcXB9p0X8nw3l@3lKmfF2w|}~O4@N)C5B%U*^2Qnd??9- zAzquod(P&l|FQXP)=1pEgTx1<|I%w>&hXPYpJ(_bya47Ien=`GRQo=U9Fb#($S;Y1 zy1tL)BM$v82NgNQyxzf2IJn56Gxtjr?771)1?PZQ3uLa|>l}RX91aQIE}Vm+g-T({ zN!jSedOkc}0UxY@f2{(3KjHY5k6i%f!|C>%5C1jc^Vt7K;2-kI(wMU+>KNFcE6_h* z0e`syPWdDG!%Hy15Ot03yZFs#C;b=Be0E+yI0+~p17D7B*qBW8^ww{bN|X4VX3vH@ z-9;jq97@*TkxKTUIXMJy(*{Z%&NR6awNs z0|SGpP0mK&pcx-dr~3QcU9&$qG$O@2jILJAh%Iq-{@l7xS zvyFGcp>)sCaAI@3ukQ{wTpw~KbYesJ;q@X0)in*m75rShmq3hi`k@j$2hUW%f92p* zUeuIa`4b+z%7Z`c!71M^{j&~E_MGp*|Kq{QN0**nN;HU`?g$q@;=wQQ;O$QRm(zdM zgJ0;O-&FyxaatwG@y5Bf0#5(9qESx&3lC2JgmLZPLWCIQc3$eVIKsX3Q4j95r?Uc{ zsDN+x;NCh};o3CCE?v2C! z9{eJYoYy?Kmp(&>7K8MD6hBw*k36`Se!mCz+Eei0AM?nGU`~UMnRC>{dRY9O#dkUD zon`n5XZ>0k?%s1PP9JdPyZ4h+pSpVx%@$uXS^D<7vt(kRr`}9#F{S$69JXxxn7eEB;>hZkc@=_B$-ye>cWL>eT%TsPriI-D3&&L$my nI>+Vw)TYXF2S_T=<&5$1GW*Mx;trpWpE(Bhm&Ba=Th9MKc(HnW literal 0 HcmV?d00001 diff --git a/lib/LuaJIT/lj_bcread_dyn.o b/lib/LuaJIT/lj_bcread_dyn.o new file mode 100644 index 0000000000000000000000000000000000000000..ad47a6aad8d6213bf93c5a936e5b313c28cf871e GIT binary patch literal 7152 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 literal 0 HcmV?d00001 diff --git a/lib/LuaJIT/lj_bcwrite.c b/lib/LuaJIT/lj_bcwrite.c new file mode 100644 index 0000000..5e05cae --- /dev/null +++ b/lib/LuaJIT/lj_bcwrite.c @@ -0,0 +1,361 @@ +/* +** Bytecode writer. +** Copyright (C) 2005-2017 Mike Pall. See Copyright Notice in luajit.h +*/ + +#define lj_bcwrite_c +#define LUA_CORE + +#include "lj_obj.h" +#include "lj_gc.h" +#include "lj_buf.h" +#include "lj_bc.h" +#if LJ_HASFFI +#include "lj_ctype.h" +#endif +#if LJ_HASJIT +#include "lj_dispatch.h" +#include "lj_jit.h" +#endif +#include "lj_strfmt.h" +#include "lj_bcdump.h" +#include "lj_vm.h" + +/* Context for bytecode writer. */ +typedef struct BCWriteCtx { + SBuf sb; /* Output buffer. */ + GCproto *pt; /* Root prototype. */ + lua_Writer wfunc; /* Writer callback. */ + void *wdata; /* Writer callback data. */ + int strip; /* Strip debug info. */ + int status; /* Status from writer callback. */ +} BCWriteCtx; + +/* -- Bytecode writer ----------------------------------------------------- */ + +/* Write a single constant key/value of a template table. */ +static void bcwrite_ktabk(BCWriteCtx *ctx, cTValue *o, int narrow) +{ + char *p = lj_buf_more(&ctx->sb, 1+10); + if (tvisstr(o)) { + const GCstr *str = strV(o); + MSize len = str->len; + p = lj_buf_more(&ctx->sb, 5+len); + p = lj_strfmt_wuleb128(p, BCDUMP_KTAB_STR+len); + p = lj_buf_wmem(p, strdata(str), len); + } else if (tvisint(o)) { + *p++ = BCDUMP_KTAB_INT; + p = lj_strfmt_wuleb128(p, intV(o)); + } else if (tvisnum(o)) { + if (!LJ_DUALNUM && narrow) { /* Narrow number constants to integers. */ + lua_Number num = numV(o); + int32_t k = lj_num2int(num); + if (num == (lua_Number)k) { /* -0 is never a constant. */ + *p++ = BCDUMP_KTAB_INT; + p = lj_strfmt_wuleb128(p, k); + setsbufP(&ctx->sb, p); + return; + } + } + *p++ = BCDUMP_KTAB_NUM; + p = lj_strfmt_wuleb128(p, o->u32.lo); + p = lj_strfmt_wuleb128(p, o->u32.hi); + } else { + lua_assert(tvispri(o)); + *p++ = BCDUMP_KTAB_NIL+~itype(o); + } + setsbufP(&ctx->sb, p); +} + +/* Write a template table. */ +static void bcwrite_ktab(BCWriteCtx *ctx, char *p, const GCtab *t) +{ + MSize narray = 0, nhash = 0; + if (t->asize > 0) { /* Determine max. length of array part. */ + ptrdiff_t i; + TValue *array = tvref(t->array); + for (i = (ptrdiff_t)t->asize-1; i >= 0; i--) + if (!tvisnil(&array[i])) + break; + narray = (MSize)(i+1); + } + if (t->hmask > 0) { /* Count number of used hash slots. */ + MSize i, hmask = t->hmask; + Node *node = noderef(t->node); + for (i = 0; i <= hmask; i++) + nhash += !tvisnil(&node[i].val); + } + /* Write number of array slots and hash slots. */ + p = lj_strfmt_wuleb128(p, narray); + p = lj_strfmt_wuleb128(p, nhash); + setsbufP(&ctx->sb, p); + if (narray) { /* Write array entries (may contain nil). */ + MSize i; + TValue *o = tvref(t->array); + for (i = 0; i < narray; i++, o++) + bcwrite_ktabk(ctx, o, 1); + } + if (nhash) { /* Write hash entries. */ + MSize i = nhash; + Node *node = noderef(t->node) + t->hmask; + for (;; node--) + if (!tvisnil(&node->val)) { + bcwrite_ktabk(ctx, &node->key, 0); + bcwrite_ktabk(ctx, &node->val, 1); + if (--i == 0) break; + } + } +} + +/* Write GC constants of a prototype. */ +static void bcwrite_kgc(BCWriteCtx *ctx, GCproto *pt) +{ + MSize i, sizekgc = pt->sizekgc; + GCRef *kr = mref(pt->k, GCRef) - (ptrdiff_t)sizekgc; + for (i = 0; i < sizekgc; i++, kr++) { + GCobj *o = gcref(*kr); + MSize tp, need = 1; + char *p; + /* Determine constant type and needed size. */ + if (o->gch.gct == ~LJ_TSTR) { + tp = BCDUMP_KGC_STR + gco2str(o)->len; + need = 5+gco2str(o)->len; + } else if (o->gch.gct == ~LJ_TPROTO) { + lua_assert((pt->flags & PROTO_CHILD)); + tp = BCDUMP_KGC_CHILD; +#if LJ_HASFFI + } else if (o->gch.gct == ~LJ_TCDATA) { + CTypeID id = gco2cd(o)->ctypeid; + need = 1+4*5; + if (id == CTID_INT64) { + tp = BCDUMP_KGC_I64; + } else if (id == CTID_UINT64) { + tp = BCDUMP_KGC_U64; + } else { + lua_assert(id == CTID_COMPLEX_DOUBLE); + tp = BCDUMP_KGC_COMPLEX; + } +#endif + } else { + lua_assert(o->gch.gct == ~LJ_TTAB); + tp = BCDUMP_KGC_TAB; + need = 1+2*5; + } + /* Write constant type. */ + p = lj_buf_more(&ctx->sb, need); + p = lj_strfmt_wuleb128(p, tp); + /* Write constant data (if any). */ + if (tp >= BCDUMP_KGC_STR) { + p = lj_buf_wmem(p, strdata(gco2str(o)), gco2str(o)->len); + } else if (tp == BCDUMP_KGC_TAB) { + bcwrite_ktab(ctx, p, gco2tab(o)); + continue; +#if LJ_HASFFI + } else if (tp != BCDUMP_KGC_CHILD) { + cTValue *q = (TValue *)cdataptr(gco2cd(o)); + p = lj_strfmt_wuleb128(p, q[0].u32.lo); + p = lj_strfmt_wuleb128(p, q[0].u32.hi); + if (tp == BCDUMP_KGC_COMPLEX) { + p = lj_strfmt_wuleb128(p, q[1].u32.lo); + p = lj_strfmt_wuleb128(p, q[1].u32.hi); + } +#endif + } + setsbufP(&ctx->sb, p); + } +} + +/* Write number constants of a prototype. */ +static void bcwrite_knum(BCWriteCtx *ctx, GCproto *pt) +{ + MSize i, sizekn = pt->sizekn; + cTValue *o = mref(pt->k, TValue); + char *p = lj_buf_more(&ctx->sb, 10*sizekn); + for (i = 0; i < sizekn; i++, o++) { + int32_t k; + if (tvisint(o)) { + k = intV(o); + goto save_int; + } else { + /* Write a 33 bit ULEB128 for the int (lsb=0) or loword (lsb=1). */ + if (!LJ_DUALNUM) { /* Narrow number constants to integers. */ + lua_Number num = numV(o); + k = lj_num2int(num); + if (num == (lua_Number)k) { /* -0 is never a constant. */ + save_int: + p = lj_strfmt_wuleb128(p, 2*(uint32_t)k | ((uint32_t)k&0x80000000u)); + if (k < 0) + p[-1] = (p[-1] & 7) | ((k>>27) & 0x18); + continue; + } + } + p = lj_strfmt_wuleb128(p, 1+(2*o->u32.lo | (o->u32.lo & 0x80000000u))); + if (o->u32.lo >= 0x80000000u) + p[-1] = (p[-1] & 7) | ((o->u32.lo>>27) & 0x18); + p = lj_strfmt_wuleb128(p, o->u32.hi); + } + } + setsbufP(&ctx->sb, p); +} + +/* Write bytecode instructions. */ +static char *bcwrite_bytecode(BCWriteCtx *ctx, char *p, GCproto *pt) +{ + MSize nbc = pt->sizebc-1; /* Omit the [JI]FUNC* header. */ +#if LJ_HASJIT + uint8_t *q = (uint8_t *)p; +#endif + p = lj_buf_wmem(p, proto_bc(pt)+1, nbc*(MSize)sizeof(BCIns)); + UNUSED(ctx); +#if LJ_HASJIT + /* Unpatch modified bytecode containing ILOOP/JLOOP etc. */ + if ((pt->flags & PROTO_ILOOP) || pt->trace) { + jit_State *J = L2J(sbufL(&ctx->sb)); + MSize i; + for (i = 0; i < nbc; i++, q += sizeof(BCIns)) { + BCOp op = (BCOp)q[LJ_ENDIAN_SELECT(0, 3)]; + if (op == BC_IFORL || op == BC_IITERL || op == BC_ILOOP || + op == BC_JFORI) { + q[LJ_ENDIAN_SELECT(0, 3)] = (uint8_t)(op-BC_IFORL+BC_FORL); + } else if (op == BC_JFORL || op == BC_JITERL || op == BC_JLOOP) { + BCReg rd = q[LJ_ENDIAN_SELECT(2, 1)] + (q[LJ_ENDIAN_SELECT(3, 0)] << 8); + BCIns ins = traceref(J, rd)->startins; + q[LJ_ENDIAN_SELECT(0, 3)] = (uint8_t)(op-BC_JFORL+BC_FORL); + q[LJ_ENDIAN_SELECT(2, 1)] = bc_c(ins); + q[LJ_ENDIAN_SELECT(3, 0)] = bc_b(ins); + } + } + } +#endif + return p; +} + +/* Write prototype. */ +static void bcwrite_proto(BCWriteCtx *ctx, GCproto *pt) +{ + MSize sizedbg = 0; + char *p; + + /* Recursively write children of prototype. */ + if ((pt->flags & PROTO_CHILD)) { + ptrdiff_t i, 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) + bcwrite_proto(ctx, gco2pt(o)); + } + } + + /* Start writing the prototype info to a buffer. */ + p = lj_buf_need(&ctx->sb, + 5+4+6*5+(pt->sizebc-1)*(MSize)sizeof(BCIns)+pt->sizeuv*2); + p += 5; /* Leave room for final size. */ + + /* Write prototype header. */ + *p++ = (pt->flags & (PROTO_CHILD|PROTO_VARARG|PROTO_FFI)); + *p++ = pt->numparams; + *p++ = pt->framesize; + *p++ = pt->sizeuv; + p = lj_strfmt_wuleb128(p, pt->sizekgc); + p = lj_strfmt_wuleb128(p, pt->sizekn); + p = lj_strfmt_wuleb128(p, pt->sizebc-1); + if (!ctx->strip) { + if (proto_lineinfo(pt)) + sizedbg = pt->sizept - (MSize)((char *)proto_lineinfo(pt) - (char *)pt); + p = lj_strfmt_wuleb128(p, sizedbg); + if (sizedbg) { + p = lj_strfmt_wuleb128(p, pt->firstline); + p = lj_strfmt_wuleb128(p, pt->numline); + } + } + + /* Write bytecode instructions and upvalue refs. */ + p = bcwrite_bytecode(ctx, p, pt); + p = lj_buf_wmem(p, proto_uv(pt), pt->sizeuv*2); + setsbufP(&ctx->sb, p); + + /* Write constants. */ + bcwrite_kgc(ctx, pt); + bcwrite_knum(ctx, pt); + + /* Write debug info, if not stripped. */ + if (sizedbg) { + p = lj_buf_more(&ctx->sb, sizedbg); + p = lj_buf_wmem(p, proto_lineinfo(pt), sizedbg); + setsbufP(&ctx->sb, p); + } + + /* Pass buffer to writer function. */ + if (ctx->status == 0) { + MSize n = sbuflen(&ctx->sb) - 5; + MSize nn = (lj_fls(n)+8)*9 >> 6; + char *q = sbufB(&ctx->sb) + (5 - nn); + p = lj_strfmt_wuleb128(q, n); /* Fill in final size. */ + lua_assert(p == sbufB(&ctx->sb) + 5); + ctx->status = ctx->wfunc(sbufL(&ctx->sb), q, nn+n, ctx->wdata); + } +} + +/* Write header of bytecode dump. */ +static void bcwrite_header(BCWriteCtx *ctx) +{ + GCstr *chunkname = proto_chunkname(ctx->pt); + const char *name = strdata(chunkname); + MSize len = chunkname->len; + char *p = lj_buf_need(&ctx->sb, 5+5+len); + *p++ = BCDUMP_HEAD1; + *p++ = BCDUMP_HEAD2; + *p++ = BCDUMP_HEAD3; + *p++ = BCDUMP_VERSION; + *p++ = (ctx->strip ? BCDUMP_F_STRIP : 0) + + LJ_BE*BCDUMP_F_BE + + ((ctx->pt->flags & PROTO_FFI) ? BCDUMP_F_FFI : 0) + + LJ_FR2*BCDUMP_F_FR2; + if (!ctx->strip) { + p = lj_strfmt_wuleb128(p, len); + p = lj_buf_wmem(p, name, len); + } + ctx->status = ctx->wfunc(sbufL(&ctx->sb), sbufB(&ctx->sb), + (MSize)(p - sbufB(&ctx->sb)), ctx->wdata); +} + +/* Write footer of bytecode dump. */ +static void bcwrite_footer(BCWriteCtx *ctx) +{ + if (ctx->status == 0) { + uint8_t zero = 0; + ctx->status = ctx->wfunc(sbufL(&ctx->sb), &zero, 1, ctx->wdata); + } +} + +/* Protected callback for bytecode writer. */ +static TValue *cpwriter(lua_State *L, lua_CFunction dummy, void *ud) +{ + BCWriteCtx *ctx = (BCWriteCtx *)ud; + UNUSED(L); UNUSED(dummy); + lj_buf_need(&ctx->sb, 1024); /* Avoids resize for most prototypes. */ + bcwrite_header(ctx); + bcwrite_proto(ctx, ctx->pt); + bcwrite_footer(ctx); + return NULL; +} + +/* Write bytecode for a prototype. */ +int lj_bcwrite(lua_State *L, GCproto *pt, lua_Writer writer, void *data, + int strip) +{ + BCWriteCtx ctx; + int status; + ctx.pt = pt; + ctx.wfunc = writer; + ctx.wdata = data; + ctx.strip = strip; + ctx.status = 0; + lj_buf_init(L, &ctx.sb); + status = lj_vm_cpcall(L, NULL, &ctx, cpwriter); + if (status == 0) status = ctx.status; + lj_buf_free(G(sbufL(&ctx.sb)), &ctx.sb); + return status; +} + diff --git a/lib/LuaJIT/lj_bcwrite.o b/lib/LuaJIT/lj_bcwrite.o new file mode 100644 index 0000000000000000000000000000000000000000..b2798d82abac57cb0e0e42446d0111292337008e GIT binary patch literal 5520 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, b); + setmref(sb->p, b + len); + setmref(sb->e, b + nsz); +} + +LJ_NOINLINE char *LJ_FASTCALL lj_buf_need2(SBuf *sb, MSize sz) +{ + lua_assert(sz > sbufsz(sb)); + if (LJ_UNLIKELY(sz > LJ_MAX_BUF)) + lj_err_mem(sbufL(sb)); + buf_grow(sb, sz); + return sbufB(sb); +} + +LJ_NOINLINE char *LJ_FASTCALL lj_buf_more2(SBuf *sb, MSize sz) +{ + MSize len = sbuflen(sb); + lua_assert(sz > sbufleft(sb)); + if (LJ_UNLIKELY(sz > LJ_MAX_BUF || len + sz > LJ_MAX_BUF)) + lj_err_mem(sbufL(sb)); + buf_grow(sb, len + sz); + return sbufP(sb); +} + +void LJ_FASTCALL lj_buf_shrink(lua_State *L, SBuf *sb) +{ + char *b = sbufB(sb); + MSize osz = (MSize)(sbufE(sb) - b); + if (osz > 2*LJ_MIN_SBUF) { + MSize n = (MSize)(sbufP(sb) - b); + b = lj_mem_realloc(L, b, osz, (osz >> 1)); + setmref(sb->b, b); + setmref(sb->p, b + n); + setmref(sb->e, b + (osz >> 1)); + } +} + +char * LJ_FASTCALL lj_buf_tmp(lua_State *L, MSize sz) +{ + SBuf *sb = &G(L)->tmpbuf; + setsbufL(sb, L); + return lj_buf_need(sb, sz); +} + +/* -- Low-level buffer put operations ------------------------------------- */ + +SBuf *lj_buf_putmem(SBuf *sb, const void *q, MSize len) +{ + char *p = lj_buf_more(sb, len); + p = lj_buf_wmem(p, q, len); + setsbufP(sb, p); + return sb; +} + +SBuf * LJ_FASTCALL lj_buf_putchar(SBuf *sb, int c) +{ + char *p = lj_buf_more(sb, 1); + *p++ = (char)c; + setsbufP(sb, p); + return sb; +} + +SBuf * LJ_FASTCALL lj_buf_putstr(SBuf *sb, GCstr *s) +{ + MSize len = s->len; + char *p = lj_buf_more(sb, len); + p = lj_buf_wmem(p, strdata(s), len); + setsbufP(sb, p); + return sb; +} + +/* -- High-level buffer put operations ------------------------------------ */ + +SBuf * LJ_FASTCALL lj_buf_putstr_reverse(SBuf *sb, GCstr *s) +{ + MSize len = s->len; + char *p = lj_buf_more(sb, len), *e = p+len; + const char *q = strdata(s)+len-1; + while (p < e) + *p++ = *q--; + setsbufP(sb, p); + return sb; +} + +SBuf * LJ_FASTCALL lj_buf_putstr_lower(SBuf *sb, GCstr *s) +{ + MSize len = s->len; + char *p = lj_buf_more(sb, len), *e = p+len; + const char *q = strdata(s); + for (; p < e; p++, q++) { + uint32_t c = *(unsigned char *)q; +#if LJ_TARGET_PPC + *p = c + ((c >= 'A' && c <= 'Z') << 5); +#else + if (c >= 'A' && c <= 'Z') c += 0x20; + *p = c; +#endif + } + setsbufP(sb, p); + return sb; +} + +SBuf * LJ_FASTCALL lj_buf_putstr_upper(SBuf *sb, GCstr *s) +{ + MSize len = s->len; + char *p = lj_buf_more(sb, len), *e = p+len; + const char *q = strdata(s); + for (; p < e; p++, q++) { + uint32_t c = *(unsigned char *)q; +#if LJ_TARGET_PPC + *p = c - ((c >= 'a' && c <= 'z') << 5); +#else + if (c >= 'a' && c <= 'z') c -= 0x20; + *p = c; +#endif + } + setsbufP(sb, p); + return sb; +} + +SBuf *lj_buf_putstr_rep(SBuf *sb, GCstr *s, int32_t rep) +{ + MSize len = s->len; + if (rep > 0 && len) { + uint64_t tlen = (uint64_t)rep * len; + char *p; + if (LJ_UNLIKELY(tlen > LJ_MAX_STR)) + lj_err_mem(sbufL(sb)); + p = lj_buf_more(sb, (MSize)tlen); + if (len == 1) { /* Optimize a common case. */ + uint32_t c = strdata(s)[0]; + do { *p++ = c; } while (--rep > 0); + } else { + const char *e = strdata(s) + len; + do { + const char *q = strdata(s); + do { *p++ = *q++; } while (q < e); + } while (--rep > 0); + } + setsbufP(sb, p); + } + return sb; +} + +SBuf *lj_buf_puttab(SBuf *sb, GCtab *t, GCstr *sep, int32_t i, int32_t e) +{ + MSize seplen = sep ? sep->len : 0; + if (i <= e) { + for (;;) { + cTValue *o = lj_tab_getint(t, i); + char *p; + if (!o) { + badtype: /* Error: bad element type. */ + setsbufP(sb, (void *)(intptr_t)i); /* Store failing index. */ + return NULL; + } else if (tvisstr(o)) { + MSize len = strV(o)->len; + p = lj_buf_wmem(lj_buf_more(sb, len + seplen), strVdata(o), len); + } else if (tvisint(o)) { + p = lj_strfmt_wint(lj_buf_more(sb, STRFMT_MAXBUF_INT+seplen), intV(o)); + } else if (tvisnum(o)) { + p = lj_buf_more(lj_strfmt_putfnum(sb, STRFMT_G14, numV(o)), seplen); + } else { + goto badtype; + } + if (i++ == e) { + setsbufP(sb, p); + break; + } + if (seplen) p = lj_buf_wmem(p, strdata(sep), seplen); + setsbufP(sb, p); + } + } + return sb; +} + +/* -- Miscellaneous buffer operations ------------------------------------- */ + +GCstr * LJ_FASTCALL lj_buf_tostr(SBuf *sb) +{ + return lj_str_new(sbufL(sb), sbufB(sb), sbuflen(sb)); +} + +/* Concatenate two strings. */ +GCstr *lj_buf_cat2str(lua_State *L, GCstr *s1, GCstr *s2) +{ + MSize len1 = s1->len, len2 = s2->len; + char *buf = lj_buf_tmp(L, len1 + len2); + memcpy(buf, strdata(s1), len1); + memcpy(buf+len1, strdata(s2), len2); + return lj_str_new(L, buf, len1 + len2); +} + +/* Read ULEB128 from buffer. */ +uint32_t LJ_FASTCALL lj_buf_ruleb128(const char **pp) +{ + const uint8_t *p = (const uint8_t *)*pp; + uint32_t v = *p++; + if (LJ_UNLIKELY(v >= 0x80)) { + int sh = 0; + v &= 0x7f; + do { v |= ((*p & 0x7f) << (sh += 7)); } while (*p++ >= 0x80); + } + *pp = (const char *)p; + return v; +} + diff --git a/lib/LuaJIT/lj_buf.h b/lib/LuaJIT/lj_buf.h new file mode 100644 index 0000000..a405169 --- /dev/null +++ b/lib/LuaJIT/lj_buf.h @@ -0,0 +1,103 @@ +/* +** Buffer handling. +** Copyright (C) 2005-2017 Mike Pall. See Copyright Notice in luajit.h +*/ + +#ifndef _LJ_BUF_H +#define _LJ_BUF_H + +#include "lj_obj.h" +#include "lj_gc.h" +#include "lj_str.h" + +/* Resizable string buffers. Struct definition in lj_obj.h. */ +#define sbufB(sb) (mref((sb)->b, char)) +#define sbufP(sb) (mref((sb)->p, char)) +#define sbufE(sb) (mref((sb)->e, char)) +#define sbufL(sb) (mref((sb)->L, lua_State)) +#define sbufsz(sb) ((MSize)(sbufE((sb)) - sbufB((sb)))) +#define sbuflen(sb) ((MSize)(sbufP((sb)) - sbufB((sb)))) +#define sbufleft(sb) ((MSize)(sbufE((sb)) - sbufP((sb)))) +#define setsbufP(sb, q) (setmref((sb)->p, (q))) +#define setsbufL(sb, l) (setmref((sb)->L, (l))) + +/* Buffer management */ +LJ_FUNC char *LJ_FASTCALL lj_buf_need2(SBuf *sb, MSize sz); +LJ_FUNC char *LJ_FASTCALL lj_buf_more2(SBuf *sb, MSize sz); +LJ_FUNC void LJ_FASTCALL lj_buf_shrink(lua_State *L, SBuf *sb); +LJ_FUNC char * LJ_FASTCALL lj_buf_tmp(lua_State *L, MSize sz); + +static LJ_AINLINE void lj_buf_init(lua_State *L, SBuf *sb) +{ + setsbufL(sb, L); + setmref(sb->p, NULL); setmref(sb->e, NULL); setmref(sb->b, NULL); +} + +static LJ_AINLINE void lj_buf_reset(SBuf *sb) +{ + setmrefr(sb->p, sb->b); +} + +static LJ_AINLINE SBuf *lj_buf_tmp_(lua_State *L) +{ + SBuf *sb = &G(L)->tmpbuf; + setsbufL(sb, L); + lj_buf_reset(sb); + return sb; +} + +static LJ_AINLINE void lj_buf_free(global_State *g, SBuf *sb) +{ + lj_mem_free(g, sbufB(sb), sbufsz(sb)); +} + +static LJ_AINLINE char *lj_buf_need(SBuf *sb, MSize sz) +{ + if (LJ_UNLIKELY(sz > sbufsz(sb))) + return lj_buf_need2(sb, sz); + return sbufB(sb); +} + +static LJ_AINLINE char *lj_buf_more(SBuf *sb, MSize sz) +{ + if (LJ_UNLIKELY(sz > sbufleft(sb))) + return lj_buf_more2(sb, sz); + return sbufP(sb); +} + +/* Low-level buffer put operations */ +LJ_FUNC SBuf *lj_buf_putmem(SBuf *sb, const void *q, MSize len); +LJ_FUNC SBuf * LJ_FASTCALL lj_buf_putchar(SBuf *sb, int c); +LJ_FUNC SBuf * LJ_FASTCALL lj_buf_putstr(SBuf *sb, GCstr *s); + +static LJ_AINLINE char *lj_buf_wmem(char *p, const void *q, MSize len) +{ + return (char *)memcpy(p, q, len) + len; +} + +static LJ_AINLINE void lj_buf_putb(SBuf *sb, int c) +{ + char *p = lj_buf_more(sb, 1); + *p++ = (char)c; + setsbufP(sb, p); +} + +/* High-level buffer put operations */ +LJ_FUNCA SBuf * LJ_FASTCALL lj_buf_putstr_reverse(SBuf *sb, GCstr *s); +LJ_FUNCA SBuf * LJ_FASTCALL lj_buf_putstr_lower(SBuf *sb, GCstr *s); +LJ_FUNCA SBuf * LJ_FASTCALL lj_buf_putstr_upper(SBuf *sb, GCstr *s); +LJ_FUNC SBuf *lj_buf_putstr_rep(SBuf *sb, GCstr *s, int32_t rep); +LJ_FUNC SBuf *lj_buf_puttab(SBuf *sb, GCtab *t, GCstr *sep, + int32_t i, int32_t e); + +/* Miscellaneous buffer operations */ +LJ_FUNCA GCstr * LJ_FASTCALL lj_buf_tostr(SBuf *sb); +LJ_FUNC GCstr *lj_buf_cat2str(lua_State *L, GCstr *s1, GCstr *s2); +LJ_FUNC uint32_t LJ_FASTCALL lj_buf_ruleb128(const char **pp); + +static LJ_AINLINE GCstr *lj_buf_str(lua_State *L, SBuf *sb) +{ + return lj_str_new(L, sbufB(sb), sbuflen(sb)); +} + +#endif diff --git a/lib/LuaJIT/lj_buf.o b/lib/LuaJIT/lj_buf.o new file mode 100644 index 0000000000000000000000000000000000000000..3faf12b6b300ad4f4fedb0b7674d42bda8e72470 GIT binary patch literal 5624 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 CrLK4Y literal 0 HcmV?d00001 diff --git a/lib/LuaJIT/lj_buf_dyn.o b/lib/LuaJIT/lj_buf_dyn.o new file mode 100644 index 0000000000000000000000000000000000000000..3faf12b6b300ad4f4fedb0b7674d42bda8e72470 GIT binary patch literal 5624 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 CrLK4Y literal 0 HcmV?d00001 diff --git a/lib/LuaJIT/lj_carith.c b/lib/LuaJIT/lj_carith.c new file mode 100644 index 0000000..1c050eb --- /dev/null +++ b/lib/LuaJIT/lj_carith.c @@ -0,0 +1,437 @@ +/* +** C data arithmetic. +** Copyright (C) 2005-2017 Mike Pall. See Copyright Notice in luajit.h +*/ + +#include "lj_obj.h" + +#if LJ_HASFFI + +#include "lj_gc.h" +#include "lj_err.h" +#include "lj_tab.h" +#include "lj_meta.h" +#include "lj_ir.h" +#include "lj_ctype.h" +#include "lj_cconv.h" +#include "lj_cdata.h" +#include "lj_carith.h" +#include "lj_strscan.h" + +/* -- C data arithmetic --------------------------------------------------- */ + +/* Binary operands of an operator converted to ctypes. */ +typedef struct CDArith { + uint8_t *p[2]; + CType *ct[2]; +} CDArith; + +/* Check arguments for arithmetic metamethods. */ +static int carith_checkarg(lua_State *L, CTState *cts, CDArith *ca) +{ + TValue *o = L->base; + int ok = 1; + MSize i; + if (o+1 >= L->top) + lj_err_argt(L, 1, LUA_TCDATA); + for (i = 0; i < 2; i++, o++) { + if (tviscdata(o)) { + GCcdata *cd = cdataV(o); + CTypeID id = (CTypeID)cd->ctypeid; + CType *ct = ctype_raw(cts, id); + uint8_t *p = (uint8_t *)cdataptr(cd); + if (ctype_isptr(ct->info)) { + p = (uint8_t *)cdata_getptr(p, ct->size); + if (ctype_isref(ct->info)) ct = ctype_rawchild(cts, ct); + } else if (ctype_isfunc(ct->info)) { + p = (uint8_t *)*(void **)p; + ct = ctype_get(cts, + lj_ctype_intern(cts, CTINFO(CT_PTR, CTALIGN_PTR|id), CTSIZE_PTR)); + } + if (ctype_isenum(ct->info)) ct = ctype_child(cts, ct); + ca->ct[i] = ct; + ca->p[i] = p; + } else if (tvisint(o)) { + ca->ct[i] = ctype_get(cts, CTID_INT32); + ca->p[i] = (uint8_t *)&o->i; + } else if (tvisnum(o)) { + ca->ct[i] = ctype_get(cts, CTID_DOUBLE); + ca->p[i] = (uint8_t *)&o->n; + } else if (tvisnil(o)) { + ca->ct[i] = ctype_get(cts, CTID_P_VOID); + ca->p[i] = (uint8_t *)0; + } else if (tvisstr(o)) { + TValue *o2 = i == 0 ? o+1 : o-1; + CType *ct = ctype_raw(cts, cdataV(o2)->ctypeid); + ca->ct[i] = NULL; + ca->p[i] = (uint8_t *)strVdata(o); + ok = 0; + if (ctype_isenum(ct->info)) { + CTSize ofs; + CType *cct = lj_ctype_getfield(cts, ct, strV(o), &ofs); + if (cct && ctype_isconstval(cct->info)) { + ca->ct[i] = ctype_child(cts, cct); + ca->p[i] = (uint8_t *)&cct->size; /* Assumes ct does not grow. */ + ok = 1; + } else { + ca->ct[1-i] = ct; /* Use enum to improve error message. */ + ca->p[1-i] = NULL; + break; + } + } + } else { + ca->ct[i] = NULL; + ca->p[i] = (void *)(intptr_t)1; /* To make it unequal. */ + ok = 0; + } + } + return ok; +} + +/* Pointer arithmetic. */ +static int carith_ptr(lua_State *L, CTState *cts, CDArith *ca, MMS mm) +{ + CType *ctp = ca->ct[0]; + uint8_t *pp = ca->p[0]; + ptrdiff_t idx; + CTSize sz; + CTypeID id; + GCcdata *cd; + if (ctype_isptr(ctp->info) || ctype_isrefarray(ctp->info)) { + if ((mm == MM_sub || mm == MM_eq || mm == MM_lt || mm == MM_le) && + (ctype_isptr(ca->ct[1]->info) || ctype_isrefarray(ca->ct[1]->info))) { + uint8_t *pp2 = ca->p[1]; + if (mm == MM_eq) { /* Pointer equality. Incompatible pointers are ok. */ + setboolV(L->top-1, (pp == pp2)); + return 1; + } + if (!lj_cconv_compatptr(cts, ctp, ca->ct[1], CCF_IGNQUAL)) + return 0; + if (mm == MM_sub) { /* Pointer difference. */ + intptr_t diff; + sz = lj_ctype_size(cts, ctype_cid(ctp->info)); /* Element size. */ + if (sz == 0 || sz == CTSIZE_INVALID) + return 0; + diff = ((intptr_t)pp - (intptr_t)pp2) / (int32_t)sz; + /* All valid pointer differences on x64 are in (-2^47, +2^47), + ** which fits into a double without loss of precision. + */ + setintptrV(L->top-1, (int32_t)diff); + return 1; + } else if (mm == MM_lt) { /* Pointer comparison (unsigned). */ + setboolV(L->top-1, ((uintptr_t)pp < (uintptr_t)pp2)); + return 1; + } else { + lua_assert(mm == MM_le); + setboolV(L->top-1, ((uintptr_t)pp <= (uintptr_t)pp2)); + return 1; + } + } + if (!((mm == MM_add || mm == MM_sub) && ctype_isnum(ca->ct[1]->info))) + return 0; + lj_cconv_ct_ct(cts, ctype_get(cts, CTID_INT_PSZ), ca->ct[1], + (uint8_t *)&idx, ca->p[1], 0); + if (mm == MM_sub) idx = -idx; + } else if (mm == MM_add && ctype_isnum(ctp->info) && + (ctype_isptr(ca->ct[1]->info) || ctype_isrefarray(ca->ct[1]->info))) { + /* Swap pointer and index. */ + ctp = ca->ct[1]; pp = ca->p[1]; + lj_cconv_ct_ct(cts, ctype_get(cts, CTID_INT_PSZ), ca->ct[0], + (uint8_t *)&idx, ca->p[0], 0); + } else { + return 0; + } + sz = lj_ctype_size(cts, ctype_cid(ctp->info)); /* Element size. */ + if (sz == CTSIZE_INVALID) + return 0; + pp += idx*(int32_t)sz; /* Compute pointer + index. */ + id = lj_ctype_intern(cts, CTINFO(CT_PTR, CTALIGN_PTR|ctype_cid(ctp->info)), + CTSIZE_PTR); + cd = lj_cdata_new(cts, id, CTSIZE_PTR); + *(uint8_t **)cdataptr(cd) = pp; + setcdataV(L, L->top-1, cd); + lj_gc_check(L); + return 1; +} + +/* 64 bit integer arithmetic. */ +static int carith_int64(lua_State *L, CTState *cts, CDArith *ca, MMS mm) +{ + if (ctype_isnum(ca->ct[0]->info) && ca->ct[0]->size <= 8 && + ctype_isnum(ca->ct[1]->info) && ca->ct[1]->size <= 8) { + CTypeID id = (((ca->ct[0]->info & CTF_UNSIGNED) && ca->ct[0]->size == 8) || + ((ca->ct[1]->info & CTF_UNSIGNED) && ca->ct[1]->size == 8)) ? + CTID_UINT64 : CTID_INT64; + CType *ct = ctype_get(cts, id); + GCcdata *cd; + uint64_t u0, u1, *up; + lj_cconv_ct_ct(cts, ct, ca->ct[0], (uint8_t *)&u0, ca->p[0], 0); + if (mm != MM_unm) + lj_cconv_ct_ct(cts, ct, ca->ct[1], (uint8_t *)&u1, ca->p[1], 0); + switch (mm) { + case MM_eq: + setboolV(L->top-1, (u0 == u1)); + return 1; + case MM_lt: + setboolV(L->top-1, + id == CTID_INT64 ? ((int64_t)u0 < (int64_t)u1) : (u0 < u1)); + return 1; + case MM_le: + setboolV(L->top-1, + id == CTID_INT64 ? ((int64_t)u0 <= (int64_t)u1) : (u0 <= u1)); + return 1; + default: break; + } + cd = lj_cdata_new(cts, id, 8); + up = (uint64_t *)cdataptr(cd); + setcdataV(L, L->top-1, cd); + switch (mm) { + case MM_add: *up = u0 + u1; break; + case MM_sub: *up = u0 - u1; break; + case MM_mul: *up = u0 * u1; break; + case MM_div: + if (id == CTID_INT64) + *up = (uint64_t)lj_carith_divi64((int64_t)u0, (int64_t)u1); + else + *up = lj_carith_divu64(u0, u1); + break; + case MM_mod: + if (id == CTID_INT64) + *up = (uint64_t)lj_carith_modi64((int64_t)u0, (int64_t)u1); + else + *up = lj_carith_modu64(u0, u1); + break; + case MM_pow: + if (id == CTID_INT64) + *up = (uint64_t)lj_carith_powi64((int64_t)u0, (int64_t)u1); + else + *up = lj_carith_powu64(u0, u1); + break; + case MM_unm: *up = (uint64_t)-(int64_t)u0; break; + default: lua_assert(0); break; + } + lj_gc_check(L); + return 1; + } + return 0; +} + +/* Handle ctype arithmetic metamethods. */ +static int lj_carith_meta(lua_State *L, CTState *cts, CDArith *ca, MMS mm) +{ + cTValue *tv = NULL; + if (tviscdata(L->base)) { + CTypeID id = cdataV(L->base)->ctypeid; + CType *ct = ctype_raw(cts, id); + if (ctype_isptr(ct->info)) id = ctype_cid(ct->info); + tv = lj_ctype_meta(cts, id, mm); + } + if (!tv && L->base+1 < L->top && tviscdata(L->base+1)) { + CTypeID id = cdataV(L->base+1)->ctypeid; + CType *ct = ctype_raw(cts, id); + if (ctype_isptr(ct->info)) id = ctype_cid(ct->info); + tv = lj_ctype_meta(cts, id, mm); + } + if (!tv) { + const char *repr[2]; + int i, isenum = -1, isstr = -1; + if (mm == MM_eq) { /* Equality checks never raise an error. */ + int eq = ca->p[0] == ca->p[1]; + setboolV(L->top-1, eq); + setboolV(&G(L)->tmptv2, eq); /* Remember for trace recorder. */ + return 1; + } + for (i = 0; i < 2; i++) { + if (ca->ct[i] && tviscdata(L->base+i)) { + if (ctype_isenum(ca->ct[i]->info)) isenum = i; + repr[i] = strdata(lj_ctype_repr(L, ctype_typeid(cts, ca->ct[i]), NULL)); + } else { + if (tvisstr(&L->base[i])) isstr = i; + repr[i] = lj_typename(&L->base[i]); + } + } + if ((isenum ^ isstr) == 1) + lj_err_callerv(L, LJ_ERR_FFI_BADCONV, repr[isstr], repr[isenum]); + lj_err_callerv(L, mm == MM_len ? LJ_ERR_FFI_BADLEN : + mm == MM_concat ? LJ_ERR_FFI_BADCONCAT : + mm < MM_add ? LJ_ERR_FFI_BADCOMP : LJ_ERR_FFI_BADARITH, + repr[0], repr[1]); + } + return lj_meta_tailcall(L, tv); +} + +/* Arithmetic operators for cdata. */ +int lj_carith_op(lua_State *L, MMS mm) +{ + CTState *cts = ctype_cts(L); + CDArith ca; + if (carith_checkarg(L, cts, &ca)) { + if (carith_int64(L, cts, &ca, mm) || carith_ptr(L, cts, &ca, mm)) { + copyTV(L, &G(L)->tmptv2, L->top-1); /* Remember for trace recorder. */ + return 1; + } + } + return lj_carith_meta(L, cts, &ca, mm); +} + +/* No built-in functionality for length of cdata. */ +int lj_carith_len(lua_State *L) +{ + CTState *cts = ctype_cts(L); + CDArith ca; + carith_checkarg(L, cts, &ca); + return lj_carith_meta(L, cts, &ca, MM_len); +} + +/* -- 64 bit bit operations helpers --------------------------------------- */ + +#if LJ_64 +#define B64DEF(name) \ + static LJ_AINLINE uint64_t lj_carith_##name(uint64_t x, int32_t sh) +#else +/* Not inlined on 32 bit archs, since some of these are quite lengthy. */ +#define B64DEF(name) \ + uint64_t LJ_NOINLINE lj_carith_##name(uint64_t x, int32_t sh) +#endif + +B64DEF(shl64) { return x << (sh&63); } +B64DEF(shr64) { return x >> (sh&63); } +B64DEF(sar64) { return (uint64_t)((int64_t)x >> (sh&63)); } +B64DEF(rol64) { return lj_rol(x, (sh&63)); } +B64DEF(ror64) { return lj_ror(x, (sh&63)); } + +#undef B64DEF + +uint64_t lj_carith_shift64(uint64_t x, int32_t sh, int op) +{ + switch (op) { + case IR_BSHL-IR_BSHL: x = lj_carith_shl64(x, sh); break; + case IR_BSHR-IR_BSHL: x = lj_carith_shr64(x, sh); break; + case IR_BSAR-IR_BSHL: x = lj_carith_sar64(x, sh); break; + case IR_BROL-IR_BSHL: x = lj_carith_rol64(x, sh); break; + case IR_BROR-IR_BSHL: x = lj_carith_ror64(x, sh); break; + default: lua_assert(0); break; + } + return x; +} + +/* Equivalent to lj_lib_checkbit(), but handles cdata. */ +uint64_t lj_carith_check64(lua_State *L, int narg, CTypeID *id) +{ + TValue *o = L->base + narg-1; + if (o >= L->top) { + err: + lj_err_argt(L, narg, LUA_TNUMBER); + } else if (LJ_LIKELY(tvisnumber(o))) { + /* Handled below. */ + } else if (tviscdata(o)) { + CTState *cts = ctype_cts(L); + uint8_t *sp = (uint8_t *)cdataptr(cdataV(o)); + CTypeID sid = cdataV(o)->ctypeid; + CType *s = ctype_get(cts, sid); + uint64_t x; + if (ctype_isref(s->info)) { + sp = *(void **)sp; + sid = ctype_cid(s->info); + } + s = ctype_raw(cts, sid); + if (ctype_isenum(s->info)) s = ctype_child(cts, s); + if ((s->info & (CTMASK_NUM|CTF_BOOL|CTF_FP|CTF_UNSIGNED)) == + CTINFO(CT_NUM, CTF_UNSIGNED) && s->size == 8) + *id = CTID_UINT64; /* Use uint64_t, since it has the highest rank. */ + else if (!*id) + *id = CTID_INT64; /* Use int64_t, unless already set. */ + lj_cconv_ct_ct(cts, ctype_get(cts, *id), s, + (uint8_t *)&x, sp, CCF_ARG(narg)); + return x; + } else if (!(tvisstr(o) && lj_strscan_number(strV(o), o))) { + goto err; + } + if (LJ_LIKELY(tvisint(o))) { + return (uint32_t)intV(o); + } else { + int32_t i = lj_num2bit(numV(o)); + if (LJ_DUALNUM) setintV(o, i); + return (uint32_t)i; + } +} + +/* -- 64 bit integer arithmetic helpers ----------------------------------- */ + +#if LJ_32 && LJ_HASJIT +/* Signed/unsigned 64 bit multiplication. */ +int64_t lj_carith_mul64(int64_t a, int64_t b) +{ + return a * b; +} +#endif + +/* Unsigned 64 bit division. */ +uint64_t lj_carith_divu64(uint64_t a, uint64_t b) +{ + if (b == 0) return U64x(80000000,00000000); + return a / b; +} + +/* Signed 64 bit division. */ +int64_t lj_carith_divi64(int64_t a, int64_t b) +{ + if (b == 0 || (a == (int64_t)U64x(80000000,00000000) && b == -1)) + return U64x(80000000,00000000); + return a / b; +} + +/* Unsigned 64 bit modulo. */ +uint64_t lj_carith_modu64(uint64_t a, uint64_t b) +{ + if (b == 0) return U64x(80000000,00000000); + return a % b; +} + +/* Signed 64 bit modulo. */ +int64_t lj_carith_modi64(int64_t a, int64_t b) +{ + if (b == 0) return U64x(80000000,00000000); + if (a == (int64_t)U64x(80000000,00000000) && b == -1) return 0; + return a % b; +} + +/* Unsigned 64 bit x^k. */ +uint64_t lj_carith_powu64(uint64_t x, uint64_t k) +{ + uint64_t y; + if (k == 0) + return 1; + for (; (k & 1) == 0; k >>= 1) x *= x; + y = x; + if ((k >>= 1) != 0) { + for (;;) { + x *= x; + if (k == 1) break; + if (k & 1) y *= x; + k >>= 1; + } + y *= x; + } + return y; +} + +/* Signed 64 bit x^k. */ +int64_t lj_carith_powi64(int64_t x, int64_t k) +{ + if (k == 0) + return 1; + if (k < 0) { + if (x == 0) + return U64x(7fffffff,ffffffff); + else if (x == 1) + return 1; + else if (x == -1) + return (k & 1) ? -1 : 1; + else + return 0; + } + return (int64_t)lj_carith_powu64((uint64_t)x, (uint64_t)k); +} + +#endif diff --git a/lib/LuaJIT/lj_carith.h b/lib/LuaJIT/lj_carith.h new file mode 100644 index 0000000..3b0a5dd --- /dev/null +++ b/lib/LuaJIT/lj_carith.h @@ -0,0 +1,38 @@ +/* +** C data arithmetic. +** Copyright (C) 2005-2017 Mike Pall. See Copyright Notice in luajit.h +*/ + +#ifndef _LJ_CARITH_H +#define _LJ_CARITH_H + +#include "lj_obj.h" + +#if LJ_HASFFI + +LJ_FUNC int lj_carith_op(lua_State *L, MMS mm); +LJ_FUNC int lj_carith_len(lua_State *L); + +#if LJ_32 +LJ_FUNC uint64_t lj_carith_shl64(uint64_t x, int32_t sh); +LJ_FUNC uint64_t lj_carith_shr64(uint64_t x, int32_t sh); +LJ_FUNC uint64_t lj_carith_sar64(uint64_t x, int32_t sh); +LJ_FUNC uint64_t lj_carith_rol64(uint64_t x, int32_t sh); +LJ_FUNC uint64_t lj_carith_ror64(uint64_t x, int32_t sh); +#endif +LJ_FUNC uint64_t lj_carith_shift64(uint64_t x, int32_t sh, int op); +LJ_FUNC uint64_t lj_carith_check64(lua_State *L, int narg, CTypeID *id); + +#if LJ_32 && LJ_HASJIT +LJ_FUNC int64_t lj_carith_mul64(int64_t x, int64_t k); +#endif +LJ_FUNC uint64_t lj_carith_divu64(uint64_t a, uint64_t b); +LJ_FUNC int64_t lj_carith_divi64(int64_t a, int64_t b); +LJ_FUNC uint64_t lj_carith_modu64(uint64_t a, uint64_t b); +LJ_FUNC int64_t lj_carith_modi64(int64_t a, int64_t b); +LJ_FUNC uint64_t lj_carith_powu64(uint64_t x, uint64_t k); +LJ_FUNC int64_t lj_carith_powi64(int64_t x, int64_t k); + +#endif + +#endif diff --git a/lib/LuaJIT/lj_carith.o b/lib/LuaJIT/lj_carith.o new file mode 100644 index 0000000000000000000000000000000000000000..e9a1f22b568c9ef01d83692ab2b860465231def6 GIT binary patch literal 8584 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 V+{qT(FMi;H{yDL$_@{LL{{Wq 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 V+{qT(FMi;H{yDL$_@{LL{{Wretref = (sz > 8); \ + if (cc->retref) cc->stack[nsp++] = (GPRArg)dp; + +#define CCALL_HANDLE_COMPLEXRET CCALL_HANDLE_STRUCTRET + +#else + +#if LJ_TARGET_OSX + +#define CCALL_HANDLE_STRUCTRET \ + /* Return structs of size 1, 2, 4 or 8 in registers. */ \ + cc->retref = !(sz == 1 || sz == 2 || sz == 4 || sz == 8); \ + if (cc->retref) { \ + if (ngpr < maxgpr) \ + cc->gpr[ngpr++] = (GPRArg)dp; \ + else \ + cc->stack[nsp++] = (GPRArg)dp; \ + } else { /* Struct with single FP field ends up in FPR. */ \ + cc->resx87 = ccall_classify_struct(cts, ctr); \ + } + +#define CCALL_HANDLE_STRUCTRET2 \ + if (cc->resx87) sp = (uint8_t *)&cc->fpr[0]; \ + memcpy(dp, sp, ctr->size); + +#else + +#define CCALL_HANDLE_STRUCTRET \ + cc->retref = 1; /* Return all structs by reference (in reg or on stack). */ \ + if (ngpr < maxgpr) \ + cc->gpr[ngpr++] = (GPRArg)dp; \ + else \ + cc->stack[nsp++] = (GPRArg)dp; + +#endif + +#define CCALL_HANDLE_COMPLEXRET \ + /* Return complex float in GPRs and complex double by reference. */ \ + cc->retref = (sz > 8); \ + if (cc->retref) { \ + if (ngpr < maxgpr) \ + cc->gpr[ngpr++] = (GPRArg)dp; \ + else \ + cc->stack[nsp++] = (GPRArg)dp; \ + } + +#endif + +#define CCALL_HANDLE_COMPLEXRET2 \ + if (!cc->retref) \ + *(int64_t *)dp = *(int64_t *)sp; /* Copy complex float from GPRs. */ + +#define CCALL_HANDLE_STRUCTARG \ + ngpr = maxgpr; /* Pass all structs by value on the stack. */ + +#define CCALL_HANDLE_COMPLEXARG \ + isfp = 1; /* Pass complex by value on stack. */ + +#define CCALL_HANDLE_REGARG \ + if (!isfp) { /* Only non-FP values may be passed in registers. */ \ + if (n > 1) { /* Anything > 32 bit is passed on the stack. */ \ + if (!LJ_ABI_WIN) ngpr = maxgpr; /* Prevent reordering. */ \ + } else if (ngpr + 1 <= maxgpr) { \ + dp = &cc->gpr[ngpr]; \ + ngpr += n; \ + goto done; \ + } \ + } + +#elif LJ_TARGET_X64 && LJ_ABI_WIN +/* -- Windows/x64 calling conventions ------------------------------------- */ + +#define CCALL_HANDLE_STRUCTRET \ + /* Return structs of size 1, 2, 4 or 8 in a GPR. */ \ + cc->retref = !(sz == 1 || sz == 2 || sz == 4 || sz == 8); \ + if (cc->retref) cc->gpr[ngpr++] = (GPRArg)dp; + +#define CCALL_HANDLE_COMPLEXRET CCALL_HANDLE_STRUCTRET + +#define CCALL_HANDLE_COMPLEXRET2 \ + if (!cc->retref) \ + *(int64_t *)dp = *(int64_t *)sp; /* Copy complex float from GPRs. */ + +#define CCALL_HANDLE_STRUCTARG \ + /* Pass structs of size 1, 2, 4 or 8 in a GPR by value. */ \ + if (!(sz == 1 || sz == 2 || sz == 4 || sz == 8)) { \ + rp = cdataptr(lj_cdata_new(cts, did, sz)); \ + sz = CTSIZE_PTR; /* Pass all other structs by reference. */ \ + } + +#define CCALL_HANDLE_COMPLEXARG \ + /* Pass complex float in a GPR and complex double by reference. */ \ + if (sz != 2*sizeof(float)) { \ + rp = cdataptr(lj_cdata_new(cts, did, sz)); \ + sz = CTSIZE_PTR; \ + } + +/* Windows/x64 argument registers are strictly positional (use ngpr). */ +#define CCALL_HANDLE_REGARG \ + if (isfp) { \ + if (ngpr < maxgpr) { dp = &cc->fpr[ngpr++]; nfpr = ngpr; goto done; } \ + } else { \ + if (ngpr < maxgpr) { dp = &cc->gpr[ngpr++]; goto done; } \ + } + +#elif LJ_TARGET_X64 +/* -- POSIX/x64 calling conventions --------------------------------------- */ + +#define CCALL_HANDLE_STRUCTRET \ + int rcl[2]; rcl[0] = rcl[1] = 0; \ + if (ccall_classify_struct(cts, ctr, rcl, 0)) { \ + cc->retref = 1; /* Return struct by reference. */ \ + cc->gpr[ngpr++] = (GPRArg)dp; \ + } else { \ + cc->retref = 0; /* Return small structs in registers. */ \ + } + +#define CCALL_HANDLE_STRUCTRET2 \ + int rcl[2]; rcl[0] = rcl[1] = 0; \ + ccall_classify_struct(cts, ctr, rcl, 0); \ + ccall_struct_ret(cc, rcl, dp, ctr->size); + +#define CCALL_HANDLE_COMPLEXRET \ + /* Complex values are returned in one or two FPRs. */ \ + cc->retref = 0; + +#define CCALL_HANDLE_COMPLEXRET2 \ + if (ctr->size == 2*sizeof(float)) { /* Copy complex float from FPR. */ \ + *(int64_t *)dp = cc->fpr[0].l[0]; \ + } else { /* Copy non-contiguous complex double from FPRs. */ \ + ((int64_t *)dp)[0] = cc->fpr[0].l[0]; \ + ((int64_t *)dp)[1] = cc->fpr[1].l[0]; \ + } + +#define CCALL_HANDLE_STRUCTARG \ + int rcl[2]; rcl[0] = rcl[1] = 0; \ + if (!ccall_classify_struct(cts, d, rcl, 0)) { \ + cc->nsp = nsp; cc->ngpr = ngpr; cc->nfpr = nfpr; \ + if (ccall_struct_arg(cc, cts, d, rcl, o, narg)) goto err_nyi; \ + nsp = cc->nsp; ngpr = cc->ngpr; nfpr = cc->nfpr; \ + continue; \ + } /* Pass all other structs by value on stack. */ + +#define CCALL_HANDLE_COMPLEXARG \ + isfp = 2; /* Pass complex in FPRs or on stack. Needs postprocessing. */ + +#define CCALL_HANDLE_REGARG \ + if (isfp) { /* Try to pass argument in FPRs. */ \ + int n2 = ctype_isvector(d->info) ? 1 : n; \ + if (nfpr + n2 <= CCALL_NARG_FPR) { \ + dp = &cc->fpr[nfpr]; \ + nfpr += n2; \ + goto done; \ + } \ + } else { /* Try to pass argument in GPRs. */ \ + /* Note that reordering is explicitly allowed in the x64 ABI. */ \ + if (n <= 2 && ngpr + n <= maxgpr) { \ + dp = &cc->gpr[ngpr]; \ + ngpr += n; \ + goto done; \ + } \ + } + +#elif LJ_TARGET_ARM +/* -- ARM calling conventions --------------------------------------------- */ + +#if LJ_ABI_SOFTFP + +#define CCALL_HANDLE_STRUCTRET \ + /* Return structs of size <= 4 in a GPR. */ \ + cc->retref = !(sz <= 4); \ + if (cc->retref) cc->gpr[ngpr++] = (GPRArg)dp; + +#define CCALL_HANDLE_COMPLEXRET \ + cc->retref = 1; /* Return all complex values by reference. */ \ + cc->gpr[ngpr++] = (GPRArg)dp; + +#define CCALL_HANDLE_COMPLEXRET2 \ + UNUSED(dp); /* Nothing to do. */ + +#define CCALL_HANDLE_STRUCTARG \ + /* Pass all structs by value in registers and/or on the stack. */ + +#define CCALL_HANDLE_COMPLEXARG \ + /* Pass complex by value in 2 or 4 GPRs. */ + +#define CCALL_HANDLE_REGARG_FP1 +#define CCALL_HANDLE_REGARG_FP2 + +#else + +#define CCALL_HANDLE_STRUCTRET \ + cc->retref = !ccall_classify_struct(cts, ctr, ct); \ + if (cc->retref) cc->gpr[ngpr++] = (GPRArg)dp; + +#define CCALL_HANDLE_STRUCTRET2 \ + if (ccall_classify_struct(cts, ctr, ct) > 1) sp = (uint8_t *)&cc->fpr[0]; \ + memcpy(dp, sp, ctr->size); + +#define CCALL_HANDLE_COMPLEXRET \ + if (!(ct->info & CTF_VARARG)) cc->retref = 0; /* Return complex in FPRs. */ + +#define CCALL_HANDLE_COMPLEXRET2 \ + if (!(ct->info & CTF_VARARG)) memcpy(dp, &cc->fpr[0], ctr->size); + +#define CCALL_HANDLE_STRUCTARG \ + isfp = (ccall_classify_struct(cts, d, ct) > 1); + /* Pass all structs by value in registers and/or on the stack. */ + +#define CCALL_HANDLE_COMPLEXARG \ + isfp = 1; /* Pass complex by value in FPRs or on stack. */ + +#define CCALL_HANDLE_REGARG_FP1 \ + if (isfp && !(ct->info & CTF_VARARG)) { \ + if ((d->info & CTF_ALIGN) > CTALIGN_PTR) { \ + if (nfpr + (n >> 1) <= CCALL_NARG_FPR) { \ + dp = &cc->fpr[nfpr]; \ + nfpr += (n >> 1); \ + goto done; \ + } \ + } else { \ + if (sz > 1 && fprodd != nfpr) fprodd = 0; \ + if (fprodd) { \ + if (2*nfpr+n <= 2*CCALL_NARG_FPR+1) { \ + dp = (void *)&cc->fpr[fprodd-1].f[1]; \ + nfpr += (n >> 1); \ + if ((n & 1)) fprodd = 0; else fprodd = nfpr-1; \ + goto done; \ + } \ + } else { \ + if (2*nfpr+n <= 2*CCALL_NARG_FPR) { \ + dp = (void *)&cc->fpr[nfpr]; \ + nfpr += (n >> 1); \ + if ((n & 1)) fprodd = ++nfpr; else fprodd = 0; \ + goto done; \ + } \ + } \ + } \ + fprodd = 0; /* No reordering after the first FP value is on stack. */ \ + } else { + +#define CCALL_HANDLE_REGARG_FP2 } + +#endif + +#define CCALL_HANDLE_REGARG \ + CCALL_HANDLE_REGARG_FP1 \ + if ((d->info & CTF_ALIGN) > CTALIGN_PTR) { \ + if (ngpr < maxgpr) \ + ngpr = (ngpr + 1u) & ~1u; /* Align to regpair. */ \ + } \ + if (ngpr < maxgpr) { \ + dp = &cc->gpr[ngpr]; \ + if (ngpr + n > maxgpr) { \ + nsp += ngpr + n - maxgpr; /* Assumes contiguous gpr/stack fields. */ \ + if (nsp > CCALL_MAXSTACK) goto err_nyi; /* Too many arguments. */ \ + ngpr = maxgpr; \ + } else { \ + ngpr += n; \ + } \ + goto done; \ + } CCALL_HANDLE_REGARG_FP2 + +#define CCALL_HANDLE_RET \ + if ((ct->info & CTF_VARARG)) sp = (uint8_t *)&cc->gpr[0]; + +#elif LJ_TARGET_ARM64 +/* -- ARM64 calling conventions ------------------------------------------- */ + +#define CCALL_HANDLE_STRUCTRET \ + cc->retref = !ccall_classify_struct(cts, ctr); \ + if (cc->retref) cc->retp = dp; + +#define CCALL_HANDLE_STRUCTRET2 \ + unsigned int cl = ccall_classify_struct(cts, ctr); \ + if ((cl & 4)) { /* Combine float HFA from separate registers. */ \ + CTSize i = (cl >> 8) - 1; \ + do { ((uint32_t *)dp)[i] = cc->fpr[i].lo; } while (i--); \ + } else { \ + if (cl > 1) sp = (uint8_t *)&cc->fpr[0]; \ + memcpy(dp, sp, ctr->size); \ + } + +#define CCALL_HANDLE_COMPLEXRET \ + /* Complex values are returned in one or two FPRs. */ \ + cc->retref = 0; + +#define CCALL_HANDLE_COMPLEXRET2 \ + if (ctr->size == 2*sizeof(float)) { /* Copy complex float from FPRs. */ \ + ((float *)dp)[0] = cc->fpr[0].f; \ + ((float *)dp)[1] = cc->fpr[1].f; \ + } else { /* Copy complex double from FPRs. */ \ + ((double *)dp)[0] = cc->fpr[0].d; \ + ((double *)dp)[1] = cc->fpr[1].d; \ + } + +#define CCALL_HANDLE_STRUCTARG \ + unsigned int cl = ccall_classify_struct(cts, d); \ + if (cl == 0) { /* Pass struct by reference. */ \ + rp = cdataptr(lj_cdata_new(cts, did, sz)); \ + sz = CTSIZE_PTR; \ + } else if (cl > 1) { /* Pass struct in FPRs or on stack. */ \ + isfp = (cl & 4) ? 2 : 1; \ + } /* else: Pass struct in GPRs or on stack. */ + +#define CCALL_HANDLE_COMPLEXARG \ + /* Pass complex by value in separate (!) FPRs or on stack. */ \ + isfp = sz == 2*sizeof(float) ? 2 : 1; + +#define CCALL_HANDLE_REGARG \ + if (LJ_TARGET_IOS && isva) { \ + /* IOS: All variadic arguments are on the stack. */ \ + } else if (isfp) { /* Try to pass argument in FPRs. */ \ + int n2 = ctype_isvector(d->info) ? 1 : n*isfp; \ + if (nfpr + n2 <= CCALL_NARG_FPR) { \ + dp = &cc->fpr[nfpr]; \ + nfpr += n2; \ + goto done; \ + } else { \ + nfpr = CCALL_NARG_FPR; /* Prevent reordering. */ \ + if (LJ_TARGET_IOS && d->size < 8) goto err_nyi; \ + } \ + } else { /* Try to pass argument in GPRs. */ \ + if (!LJ_TARGET_IOS && (d->info & CTF_ALIGN) > CTALIGN_PTR) \ + ngpr = (ngpr + 1u) & ~1u; /* Align to regpair. */ \ + if (ngpr + n <= maxgpr) { \ + dp = &cc->gpr[ngpr]; \ + ngpr += n; \ + goto done; \ + } else { \ + ngpr = maxgpr; /* Prevent reordering. */ \ + if (LJ_TARGET_IOS && d->size < 8) goto err_nyi; \ + } \ + } + +#if LJ_BE +#define CCALL_HANDLE_RET \ + if (ctype_isfp(ctr->info) && ctr->size == sizeof(float)) \ + sp = (uint8_t *)&cc->fpr[0].f; +#endif + + +#elif LJ_TARGET_PPC +/* -- PPC calling conventions --------------------------------------------- */ + +#define CCALL_HANDLE_STRUCTRET \ + cc->retref = 1; /* Return all structs by reference. */ \ + cc->gpr[ngpr++] = (GPRArg)dp; + +#define CCALL_HANDLE_COMPLEXRET \ + /* Complex values are returned in 2 or 4 GPRs. */ \ + cc->retref = 0; + +#define CCALL_HANDLE_COMPLEXRET2 \ + memcpy(dp, sp, ctr->size); /* Copy complex from GPRs. */ + +#define CCALL_HANDLE_STRUCTARG \ + rp = cdataptr(lj_cdata_new(cts, did, sz)); \ + sz = CTSIZE_PTR; /* Pass all structs by reference. */ + +#define CCALL_HANDLE_COMPLEXARG \ + /* Pass complex by value in 2 or 4 GPRs. */ + +#define CCALL_HANDLE_GPR \ + /* Try to pass argument in GPRs. */ \ + if (n > 1) { \ + lua_assert(n == 2 || n == 4); /* int64_t or complex (float). */ \ + if (ctype_isinteger(d->info) || ctype_isfp(d->info)) \ + ngpr = (ngpr + 1u) & ~1u; /* Align int64_t to regpair. */ \ + else if (ngpr + n > maxgpr) \ + ngpr = maxgpr; /* Prevent reordering. */ \ + } \ + if (ngpr + n <= maxgpr) { \ + dp = &cc->gpr[ngpr]; \ + ngpr += n; \ + goto done; \ + } \ + +#if LJ_ABI_SOFTFP +#define CCALL_HANDLE_REGARG CCALL_HANDLE_GPR +#else +#define CCALL_HANDLE_REGARG \ + if (isfp) { /* Try to pass argument in FPRs. */ \ + if (nfpr + 1 <= CCALL_NARG_FPR) { \ + dp = &cc->fpr[nfpr]; \ + nfpr += 1; \ + d = ctype_get(cts, CTID_DOUBLE); /* FPRs always hold doubles. */ \ + goto done; \ + } \ + } else { \ + CCALL_HANDLE_GPR \ + } +#endif + +#if !LJ_ABI_SOFTFP +#define CCALL_HANDLE_RET \ + if (ctype_isfp(ctr->info) && ctr->size == sizeof(float)) \ + ctr = ctype_get(cts, CTID_DOUBLE); /* FPRs always hold doubles. */ +#endif + +#elif LJ_TARGET_MIPS32 +/* -- MIPS o32 calling conventions ---------------------------------------- */ + +#define CCALL_HANDLE_STRUCTRET \ + cc->retref = 1; /* Return all structs by reference. */ \ + cc->gpr[ngpr++] = (GPRArg)dp; + +#define CCALL_HANDLE_COMPLEXRET \ + /* Complex values are returned in 1 or 2 FPRs. */ \ + cc->retref = 0; + +#if LJ_ABI_SOFTFP +#define CCALL_HANDLE_COMPLEXRET2 \ + if (ctr->size == 2*sizeof(float)) { /* Copy complex float from GPRs. */ \ + ((intptr_t *)dp)[0] = cc->gpr[0]; \ + ((intptr_t *)dp)[1] = cc->gpr[1]; \ + } else { /* Copy complex double from GPRs. */ \ + ((intptr_t *)dp)[0] = cc->gpr[0]; \ + ((intptr_t *)dp)[1] = cc->gpr[1]; \ + ((intptr_t *)dp)[2] = cc->gpr[2]; \ + ((intptr_t *)dp)[3] = cc->gpr[3]; \ + } +#else +#define CCALL_HANDLE_COMPLEXRET2 \ + if (ctr->size == 2*sizeof(float)) { /* Copy complex float from FPRs. */ \ + ((float *)dp)[0] = cc->fpr[0].f; \ + ((float *)dp)[1] = cc->fpr[1].f; \ + } else { /* Copy complex double from FPRs. */ \ + ((double *)dp)[0] = cc->fpr[0].d; \ + ((double *)dp)[1] = cc->fpr[1].d; \ + } +#endif + +#define CCALL_HANDLE_STRUCTARG \ + /* Pass all structs by value in registers and/or on the stack. */ + +#define CCALL_HANDLE_COMPLEXARG \ + /* Pass complex by value in 2 or 4 GPRs. */ + +#define CCALL_HANDLE_GPR \ + if ((d->info & CTF_ALIGN) > CTALIGN_PTR) \ + ngpr = (ngpr + 1u) & ~1u; /* Align to regpair. */ \ + if (ngpr < maxgpr) { \ + dp = &cc->gpr[ngpr]; \ + if (ngpr + n > maxgpr) { \ + nsp += ngpr + n - maxgpr; /* Assumes contiguous gpr/stack fields. */ \ + if (nsp > CCALL_MAXSTACK) goto err_nyi; /* Too many arguments. */ \ + ngpr = maxgpr; \ + } else { \ + ngpr += n; \ + } \ + goto done; \ + } + +#if !LJ_ABI_SOFTFP /* MIPS32 hard-float */ +#define CCALL_HANDLE_REGARG \ + if (isfp && nfpr < CCALL_NARG_FPR && !(ct->info & CTF_VARARG)) { \ + /* Try to pass argument in FPRs. */ \ + dp = n == 1 ? (void *)&cc->fpr[nfpr].f : (void *)&cc->fpr[nfpr].d; \ + nfpr++; ngpr += n; \ + goto done; \ + } else { /* Try to pass argument in GPRs. */ \ + nfpr = CCALL_NARG_FPR; \ + CCALL_HANDLE_GPR \ + } +#else /* MIPS32 soft-float */ +#define CCALL_HANDLE_REGARG CCALL_HANDLE_GPR +#endif + +#if !LJ_ABI_SOFTFP +/* On MIPS64 soft-float, position of float return values is endian-dependant. */ +#define CCALL_HANDLE_RET \ + if (ctype_isfp(ctr->info) && ctr->size == sizeof(float)) \ + sp = (uint8_t *)&cc->fpr[0].f; +#endif + +#elif LJ_TARGET_MIPS64 +/* -- MIPS n64 calling conventions ---------------------------------------- */ + +#define CCALL_HANDLE_STRUCTRET \ + cc->retref = !(sz <= 16); \ + if (cc->retref) cc->gpr[ngpr++] = (GPRArg)dp; + +#define CCALL_HANDLE_STRUCTRET2 \ + ccall_copy_struct(cc, ctr, dp, sp, ccall_classify_struct(cts, ctr, ct)); + +#define CCALL_HANDLE_COMPLEXRET \ + /* Complex values are returned in 1 or 2 FPRs. */ \ + cc->retref = 0; + +#if LJ_ABI_SOFTFP /* MIPS64 soft-float */ + +#define CCALL_HANDLE_COMPLEXRET2 \ + if (ctr->size == 2*sizeof(float)) { /* Copy complex float from GPRs. */ \ + ((intptr_t *)dp)[0] = cc->gpr[0]; \ + } else { /* Copy complex double from GPRs. */ \ + ((intptr_t *)dp)[0] = cc->gpr[0]; \ + ((intptr_t *)dp)[1] = cc->gpr[1]; \ + } + +#define CCALL_HANDLE_COMPLEXARG \ + /* Pass complex by value in 2 or 4 GPRs. */ + +/* Position of soft-float 'float' return value depends on endianess. */ +#define CCALL_HANDLE_RET \ + if (ctype_isfp(ctr->info) && ctr->size == sizeof(float)) \ + sp = (uint8_t *)cc->gpr + LJ_ENDIAN_SELECT(0, 4); + +#else /* MIPS64 hard-float */ + +#define CCALL_HANDLE_COMPLEXRET2 \ + if (ctr->size == 2*sizeof(float)) { /* Copy complex float from FPRs. */ \ + ((float *)dp)[0] = cc->fpr[0].f; \ + ((float *)dp)[1] = cc->fpr[1].f; \ + } else { /* Copy complex double from FPRs. */ \ + ((double *)dp)[0] = cc->fpr[0].d; \ + ((double *)dp)[1] = cc->fpr[1].d; \ + } + +#define CCALL_HANDLE_COMPLEXARG \ + if (sz == 2*sizeof(float)) { \ + isfp = 2; \ + if (ngpr < maxgpr) \ + sz *= 2; \ + } + +#define CCALL_HANDLE_RET \ + if (ctype_isfp(ctr->info) && ctr->size == sizeof(float)) \ + sp = (uint8_t *)&cc->fpr[0].f; + +#endif + +#define CCALL_HANDLE_STRUCTARG \ + /* Pass all structs by value in registers and/or on the stack. */ + +#define CCALL_HANDLE_REGARG \ + if (ngpr < maxgpr) { \ + dp = &cc->gpr[ngpr]; \ + if (ngpr + n > maxgpr) { \ + nsp += ngpr + n - maxgpr; /* Assumes contiguous gpr/stack fields. */ \ + if (nsp > CCALL_MAXSTACK) goto err_nyi; /* Too many arguments. */ \ + ngpr = maxgpr; \ + } else { \ + ngpr += n; \ + } \ + goto done; \ + } + +#else +#error "Missing calling convention definitions for this architecture" +#endif + +#ifndef CCALL_HANDLE_STRUCTRET2 +#define CCALL_HANDLE_STRUCTRET2 \ + memcpy(dp, sp, ctr->size); /* Copy struct return value from GPRs. */ +#endif + +/* -- x86 OSX ABI struct classification ----------------------------------- */ + +#if LJ_TARGET_X86 && LJ_TARGET_OSX + +/* Check for struct with single FP field. */ +static int ccall_classify_struct(CTState *cts, CType *ct) +{ + CTSize sz = ct->size; + if (!(sz == sizeof(float) || sz == sizeof(double))) return 0; + if ((ct->info & CTF_UNION)) return 0; + while (ct->sib) { + ct = ctype_get(cts, ct->sib); + if (ctype_isfield(ct->info)) { + CType *sct = ctype_rawchild(cts, ct); + if (ctype_isfp(sct->info)) { + if (sct->size == sz) + return (sz >> 2); /* Return 1 for float or 2 for double. */ + } else if (ctype_isstruct(sct->info)) { + if (sct->size) + return ccall_classify_struct(cts, sct); + } else { + break; + } + } else if (ctype_isbitfield(ct->info)) { + break; + } else if (ctype_isxattrib(ct->info, CTA_SUBTYPE)) { + CType *sct = ctype_rawchild(cts, ct); + if (sct->size) + return ccall_classify_struct(cts, sct); + } + } + return 0; +} + +#endif + +/* -- x64 struct classification ------------------------------------------- */ + +#if LJ_TARGET_X64 && !LJ_ABI_WIN + +/* Register classes for x64 struct classification. */ +#define CCALL_RCL_INT 1 +#define CCALL_RCL_SSE 2 +#define CCALL_RCL_MEM 4 +/* NYI: classify vectors. */ + +static int ccall_classify_struct(CTState *cts, CType *ct, int *rcl, CTSize ofs); + +/* Classify a C type. */ +static void ccall_classify_ct(CTState *cts, CType *ct, int *rcl, CTSize ofs) +{ + if (ctype_isarray(ct->info)) { + CType *cct = ctype_rawchild(cts, ct); + CTSize eofs, esz = cct->size, asz = ct->size; + for (eofs = 0; eofs < asz; eofs += esz) + ccall_classify_ct(cts, cct, rcl, ofs+eofs); + } else if (ctype_isstruct(ct->info)) { + ccall_classify_struct(cts, ct, rcl, ofs); + } else { + int cl = ctype_isfp(ct->info) ? CCALL_RCL_SSE : CCALL_RCL_INT; + lua_assert(ctype_hassize(ct->info)); + if ((ofs & (ct->size-1))) cl = CCALL_RCL_MEM; /* Unaligned. */ + rcl[(ofs >= 8)] |= cl; + } +} + +/* Recursively classify a struct based on its fields. */ +static int ccall_classify_struct(CTState *cts, CType *ct, int *rcl, CTSize ofs) +{ + if (ct->size > 16) return CCALL_RCL_MEM; /* Too big, gets memory class. */ + while (ct->sib) { + CTSize fofs; + ct = ctype_get(cts, ct->sib); + fofs = ofs+ct->size; + if (ctype_isfield(ct->info)) + ccall_classify_ct(cts, ctype_rawchild(cts, ct), rcl, fofs); + else if (ctype_isbitfield(ct->info)) + rcl[(fofs >= 8)] |= CCALL_RCL_INT; /* NYI: unaligned bitfields? */ + else if (ctype_isxattrib(ct->info, CTA_SUBTYPE)) + ccall_classify_struct(cts, ctype_rawchild(cts, ct), rcl, fofs); + } + return ((rcl[0]|rcl[1]) & CCALL_RCL_MEM); /* Memory class? */ +} + +/* Try to split up a small struct into registers. */ +static int ccall_struct_reg(CCallState *cc, GPRArg *dp, int *rcl) +{ + MSize ngpr = cc->ngpr, nfpr = cc->nfpr; + uint32_t i; + for (i = 0; i < 2; i++) { + lua_assert(!(rcl[i] & CCALL_RCL_MEM)); + if ((rcl[i] & CCALL_RCL_INT)) { /* Integer class takes precedence. */ + if (ngpr >= CCALL_NARG_GPR) return 1; /* Register overflow. */ + cc->gpr[ngpr++] = dp[i]; + } else if ((rcl[i] & CCALL_RCL_SSE)) { + if (nfpr >= CCALL_NARG_FPR) return 1; /* Register overflow. */ + cc->fpr[nfpr++].l[0] = dp[i]; + } + } + cc->ngpr = ngpr; cc->nfpr = nfpr; + return 0; /* Ok. */ +} + +/* Pass a small struct argument. */ +static int ccall_struct_arg(CCallState *cc, CTState *cts, CType *d, int *rcl, + TValue *o, int narg) +{ + GPRArg dp[2]; + dp[0] = dp[1] = 0; + /* Convert to temp. struct. */ + lj_cconv_ct_tv(cts, d, (uint8_t *)dp, o, CCF_ARG(narg)); + if (ccall_struct_reg(cc, dp, rcl)) { /* Register overflow? Pass on stack. */ + MSize nsp = cc->nsp, n = rcl[1] ? 2 : 1; + if (nsp + n > CCALL_MAXSTACK) return 1; /* Too many arguments. */ + cc->nsp = nsp + n; + memcpy(&cc->stack[nsp], dp, n*CTSIZE_PTR); + } + return 0; /* Ok. */ +} + +/* Combine returned small struct. */ +static void ccall_struct_ret(CCallState *cc, int *rcl, uint8_t *dp, CTSize sz) +{ + GPRArg sp[2]; + MSize ngpr = 0, nfpr = 0; + uint32_t i; + for (i = 0; i < 2; i++) { + if ((rcl[i] & CCALL_RCL_INT)) { /* Integer class takes precedence. */ + sp[i] = cc->gpr[ngpr++]; + } else if ((rcl[i] & CCALL_RCL_SSE)) { + sp[i] = cc->fpr[nfpr++].l[0]; + } + } + memcpy(dp, sp, sz); +} +#endif + +/* -- ARM hard-float ABI struct classification ---------------------------- */ + +#if LJ_TARGET_ARM && !LJ_ABI_SOFTFP + +/* Classify a struct based on its fields. */ +static unsigned int ccall_classify_struct(CTState *cts, CType *ct, CType *ctf) +{ + CTSize sz = ct->size; + unsigned int r = 0, n = 0, isu = (ct->info & CTF_UNION); + if ((ctf->info & CTF_VARARG)) goto noth; + while (ct->sib) { + CType *sct; + ct = ctype_get(cts, ct->sib); + if (ctype_isfield(ct->info)) { + sct = ctype_rawchild(cts, ct); + if (ctype_isfp(sct->info)) { + r |= sct->size; + if (!isu) n++; else if (n == 0) n = 1; + } else if (ctype_iscomplex(sct->info)) { + r |= (sct->size >> 1); + if (!isu) n += 2; else if (n < 2) n = 2; + } else if (ctype_isstruct(sct->info)) { + goto substruct; + } else { + goto noth; + } + } else if (ctype_isbitfield(ct->info)) { + goto noth; + } else if (ctype_isxattrib(ct->info, CTA_SUBTYPE)) { + sct = ctype_rawchild(cts, ct); + substruct: + if (sct->size > 0) { + unsigned int s = ccall_classify_struct(cts, sct, ctf); + if (s <= 1) goto noth; + r |= (s & 255); + if (!isu) n += (s >> 8); else if (n < (s >>8)) n = (s >> 8); + } + } + } + if ((r == 4 || r == 8) && n <= 4) + return r + (n << 8); +noth: /* Not a homogeneous float/double aggregate. */ + return (sz <= 4); /* Return structs of size <= 4 in a GPR. */ +} + +#endif + +/* -- ARM64 ABI struct classification ------------------------------------- */ + +#if LJ_TARGET_ARM64 + +/* Classify a struct based on its fields. */ +static unsigned int ccall_classify_struct(CTState *cts, CType *ct) +{ + CTSize sz = ct->size; + unsigned int r = 0, n = 0, isu = (ct->info & CTF_UNION); + while (ct->sib) { + CType *sct; + ct = ctype_get(cts, ct->sib); + if (ctype_isfield(ct->info)) { + sct = ctype_rawchild(cts, ct); + if (ctype_isfp(sct->info)) { + r |= sct->size; + if (!isu) n++; else if (n == 0) n = 1; + } else if (ctype_iscomplex(sct->info)) { + r |= (sct->size >> 1); + if (!isu) n += 2; else if (n < 2) n = 2; + } else if (ctype_isstruct(sct->info)) { + goto substruct; + } else { + goto noth; + } + } else if (ctype_isbitfield(ct->info)) { + goto noth; + } else if (ctype_isxattrib(ct->info, CTA_SUBTYPE)) { + sct = ctype_rawchild(cts, ct); + substruct: + if (sct->size > 0) { + unsigned int s = ccall_classify_struct(cts, sct); + if (s <= 1) goto noth; + r |= (s & 255); + if (!isu) n += (s >> 8); else if (n < (s >>8)) n = (s >> 8); + } + } + } + if ((r == 4 || r == 8) && n <= 4) + return r + (n << 8); +noth: /* Not a homogeneous float/double aggregate. */ + return (sz <= 16); /* Return structs of size <= 16 in GPRs. */ +} + +#endif + +/* -- MIPS64 ABI struct classification ---------------------------- */ + +#if LJ_TARGET_MIPS64 + +#define FTYPE_FLOAT 1 +#define FTYPE_DOUBLE 2 + +/* Classify FP fields (max. 2) and their types. */ +static unsigned int ccall_classify_struct(CTState *cts, CType *ct, CType *ctf) +{ + int n = 0, ft = 0; + if ((ctf->info & CTF_VARARG) || (ct->info & CTF_UNION)) + goto noth; + while (ct->sib) { + CType *sct; + ct = ctype_get(cts, ct->sib); + if (n == 2) { + goto noth; + } else if (ctype_isfield(ct->info)) { + sct = ctype_rawchild(cts, ct); + if (ctype_isfp(sct->info)) { + ft |= (sct->size == 4 ? FTYPE_FLOAT : FTYPE_DOUBLE) << 2*n; + n++; + } else { + goto noth; + } + } else if (ctype_isbitfield(ct->info) || + ctype_isxattrib(ct->info, CTA_SUBTYPE)) { + goto noth; + } + } + if (n <= 2) + return ft; +noth: /* Not a homogeneous float/double aggregate. */ + return 0; /* Struct is in GPRs. */ +} + +static void ccall_copy_struct(CCallState *cc, CType *ctr, void *dp, void *sp, + int ft) +{ + if (LJ_ABI_SOFTFP ? ft : + ((ft & 3) == FTYPE_FLOAT || (ft >> 2) == FTYPE_FLOAT)) { + int i, ofs = 0; + for (i = 0; ft != 0; i++, ft >>= 2) { + if ((ft & 3) == FTYPE_FLOAT) { +#if LJ_ABI_SOFTFP + /* The 2nd FP struct result is in CARG1 (gpr[2]) and not CRET2. */ + memcpy((uint8_t *)dp + ofs, + (uint8_t *)&cc->gpr[2*i] + LJ_ENDIAN_SELECT(0, 4), 4); +#else + *(float *)((uint8_t *)dp + ofs) = cc->fpr[i].f; +#endif + ofs += 4; + } else { + ofs = (ofs + 7) & ~7; /* 64 bit alignment. */ +#if LJ_ABI_SOFTFP + *(intptr_t *)((uint8_t *)dp + ofs) = cc->gpr[2*i]; +#else + *(double *)((uint8_t *)dp + ofs) = cc->fpr[i].d; +#endif + ofs += 8; + } + } + } else { +#if !LJ_ABI_SOFTFP + if (ft) sp = (uint8_t *)&cc->fpr[0]; +#endif + memcpy(dp, sp, ctr->size); + } +} + +#endif + +/* -- Common C call handling ---------------------------------------------- */ + +/* Infer the destination CTypeID for a vararg argument. */ +CTypeID lj_ccall_ctid_vararg(CTState *cts, cTValue *o) +{ + if (tvisnumber(o)) { + return CTID_DOUBLE; + } else if (tviscdata(o)) { + CTypeID id = cdataV(o)->ctypeid; + CType *s = ctype_get(cts, id); + if (ctype_isrefarray(s->info)) { + return lj_ctype_intern(cts, + CTINFO(CT_PTR, CTALIGN_PTR|ctype_cid(s->info)), CTSIZE_PTR); + } else if (ctype_isstruct(s->info) || ctype_isfunc(s->info)) { + /* NYI: how to pass a struct by value in a vararg argument? */ + return lj_ctype_intern(cts, CTINFO(CT_PTR, CTALIGN_PTR|id), CTSIZE_PTR); + } else if (ctype_isfp(s->info) && s->size == sizeof(float)) { + return CTID_DOUBLE; + } else { + return id; + } + } else if (tvisstr(o)) { + return CTID_P_CCHAR; + } else if (tvisbool(o)) { + return CTID_BOOL; + } else { + return CTID_P_VOID; + } +} + +/* Setup arguments for C call. */ +static int ccall_set_args(lua_State *L, CTState *cts, CType *ct, + CCallState *cc) +{ + int gcsteps = 0; + TValue *o, *top = L->top; + CTypeID fid; + CType *ctr; + MSize maxgpr, ngpr = 0, nsp = 0, narg; +#if CCALL_NARG_FPR + MSize nfpr = 0; +#if LJ_TARGET_ARM + MSize fprodd = 0; +#endif +#endif + + /* Clear unused regs to get some determinism in case of misdeclaration. */ + memset(cc->gpr, 0, sizeof(cc->gpr)); +#if CCALL_NUM_FPR + memset(cc->fpr, 0, sizeof(cc->fpr)); +#endif + +#if LJ_TARGET_X86 + /* x86 has several different calling conventions. */ + cc->resx87 = 0; + switch (ctype_cconv(ct->info)) { + case CTCC_FASTCALL: maxgpr = 2; break; + case CTCC_THISCALL: maxgpr = 1; break; + default: maxgpr = 0; break; + } +#else + maxgpr = CCALL_NARG_GPR; +#endif + + /* Perform required setup for some result types. */ + ctr = ctype_rawchild(cts, ct); + if (ctype_isvector(ctr->info)) { + if (!(CCALL_VECTOR_REG && (ctr->size == 8 || ctr->size == 16))) + goto err_nyi; + } else if (ctype_iscomplex(ctr->info) || ctype_isstruct(ctr->info)) { + /* Preallocate cdata object and anchor it after arguments. */ + CTSize sz = ctr->size; + GCcdata *cd = lj_cdata_new(cts, ctype_cid(ct->info), sz); + void *dp = cdataptr(cd); + setcdataV(L, L->top++, cd); + if (ctype_isstruct(ctr->info)) { + CCALL_HANDLE_STRUCTRET + } else { + CCALL_HANDLE_COMPLEXRET + } +#if LJ_TARGET_X86 + } else if (ctype_isfp(ctr->info)) { + cc->resx87 = ctr->size == sizeof(float) ? 1 : 2; +#endif + } + + /* Skip initial attributes. */ + fid = ct->sib; + while (fid) { + CType *ctf = ctype_get(cts, fid); + if (!ctype_isattrib(ctf->info)) break; + fid = ctf->sib; + } + + /* Walk through all passed arguments. */ + for (o = L->base+1, narg = 1; o < top; o++, narg++) { + CTypeID did; + CType *d; + CTSize sz; + MSize n, isfp = 0, isva = 0; + void *dp, *rp = NULL; + + if (fid) { /* Get argument type from field. */ + CType *ctf = ctype_get(cts, fid); + fid = ctf->sib; + lua_assert(ctype_isfield(ctf->info)); + did = ctype_cid(ctf->info); + } else { + if (!(ct->info & CTF_VARARG)) + lj_err_caller(L, LJ_ERR_FFI_NUMARG); /* Too many arguments. */ + did = lj_ccall_ctid_vararg(cts, o); /* Infer vararg type. */ + isva = 1; + } + d = ctype_raw(cts, did); + sz = d->size; + + /* Find out how (by value/ref) and where (GPR/FPR) to pass an argument. */ + if (ctype_isnum(d->info)) { + if (sz > 8) goto err_nyi; + if ((d->info & CTF_FP)) + isfp = 1; + } else if (ctype_isvector(d->info)) { + if (CCALL_VECTOR_REG && (sz == 8 || sz == 16)) + isfp = 1; + else + goto err_nyi; + } else if (ctype_isstruct(d->info)) { + CCALL_HANDLE_STRUCTARG + } else if (ctype_iscomplex(d->info)) { + CCALL_HANDLE_COMPLEXARG + } else { + sz = CTSIZE_PTR; + } + sz = (sz + CTSIZE_PTR-1) & ~(CTSIZE_PTR-1); + n = sz / CTSIZE_PTR; /* Number of GPRs or stack slots needed. */ + + CCALL_HANDLE_REGARG /* Handle register arguments. */ + + /* Otherwise pass argument on stack. */ + if (CCALL_ALIGN_STACKARG && !rp && (d->info & CTF_ALIGN) > CTALIGN_PTR) { + MSize align = (1u << ctype_align(d->info-CTALIGN_PTR)) -1; + nsp = (nsp + align) & ~align; /* Align argument on stack. */ + } + if (nsp + n > CCALL_MAXSTACK) { /* Too many arguments. */ + err_nyi: + lj_err_caller(L, LJ_ERR_FFI_NYICALL); + } + dp = &cc->stack[nsp]; + nsp += n; + isva = 0; + + done: + if (rp) { /* Pass by reference. */ + gcsteps++; + *(void **)dp = rp; + dp = rp; + } + lj_cconv_ct_tv(cts, d, (uint8_t *)dp, o, CCF_ARG(narg)); + /* Extend passed integers to 32 bits at least. */ + if (ctype_isinteger_or_bool(d->info) && d->size < 4) { + if (d->info & CTF_UNSIGNED) + *(uint32_t *)dp = d->size == 1 ? (uint32_t)*(uint8_t *)dp : + (uint32_t)*(uint16_t *)dp; + else + *(int32_t *)dp = d->size == 1 ? (int32_t)*(int8_t *)dp : + (int32_t)*(int16_t *)dp; + } +#if LJ_TARGET_ARM64 && LJ_BE + if (isfp && d->size == sizeof(float)) + ((float *)dp)[1] = ((float *)dp)[0]; /* Floats occupy high slot. */ +#endif +#if LJ_TARGET_MIPS64 || (LJ_TARGET_ARM64 && LJ_BE) + if ((ctype_isinteger_or_bool(d->info) || ctype_isenum(d->info) +#if LJ_TARGET_MIPS64 + || (isfp && nsp == 0) +#endif + ) && d->size <= 4) { + *(int64_t *)dp = (int64_t)*(int32_t *)dp; /* Sign-extend to 64 bit. */ + } +#endif +#if LJ_TARGET_X64 && LJ_ABI_WIN + if (isva) { /* Windows/x64 mirrors varargs in both register sets. */ + if (nfpr == ngpr) + cc->gpr[ngpr-1] = cc->fpr[ngpr-1].l[0]; + else + cc->fpr[ngpr-1].l[0] = cc->gpr[ngpr-1]; + } +#else + UNUSED(isva); +#endif +#if LJ_TARGET_X64 && !LJ_ABI_WIN + if (isfp == 2 && n == 2 && (uint8_t *)dp == (uint8_t *)&cc->fpr[nfpr-2]) { + cc->fpr[nfpr-1].d[0] = cc->fpr[nfpr-2].d[1]; /* Split complex double. */ + cc->fpr[nfpr-2].d[1] = 0; + } +#elif LJ_TARGET_ARM64 || (LJ_TARGET_MIPS64 && !LJ_ABI_SOFTFP) + if (isfp == 2 && (uint8_t *)dp < (uint8_t *)cc->stack) { + /* Split float HFA or complex float into separate registers. */ + CTSize i = (sz >> 2) - 1; + do { ((uint64_t *)dp)[i] = ((uint32_t *)dp)[i]; } while (i--); + } +#else + UNUSED(isfp); +#endif + } + if (fid) lj_err_caller(L, LJ_ERR_FFI_NUMARG); /* Too few arguments. */ + +#if LJ_TARGET_X64 || (LJ_TARGET_PPC && !LJ_ABI_SOFTFP) + cc->nfpr = nfpr; /* Required for vararg functions. */ +#endif + cc->nsp = nsp; + cc->spadj = (CCALL_SPS_FREE + CCALL_SPS_EXTRA)*CTSIZE_PTR; + if (nsp > CCALL_SPS_FREE) + cc->spadj += (((nsp-CCALL_SPS_FREE)*CTSIZE_PTR + 15u) & ~15u); + return gcsteps; +} + +/* Get results from C call. */ +static int ccall_get_results(lua_State *L, CTState *cts, CType *ct, + CCallState *cc, int *ret) +{ + CType *ctr = ctype_rawchild(cts, ct); + uint8_t *sp = (uint8_t *)&cc->gpr[0]; + if (ctype_isvoid(ctr->info)) { + *ret = 0; /* Zero results. */ + return 0; /* No additional GC step. */ + } + *ret = 1; /* One result. */ + if (ctype_isstruct(ctr->info)) { + /* Return cdata object which is already on top of stack. */ + if (!cc->retref) { + void *dp = cdataptr(cdataV(L->top-1)); /* Use preallocated object. */ + CCALL_HANDLE_STRUCTRET2 + } + return 1; /* One GC step. */ + } + if (ctype_iscomplex(ctr->info)) { + /* Return cdata object which is already on top of stack. */ + void *dp = cdataptr(cdataV(L->top-1)); /* Use preallocated object. */ + CCALL_HANDLE_COMPLEXRET2 + return 1; /* One GC step. */ + } + if (LJ_BE && ctr->size < CTSIZE_PTR && + (ctype_isinteger_or_bool(ctr->info) || ctype_isenum(ctr->info))) + sp += (CTSIZE_PTR - ctr->size); +#if CCALL_NUM_FPR + if (ctype_isfp(ctr->info) || ctype_isvector(ctr->info)) + sp = (uint8_t *)&cc->fpr[0]; +#endif +#ifdef CCALL_HANDLE_RET + CCALL_HANDLE_RET +#endif + /* No reference types end up here, so there's no need for the CTypeID. */ + lua_assert(!(ctype_isrefarray(ctr->info) || ctype_isstruct(ctr->info))); + return lj_cconv_tv_ct(cts, ctr, 0, L->top-1, sp); +} + +/* Call C function. */ +int lj_ccall_func(lua_State *L, GCcdata *cd) +{ + CTState *cts = ctype_cts(L); + CType *ct = ctype_raw(cts, cd->ctypeid); + CTSize sz = CTSIZE_PTR; + if (ctype_isptr(ct->info)) { + sz = ct->size; + ct = ctype_rawchild(cts, ct); + } + if (ctype_isfunc(ct->info)) { + CCallState cc; + int gcsteps, ret; + cc.func = (void (*)(void))cdata_getptr(cdataptr(cd), sz); + gcsteps = ccall_set_args(L, cts, ct, &cc); + ct = (CType *)((intptr_t)ct-(intptr_t)cts->tab); + cts->cb.slot = ~0u; + lj_vm_ffi_call(&cc); + if (cts->cb.slot != ~0u) { /* Blacklist function that called a callback. */ + TValue tv; + setlightudV(&tv, (void *)cc.func); + setboolV(lj_tab_set(L, cts->miscmap, &tv), 1); + } + ct = (CType *)((intptr_t)ct+(intptr_t)cts->tab); /* May be reallocated. */ + gcsteps += ccall_get_results(L, cts, ct, &cc, &ret); +#if LJ_TARGET_X86 && LJ_ABI_WIN + /* Automatically detect __stdcall and fix up C function declaration. */ + if (cc.spadj && ctype_cconv(ct->info) == CTCC_CDECL) { + CTF_INSERT(ct->info, CCONV, CTCC_STDCALL); + lj_trace_abort(G(L)); + } +#endif + while (gcsteps-- > 0) + lj_gc_check(L); + return ret; + } + return -1; /* Not a function. */ +} + +#endif diff --git a/lib/LuaJIT/lj_ccall.h b/lib/LuaJIT/lj_ccall.h new file mode 100644 index 0000000..6efa48c --- /dev/null +++ b/lib/LuaJIT/lj_ccall.h @@ -0,0 +1,194 @@ +/* +** FFI C call handling. +** Copyright (C) 2005-2017 Mike Pall. See Copyright Notice in luajit.h +*/ + +#ifndef _LJ_CCALL_H +#define _LJ_CCALL_H + +#include "lj_obj.h" +#include "lj_ctype.h" + +#if LJ_HASFFI + +/* -- C calling conventions ----------------------------------------------- */ + +#if LJ_TARGET_X86ORX64 + +#if LJ_TARGET_X86 +#define CCALL_NARG_GPR 2 /* For fastcall arguments. */ +#define CCALL_NARG_FPR 0 +#define CCALL_NRET_GPR 2 +#define CCALL_NRET_FPR 1 /* For FP results on x87 stack. */ +#define CCALL_ALIGN_STACKARG 0 /* Don't align argument on stack. */ +#elif LJ_ABI_WIN +#define CCALL_NARG_GPR 4 +#define CCALL_NARG_FPR 4 +#define CCALL_NRET_GPR 1 +#define CCALL_NRET_FPR 1 +#define CCALL_SPS_EXTRA 4 +#else +#define CCALL_NARG_GPR 6 +#define CCALL_NARG_FPR 8 +#define CCALL_NRET_GPR 2 +#define CCALL_NRET_FPR 2 +#define CCALL_VECTOR_REG 1 /* Pass vectors in registers. */ +#endif + +#define CCALL_SPS_FREE 1 +#define CCALL_ALIGN_CALLSTATE 16 + +typedef LJ_ALIGN(16) union FPRArg { + double d[2]; + float f[4]; + uint8_t b[16]; + uint16_t s[8]; + int i[4]; + int64_t l[2]; +} FPRArg; + +typedef intptr_t GPRArg; + +#elif LJ_TARGET_ARM + +#define CCALL_NARG_GPR 4 +#define CCALL_NRET_GPR 2 /* For softfp double. */ +#if LJ_ABI_SOFTFP +#define CCALL_NARG_FPR 0 +#define CCALL_NRET_FPR 0 +#else +#define CCALL_NARG_FPR 8 +#define CCALL_NRET_FPR 4 +#endif +#define CCALL_SPS_FREE 0 + +typedef intptr_t GPRArg; +typedef union FPRArg { + double d; + float f[2]; +} FPRArg; + +#elif LJ_TARGET_ARM64 + +#define CCALL_NARG_GPR 8 +#define CCALL_NRET_GPR 2 +#define CCALL_NARG_FPR 8 +#define CCALL_NRET_FPR 4 +#define CCALL_SPS_FREE 0 + +typedef intptr_t GPRArg; +typedef union FPRArg { + double d; + struct { LJ_ENDIAN_LOHI(float f; , float g;) }; + struct { LJ_ENDIAN_LOHI(uint32_t lo; , uint32_t hi;) }; +} FPRArg; + +#elif LJ_TARGET_PPC + +#define CCALL_NARG_GPR 8 +#define CCALL_NARG_FPR (LJ_ABI_SOFTFP ? 0 : 8) +#define CCALL_NRET_GPR 4 /* For complex double. */ +#define CCALL_NRET_FPR (LJ_ABI_SOFTFP ? 0 : 1) +#define CCALL_SPS_EXTRA 4 +#define CCALL_SPS_FREE 0 + +typedef intptr_t GPRArg; +typedef double FPRArg; + +#elif LJ_TARGET_MIPS32 + +#define CCALL_NARG_GPR 4 +#define CCALL_NARG_FPR (LJ_ABI_SOFTFP ? 0 : 2) +#define CCALL_NRET_GPR (LJ_ABI_SOFTFP ? 4 : 2) +#define CCALL_NRET_FPR (LJ_ABI_SOFTFP ? 0 : 2) +#define CCALL_SPS_EXTRA 7 +#define CCALL_SPS_FREE 1 + +typedef intptr_t GPRArg; +typedef union FPRArg { + double d; + struct { LJ_ENDIAN_LOHI(float f; , float g;) }; +} FPRArg; + +#elif LJ_TARGET_MIPS64 + +/* FP args are positional and overlay the GPR array. */ +#define CCALL_NARG_GPR 8 +#define CCALL_NARG_FPR 0 +#define CCALL_NRET_GPR 2 +#define CCALL_NRET_FPR (LJ_ABI_SOFTFP ? 0 : 2) +#define CCALL_SPS_EXTRA 3 +#define CCALL_SPS_FREE 1 + +typedef intptr_t GPRArg; +typedef union FPRArg { + double d; + struct { LJ_ENDIAN_LOHI(float f; , float g;) }; +} FPRArg; + +#else +#error "Missing calling convention definitions for this architecture" +#endif + +#ifndef CCALL_SPS_EXTRA +#define CCALL_SPS_EXTRA 0 +#endif +#ifndef CCALL_VECTOR_REG +#define CCALL_VECTOR_REG 0 +#endif +#ifndef CCALL_ALIGN_STACKARG +#define CCALL_ALIGN_STACKARG 1 +#endif +#ifndef CCALL_ALIGN_CALLSTATE +#define CCALL_ALIGN_CALLSTATE 8 +#endif + +#define CCALL_NUM_GPR \ + (CCALL_NARG_GPR > CCALL_NRET_GPR ? CCALL_NARG_GPR : CCALL_NRET_GPR) +#define CCALL_NUM_FPR \ + (CCALL_NARG_FPR > CCALL_NRET_FPR ? CCALL_NARG_FPR : CCALL_NRET_FPR) + +/* Check against constants in lj_ctype.h. */ +LJ_STATIC_ASSERT(CCALL_NUM_GPR <= CCALL_MAX_GPR); +LJ_STATIC_ASSERT(CCALL_NUM_FPR <= CCALL_MAX_FPR); + +#define CCALL_MAXSTACK 32 + +/* -- C call state -------------------------------------------------------- */ + +typedef LJ_ALIGN(CCALL_ALIGN_CALLSTATE) struct CCallState { + void (*func)(void); /* Pointer to called function. */ + uint32_t spadj; /* Stack pointer adjustment. */ + uint8_t nsp; /* Number of stack slots. */ + uint8_t retref; /* Return value by reference. */ +#if LJ_TARGET_X64 + uint8_t ngpr; /* Number of arguments in GPRs. */ + uint8_t nfpr; /* Number of arguments in FPRs. */ +#elif LJ_TARGET_X86 + uint8_t resx87; /* Result on x87 stack: 1:float, 2:double. */ +#elif LJ_TARGET_ARM64 + void *retp; /* Aggregate return pointer in x8. */ +#elif LJ_TARGET_PPC + uint8_t nfpr; /* Number of arguments in FPRs. */ +#endif +#if LJ_32 + int32_t align1; +#endif +#if CCALL_NUM_FPR + FPRArg fpr[CCALL_NUM_FPR]; /* Arguments/results in FPRs. */ +#endif + GPRArg gpr[CCALL_NUM_GPR]; /* Arguments/results in GPRs. */ + GPRArg stack[CCALL_MAXSTACK]; /* Stack slots. */ +} CCallState; + +/* -- C call handling ----------------------------------------------------- */ + +/* Really belongs to lj_vm.h. */ +LJ_ASMF void LJ_FASTCALL lj_vm_ffi_call(CCallState *cc); + +LJ_FUNC CTypeID lj_ccall_ctid_vararg(CTState *cts, cTValue *o); +LJ_FUNC int lj_ccall_func(lua_State *L, GCcdata *cd); + +#endif + +#endif diff --git a/lib/LuaJIT/lj_ccall.o b/lib/LuaJIT/lj_ccall.o new file mode 100644 index 0000000000000000000000000000000000000000..94fc5995775f278ac80e88f272330067152b7a7d GIT binary patch literal 5928 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 LGMwp6HvfMBKa4W2 literal 0 HcmV?d00001 diff --git a/lib/LuaJIT/lj_ccall_dyn.o b/lib/LuaJIT/lj_ccall_dyn.o new file mode 100644 index 0000000000000000000000000000000000000000..94fc5995775f278ac80e88f272330067152b7a7d GIT binary patch literal 5928 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 LGMwp6HvfMBKa4W2 literal 0 HcmV?d00001 diff --git a/lib/LuaJIT/lj_ccallback.c b/lib/LuaJIT/lj_ccallback.c new file mode 100644 index 0000000..412dbf8 --- /dev/null +++ b/lib/LuaJIT/lj_ccallback.c @@ -0,0 +1,787 @@ +/* +** FFI C callback handling. +** Copyright (C) 2005-2017 Mike Pall. See Copyright Notice in luajit.h +*/ + +#include "lj_obj.h" + +#if LJ_HASFFI + +#include "lj_gc.h" +#include "lj_err.h" +#include "lj_tab.h" +#include "lj_state.h" +#include "lj_frame.h" +#include "lj_ctype.h" +#include "lj_cconv.h" +#include "lj_ccall.h" +#include "lj_ccallback.h" +#include "lj_target.h" +#include "lj_mcode.h" +#include "lj_trace.h" +#include "lj_vm.h" + +/* -- Target-specific handling of callback slots -------------------------- */ + +#define CALLBACK_MCODE_SIZE (LJ_PAGESIZE * LJ_NUM_CBPAGE) + +#if LJ_OS_NOJIT + +/* Callbacks disabled. */ +#define CALLBACK_SLOT2OFS(slot) (0*(slot)) +#define CALLBACK_OFS2SLOT(ofs) (0*(ofs)) +#define CALLBACK_MAX_SLOT 0 + +#elif LJ_TARGET_X86ORX64 + +#define CALLBACK_MCODE_HEAD (LJ_64 ? 8 : 0) +#define CALLBACK_MCODE_GROUP (-2+1+2+(LJ_GC64 ? 10 : 5)+(LJ_64 ? 6 : 5)) + +#define CALLBACK_SLOT2OFS(slot) \ + (CALLBACK_MCODE_HEAD + CALLBACK_MCODE_GROUP*((slot)/32) + 4*(slot)) + +static MSize CALLBACK_OFS2SLOT(MSize ofs) +{ + MSize group; + ofs -= CALLBACK_MCODE_HEAD; + group = ofs / (32*4 + CALLBACK_MCODE_GROUP); + return (ofs % (32*4 + CALLBACK_MCODE_GROUP))/4 + group*32; +} + +#define CALLBACK_MAX_SLOT \ + (((CALLBACK_MCODE_SIZE-CALLBACK_MCODE_HEAD)/(CALLBACK_MCODE_GROUP+4*32))*32) + +#elif LJ_TARGET_ARM + +#define CALLBACK_MCODE_HEAD 32 + +#elif LJ_TARGET_ARM64 + +#define CALLBACK_MCODE_HEAD 32 + +#elif LJ_TARGET_PPC + +#define CALLBACK_MCODE_HEAD 24 + +#elif LJ_TARGET_MIPS32 + +#define CALLBACK_MCODE_HEAD 20 + +#elif LJ_TARGET_MIPS64 + +#define CALLBACK_MCODE_HEAD 52 + +#else + +/* Missing support for this architecture. */ +#define CALLBACK_SLOT2OFS(slot) (0*(slot)) +#define CALLBACK_OFS2SLOT(ofs) (0*(ofs)) +#define CALLBACK_MAX_SLOT 0 + +#endif + +#ifndef CALLBACK_SLOT2OFS +#define CALLBACK_SLOT2OFS(slot) (CALLBACK_MCODE_HEAD + 8*(slot)) +#define CALLBACK_OFS2SLOT(ofs) (((ofs)-CALLBACK_MCODE_HEAD)/8) +#define CALLBACK_MAX_SLOT (CALLBACK_OFS2SLOT(CALLBACK_MCODE_SIZE)) +#endif + +/* Convert callback slot number to callback function pointer. */ +static void *callback_slot2ptr(CTState *cts, MSize slot) +{ + return (uint8_t *)cts->cb.mcode + CALLBACK_SLOT2OFS(slot); +} + +/* Convert callback function pointer to slot number. */ +MSize lj_ccallback_ptr2slot(CTState *cts, void *p) +{ + uintptr_t ofs = (uintptr_t)((uint8_t *)p -(uint8_t *)cts->cb.mcode); + if (ofs < CALLBACK_MCODE_SIZE) { + MSize slot = CALLBACK_OFS2SLOT((MSize)ofs); + if (CALLBACK_SLOT2OFS(slot) == (MSize)ofs) + return slot; + } + return ~0u; /* Not a known callback function pointer. */ +} + +/* Initialize machine code for callback function pointers. */ +#if LJ_OS_NOJIT +/* Disabled callback support. */ +#define callback_mcode_init(g, p) UNUSED(p) +#elif LJ_TARGET_X86ORX64 +static void callback_mcode_init(global_State *g, uint8_t *page) +{ + uint8_t *p = page; + uint8_t *target = (uint8_t *)(void *)lj_vm_ffi_callback; + MSize slot; +#if LJ_64 + *(void **)p = target; p += 8; +#endif + for (slot = 0; slot < CALLBACK_MAX_SLOT; slot++) { + /* mov al, slot; jmp group */ + *p++ = XI_MOVrib | RID_EAX; *p++ = (uint8_t)slot; + if ((slot & 31) == 31 || slot == CALLBACK_MAX_SLOT-1) { + /* push ebp/rbp; mov ah, slot>>8; mov ebp, &g. */ + *p++ = XI_PUSH + RID_EBP; + *p++ = XI_MOVrib | (RID_EAX+4); *p++ = (uint8_t)(slot >> 8); +#if LJ_GC64 + *p++ = 0x48; *p++ = XI_MOVri | RID_EBP; + *(uint64_t *)p = (uint64_t)(g); p += 8; +#else + *p++ = XI_MOVri | RID_EBP; + *(int32_t *)p = i32ptr(g); p += 4; +#endif +#if LJ_64 + /* jmp [rip-pageofs] where lj_vm_ffi_callback is stored. */ + *p++ = XI_GROUP5; *p++ = XM_OFS0 + (XOg_JMP<<3) + RID_EBP; + *(int32_t *)p = (int32_t)(page-(p+4)); p += 4; +#else + /* jmp lj_vm_ffi_callback. */ + *p++ = XI_JMP; *(int32_t *)p = target-(p+4); p += 4; +#endif + } else { + *p++ = XI_JMPs; *p++ = (uint8_t)((2+2)*(31-(slot&31)) - 2); + } + } + lua_assert(p - page <= CALLBACK_MCODE_SIZE); +} +#elif LJ_TARGET_ARM +static void callback_mcode_init(global_State *g, uint32_t *page) +{ + uint32_t *p = page; + void *target = (void *)lj_vm_ffi_callback; + MSize slot; + /* This must match with the saveregs macro in buildvm_arm.dasc. */ + *p++ = ARMI_SUB|ARMF_D(RID_R12)|ARMF_N(RID_R12)|ARMF_M(RID_PC); + *p++ = ARMI_PUSH|ARMF_N(RID_SP)|RSET_RANGE(RID_R4,RID_R11+1)|RID2RSET(RID_LR); + *p++ = ARMI_SUB|ARMI_K12|ARMF_D(RID_R12)|ARMF_N(RID_R12)|CALLBACK_MCODE_HEAD; + *p++ = ARMI_STR|ARMI_LS_P|ARMI_LS_W|ARMF_D(RID_R12)|ARMF_N(RID_SP)|(CFRAME_SIZE-4*9); + *p++ = ARMI_LDR|ARMI_LS_P|ARMI_LS_U|ARMF_D(RID_R12)|ARMF_N(RID_PC); + *p++ = ARMI_LDR|ARMI_LS_P|ARMI_LS_U|ARMF_D(RID_PC)|ARMF_N(RID_PC); + *p++ = u32ptr(g); + *p++ = u32ptr(target); + for (slot = 0; slot < CALLBACK_MAX_SLOT; slot++) { + *p++ = ARMI_MOV|ARMF_D(RID_R12)|ARMF_M(RID_PC); + *p = ARMI_B | ((page-p-2) & 0x00ffffffu); + p++; + } + lua_assert(p - page <= CALLBACK_MCODE_SIZE); +} +#elif LJ_TARGET_ARM64 +static void callback_mcode_init(global_State *g, uint32_t *page) +{ + uint32_t *p = page; + void *target = (void *)lj_vm_ffi_callback; + MSize slot; + *p++ = A64I_LE(A64I_LDRLx | A64F_D(RID_X11) | A64F_S19(4)); + *p++ = A64I_LE(A64I_LDRLx | A64F_D(RID_X10) | A64F_S19(5)); + *p++ = A64I_LE(A64I_BR | A64F_N(RID_X11)); + *p++ = A64I_LE(A64I_NOP); + ((void **)p)[0] = target; + ((void **)p)[1] = g; + p += 4; + for (slot = 0; slot < CALLBACK_MAX_SLOT; slot++) { + *p++ = A64I_LE(A64I_MOVZw | A64F_D(RID_X9) | A64F_U16(slot)); + *p = A64I_LE(A64I_B | A64F_S26((page-p) & 0x03ffffffu)); + p++; + } + lua_assert(p - page <= CALLBACK_MCODE_SIZE); +} +#elif LJ_TARGET_PPC +static void callback_mcode_init(global_State *g, uint32_t *page) +{ + uint32_t *p = page; + void *target = (void *)lj_vm_ffi_callback; + MSize slot; + *p++ = PPCI_LIS | PPCF_T(RID_TMP) | (u32ptr(target) >> 16); + *p++ = PPCI_LIS | PPCF_T(RID_R12) | (u32ptr(g) >> 16); + *p++ = PPCI_ORI | PPCF_A(RID_TMP)|PPCF_T(RID_TMP) | (u32ptr(target) & 0xffff); + *p++ = PPCI_ORI | PPCF_A(RID_R12)|PPCF_T(RID_R12) | (u32ptr(g) & 0xffff); + *p++ = PPCI_MTCTR | PPCF_T(RID_TMP); + *p++ = PPCI_BCTR; + for (slot = 0; slot < CALLBACK_MAX_SLOT; slot++) { + *p++ = PPCI_LI | PPCF_T(RID_R11) | slot; + *p = PPCI_B | (((page-p) & 0x00ffffffu) << 2); + p++; + } + lua_assert(p - page <= CALLBACK_MCODE_SIZE); +} +#elif LJ_TARGET_MIPS +static void callback_mcode_init(global_State *g, uint32_t *page) +{ + uint32_t *p = page; + uintptr_t target = (uintptr_t)(void *)lj_vm_ffi_callback; + uintptr_t ug = (uintptr_t)(void *)g; + MSize slot; +#if LJ_TARGET_MIPS32 + *p++ = MIPSI_LUI | MIPSF_T(RID_R3) | (target >> 16); + *p++ = MIPSI_LUI | MIPSF_T(RID_R2) | (ug >> 16); +#else + *p++ = MIPSI_LUI | MIPSF_T(RID_R3) | (target >> 48); + *p++ = MIPSI_LUI | MIPSF_T(RID_R2) | (ug >> 48); + *p++ = MIPSI_ORI | MIPSF_T(RID_R3)|MIPSF_S(RID_R3) | ((target >> 32) & 0xffff); + *p++ = MIPSI_ORI | MIPSF_T(RID_R2)|MIPSF_S(RID_R2) | ((ug >> 32) & 0xffff); + *p++ = MIPSI_DSLL | MIPSF_D(RID_R3)|MIPSF_T(RID_R3) | MIPSF_A(16); + *p++ = MIPSI_DSLL | MIPSF_D(RID_R2)|MIPSF_T(RID_R2) | MIPSF_A(16); + *p++ = MIPSI_ORI | MIPSF_T(RID_R3)|MIPSF_S(RID_R3) | ((target >> 16) & 0xffff); + *p++ = MIPSI_ORI | MIPSF_T(RID_R2)|MIPSF_S(RID_R2) | ((ug >> 16) & 0xffff); + *p++ = MIPSI_DSLL | MIPSF_D(RID_R3)|MIPSF_T(RID_R3) | MIPSF_A(16); + *p++ = MIPSI_DSLL | MIPSF_D(RID_R2)|MIPSF_T(RID_R2) | MIPSF_A(16); +#endif + *p++ = MIPSI_ORI | MIPSF_T(RID_R3)|MIPSF_S(RID_R3) | (target & 0xffff); + *p++ = MIPSI_JR | MIPSF_S(RID_R3); + *p++ = MIPSI_ORI | MIPSF_T(RID_R2)|MIPSF_S(RID_R2) | (ug & 0xffff); + for (slot = 0; slot < CALLBACK_MAX_SLOT; slot++) { + *p = MIPSI_B | ((page-p-1) & 0x0000ffffu); + p++; + *p++ = MIPSI_LI | MIPSF_T(RID_R1) | slot; + } + lua_assert(p - page <= CALLBACK_MCODE_SIZE); +} +#else +/* Missing support for this architecture. */ +#define callback_mcode_init(g, p) UNUSED(p) +#endif + +/* -- Machine code management --------------------------------------------- */ + +#if LJ_TARGET_WINDOWS + +#define WIN32_LEAN_AND_MEAN +#include + +#elif LJ_TARGET_POSIX + +#include +#ifndef MAP_ANONYMOUS +#define MAP_ANONYMOUS MAP_ANON +#endif + +#endif + +/* Allocate and initialize area for callback function pointers. */ +static void callback_mcode_new(CTState *cts) +{ + size_t sz = (size_t)CALLBACK_MCODE_SIZE; + void *p; + if (CALLBACK_MAX_SLOT == 0) + lj_err_caller(cts->L, LJ_ERR_FFI_CBACKOV); +#if LJ_TARGET_WINDOWS + p = LJ_WIN_VALLOC(NULL, sz, MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE); + if (!p) + lj_err_caller(cts->L, LJ_ERR_FFI_CBACKOV); +#elif LJ_TARGET_POSIX + p = mmap(NULL, sz, (PROT_READ|PROT_WRITE), MAP_PRIVATE|MAP_ANONYMOUS, + -1, 0); + if (p == MAP_FAILED) + lj_err_caller(cts->L, LJ_ERR_FFI_CBACKOV); +#else + /* Fallback allocator. Fails if memory is not executable by default. */ + p = lj_mem_new(cts->L, sz); +#endif + cts->cb.mcode = p; + callback_mcode_init(cts->g, p); + lj_mcode_sync(p, (char *)p + sz); +#if LJ_TARGET_WINDOWS + { + DWORD oprot; + LJ_WIN_VPROTECT(p, sz, PAGE_EXECUTE_READ, &oprot); + } +#elif LJ_TARGET_POSIX + mprotect(p, sz, (PROT_READ|PROT_EXEC)); +#endif +} + +/* Free area for callback function pointers. */ +void lj_ccallback_mcode_free(CTState *cts) +{ + size_t sz = (size_t)CALLBACK_MCODE_SIZE; + void *p = cts->cb.mcode; + if (p == NULL) return; +#if LJ_TARGET_WINDOWS + VirtualFree(p, 0, MEM_RELEASE); + UNUSED(sz); +#elif LJ_TARGET_POSIX + munmap(p, sz); +#else + lj_mem_free(cts->g, p, sz); +#endif +} + +/* -- C callback entry ---------------------------------------------------- */ + +/* Target-specific handling of register arguments. Similar to lj_ccall.c. */ +#if LJ_TARGET_X86 + +#define CALLBACK_HANDLE_REGARG \ + if (!isfp) { /* Only non-FP values may be passed in registers. */ \ + if (n > 1) { /* Anything > 32 bit is passed on the stack. */ \ + if (!LJ_ABI_WIN) ngpr = maxgpr; /* Prevent reordering. */ \ + } else if (ngpr + 1 <= maxgpr) { \ + sp = &cts->cb.gpr[ngpr]; \ + ngpr += n; \ + goto done; \ + } \ + } + +#elif LJ_TARGET_X64 && LJ_ABI_WIN + +/* Windows/x64 argument registers are strictly positional (use ngpr). */ +#define CALLBACK_HANDLE_REGARG \ + if (isfp) { \ + if (ngpr < maxgpr) { sp = &cts->cb.fpr[ngpr++]; UNUSED(nfpr); goto done; } \ + } else { \ + if (ngpr < maxgpr) { sp = &cts->cb.gpr[ngpr++]; goto done; } \ + } + +#elif LJ_TARGET_X64 + +#define CALLBACK_HANDLE_REGARG \ + if (isfp) { \ + if (nfpr + n <= CCALL_NARG_FPR) { \ + sp = &cts->cb.fpr[nfpr]; \ + nfpr += n; \ + goto done; \ + } \ + } else { \ + if (ngpr + n <= maxgpr) { \ + sp = &cts->cb.gpr[ngpr]; \ + ngpr += n; \ + goto done; \ + } \ + } + +#elif LJ_TARGET_ARM + +#if LJ_ABI_SOFTFP + +#define CALLBACK_HANDLE_REGARG_FP1 UNUSED(isfp); +#define CALLBACK_HANDLE_REGARG_FP2 + +#else + +#define CALLBACK_HANDLE_REGARG_FP1 \ + if (isfp) { \ + if (n == 1) { \ + if (fprodd) { \ + sp = &cts->cb.fpr[fprodd-1]; \ + fprodd = 0; \ + goto done; \ + } else if (nfpr + 1 <= CCALL_NARG_FPR) { \ + sp = &cts->cb.fpr[nfpr++]; \ + fprodd = nfpr; \ + goto done; \ + } \ + } else { \ + if (nfpr + 1 <= CCALL_NARG_FPR) { \ + sp = &cts->cb.fpr[nfpr++]; \ + goto done; \ + } \ + } \ + fprodd = 0; /* No reordering after the first FP value is on stack. */ \ + } else { + +#define CALLBACK_HANDLE_REGARG_FP2 } + +#endif + +#define CALLBACK_HANDLE_REGARG \ + CALLBACK_HANDLE_REGARG_FP1 \ + if (n > 1) ngpr = (ngpr + 1u) & ~1u; /* Align to regpair. */ \ + if (ngpr + n <= maxgpr) { \ + sp = &cts->cb.gpr[ngpr]; \ + ngpr += n; \ + goto done; \ + } CALLBACK_HANDLE_REGARG_FP2 + +#elif LJ_TARGET_ARM64 + +#define CALLBACK_HANDLE_REGARG \ + if (isfp) { \ + if (nfpr + n <= CCALL_NARG_FPR) { \ + sp = &cts->cb.fpr[nfpr]; \ + nfpr += n; \ + goto done; \ + } else { \ + nfpr = CCALL_NARG_FPR; /* Prevent reordering. */ \ + } \ + } else { \ + if (!LJ_TARGET_IOS && n > 1) \ + ngpr = (ngpr + 1u) & ~1u; /* Align to regpair. */ \ + if (ngpr + n <= maxgpr) { \ + sp = &cts->cb.gpr[ngpr]; \ + ngpr += n; \ + goto done; \ + } else { \ + ngpr = CCALL_NARG_GPR; /* Prevent reordering. */ \ + } \ + } + +#elif LJ_TARGET_PPC + +#define CALLBACK_HANDLE_GPR \ + if (n > 1) { \ + lua_assert(((LJ_ABI_SOFTFP && ctype_isnum(cta->info)) || /* double. */ \ + ctype_isinteger(cta->info)) && n == 2); /* int64_t. */ \ + ngpr = (ngpr + 1u) & ~1u; /* Align int64_t to regpair. */ \ + } \ + if (ngpr + n <= maxgpr) { \ + sp = &cts->cb.gpr[ngpr]; \ + ngpr += n; \ + goto done; \ + } + +#if LJ_ABI_SOFTFP +#define CALLBACK_HANDLE_REGARG \ + CALLBACK_HANDLE_GPR \ + UNUSED(isfp); +#else +#define CALLBACK_HANDLE_REGARG \ + if (isfp) { \ + if (nfpr + 1 <= CCALL_NARG_FPR) { \ + sp = &cts->cb.fpr[nfpr++]; \ + cta = ctype_get(cts, CTID_DOUBLE); /* FPRs always hold doubles. */ \ + goto done; \ + } \ + } else { /* Try to pass argument in GPRs. */ \ + CALLBACK_HANDLE_GPR \ + } +#endif + +#if !LJ_ABI_SOFTFP +#define CALLBACK_HANDLE_RET \ + if (ctype_isfp(ctr->info) && ctr->size == sizeof(float)) \ + *(double *)dp = *(float *)dp; /* FPRs always hold doubles. */ +#endif + +#elif LJ_TARGET_MIPS32 + +#define CALLBACK_HANDLE_GPR \ + if (n > 1) ngpr = (ngpr + 1u) & ~1u; /* Align to regpair. */ \ + if (ngpr + n <= maxgpr) { \ + sp = &cts->cb.gpr[ngpr]; \ + ngpr += n; \ + goto done; \ + } + +#if !LJ_ABI_SOFTFP /* MIPS32 hard-float */ +#define CALLBACK_HANDLE_REGARG \ + if (isfp && nfpr < CCALL_NARG_FPR) { /* Try to pass argument in FPRs. */ \ + sp = (void *)((uint8_t *)&cts->cb.fpr[nfpr] + ((LJ_BE && n==1) ? 4 : 0)); \ + nfpr++; ngpr += n; \ + goto done; \ + } else { /* Try to pass argument in GPRs. */ \ + nfpr = CCALL_NARG_FPR; \ + CALLBACK_HANDLE_GPR \ + } +#else /* MIPS32 soft-float */ +#define CALLBACK_HANDLE_REGARG \ + CALLBACK_HANDLE_GPR \ + UNUSED(isfp); +#endif + +#define CALLBACK_HANDLE_RET \ + if (ctype_isfp(ctr->info) && ctr->size == sizeof(float)) \ + ((float *)dp)[1] = *(float *)dp; + +#elif LJ_TARGET_MIPS64 + +#if !LJ_ABI_SOFTFP /* MIPS64 hard-float */ +#define CALLBACK_HANDLE_REGARG \ + if (ngpr + n <= maxgpr) { \ + sp = isfp ? (void*) &cts->cb.fpr[ngpr] : (void*) &cts->cb.gpr[ngpr]; \ + ngpr += n; \ + goto done; \ + } +#else /* MIPS64 soft-float */ +#define CALLBACK_HANDLE_REGARG \ + if (ngpr + n <= maxgpr) { \ + UNUSED(isfp); \ + sp = (void*) &cts->cb.gpr[ngpr]; \ + ngpr += n; \ + goto done; \ + } +#endif + +#define CALLBACK_HANDLE_RET \ + if (ctype_isfp(ctr->info) && ctr->size == sizeof(float)) \ + ((float *)dp)[1] = *(float *)dp; + +#else +#error "Missing calling convention definitions for this architecture" +#endif + +/* Convert and push callback arguments to Lua stack. */ +static void callback_conv_args(CTState *cts, lua_State *L) +{ + TValue *o = L->top; + intptr_t *stack = cts->cb.stack; + MSize slot = cts->cb.slot; + CTypeID id = 0, rid, fid; + int gcsteps = 0; + CType *ct; + GCfunc *fn; + int fntp; + MSize ngpr = 0, nsp = 0, maxgpr = CCALL_NARG_GPR; +#if CCALL_NARG_FPR + MSize nfpr = 0; +#if LJ_TARGET_ARM + MSize fprodd = 0; +#endif +#endif + + if (slot < cts->cb.sizeid && (id = cts->cb.cbid[slot]) != 0) { + ct = ctype_get(cts, id); + rid = ctype_cid(ct->info); /* Return type. x86: +(spadj<<16). */ + fn = funcV(lj_tab_getint(cts->miscmap, (int32_t)slot)); + fntp = LJ_TFUNC; + } else { /* Must set up frame first, before throwing the error. */ + ct = NULL; + rid = 0; + fn = (GCfunc *)L; + fntp = LJ_TTHREAD; + } + /* Continuation returns from callback. */ + if (LJ_FR2) { + (o++)->u64 = LJ_CONT_FFI_CALLBACK; + (o++)->u64 = rid; + o++; + } else { + o->u32.lo = LJ_CONT_FFI_CALLBACK; + o->u32.hi = rid; + o++; + } + setframe_gc(o, obj2gco(fn), fntp); + setframe_ftsz(o, ((char *)(o+1) - (char *)L->base) + FRAME_CONT); + L->top = L->base = ++o; + if (!ct) + lj_err_caller(cts->L, LJ_ERR_FFI_BADCBACK); + if (isluafunc(fn)) + setcframe_pc(L->cframe, proto_bc(funcproto(fn))+1); + lj_state_checkstack(L, LUA_MINSTACK); /* May throw. */ + o = L->base; /* Might have been reallocated. */ + +#if LJ_TARGET_X86 + /* x86 has several different calling conventions. */ + switch (ctype_cconv(ct->info)) { + case CTCC_FASTCALL: maxgpr = 2; break; + case CTCC_THISCALL: maxgpr = 1; break; + default: maxgpr = 0; break; + } +#endif + + fid = ct->sib; + while (fid) { + CType *ctf = ctype_get(cts, fid); + if (!ctype_isattrib(ctf->info)) { + CType *cta; + void *sp; + CTSize sz; + int isfp; + MSize n; + lua_assert(ctype_isfield(ctf->info)); + cta = ctype_rawchild(cts, ctf); + isfp = ctype_isfp(cta->info); + sz = (cta->size + CTSIZE_PTR-1) & ~(CTSIZE_PTR-1); + n = sz / CTSIZE_PTR; /* Number of GPRs or stack slots needed. */ + + CALLBACK_HANDLE_REGARG /* Handle register arguments. */ + + /* Otherwise pass argument on stack. */ + if (CCALL_ALIGN_STACKARG && LJ_32 && sz == 8) + nsp = (nsp + 1) & ~1u; /* Align 64 bit argument on stack. */ + sp = &stack[nsp]; + nsp += n; + + done: + if (LJ_BE && cta->size < CTSIZE_PTR +#if LJ_TARGET_MIPS64 + && !(isfp && nsp) +#endif + ) + sp = (void *)((uint8_t *)sp + CTSIZE_PTR-cta->size); + gcsteps += lj_cconv_tv_ct(cts, cta, 0, o++, sp); + } + fid = ctf->sib; + } + L->top = o; +#if LJ_TARGET_X86 + /* Store stack adjustment for returns from non-cdecl callbacks. */ + if (ctype_cconv(ct->info) != CTCC_CDECL) { +#if LJ_FR2 + (L->base-3)->u64 |= (nsp << (16+2)); +#else + (L->base-2)->u32.hi |= (nsp << (16+2)); +#endif + } +#endif + while (gcsteps-- > 0) + lj_gc_check(L); +} + +/* Convert Lua object to callback result. */ +static void callback_conv_result(CTState *cts, lua_State *L, TValue *o) +{ +#if LJ_FR2 + CType *ctr = ctype_raw(cts, (uint16_t)(L->base-3)->u64); +#else + CType *ctr = ctype_raw(cts, (uint16_t)(L->base-2)->u32.hi); +#endif +#if LJ_TARGET_X86 + cts->cb.gpr[2] = 0; +#endif + if (!ctype_isvoid(ctr->info)) { + uint8_t *dp = (uint8_t *)&cts->cb.gpr[0]; +#if CCALL_NUM_FPR + if (ctype_isfp(ctr->info)) + dp = (uint8_t *)&cts->cb.fpr[0]; +#endif +#if LJ_TARGET_ARM64 && LJ_BE + if (ctype_isfp(ctr->info) && ctr->size == sizeof(float)) + dp = (uint8_t *)&cts->cb.fpr[0].f[1]; +#endif + lj_cconv_ct_tv(cts, ctr, dp, o, 0); +#ifdef CALLBACK_HANDLE_RET + CALLBACK_HANDLE_RET +#endif + /* Extend returned integers to (at least) 32 bits. */ + if (ctype_isinteger_or_bool(ctr->info) && ctr->size < 4) { + if (ctr->info & CTF_UNSIGNED) + *(uint32_t *)dp = ctr->size == 1 ? (uint32_t)*(uint8_t *)dp : + (uint32_t)*(uint16_t *)dp; + else + *(int32_t *)dp = ctr->size == 1 ? (int32_t)*(int8_t *)dp : + (int32_t)*(int16_t *)dp; + } +#if LJ_TARGET_MIPS64 || (LJ_TARGET_ARM64 && LJ_BE) + /* Always sign-extend results to 64 bits. Even a soft-fp 'float'. */ + if (ctr->size <= 4 && + (LJ_ABI_SOFTFP || ctype_isinteger_or_bool(ctr->info))) + *(int64_t *)dp = (int64_t)*(int32_t *)dp; +#endif +#if LJ_TARGET_X86 + if (ctype_isfp(ctr->info)) + cts->cb.gpr[2] = ctr->size == sizeof(float) ? 1 : 2; +#endif + } +} + +/* Enter callback. */ +lua_State * LJ_FASTCALL lj_ccallback_enter(CTState *cts, void *cf) +{ + lua_State *L = cts->L; + global_State *g = cts->g; + lua_assert(L != NULL); + if (tvref(g->jit_base)) { + setstrV(L, L->top++, lj_err_str(L, LJ_ERR_FFI_BADCBACK)); + if (g->panic) g->panic(L); + exit(EXIT_FAILURE); + } + lj_trace_abort(g); /* Never record across callback. */ + /* Setup C frame. */ + cframe_prev(cf) = L->cframe; + setcframe_L(cf, L); + cframe_errfunc(cf) = -1; + cframe_nres(cf) = 0; + L->cframe = cf; + callback_conv_args(cts, L); + return L; /* Now call the function on this stack. */ +} + +/* Leave callback. */ +void LJ_FASTCALL lj_ccallback_leave(CTState *cts, TValue *o) +{ + lua_State *L = cts->L; + GCfunc *fn; + TValue *obase = L->base; + L->base = L->top; /* Keep continuation frame for throwing errors. */ + if (o >= L->base) { + /* PC of RET* is lost. Point to last line for result conv. errors. */ + fn = curr_func(L); + if (isluafunc(fn)) { + GCproto *pt = funcproto(fn); + setcframe_pc(L->cframe, proto_bc(pt)+pt->sizebc+1); + } + } + callback_conv_result(cts, L, o); + /* Finally drop C frame and continuation frame. */ + L->top -= 2+2*LJ_FR2; + L->base = obase; + L->cframe = cframe_prev(L->cframe); + cts->cb.slot = 0; /* Blacklist C function that called the callback. */ +} + +/* -- C callback management ----------------------------------------------- */ + +/* Get an unused slot in the callback slot table. */ +static MSize callback_slot_new(CTState *cts, CType *ct) +{ + CTypeID id = ctype_typeid(cts, ct); + CTypeID1 *cbid = cts->cb.cbid; + MSize top; + for (top = cts->cb.topid; top < cts->cb.sizeid; top++) + if (LJ_LIKELY(cbid[top] == 0)) + goto found; +#if CALLBACK_MAX_SLOT + if (top >= CALLBACK_MAX_SLOT) +#endif + lj_err_caller(cts->L, LJ_ERR_FFI_CBACKOV); + if (!cts->cb.mcode) + callback_mcode_new(cts); + lj_mem_growvec(cts->L, cbid, cts->cb.sizeid, CALLBACK_MAX_SLOT, CTypeID1); + cts->cb.cbid = cbid; + memset(cbid+top, 0, (cts->cb.sizeid-top)*sizeof(CTypeID1)); +found: + cbid[top] = id; + cts->cb.topid = top+1; + return top; +} + +/* Check for function pointer and supported argument/result types. */ +static CType *callback_checkfunc(CTState *cts, CType *ct) +{ + int narg = 0; + if (!ctype_isptr(ct->info) || (LJ_64 && ct->size != CTSIZE_PTR)) + return NULL; + ct = ctype_rawchild(cts, ct); + if (ctype_isfunc(ct->info)) { + CType *ctr = ctype_rawchild(cts, ct); + CTypeID fid = ct->sib; + if (!(ctype_isvoid(ctr->info) || ctype_isenum(ctr->info) || + ctype_isptr(ctr->info) || (ctype_isnum(ctr->info) && ctr->size <= 8))) + return NULL; + if ((ct->info & CTF_VARARG)) + return NULL; + while (fid) { + CType *ctf = ctype_get(cts, fid); + if (!ctype_isattrib(ctf->info)) { + CType *cta; + lua_assert(ctype_isfield(ctf->info)); + cta = ctype_rawchild(cts, ctf); + if (!(ctype_isenum(cta->info) || ctype_isptr(cta->info) || + (ctype_isnum(cta->info) && cta->size <= 8)) || + ++narg >= LUA_MINSTACK-3) + return NULL; + } + fid = ctf->sib; + } + return ct; + } + return NULL; +} + +/* Create a new callback and return the callback function pointer. */ +void *lj_ccallback_new(CTState *cts, CType *ct, GCfunc *fn) +{ + ct = callback_checkfunc(cts, ct); + if (ct) { + MSize slot = callback_slot_new(cts, ct); + GCtab *t = cts->miscmap; + setfuncV(cts->L, lj_tab_setint(cts->L, t, (int32_t)slot), fn); + lj_gc_anybarriert(cts->L, t); + return callback_slot2ptr(cts, slot); + } + return NULL; /* Bad conversion. */ +} + +#endif diff --git a/lib/LuaJIT/lj_ccallback.h b/lib/LuaJIT/lj_ccallback.h new file mode 100644 index 0000000..a8cdad3 --- /dev/null +++ b/lib/LuaJIT/lj_ccallback.h @@ -0,0 +1,25 @@ +/* +** FFI C callback handling. +** Copyright (C) 2005-2017 Mike Pall. See Copyright Notice in luajit.h +*/ + +#ifndef _LJ_CCALLBACK_H +#define _LJ_CCALLBACK_H + +#include "lj_obj.h" +#include "lj_ctype.h" + +#if LJ_HASFFI + +/* Really belongs to lj_vm.h. */ +LJ_ASMF void lj_vm_ffi_callback(void); + +LJ_FUNC MSize lj_ccallback_ptr2slot(CTState *cts, void *p); +LJ_FUNCA lua_State * LJ_FASTCALL lj_ccallback_enter(CTState *cts, void *cf); +LJ_FUNCA void LJ_FASTCALL lj_ccallback_leave(CTState *cts, TValue *o); +LJ_FUNC void *lj_ccallback_new(CTState *cts, CType *ct, GCfunc *fn); +LJ_FUNC void lj_ccallback_mcode_free(CTState *cts); + +#endif + +#endif diff --git a/lib/LuaJIT/lj_ccallback.o b/lib/LuaJIT/lj_ccallback.o new file mode 100644 index 0000000000000000000000000000000000000000..6a58917c4fcbf55ed2cdc9c5cee0b0805cafbd10 GIT binary patch literal 4920 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 bC1h2O|2bQNGi)~bE4u$*;aaADa^?RGz)f~) literal 0 HcmV?d00001 diff --git a/lib/LuaJIT/lj_ccallback_dyn.o b/lib/LuaJIT/lj_ccallback_dyn.o new file mode 100644 index 0000000000000000000000000000000000000000..6a58917c4fcbf55ed2cdc9c5cee0b0805cafbd10 GIT binary patch literal 4920 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 bC1h2O|2bQNGi)~bE4u$*;aaADa^?RGz)f~) literal 0 HcmV?d00001 diff --git a/lib/LuaJIT/lj_cconv.c b/lib/LuaJIT/lj_cconv.c new file mode 100644 index 0000000..13b8230 --- /dev/null +++ b/lib/LuaJIT/lj_cconv.c @@ -0,0 +1,754 @@ +/* +** C type conversions. +** Copyright (C) 2005-2017 Mike Pall. See Copyright Notice in luajit.h +*/ + +#include "lj_obj.h" + +#if LJ_HASFFI + +#include "lj_err.h" +#include "lj_tab.h" +#include "lj_ctype.h" +#include "lj_cdata.h" +#include "lj_cconv.h" +#include "lj_ccallback.h" + +/* -- Conversion errors --------------------------------------------------- */ + +/* Bad conversion. */ +LJ_NORET static void cconv_err_conv(CTState *cts, CType *d, CType *s, + CTInfo flags) +{ + const char *dst = strdata(lj_ctype_repr(cts->L, ctype_typeid(cts, d), NULL)); + const char *src; + if ((flags & CCF_FROMTV)) + src = lj_obj_typename[1+(ctype_isnum(s->info) ? LUA_TNUMBER : + ctype_isarray(s->info) ? LUA_TSTRING : LUA_TNIL)]; + else + src = strdata(lj_ctype_repr(cts->L, ctype_typeid(cts, s), NULL)); + if (CCF_GETARG(flags)) + lj_err_argv(cts->L, CCF_GETARG(flags), LJ_ERR_FFI_BADCONV, src, dst); + else + lj_err_callerv(cts->L, LJ_ERR_FFI_BADCONV, src, dst); +} + +/* Bad conversion from TValue. */ +LJ_NORET static void cconv_err_convtv(CTState *cts, CType *d, TValue *o, + CTInfo flags) +{ + const char *dst = strdata(lj_ctype_repr(cts->L, ctype_typeid(cts, d), NULL)); + const char *src = lj_typename(o); + if (CCF_GETARG(flags)) + lj_err_argv(cts->L, CCF_GETARG(flags), LJ_ERR_FFI_BADCONV, src, dst); + else + lj_err_callerv(cts->L, LJ_ERR_FFI_BADCONV, src, dst); +} + +/* Initializer overflow. */ +LJ_NORET static void cconv_err_initov(CTState *cts, CType *d) +{ + const char *dst = strdata(lj_ctype_repr(cts->L, ctype_typeid(cts, d), NULL)); + lj_err_callerv(cts->L, LJ_ERR_FFI_INITOV, dst); +} + +/* -- C type compatibility checks ----------------------------------------- */ + +/* Get raw type and qualifiers for a child type. Resolves enums, too. */ +static CType *cconv_childqual(CTState *cts, CType *ct, CTInfo *qual) +{ + ct = ctype_child(cts, ct); + for (;;) { + if (ctype_isattrib(ct->info)) { + if (ctype_attrib(ct->info) == CTA_QUAL) *qual |= ct->size; + } else if (!ctype_isenum(ct->info)) { + break; + } + ct = ctype_child(cts, ct); + } + *qual |= (ct->info & CTF_QUAL); + return ct; +} + +/* Check for compatible types when converting to a pointer. +** Note: these checks are more relaxed than what C99 mandates. +*/ +int lj_cconv_compatptr(CTState *cts, CType *d, CType *s, CTInfo flags) +{ + if (!((flags & CCF_CAST) || d == s)) { + CTInfo dqual = 0, squal = 0; + d = cconv_childqual(cts, d, &dqual); + if (!ctype_isstruct(s->info)) + s = cconv_childqual(cts, s, &squal); + if ((flags & CCF_SAME)) { + if (dqual != squal) + return 0; /* Different qualifiers. */ + } else if (!(flags & CCF_IGNQUAL)) { + if ((dqual & squal) != squal) + return 0; /* Discarded qualifiers. */ + if (ctype_isvoid(d->info) || ctype_isvoid(s->info)) + return 1; /* Converting to/from void * is always ok. */ + } + if (ctype_type(d->info) != ctype_type(s->info) || + d->size != s->size) + return 0; /* Different type or different size. */ + if (ctype_isnum(d->info)) { + if (((d->info ^ s->info) & (CTF_BOOL|CTF_FP))) + return 0; /* Different numeric types. */ + } else if (ctype_ispointer(d->info)) { + /* Check child types for compatibility. */ + return lj_cconv_compatptr(cts, d, s, flags|CCF_SAME); + } else if (ctype_isstruct(d->info)) { + if (d != s) + return 0; /* Must be exact same type for struct/union. */ + } else if (ctype_isfunc(d->info)) { + /* NYI: structural equality of functions. */ + } + } + return 1; /* Types are compatible. */ +} + +/* -- C type to C type conversion ----------------------------------------- */ + +/* Convert C type to C type. Caveat: expects to get the raw CType! +** +** Note: This is only used by the interpreter and not optimized at all. +** The JIT compiler will do a much better job specializing for each case. +*/ +void lj_cconv_ct_ct(CTState *cts, CType *d, CType *s, + uint8_t *dp, uint8_t *sp, CTInfo flags) +{ + CTSize dsize = d->size, ssize = s->size; + CTInfo dinfo = d->info, sinfo = s->info; + void *tmpptr; + + lua_assert(!ctype_isenum(dinfo) && !ctype_isenum(sinfo)); + lua_assert(!ctype_isattrib(dinfo) && !ctype_isattrib(sinfo)); + + if (ctype_type(dinfo) > CT_MAYCONVERT || ctype_type(sinfo) > CT_MAYCONVERT) + goto err_conv; + + /* Some basic sanity checks. */ + lua_assert(!ctype_isnum(dinfo) || dsize > 0); + lua_assert(!ctype_isnum(sinfo) || ssize > 0); + lua_assert(!ctype_isbool(dinfo) || dsize == 1 || dsize == 4); + lua_assert(!ctype_isbool(sinfo) || ssize == 1 || ssize == 4); + lua_assert(!ctype_isinteger(dinfo) || (1u< ssize) { /* Zero-extend or sign-extend LSB. */ +#if LJ_LE + uint8_t fill = (!(sinfo & CTF_UNSIGNED) && (sp[ssize-1]&0x80)) ? 0xff : 0; + memcpy(dp, sp, ssize); + memset(dp + ssize, fill, dsize-ssize); +#else + uint8_t fill = (!(sinfo & CTF_UNSIGNED) && (sp[0]&0x80)) ? 0xff : 0; + memset(dp, fill, dsize-ssize); + memcpy(dp + (dsize-ssize), sp, ssize); +#endif + } else { /* Copy LSB. */ +#if LJ_LE + memcpy(dp, sp, dsize); +#else + memcpy(dp, sp + (ssize-dsize), dsize); +#endif + } + break; + case CCX(I, F): { + double n; /* Always convert via double. */ + conv_I_F: + /* Convert source to double. */ + if (ssize == sizeof(double)) n = *(double *)sp; + else if (ssize == sizeof(float)) n = (double)*(float *)sp; + else goto err_conv; /* NYI: long double. */ + /* Then convert double to integer. */ + /* The conversion must exactly match the semantics of JIT-compiled code! */ + if (dsize < 4 || (dsize == 4 && !(dinfo & CTF_UNSIGNED))) { + int32_t i = (int32_t)n; + if (dsize == 4) *(int32_t *)dp = i; + else if (dsize == 2) *(int16_t *)dp = (int16_t)i; + else *(int8_t *)dp = (int8_t)i; + } else if (dsize == 4) { + *(uint32_t *)dp = (uint32_t)n; + } else if (dsize == 8) { + if (!(dinfo & CTF_UNSIGNED)) + *(int64_t *)dp = (int64_t)n; + else + *(uint64_t *)dp = lj_num2u64(n); + } else { + goto err_conv; /* NYI: conversion to >64 bit integers. */ + } + break; + } + case CCX(I, C): + s = ctype_child(cts, s); + sinfo = s->info; + ssize = s->size; + goto conv_I_F; /* Just convert re. */ + case CCX(I, P): + if (!(flags & CCF_CAST)) goto err_conv; + sinfo = CTINFO(CT_NUM, CTF_UNSIGNED); + goto conv_I_I; + case CCX(I, A): + if (!(flags & CCF_CAST)) goto err_conv; + sinfo = CTINFO(CT_NUM, CTF_UNSIGNED); + ssize = CTSIZE_PTR; + tmpptr = sp; + sp = (uint8_t *)&tmpptr; + goto conv_I_I; + + /* Destination is a floating-point number. */ + case CCX(F, B): + case CCX(F, I): { + double n; /* Always convert via double. */ + conv_F_I: + /* First convert source to double. */ + /* The conversion must exactly match the semantics of JIT-compiled code! */ + if (ssize < 4 || (ssize == 4 && !(sinfo & CTF_UNSIGNED))) { + int32_t i; + if (ssize == 4) { + i = *(int32_t *)sp; + } else if (!(sinfo & CTF_UNSIGNED)) { + if (ssize == 2) i = *(int16_t *)sp; + else i = *(int8_t *)sp; + } else { + if (ssize == 2) i = *(uint16_t *)sp; + else i = *(uint8_t *)sp; + } + n = (double)i; + } else if (ssize == 4) { + n = (double)*(uint32_t *)sp; + } else if (ssize == 8) { + if (!(sinfo & CTF_UNSIGNED)) n = (double)*(int64_t *)sp; + else n = (double)*(uint64_t *)sp; + } else { + goto err_conv; /* NYI: conversion from >64 bit integers. */ + } + /* Convert double to destination. */ + if (dsize == sizeof(double)) *(double *)dp = n; + else if (dsize == sizeof(float)) *(float *)dp = (float)n; + else goto err_conv; /* NYI: long double. */ + break; + } + case CCX(F, F): { + double n; /* Always convert via double. */ + conv_F_F: + if (ssize == dsize) goto copyval; + /* Convert source to double. */ + if (ssize == sizeof(double)) n = *(double *)sp; + else if (ssize == sizeof(float)) n = (double)*(float *)sp; + else goto err_conv; /* NYI: long double. */ + /* Convert double to destination. */ + if (dsize == sizeof(double)) *(double *)dp = n; + else if (dsize == sizeof(float)) *(float *)dp = (float)n; + else goto err_conv; /* NYI: long double. */ + break; + } + case CCX(F, C): + s = ctype_child(cts, s); + sinfo = s->info; + ssize = s->size; + goto conv_F_F; /* Ignore im, and convert from re. */ + + /* Destination is a complex number. */ + case CCX(C, I): + d = ctype_child(cts, d); + dinfo = d->info; + dsize = d->size; + memset(dp + dsize, 0, dsize); /* Clear im. */ + goto conv_F_I; /* Convert to re. */ + case CCX(C, F): + d = ctype_child(cts, d); + dinfo = d->info; + dsize = d->size; + memset(dp + dsize, 0, dsize); /* Clear im. */ + goto conv_F_F; /* Convert to re. */ + + case CCX(C, C): + if (dsize != ssize) { /* Different types: convert re/im separately. */ + CType *dc = ctype_child(cts, d); + CType *sc = ctype_child(cts, s); + lj_cconv_ct_ct(cts, dc, sc, dp, sp, flags); + lj_cconv_ct_ct(cts, dc, sc, dp + dc->size, sp + sc->size, flags); + return; + } + goto copyval; /* Otherwise this is easy. */ + + /* Destination is a vector. */ + case CCX(V, I): + case CCX(V, F): + case CCX(V, C): { + CType *dc = ctype_child(cts, d); + CTSize esize; + /* First convert the scalar to the first element. */ + lj_cconv_ct_ct(cts, dc, s, dp, sp, flags); + /* Then replicate it to the other elements (splat). */ + for (sp = dp, esize = dc->size; dsize > esize; dsize -= esize) { + dp += esize; + memcpy(dp, sp, esize); + } + break; + } + + case CCX(V, V): + /* Copy same-sized vectors, even for different lengths/element-types. */ + if (dsize != ssize) goto err_conv; + goto copyval; + + /* Destination is a pointer. */ + case CCX(P, I): + if (!(flags & CCF_CAST)) goto err_conv; + dinfo = CTINFO(CT_NUM, CTF_UNSIGNED); + goto conv_I_I; + + case CCX(P, F): + if (!(flags & CCF_CAST) || !(flags & CCF_FROMTV)) goto err_conv; + /* The signed conversion is cheaper. x64 really has 47 bit pointers. */ + dinfo = CTINFO(CT_NUM, (LJ_64 && dsize == 8) ? 0 : CTF_UNSIGNED); + goto conv_I_F; + + case CCX(P, P): + if (!lj_cconv_compatptr(cts, d, s, flags)) goto err_conv; + cdata_setptr(dp, dsize, cdata_getptr(sp, ssize)); + break; + + case CCX(P, A): + case CCX(P, S): + if (!lj_cconv_compatptr(cts, d, s, flags)) goto err_conv; + cdata_setptr(dp, dsize, sp); + break; + + /* Destination is an array. */ + case CCX(A, A): + if ((flags & CCF_CAST) || (d->info & CTF_VLA) || dsize != ssize || + d->size == CTSIZE_INVALID || !lj_cconv_compatptr(cts, d, s, flags)) + goto err_conv; + goto copyval; + + /* Destination is a struct/union. */ + case CCX(S, S): + if ((flags & CCF_CAST) || (d->info & CTF_VLA) || d != s) + goto err_conv; /* Must be exact same type. */ +copyval: /* Copy value. */ + lua_assert(dsize == ssize); + memcpy(dp, sp, dsize); + break; + + default: + err_conv: + cconv_err_conv(cts, d, s, flags); + } +} + +/* -- C type to TValue conversion ----------------------------------------- */ + +/* Convert C type to TValue. Caveat: expects to get the raw CType! */ +int lj_cconv_tv_ct(CTState *cts, CType *s, CTypeID sid, + TValue *o, uint8_t *sp) +{ + CTInfo sinfo = s->info; + if (ctype_isnum(sinfo)) { + if (!ctype_isbool(sinfo)) { + if (ctype_isinteger(sinfo) && s->size > 4) goto copyval; + if (LJ_DUALNUM && ctype_isinteger(sinfo)) { + int32_t i; + lj_cconv_ct_ct(cts, ctype_get(cts, CTID_INT32), s, + (uint8_t *)&i, sp, 0); + if ((sinfo & CTF_UNSIGNED) && i < 0) + setnumV(o, (lua_Number)(uint32_t)i); + else + setintV(o, i); + } else { + lj_cconv_ct_ct(cts, ctype_get(cts, CTID_DOUBLE), s, + (uint8_t *)&o->n, sp, 0); + /* Numbers are NOT canonicalized here! Beware of uninitialized data. */ + lua_assert(tvisnum(o)); + } + } else { + uint32_t b = s->size == 1 ? (*sp != 0) : (*(int *)sp != 0); + setboolV(o, b); + setboolV(&cts->g->tmptv2, b); /* Remember for trace recorder. */ + } + return 0; + } else if (ctype_isrefarray(sinfo) || ctype_isstruct(sinfo)) { + /* Create reference. */ + setcdataV(cts->L, o, lj_cdata_newref(cts, sp, sid)); + return 1; /* Need GC step. */ + } else { + GCcdata *cd; + CTSize sz; + copyval: /* Copy value. */ + sz = s->size; + lua_assert(sz != CTSIZE_INVALID); + /* Attributes are stripped, qualifiers are kept (but mostly ignored). */ + cd = lj_cdata_new(cts, ctype_typeid(cts, s), sz); + setcdataV(cts->L, o, cd); + memcpy(cdataptr(cd), sp, sz); + return 1; /* Need GC step. */ + } +} + +/* Convert bitfield to TValue. */ +int lj_cconv_tv_bf(CTState *cts, CType *s, TValue *o, uint8_t *sp) +{ + CTInfo info = s->info; + CTSize pos, bsz; + uint32_t val; + lua_assert(ctype_isbitfield(info)); + /* NYI: packed bitfields may cause misaligned reads. */ + switch (ctype_bitcsz(info)) { + case 4: val = *(uint32_t *)sp; break; + case 2: val = *(uint16_t *)sp; break; + case 1: val = *(uint8_t *)sp; break; + default: lua_assert(0); val = 0; break; + } + /* Check if a packed bitfield crosses a container boundary. */ + pos = ctype_bitpos(info); + bsz = ctype_bitbsz(info); + lua_assert(pos < 8*ctype_bitcsz(info)); + lua_assert(bsz > 0 && bsz <= 8*ctype_bitcsz(info)); + if (pos + bsz > 8*ctype_bitcsz(info)) + lj_err_caller(cts->L, LJ_ERR_FFI_NYIPACKBIT); + if (!(info & CTF_BOOL)) { + CTSize shift = 32 - bsz; + if (!(info & CTF_UNSIGNED)) { + setintV(o, (int32_t)(val << (shift-pos)) >> shift); + } else { + val = (val << (shift-pos)) >> shift; + if (!LJ_DUALNUM || (int32_t)val < 0) + setnumV(o, (lua_Number)(uint32_t)val); + else + setintV(o, (int32_t)val); + } + } else { + uint32_t b = (val >> pos) & 1; + lua_assert(bsz == 1); + setboolV(o, b); + setboolV(&cts->g->tmptv2, b); /* Remember for trace recorder. */ + } + return 0; /* No GC step needed. */ +} + +/* -- TValue to C type conversion ----------------------------------------- */ + +/* Convert table to array. */ +static void cconv_array_tab(CTState *cts, CType *d, + uint8_t *dp, GCtab *t, CTInfo flags) +{ + int32_t i; + CType *dc = ctype_rawchild(cts, d); /* Array element type. */ + CTSize size = d->size, esize = dc->size, ofs = 0; + for (i = 0; ; i++) { + TValue *tv = (TValue *)lj_tab_getint(t, i); + if (!tv || tvisnil(tv)) { + if (i == 0) continue; /* Try again for 1-based tables. */ + break; /* Stop at first nil. */ + } + if (ofs >= size) + cconv_err_initov(cts, d); + lj_cconv_ct_tv(cts, dc, dp + ofs, tv, flags); + ofs += esize; + } + if (size != CTSIZE_INVALID) { /* Only fill up arrays with known size. */ + if (ofs == esize) { /* Replicate a single element. */ + for (; ofs < size; ofs += esize) memcpy(dp + ofs, dp, esize); + } else { /* Otherwise fill the remainder with zero. */ + memset(dp + ofs, 0, size - ofs); + } + } +} + +/* Convert table to sub-struct/union. */ +static void cconv_substruct_tab(CTState *cts, CType *d, uint8_t *dp, + GCtab *t, int32_t *ip, CTInfo flags) +{ + CTypeID id = d->sib; + while (id) { + CType *df = ctype_get(cts, id); + id = df->sib; + if (ctype_isfield(df->info) || ctype_isbitfield(df->info)) { + TValue *tv; + int32_t i = *ip, iz = i; + if (!gcref(df->name)) continue; /* Ignore unnamed fields. */ + if (i >= 0) { + retry: + tv = (TValue *)lj_tab_getint(t, i); + if (!tv || tvisnil(tv)) { + if (i == 0) { i = 1; goto retry; } /* 1-based tables. */ + if (iz == 0) { *ip = i = -1; goto tryname; } /* Init named fields. */ + break; /* Stop at first nil. */ + } + *ip = i + 1; + } else { + tryname: + tv = (TValue *)lj_tab_getstr(t, gco2str(gcref(df->name))); + if (!tv || tvisnil(tv)) continue; + } + if (ctype_isfield(df->info)) + lj_cconv_ct_tv(cts, ctype_rawchild(cts, df), dp+df->size, tv, flags); + else + lj_cconv_bf_tv(cts, df, dp+df->size, tv); + if ((d->info & CTF_UNION)) break; + } else if (ctype_isxattrib(df->info, CTA_SUBTYPE)) { + cconv_substruct_tab(cts, ctype_rawchild(cts, df), + dp+df->size, t, ip, flags); + } /* Ignore all other entries in the chain. */ + } +} + +/* Convert table to struct/union. */ +static void cconv_struct_tab(CTState *cts, CType *d, + uint8_t *dp, GCtab *t, CTInfo flags) +{ + int32_t i = 0; + memset(dp, 0, d->size); /* Much simpler to clear the struct first. */ + cconv_substruct_tab(cts, d, dp, t, &i, flags); +} + +/* Convert TValue to C type. Caveat: expects to get the raw CType! */ +void lj_cconv_ct_tv(CTState *cts, CType *d, + uint8_t *dp, TValue *o, CTInfo flags) +{ + CTypeID sid = CTID_P_VOID; + CType *s; + void *tmpptr; + uint8_t tmpbool, *sp = (uint8_t *)&tmpptr; + if (LJ_LIKELY(tvisint(o))) { + sp = (uint8_t *)&o->i; + sid = CTID_INT32; + flags |= CCF_FROMTV; + } else if (LJ_LIKELY(tvisnum(o))) { + sp = (uint8_t *)&o->n; + sid = CTID_DOUBLE; + flags |= CCF_FROMTV; + } else if (tviscdata(o)) { + sp = cdataptr(cdataV(o)); + sid = cdataV(o)->ctypeid; + s = ctype_get(cts, sid); + if (ctype_isref(s->info)) { /* Resolve reference for value. */ + lua_assert(s->size == CTSIZE_PTR); + sp = *(void **)sp; + sid = ctype_cid(s->info); + } + s = ctype_raw(cts, sid); + if (ctype_isfunc(s->info)) { + sid = lj_ctype_intern(cts, CTINFO(CT_PTR, CTALIGN_PTR|sid), CTSIZE_PTR); + } else { + if (ctype_isenum(s->info)) s = ctype_child(cts, s); + goto doconv; + } + } else if (tvisstr(o)) { + GCstr *str = strV(o); + if (ctype_isenum(d->info)) { /* Match string against enum constant. */ + CTSize ofs; + CType *cct = lj_ctype_getfield(cts, d, str, &ofs); + if (!cct || !ctype_isconstval(cct->info)) + goto err_conv; + lua_assert(d->size == 4); + sp = (uint8_t *)&cct->size; + sid = ctype_cid(cct->info); + } else if (ctype_isrefarray(d->info)) { /* Copy string to array. */ + CType *dc = ctype_rawchild(cts, d); + CTSize sz = str->len+1; + if (!ctype_isinteger(dc->info) || dc->size != 1) + goto err_conv; + if (d->size != 0 && d->size < sz) + sz = d->size; + memcpy(dp, strdata(str), sz); + return; + } else { /* Otherwise pass it as a const char[]. */ + sp = (uint8_t *)strdata(str); + sid = CTID_A_CCHAR; + flags |= CCF_FROMTV; + } + } else if (tvistab(o)) { + if (ctype_isarray(d->info)) { + cconv_array_tab(cts, d, dp, tabV(o), flags); + return; + } else if (ctype_isstruct(d->info)) { + cconv_struct_tab(cts, d, dp, tabV(o), flags); + return; + } else { + goto err_conv; + } + } else if (tvisbool(o)) { + tmpbool = boolV(o); + sp = &tmpbool; + sid = CTID_BOOL; + } else if (tvisnil(o)) { + tmpptr = (void *)0; + flags |= CCF_FROMTV; + } else if (tvisudata(o)) { + GCudata *ud = udataV(o); + tmpptr = uddata(ud); + if (ud->udtype == UDTYPE_IO_FILE) + tmpptr = *(void **)tmpptr; + } else if (tvislightud(o)) { + tmpptr = lightudV(o); + } else if (tvisfunc(o)) { + void *p = lj_ccallback_new(cts, d, funcV(o)); + if (p) { + *(void **)dp = p; + return; + } + goto err_conv; + } else { + err_conv: + cconv_err_convtv(cts, d, o, flags); + } + s = ctype_get(cts, sid); +doconv: + if (ctype_isenum(d->info)) d = ctype_child(cts, d); + lj_cconv_ct_ct(cts, d, s, dp, sp, flags); +} + +/* Convert TValue to bitfield. */ +void lj_cconv_bf_tv(CTState *cts, CType *d, uint8_t *dp, TValue *o) +{ + CTInfo info = d->info; + CTSize pos, bsz; + uint32_t val, mask; + lua_assert(ctype_isbitfield(info)); + if ((info & CTF_BOOL)) { + uint8_t tmpbool; + lua_assert(ctype_bitbsz(info) == 1); + lj_cconv_ct_tv(cts, ctype_get(cts, CTID_BOOL), &tmpbool, o, 0); + val = tmpbool; + } else { + CTypeID did = (info & CTF_UNSIGNED) ? CTID_UINT32 : CTID_INT32; + lj_cconv_ct_tv(cts, ctype_get(cts, did), (uint8_t *)&val, o, 0); + } + pos = ctype_bitpos(info); + bsz = ctype_bitbsz(info); + lua_assert(pos < 8*ctype_bitcsz(info)); + lua_assert(bsz > 0 && bsz <= 8*ctype_bitcsz(info)); + /* Check if a packed bitfield crosses a container boundary. */ + if (pos + bsz > 8*ctype_bitcsz(info)) + lj_err_caller(cts->L, LJ_ERR_FFI_NYIPACKBIT); + mask = ((1u << bsz) - 1u) << pos; + val = (val << pos) & mask; + /* NYI: packed bitfields may cause misaligned reads/writes. */ + switch (ctype_bitcsz(info)) { + case 4: *(uint32_t *)dp = (*(uint32_t *)dp & ~mask) | (uint32_t)val; break; + case 2: *(uint16_t *)dp = (*(uint16_t *)dp & ~mask) | (uint16_t)val; break; + case 1: *(uint8_t *)dp = (*(uint8_t *)dp & ~mask) | (uint8_t)val; break; + default: lua_assert(0); break; + } +} + +/* -- Initialize C type with TValues -------------------------------------- */ + +/* Initialize an array with TValues. */ +static void cconv_array_init(CTState *cts, CType *d, CTSize sz, uint8_t *dp, + TValue *o, MSize len) +{ + CType *dc = ctype_rawchild(cts, d); /* Array element type. */ + CTSize ofs, esize = dc->size; + MSize i; + if (len*esize > sz) + cconv_err_initov(cts, d); + for (i = 0, ofs = 0; i < len; i++, ofs += esize) + lj_cconv_ct_tv(cts, dc, dp + ofs, o + i, 0); + if (ofs == esize) { /* Replicate a single element. */ + for (; ofs < sz; ofs += esize) memcpy(dp + ofs, dp, esize); + } else { /* Otherwise fill the remainder with zero. */ + memset(dp + ofs, 0, sz - ofs); + } +} + +/* Initialize a sub-struct/union with TValues. */ +static void cconv_substruct_init(CTState *cts, CType *d, uint8_t *dp, + TValue *o, MSize len, MSize *ip) +{ + CTypeID id = d->sib; + while (id) { + CType *df = ctype_get(cts, id); + id = df->sib; + if (ctype_isfield(df->info) || ctype_isbitfield(df->info)) { + MSize i = *ip; + if (!gcref(df->name)) continue; /* Ignore unnamed fields. */ + if (i >= len) break; + *ip = i + 1; + if (ctype_isfield(df->info)) + lj_cconv_ct_tv(cts, ctype_rawchild(cts, df), dp+df->size, o + i, 0); + else + lj_cconv_bf_tv(cts, df, dp+df->size, o + i); + if ((d->info & CTF_UNION)) break; + } else if (ctype_isxattrib(df->info, CTA_SUBTYPE)) { + cconv_substruct_init(cts, ctype_rawchild(cts, df), + dp+df->size, o, len, ip); + if ((d->info & CTF_UNION)) break; + } /* Ignore all other entries in the chain. */ + } +} + +/* Initialize a struct/union with TValues. */ +static void cconv_struct_init(CTState *cts, CType *d, CTSize sz, uint8_t *dp, + TValue *o, MSize len) +{ + MSize i = 0; + memset(dp, 0, sz); /* Much simpler to clear the struct first. */ + cconv_substruct_init(cts, d, dp, o, len, &i); + if (i < len) + cconv_err_initov(cts, d); +} + +/* Check whether to use a multi-value initializer. +** This is true if an aggregate is to be initialized with a value. +** Valarrays are treated as values here so ct_tv handles (V|C, I|F). +*/ +int lj_cconv_multi_init(CTState *cts, CType *d, TValue *o) +{ + if (!(ctype_isrefarray(d->info) || ctype_isstruct(d->info))) + return 0; /* Destination is not an aggregate. */ + if (tvistab(o) || (tvisstr(o) && !ctype_isstruct(d->info))) + return 0; /* Initializer is not a value. */ + if (tviscdata(o) && lj_ctype_rawref(cts, cdataV(o)->ctypeid) == d) + return 0; /* Source and destination are identical aggregates. */ + return 1; /* Otherwise the initializer is a value. */ +} + +/* Initialize C type with TValues. Caveat: expects to get the raw CType! */ +void lj_cconv_ct_init(CTState *cts, CType *d, CTSize sz, + uint8_t *dp, TValue *o, MSize len) +{ + if (len == 0) + memset(dp, 0, sz); + else if (len == 1 && !lj_cconv_multi_init(cts, d, o)) + lj_cconv_ct_tv(cts, d, dp, o, 0); + else if (ctype_isarray(d->info)) /* Also handles valarray init with len>1. */ + cconv_array_init(cts, d, sz, dp, o, len); + else if (ctype_isstruct(d->info)) + cconv_struct_init(cts, d, sz, dp, o, len); + else + cconv_err_initov(cts, d); +} + +#endif diff --git a/lib/LuaJIT/lj_cconv.h b/lib/LuaJIT/lj_cconv.h new file mode 100644 index 0000000..0a0b66c --- /dev/null +++ b/lib/LuaJIT/lj_cconv.h @@ -0,0 +1,70 @@ +/* +** C type conversions. +** Copyright (C) 2005-2017 Mike Pall. See Copyright Notice in luajit.h +*/ + +#ifndef _LJ_CCONV_H +#define _LJ_CCONV_H + +#include "lj_obj.h" +#include "lj_ctype.h" + +#if LJ_HASFFI + +/* Compressed C type index. ORDER CCX. */ +enum { + CCX_B, /* Bool. */ + CCX_I, /* Integer. */ + CCX_F, /* Floating-point number. */ + CCX_C, /* Complex. */ + CCX_V, /* Vector. */ + CCX_P, /* Pointer. */ + CCX_A, /* Refarray. */ + CCX_S /* Struct/union. */ +}; + +/* Convert C type info to compressed C type index. ORDER CT. ORDER CCX. */ +static LJ_AINLINE uint32_t cconv_idx(CTInfo info) +{ + uint32_t idx = ((info >> 26) & 15u); /* Dispatch bits. */ + lua_assert(ctype_type(info) <= CT_MAYCONVERT); +#if LJ_64 + idx = ((uint32_t)(U64x(f436fff5,fff7f021) >> 4*idx) & 15u); +#else + idx = (((idx < 8 ? 0xfff7f021u : 0xf436fff5) >> 4*(idx & 7u)) & 15u); +#endif + lua_assert(idx < 8); + return idx; +} + +#define cconv_idx2(dinfo, sinfo) \ + ((cconv_idx((dinfo)) << 3) + cconv_idx((sinfo))) + +#define CCX(dst, src) ((CCX_##dst << 3) + CCX_##src) + +/* Conversion flags. */ +#define CCF_CAST 0x00000001u +#define CCF_FROMTV 0x00000002u +#define CCF_SAME 0x00000004u +#define CCF_IGNQUAL 0x00000008u + +#define CCF_ARG_SHIFT 8 +#define CCF_ARG(n) ((n) << CCF_ARG_SHIFT) +#define CCF_GETARG(f) ((f) >> CCF_ARG_SHIFT) + +LJ_FUNC int lj_cconv_compatptr(CTState *cts, CType *d, CType *s, CTInfo flags); +LJ_FUNC void lj_cconv_ct_ct(CTState *cts, CType *d, CType *s, + uint8_t *dp, uint8_t *sp, CTInfo flags); +LJ_FUNC int lj_cconv_tv_ct(CTState *cts, CType *s, CTypeID sid, + TValue *o, uint8_t *sp); +LJ_FUNC int lj_cconv_tv_bf(CTState *cts, CType *s, TValue *o, uint8_t *sp); +LJ_FUNC void lj_cconv_ct_tv(CTState *cts, CType *d, + uint8_t *dp, TValue *o, CTInfo flags); +LJ_FUNC void lj_cconv_bf_tv(CTState *cts, CType *d, uint8_t *dp, TValue *o); +LJ_FUNC int lj_cconv_multi_init(CTState *cts, CType *d, TValue *o); +LJ_FUNC void lj_cconv_ct_init(CTState *cts, CType *d, CTSize sz, + uint8_t *dp, TValue *o, MSize len); + +#endif + +#endif diff --git a/lib/LuaJIT/lj_cconv.o b/lib/LuaJIT/lj_cconv.o new file mode 100644 index 0000000000000000000000000000000000000000..7ae4ad78ef5f35950bf331c32305708826ac76ba GIT binary patch literal 13280 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 CT_MEMALIGN ? (1u<offset = (uint16_t)((char *)cd - p); + cdatav(cd)->extra = extra; + cdatav(cd)->len = sz; + g = G(L); + setgcrefr(cd->nextgc, g->gc.root); + setgcref(g->gc.root, obj2gco(cd)); + newwhite(g, obj2gco(cd)); + cd->marked |= 0x80; + cd->gct = ~LJ_TCDATA; + cd->ctypeid = id; + return cd; +} + +/* Allocate arbitrary C data object. */ +GCcdata *lj_cdata_newx(CTState *cts, CTypeID id, CTSize sz, CTInfo info) +{ + if (!(info & CTF_VLA) && ctype_align(info) <= CT_MEMALIGN) + return lj_cdata_new(cts, id, sz); + else + return lj_cdata_newv(cts->L, id, sz, ctype_align(info)); +} + +/* Free a C data object. */ +void LJ_FASTCALL lj_cdata_free(global_State *g, GCcdata *cd) +{ + if (LJ_UNLIKELY(cd->marked & LJ_GC_CDATA_FIN)) { + GCobj *root; + makewhite(g, obj2gco(cd)); + markfinalized(obj2gco(cd)); + if ((root = gcref(g->gc.mmudata)) != NULL) { + setgcrefr(cd->nextgc, root->gch.nextgc); + setgcref(root->gch.nextgc, obj2gco(cd)); + setgcref(g->gc.mmudata, obj2gco(cd)); + } else { + setgcref(cd->nextgc, obj2gco(cd)); + setgcref(g->gc.mmudata, obj2gco(cd)); + } + } else if (LJ_LIKELY(!cdataisv(cd))) { + CType *ct = ctype_raw(ctype_ctsG(g), cd->ctypeid); + CTSize sz = ctype_hassize(ct->info) ? ct->size : CTSIZE_PTR; + lua_assert(ctype_hassize(ct->info) || ctype_isfunc(ct->info) || + ctype_isextern(ct->info)); + lj_mem_free(g, cd, sizeof(GCcdata) + sz); + } else { + lj_mem_free(g, memcdatav(cd), sizecdatav(cd)); + } +} + +void lj_cdata_setfin(lua_State *L, GCcdata *cd, GCobj *obj, uint32_t it) +{ + GCtab *t = ctype_ctsG(G(L))->finalizer; + if (gcref(t->metatable)) { + /* Add cdata to finalizer table, if still enabled. */ + TValue *tv, tmp; + setcdataV(L, &tmp, cd); + lj_gc_anybarriert(L, t); + tv = lj_tab_set(L, t, &tmp); + if (it == LJ_TNIL) { + setnilV(tv); + cd->marked &= ~LJ_GC_CDATA_FIN; + } else { + setgcV(L, tv, obj, it); + cd->marked |= LJ_GC_CDATA_FIN; + } + } +} + +/* -- C data indexing ----------------------------------------------------- */ + +/* Index C data by a TValue. Return CType and pointer. */ +CType *lj_cdata_index(CTState *cts, GCcdata *cd, cTValue *key, uint8_t **pp, + CTInfo *qual) +{ + uint8_t *p = (uint8_t *)cdataptr(cd); + CType *ct = ctype_get(cts, cd->ctypeid); + ptrdiff_t idx; + + /* Resolve reference for cdata object. */ + if (ctype_isref(ct->info)) { + lua_assert(ct->size == CTSIZE_PTR); + p = *(uint8_t **)p; + ct = ctype_child(cts, ct); + } + +collect_attrib: + /* Skip attributes and collect qualifiers. */ + while (ctype_isattrib(ct->info)) { + if (ctype_attrib(ct->info) == CTA_QUAL) *qual |= ct->size; + ct = ctype_child(cts, ct); + } + lua_assert(!ctype_isref(ct->info)); /* Interning rejects refs to refs. */ + + if (tvisint(key)) { + idx = (ptrdiff_t)intV(key); + goto integer_key; + } else if (tvisnum(key)) { /* Numeric key. */ +#ifdef _MSC_VER + /* Workaround for MSVC bug. */ + volatile +#endif + lua_Number n = numV(key); + idx = LJ_64 ? (ptrdiff_t)n : (ptrdiff_t)lj_num2int(n); + integer_key: + if (ctype_ispointer(ct->info)) { + CTSize sz = lj_ctype_size(cts, ctype_cid(ct->info)); /* Element size. */ + if (sz == CTSIZE_INVALID) + lj_err_caller(cts->L, LJ_ERR_FFI_INVSIZE); + if (ctype_isptr(ct->info)) { + p = (uint8_t *)cdata_getptr(p, ct->size); + } else if ((ct->info & (CTF_VECTOR|CTF_COMPLEX))) { + if ((ct->info & CTF_COMPLEX)) idx &= 1; + *qual |= CTF_CONST; /* Valarray elements are constant. */ + } + *pp = p + idx*(int32_t)sz; + return ct; + } + } else if (tviscdata(key)) { /* Integer cdata key. */ + GCcdata *cdk = cdataV(key); + CType *ctk = ctype_raw(cts, cdk->ctypeid); + if (ctype_isenum(ctk->info)) ctk = ctype_child(cts, ctk); + if (ctype_isinteger(ctk->info)) { + lj_cconv_ct_ct(cts, ctype_get(cts, CTID_INT_PSZ), ctk, + (uint8_t *)&idx, cdataptr(cdk), 0); + goto integer_key; + } + } else if (tvisstr(key)) { /* String key. */ + GCstr *name = strV(key); + if (ctype_isstruct(ct->info)) { + CTSize ofs; + CType *fct = lj_ctype_getfieldq(cts, ct, name, &ofs, qual); + if (fct) { + *pp = p + ofs; + return fct; + } + } else if (ctype_iscomplex(ct->info)) { + if (name->len == 2) { + *qual |= CTF_CONST; /* Complex fields are constant. */ + if (strdata(name)[0] == 'r' && strdata(name)[1] == 'e') { + *pp = p; + return ct; + } else if (strdata(name)[0] == 'i' && strdata(name)[1] == 'm') { + *pp = p + (ct->size >> 1); + return ct; + } + } + } else if (cd->ctypeid == CTID_CTYPEID) { + /* Allow indexing a (pointer to) struct constructor to get constants. */ + CType *sct = ctype_raw(cts, *(CTypeID *)p); + if (ctype_isptr(sct->info)) + sct = ctype_rawchild(cts, sct); + if (ctype_isstruct(sct->info)) { + CTSize ofs; + CType *fct = lj_ctype_getfield(cts, sct, name, &ofs); + if (fct && ctype_isconstval(fct->info)) + return fct; + } + ct = sct; /* Allow resolving metamethods for constructors, too. */ + } + } + if (ctype_isptr(ct->info)) { /* Automatically perform '->'. */ + if (ctype_isstruct(ctype_rawchild(cts, ct)->info)) { + p = (uint8_t *)cdata_getptr(p, ct->size); + ct = ctype_child(cts, ct); + goto collect_attrib; + } + } + *qual |= 1; /* Lookup failed. */ + return ct; /* But return the resolved raw type. */ +} + +/* -- C data getters ------------------------------------------------------ */ + +/* Get constant value and convert to TValue. */ +static void cdata_getconst(CTState *cts, TValue *o, CType *ct) +{ + CType *ctt = ctype_child(cts, ct); + lua_assert(ctype_isinteger(ctt->info) && ctt->size <= 4); + /* Constants are already zero-extended/sign-extended to 32 bits. */ + if ((ctt->info & CTF_UNSIGNED) && (int32_t)ct->size < 0) + setnumV(o, (lua_Number)(uint32_t)ct->size); + else + setintV(o, (int32_t)ct->size); +} + +/* Get C data value and convert to TValue. */ +int lj_cdata_get(CTState *cts, CType *s, TValue *o, uint8_t *sp) +{ + CTypeID sid; + + if (ctype_isconstval(s->info)) { + cdata_getconst(cts, o, s); + return 0; /* No GC step needed. */ + } else if (ctype_isbitfield(s->info)) { + return lj_cconv_tv_bf(cts, s, o, sp); + } + + /* Get child type of pointer/array/field. */ + lua_assert(ctype_ispointer(s->info) || ctype_isfield(s->info)); + sid = ctype_cid(s->info); + s = ctype_get(cts, sid); + + /* Resolve reference for field. */ + if (ctype_isref(s->info)) { + lua_assert(s->size == CTSIZE_PTR); + sp = *(uint8_t **)sp; + sid = ctype_cid(s->info); + s = ctype_get(cts, sid); + } + + /* Skip attributes. */ + while (ctype_isattrib(s->info)) + s = ctype_child(cts, s); + + return lj_cconv_tv_ct(cts, s, sid, o, sp); +} + +/* -- C data setters ------------------------------------------------------ */ + +/* Convert TValue and set C data value. */ +void lj_cdata_set(CTState *cts, CType *d, uint8_t *dp, TValue *o, CTInfo qual) +{ + if (ctype_isconstval(d->info)) { + goto err_const; + } else if (ctype_isbitfield(d->info)) { + if (((d->info|qual) & CTF_CONST)) goto err_const; + lj_cconv_bf_tv(cts, d, dp, o); + return; + } + + /* Get child type of pointer/array/field. */ + lua_assert(ctype_ispointer(d->info) || ctype_isfield(d->info)); + d = ctype_child(cts, d); + + /* Resolve reference for field. */ + if (ctype_isref(d->info)) { + lua_assert(d->size == CTSIZE_PTR); + dp = *(uint8_t **)dp; + d = ctype_child(cts, d); + } + + /* Skip attributes and collect qualifiers. */ + for (;;) { + if (ctype_isattrib(d->info)) { + if (ctype_attrib(d->info) == CTA_QUAL) qual |= d->size; + } else { + break; + } + d = ctype_child(cts, d); + } + + lua_assert(ctype_hassize(d->info) && !ctype_isvoid(d->info)); + + if (((d->info|qual) & CTF_CONST)) { + err_const: + lj_err_caller(cts->L, LJ_ERR_FFI_WRCONST); + } + + lj_cconv_ct_tv(cts, d, dp, o, 0); +} + +#endif diff --git a/lib/LuaJIT/lj_cdata.h b/lib/LuaJIT/lj_cdata.h new file mode 100644 index 0000000..5bb0f5d --- /dev/null +++ b/lib/LuaJIT/lj_cdata.h @@ -0,0 +1,78 @@ +/* +** C data management. +** Copyright (C) 2005-2017 Mike Pall. See Copyright Notice in luajit.h +*/ + +#ifndef _LJ_CDATA_H +#define _LJ_CDATA_H + +#include "lj_obj.h" +#include "lj_gc.h" +#include "lj_ctype.h" + +#if LJ_HASFFI + +/* Get C data pointer. */ +static LJ_AINLINE void *cdata_getptr(void *p, CTSize sz) +{ + if (LJ_64 && sz == 4) { /* Support 32 bit pointers on 64 bit targets. */ + return ((void *)(uintptr_t)*(uint32_t *)p); + } else { + lua_assert(sz == CTSIZE_PTR); + return *(void **)p; + } +} + +/* Set C data pointer. */ +static LJ_AINLINE void cdata_setptr(void *p, CTSize sz, const void *v) +{ + if (LJ_64 && sz == 4) { /* Support 32 bit pointers on 64 bit targets. */ + *(uint32_t *)p = (uint32_t)(uintptr_t)v; + } else { + lua_assert(sz == CTSIZE_PTR); + *(void **)p = (void *)v; + } +} + +/* Allocate fixed-size C data object. */ +static LJ_AINLINE GCcdata *lj_cdata_new(CTState *cts, CTypeID id, CTSize sz) +{ + GCcdata *cd; +#ifdef LUA_USE_ASSERT + CType *ct = ctype_raw(cts, id); + lua_assert((ctype_hassize(ct->info) ? ct->size : CTSIZE_PTR) == sz); +#endif + cd = (GCcdata *)lj_mem_newgco(cts->L, sizeof(GCcdata) + sz); + cd->gct = ~LJ_TCDATA; + cd->ctypeid = ctype_check(cts, id); + return cd; +} + +/* Variant which works without a valid CTState. */ +static LJ_AINLINE GCcdata *lj_cdata_new_(lua_State *L, CTypeID id, CTSize sz) +{ + GCcdata *cd = (GCcdata *)lj_mem_newgco(L, sizeof(GCcdata) + sz); + cd->gct = ~LJ_TCDATA; + cd->ctypeid = id; + return cd; +} + +LJ_FUNC GCcdata *lj_cdata_newref(CTState *cts, const void *pp, CTypeID id); +LJ_FUNC GCcdata *lj_cdata_newv(lua_State *L, CTypeID id, CTSize sz, + CTSize align); +LJ_FUNC GCcdata *lj_cdata_newx(CTState *cts, CTypeID id, CTSize sz, + CTInfo info); + +LJ_FUNC void LJ_FASTCALL lj_cdata_free(global_State *g, GCcdata *cd); +LJ_FUNC void lj_cdata_setfin(lua_State *L, GCcdata *cd, GCobj *obj, + uint32_t it); + +LJ_FUNC CType *lj_cdata_index(CTState *cts, GCcdata *cd, cTValue *key, + uint8_t **pp, CTInfo *qual); +LJ_FUNC int lj_cdata_get(CTState *cts, CType *s, TValue *o, uint8_t *sp); +LJ_FUNC void lj_cdata_set(CTState *cts, CType *d, uint8_t *dp, TValue *o, + CTInfo qual); + +#endif + +#endif diff --git a/lib/LuaJIT/lj_cdata.o b/lib/LuaJIT/lj_cdata.o new file mode 100644 index 0000000000000000000000000000000000000000..9045e9da53d3dfaa7e16180f16f405a49ee652e6 GIT binary patch literal 4864 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 1)) +#define lj_char_tolower(c) ((c) + lj_char_isupper(c)) + +LJ_DATA const uint8_t lj_char_bits[257]; + +#endif diff --git a/lib/LuaJIT/lj_char.o b/lib/LuaJIT/lj_char.o new file mode 100644 index 0000000000000000000000000000000000000000..2dc580e9e5ec17933de4c824128219f35a3a56f4 GIT binary patch literal 1304 zcmb<-^>JfjWMqH=Mg}_u1P><4z%YXe!FB*M9T>P6I2jliVSpI~U>pR^#DWTDzyVZz z0~jp80ze8{ST2CU0W1I{&)6`)fWh6_*-Amf-7i#A!BWpq&j1ll*jS*jWnuXEADbLA z){tjlU}iuHd?by`KpAX+8E6AGz|6pgO&DkjD~32XP(+XkVGcZr*%BKpLnA6xQfIcEDj4dYV^+>PHV@V)d&7^>Y9*x%zdW_M^u< z%zk)$0M&tj0+>V)CO{_0Pw2_f5$aJPgiIa1p41JfjWMqH=Mg}_u1P><4z%YXe!FB*M9T>P6I2jliVSpI~U>pR^#DWTDzyVZz z0~jp80ze8{ST2CU0W1I{&)6`)fWh6_*-Amf-7i#A!BWpq&j1ll*jS*jWnuXEADbLA z){tjlU}iuHd?by`KpAX+8E6AGz|6pgO&DkjD~32XP(+XkVGcZr*%BKpLnA6xQfIcEDj4dYV^+>PHV@V)d&7^>Y9*x%zdW_M^u< z%zk)$0M&tj0+>V)CO{_0Pw2_f5$aJPgiIa1p41 +#include + +#if defined(RTLD_DEFAULT) +#define CLIB_DEFHANDLE RTLD_DEFAULT +#elif LJ_TARGET_OSX || LJ_TARGET_BSD +#define CLIB_DEFHANDLE ((void *)(intptr_t)-2) +#else +#define CLIB_DEFHANDLE NULL +#endif + +LJ_NORET LJ_NOINLINE static void clib_error_(lua_State *L) +{ + lj_err_callermsg(L, dlerror()); +} + +#define clib_error(L, fmt, name) clib_error_(L) + +#if LJ_TARGET_CYGWIN +#define CLIB_SOPREFIX "cyg" +#else +#define CLIB_SOPREFIX "lib" +#endif + +#if LJ_TARGET_OSX +#define CLIB_SOEXT "%s.dylib" +#elif LJ_TARGET_CYGWIN +#define CLIB_SOEXT "%s.dll" +#else +#define CLIB_SOEXT "%s.so" +#endif + +static const char *clib_extname(lua_State *L, const char *name) +{ + if (!strchr(name, '/') +#if LJ_TARGET_CYGWIN + && !strchr(name, '\\') +#endif + ) { + if (!strchr(name, '.')) { + name = lj_strfmt_pushf(L, CLIB_SOEXT, name); + L->top--; +#if LJ_TARGET_CYGWIN + } else { + return name; +#endif + } + if (!(name[0] == CLIB_SOPREFIX[0] && name[1] == CLIB_SOPREFIX[1] && + name[2] == CLIB_SOPREFIX[2])) { + name = lj_strfmt_pushf(L, CLIB_SOPREFIX "%s", name); + L->top--; + } + } + return name; +} + +/* Check for a recognized ld script line. */ +static const char *clib_check_lds(lua_State *L, const char *buf) +{ + char *p, *e; + if ((!strncmp(buf, "GROUP", 5) || !strncmp(buf, "INPUT", 5)) && + (p = strchr(buf, '('))) { + while (*++p == ' ') ; + for (e = p; *e && *e != ' ' && *e != ')'; e++) ; + return strdata(lj_str_new(L, p, e-p)); + } + return NULL; +} + +/* Quick and dirty solution to resolve shared library name from ld script. */ +static const char *clib_resolve_lds(lua_State *L, const char *name) +{ + FILE *fp = fopen(name, "r"); + const char *p = NULL; + if (fp) { + char buf[256]; + if (fgets(buf, sizeof(buf), fp)) { + if (!strncmp(buf, "/* GNU ld script", 16)) { /* ld script magic? */ + while (fgets(buf, sizeof(buf), fp)) { /* Check all lines. */ + p = clib_check_lds(L, buf); + if (p) break; + } + } else { /* Otherwise check only the first line. */ + p = clib_check_lds(L, buf); + } + } + fclose(fp); + } + return p; +} + +static void *clib_loadlib(lua_State *L, const char *name, int global) +{ + void *h = dlopen(clib_extname(L, name), + RTLD_LAZY | (global?RTLD_GLOBAL:RTLD_LOCAL)); + if (!h) { + const char *e, *err = dlerror(); + if (*err == '/' && (e = strchr(err, ':')) && + (name = clib_resolve_lds(L, strdata(lj_str_new(L, err, e-err))))) { + h = dlopen(name, RTLD_LAZY | (global?RTLD_GLOBAL:RTLD_LOCAL)); + if (h) return h; + err = dlerror(); + } + lj_err_callermsg(L, err); + } + return h; +} + +static void clib_unloadlib(CLibrary *cl) +{ + if (cl->handle && cl->handle != CLIB_DEFHANDLE) + dlclose(cl->handle); +} + +static void *clib_getsym(CLibrary *cl, const char *name) +{ + void *p = dlsym(cl->handle, name); + return p; +} + +#elif LJ_TARGET_WINDOWS + +#define WIN32_LEAN_AND_MEAN +#include + +#ifndef GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS +#define GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS 4 +#define GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT 2 +BOOL WINAPI GetModuleHandleExA(DWORD, LPCSTR, HMODULE*); +#endif + +#define CLIB_DEFHANDLE ((void *)-1) + +/* Default libraries. */ +enum { + CLIB_HANDLE_EXE, +#if !LJ_TARGET_UWP + CLIB_HANDLE_DLL, + CLIB_HANDLE_CRT, + CLIB_HANDLE_KERNEL32, + CLIB_HANDLE_USER32, + CLIB_HANDLE_GDI32, +#endif + CLIB_HANDLE_MAX +}; + +static void *clib_def_handle[CLIB_HANDLE_MAX]; + +LJ_NORET LJ_NOINLINE static void clib_error(lua_State *L, const char *fmt, + const char *name) +{ + DWORD err = GetLastError(); +#if LJ_TARGET_XBOXONE + wchar_t wbuf[128]; + char buf[128*2]; + if (!FormatMessageW(FORMAT_MESSAGE_IGNORE_INSERTS|FORMAT_MESSAGE_FROM_SYSTEM, + NULL, err, 0, wbuf, sizeof(wbuf)/sizeof(wchar_t), NULL) || + !WideCharToMultiByte(CP_ACP, 0, wbuf, 128, buf, 128*2, NULL, NULL)) +#else + char buf[128]; + if (!FormatMessageA(FORMAT_MESSAGE_IGNORE_INSERTS|FORMAT_MESSAGE_FROM_SYSTEM, + NULL, err, 0, buf, sizeof(buf), NULL)) +#endif + buf[0] = '\0'; + lj_err_callermsg(L, lj_strfmt_pushf(L, fmt, name, buf)); +} + +static int clib_needext(const char *s) +{ + while (*s) { + if (*s == '/' || *s == '\\' || *s == '.') return 0; + s++; + } + return 1; +} + +static const char *clib_extname(lua_State *L, const char *name) +{ + if (clib_needext(name)) { + name = lj_strfmt_pushf(L, "%s.dll", name); + L->top--; + } + return name; +} + +static void *clib_loadlib(lua_State *L, const char *name, int global) +{ + DWORD oldwerr = GetLastError(); + void *h = LJ_WIN_LOADLIBA(clib_extname(L, name)); + if (!h) clib_error(L, "cannot load module " LUA_QS ": %s", name); + SetLastError(oldwerr); + UNUSED(global); + return h; +} + +static void clib_unloadlib(CLibrary *cl) +{ + if (cl->handle == CLIB_DEFHANDLE) { +#if !LJ_TARGET_UWP + MSize i; + for (i = CLIB_HANDLE_KERNEL32; i < CLIB_HANDLE_MAX; i++) { + void *h = clib_def_handle[i]; + if (h) { + clib_def_handle[i] = NULL; + FreeLibrary((HINSTANCE)h); + } + } +#endif + } else if (cl->handle) { + FreeLibrary((HINSTANCE)cl->handle); + } +} + +#if LJ_TARGET_UWP +EXTERN_C IMAGE_DOS_HEADER __ImageBase; +#endif + +static void *clib_getsym(CLibrary *cl, const char *name) +{ + void *p = NULL; + if (cl->handle == CLIB_DEFHANDLE) { /* Search default libraries. */ + MSize i; + for (i = 0; i < CLIB_HANDLE_MAX; i++) { + HINSTANCE h = (HINSTANCE)clib_def_handle[i]; + if (!(void *)h) { /* Resolve default library handles (once). */ +#if LJ_TARGET_UWP + h = (HINSTANCE)&__ImageBase; +#else + switch (i) { + case CLIB_HANDLE_EXE: GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, NULL, &h); break; + case CLIB_HANDLE_DLL: + GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS|GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, + (const char *)clib_def_handle, &h); + break; + case CLIB_HANDLE_CRT: + GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS|GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, + (const char *)&_fmode, &h); + break; + case CLIB_HANDLE_KERNEL32: h = LJ_WIN_LOADLIBA("kernel32.dll"); break; + case CLIB_HANDLE_USER32: h = LJ_WIN_LOADLIBA("user32.dll"); break; + case CLIB_HANDLE_GDI32: h = LJ_WIN_LOADLIBA("gdi32.dll"); break; + } + if (!h) continue; +#endif + clib_def_handle[i] = (void *)h; + } + p = (void *)GetProcAddress(h, name); + if (p) break; + } + } else { + p = (void *)GetProcAddress((HINSTANCE)cl->handle, name); + } + return p; +} + +#else + +#define CLIB_DEFHANDLE NULL + +LJ_NORET LJ_NOINLINE static void clib_error(lua_State *L, const char *fmt, + const char *name) +{ + lj_err_callermsg(L, lj_strfmt_pushf(L, fmt, name, "no support for this OS")); +} + +static void *clib_loadlib(lua_State *L, const char *name, int global) +{ + lj_err_callermsg(L, "no support for loading dynamic libraries for this OS"); + UNUSED(name); UNUSED(global); + return NULL; +} + +static void clib_unloadlib(CLibrary *cl) +{ + UNUSED(cl); +} + +static void *clib_getsym(CLibrary *cl, const char *name) +{ + UNUSED(cl); UNUSED(name); + return NULL; +} + +#endif + +/* -- C library indexing -------------------------------------------------- */ + +#if LJ_TARGET_X86 && LJ_ABI_WIN +/* Compute argument size for fastcall/stdcall functions. */ +static CTSize clib_func_argsize(CTState *cts, CType *ct) +{ + CTSize n = 0; + while (ct->sib) { + CType *d; + ct = ctype_get(cts, ct->sib); + if (ctype_isfield(ct->info)) { + d = ctype_rawchild(cts, ct); + n += ((d->size + 3) & ~3); + } + } + return n; +} +#endif + +/* Get redirected or mangled external symbol. */ +static const char *clib_extsym(CTState *cts, CType *ct, GCstr *name) +{ + if (ct->sib) { + CType *ctf = ctype_get(cts, ct->sib); + if (ctype_isxattrib(ctf->info, CTA_REDIR)) + return strdata(gco2str(gcref(ctf->name))); + } + return strdata(name); +} + +/* Index a C library by name. */ +TValue *lj_clib_index(lua_State *L, CLibrary *cl, GCstr *name) +{ + TValue *tv = lj_tab_setstr(L, cl->cache, name); + if (LJ_UNLIKELY(tvisnil(tv))) { + CTState *cts = ctype_cts(L); + CType *ct; + CTypeID id = lj_ctype_getname(cts, &ct, name, CLNS_INDEX); + if (!id) + lj_err_callerv(L, LJ_ERR_FFI_NODECL, strdata(name)); + if (ctype_isconstval(ct->info)) { + CType *ctt = ctype_child(cts, ct); + lua_assert(ctype_isinteger(ctt->info) && ctt->size <= 4); + if ((ctt->info & CTF_UNSIGNED) && (int32_t)ct->size < 0) + setnumV(tv, (lua_Number)(uint32_t)ct->size); + else + setintV(tv, (int32_t)ct->size); + } else { + const char *sym = clib_extsym(cts, ct, name); +#if LJ_TARGET_WINDOWS + DWORD oldwerr = GetLastError(); +#endif + void *p = clib_getsym(cl, sym); + GCcdata *cd; + lua_assert(ctype_isfunc(ct->info) || ctype_isextern(ct->info)); +#if LJ_TARGET_X86 && LJ_ABI_WIN + /* Retry with decorated name for fastcall/stdcall functions. */ + if (!p && ctype_isfunc(ct->info)) { + CTInfo cconv = ctype_cconv(ct->info); + if (cconv == CTCC_FASTCALL || cconv == CTCC_STDCALL) { + CTSize sz = clib_func_argsize(cts, ct); + const char *symd = lj_strfmt_pushf(L, + cconv == CTCC_FASTCALL ? "@%s@%d" : "_%s@%d", + sym, sz); + L->top--; + p = clib_getsym(cl, symd); + } + } +#endif + if (!p) + clib_error(L, "cannot resolve symbol " LUA_QS ": %s", sym); +#if LJ_TARGET_WINDOWS + SetLastError(oldwerr); +#endif + cd = lj_cdata_new(cts, id, CTSIZE_PTR); + *(void **)cdataptr(cd) = p; + setcdataV(L, tv, cd); + } + } + return tv; +} + +/* -- C library management ------------------------------------------------ */ + +/* Create a new CLibrary object and push it on the stack. */ +static CLibrary *clib_new(lua_State *L, GCtab *mt) +{ + GCtab *t = lj_tab_new(L, 0, 0); + GCudata *ud = lj_udata_new(L, sizeof(CLibrary), t); + CLibrary *cl = (CLibrary *)uddata(ud); + cl->cache = t; + ud->udtype = UDTYPE_FFI_CLIB; + /* NOBARRIER: The GCudata is new (marked white). */ + setgcref(ud->metatable, obj2gco(mt)); + setudataV(L, L->top++, ud); + return cl; +} + +/* Load a C library. */ +void lj_clib_load(lua_State *L, GCtab *mt, GCstr *name, int global) +{ + void *handle = clib_loadlib(L, strdata(name), global); + CLibrary *cl = clib_new(L, mt); + cl->handle = handle; +} + +/* Unload a C library. */ +void lj_clib_unload(CLibrary *cl) +{ + clib_unloadlib(cl); + cl->handle = NULL; +} + +/* Create the default C library object. */ +void lj_clib_default(lua_State *L, GCtab *mt) +{ + CLibrary *cl = clib_new(L, mt); + cl->handle = CLIB_DEFHANDLE; +} + +#endif diff --git a/lib/LuaJIT/lj_clib.h b/lib/LuaJIT/lj_clib.h new file mode 100644 index 0000000..fcc9dac --- /dev/null +++ b/lib/LuaJIT/lj_clib.h @@ -0,0 +1,29 @@ +/* +** FFI C library loader. +** Copyright (C) 2005-2017 Mike Pall. See Copyright Notice in luajit.h +*/ + +#ifndef _LJ_CLIB_H +#define _LJ_CLIB_H + +#include "lj_obj.h" + +#if LJ_HASFFI + +/* Namespace for C library indexing. */ +#define CLNS_INDEX ((1u<env. */ +} CLibrary; + +LJ_FUNC TValue *lj_clib_index(lua_State *L, CLibrary *cl, GCstr *name); +LJ_FUNC void lj_clib_load(lua_State *L, GCtab *mt, GCstr *name, int global); +LJ_FUNC void lj_clib_unload(CLibrary *cl); +LJ_FUNC void lj_clib_default(lua_State *L, GCtab *mt); + +#endif + +#endif diff --git a/lib/LuaJIT/lj_clib.o b/lib/LuaJIT/lj_clib.o new file mode 100644 index 0000000000000000000000000000000000000000..7498ad9d35776e1dcbe6279a19a11165b63c4e75 GIT binary patch literal 4880 zcmbuCZ){uD6~M3mBsFRKY-piow2E0>lysXL({4(ujpn&da$P){wGI^Zk8zymrr^ZE zeul6q)RB`)SH_SJmH0A{5DYPC(l%*=GHsEYw8@7}Q3*!Dce)#i`~5B(EmmxWJ3js9=&ZiCaHm<{L)qk&26tnakizX| zeZBkr{h%~yTR3+q{{?NHguoy?(@(MDIX1NvU~u-zKjnDUSa3GNz6vgMAENAQ(}Z1` z@ED5#M;8n<-Bxd>r9)oI`h2v!-xX%hcqn_^%j^)FcP8Y#S*=!&98-@@sZXg-s@)NG zOO0Ww!0F#<@v4`WUT_(nibKw7gq_zn(c*b;lwC$6^_Y5GjZYctDYd(3S@*j>Z1ZA{ zKMt49;{HTf#uH^#!-dMG$13j8DGad`^SerRcJAE#=Hl(O`t$A&Lax+UB~NKN=ZTbt z3186^MU%I2LrSMS#`5^W>KamEHds&DVVBCBesw17ciw{4%pZYUYMh?C?2I_plsqG! z5>mZBa)%ISA^%osa2+j9d#?P_{XwO4&?&UYuVF`s?i`yP+K_TztK4vJpS+>E?{)g; z*P%N^bcy}@+iI1T4|yiGDn>yCD?Hq5b7$^WtM0}&vY5|_9T2=4i?DCHY57?XB=#B| zx$1@76mQ$wA6#f}Sx`r&v9e7cP(M3CC(IVO&kK+OeJIMlh_cV->TqV(L`Tk}2Wb-~ z0+?1&Jeaf|P$&$Obbe!D*mg3n6o&0R`maR6Q}!=f{vp^v<$lKFj}+5>uM28t{ujog zu}+O33avXR3;SVc%02ccu$kCn%(hta?$LJ-?r#h3XR3PRBmiA6}6=_gDpzW=>%&q6B4%xrC`mYAGKW_}s0pF~6Ad{7<9ojy1si zN1~XooCQuCF zv>09p*~p)^pC2T)A&>2$b!%MXunWjH%Q~v!Ic+!DF3#Vb_H4tJT3pkqbUoRiw1$-yRcQhRPy_?;)BzKtufL)rkS!3W)z^OX1*WI#+tJs4<)Yml z0&n2n;!Rx(ClxO8xv_V=?I3G-Yx%=LFeA$a zPd8FyUbANENd0nkvYYGQXW@@=j(Z`?KBH3yU+|UUSmE8Q3(rbbavWGKoQdE&ul;w<<$_%PM^9DttR|jAxv`0>*m@es-^-uX6q0aed=m#W>Q> z#*=VL29g9HaV?w8WaDN#*{3C+i>G^YW_Lh)kp!aqTJRRc+fKag!rP-H-Wfd*Qls$$ z9UX_-WAT_8inhl|Z`$yOlRzV$Or!zy=T4BEo=x^;kwYtvo}sBHdPFndo6r-YV+3|` zpjR6bS~*PzJM1O(mj<=?2~8hJ^lLC*?xlXj{aQa-pGd+dK%V;r!v+_c&Lnz4KQpKe zAOm-)emy>z&-JB93h9q_krepNkyJ9B$!Uv*`2lXa9QSIeL_V!!N>Ri~gXzY18NO78 zh`mR?LE_(sHVT#(o?$}210e`){=CFx z{Rj9b1Bcg3`aaHaUZ2EwOI+Tsrz9@>e_rBpo`*QcdEv+J;(A9U{f8y~ro_K1@yin5 zD)H+Qm-Ar51%cp0j(>y1<$BvIae3aT#N~O9bB^o6zmQ@dQeKjIwU%pDR4{~}U`5fq>wbmC; zW$`5Yf5it5a0%Am7j3{$F7SOYdn><5_zt5=n^54-aXg$rT6i5cKpW=~zkI~+lSeq` zYS1&KP8^H^@F!3ABE|C%eg3cZL9j)ikHRI3nE!h+gtnMpJli;HvFNq%`0*W+Mc9dF z1O`Q0)(icY>&M??XpeO%3+49I;#}lk=spdNTKiYQLkZ+P^FeU`&{o)s{i`)MnvC;; sdXH}mmczX@?C$|)-8(>}6(aB2WlysXL({4(ujpn&da$P){wGI^Zk8zymrr^ZE zeul6q)RB`)SH_SJmH0A{5DYPC(l%*=GHsEYw8@7}Q3*!Dce)#i`~5B(EmmxWJ3js9=&ZiCaHm<{L)qk&26tnakizX| zeZBkr{h%~yTR3+q{{?NHguoy?(@(MDIX1NvU~u-zKjnDUSa3GNz6vgMAENAQ(}Z1` z@ED5#M;8n<-Bxd>r9)oI`h2v!-xX%hcqn_^%j^)FcP8Y#S*=!&98-@@sZXg-s@)NG zOO0Ww!0F#<@v4`WUT_(nibKw7gq_zn(c*b;lwC$6^_Y5GjZYctDYd(3S@*j>Z1ZA{ zKMt49;{HTf#uH^#!-dMG$13j8DGad`^SerRcJAE#=Hl(O`t$A&Lax+UB~NKN=ZTbt z3186^MU%I2LrSMS#`5^W>KamEHds&DVVBCBesw17ciw{4%pZYUYMh?C?2I_plsqG! z5>mZBa)%ISA^%osa2+j9d#?P_{XwO4&?&UYuVF`s?i`yP+K_TztK4vJpS+>E?{)g; z*P%N^bcy}@+iI1T4|yiGDn>yCD?Hq5b7$^WtM0}&vY5|_9T2=4i?DCHY57?XB=#B| zx$1@76mQ$wA6#f}Sx`r&v9e7cP(M3CC(IVO&kK+OeJIMlh_cV->TqV(L`Tk}2Wb-~ z0+?1&Jeaf|P$&$Obbe!D*mg3n6o&0R`maR6Q}!=f{vp^v<$lKFj}+5>uM28t{ujog zu}+O33avXR3;SVc%02ccu$kCn%(hta?$LJ-?r#h3XR3PRBmiA6}6=_gDpzW=>%&q6B4%xrC`mYAGKW_}s0pF~6Ad{7<9ojy1si zN1~XooCQuCF zv>09p*~p)^pC2T)A&>2$b!%MXunWjH%Q~v!Ic+!DF3#Vb_H4tJT3pkqbUoRiw1$-yRcQhRPy_?;)BzKtufL)rkS!3W)z^OX1*WI#+tJs4<)Yml z0&n2n;!Rx(ClxO8xv_V=?I3G-Yx%=LFeA$a zPd8FyUbANENd0nkvYYGQXW@@=j(Z`?KBH3yU+|UUSmE8Q3(rbbavWGKoQdE&ul;w<<$_%PM^9DttR|jAxv`0>*m@es-^-uX6q0aed=m#W>Q> z#*=VL29g9HaV?w8WaDN#*{3C+i>G^YW_Lh)kp!aqTJRRc+fKag!rP-H-Wfd*Qls$$ z9UX_-WAT_8inhl|Z`$yOlRzV$Or!zy=T4BEo=x^;kwYtvo}sBHdPFndo6r-YV+3|` zpjR6bS~*PzJM1O(mj<=?2~8hJ^lLC*?xlXj{aQa-pGd+dK%V;r!v+_c&Lnz4KQpKe zAOm-)emy>z&-JB93h9q_krepNkyJ9B$!Uv*`2lXa9QSIeL_V!!N>Ri~gXzY18NO78 zh`mR?LE_(sHVT#(o?$}210e`){=CFx z{Rj9b1Bcg3`aaHaUZ2EwOI+Tsrz9@>e_rBpo`*QcdEv+J;(A9U{f8y~ro_K1@yin5 zD)H+Qm-Ar51%cp0j(>y1<$BvIae3aT#N~O9bB^o6zmQ@dQeKjIwU%pDR4{~}U`5fq>wbmC; zW$`5Yf5it5a0%Am7j3{$F7SOYdn><5_zt5=n^54-aXg$rT6i5cKpW=~zkI~+lSeq` zYS1&KP8^H^@F!3ABE|C%eg3cZL9j)ikHRI3nE!h+gtnMpJli;HvFNq%`0*W+Mc9dF z1O`Q0)(icY>&M??XpeO%3+49I;#}lk=spdNTKiYQLkZ+P^FeU`&{o)s{i`)MnvC;; sdXH}mmczX@?C$|)-8(>}6(aB2W CTOK_OFS) + return ctoknames[tok-CTOK_OFS-1]; + else if (!lj_char_iscntrl(tok)) + return lj_strfmt_pushf(cp->L, "%c", tok); + else + return lj_strfmt_pushf(cp->L, "char(%d)", tok); +} + +/* End-of-line? */ +static LJ_AINLINE int cp_iseol(CPChar c) +{ + return (c == '\n' || c == '\r'); +} + +/* Peek next raw character. */ +static LJ_AINLINE CPChar cp_rawpeek(CPState *cp) +{ + return (CPChar)(uint8_t)(*cp->p); +} + +static LJ_NOINLINE CPChar cp_get_bs(CPState *cp); + +/* Get next character. */ +static LJ_AINLINE CPChar cp_get(CPState *cp) +{ + cp->c = (CPChar)(uint8_t)(*cp->p++); + if (LJ_LIKELY(cp->c != '\\')) return cp->c; + return cp_get_bs(cp); +} + +/* Transparently skip backslash-escaped line breaks. */ +static LJ_NOINLINE CPChar cp_get_bs(CPState *cp) +{ + CPChar c2, c = cp_rawpeek(cp); + if (!cp_iseol(c)) return cp->c; + cp->p++; + c2 = cp_rawpeek(cp); + if (cp_iseol(c2) && c2 != c) cp->p++; + cp->linenumber++; + return cp_get(cp); +} + +/* Save character in buffer. */ +static LJ_AINLINE void cp_save(CPState *cp, CPChar c) +{ + lj_buf_putb(&cp->sb, c); +} + +/* Skip line break. Handles "\n", "\r", "\r\n" or "\n\r". */ +static void cp_newline(CPState *cp) +{ + CPChar c = cp_rawpeek(cp); + if (cp_iseol(c) && c != cp->c) cp->p++; + cp->linenumber++; +} + +LJ_NORET static void cp_errmsg(CPState *cp, CPToken tok, ErrMsg em, ...) +{ + const char *msg, *tokstr; + lua_State *L; + va_list argp; + if (tok == 0) { + tokstr = NULL; + } else if (tok == CTOK_IDENT || tok == CTOK_INTEGER || tok == CTOK_STRING || + tok >= CTOK_FIRSTDECL) { + if (sbufP(&cp->sb) == sbufB(&cp->sb)) cp_save(cp, '$'); + cp_save(cp, '\0'); + tokstr = sbufB(&cp->sb); + } else { + tokstr = cp_tok2str(cp, tok); + } + L = cp->L; + va_start(argp, em); + msg = lj_strfmt_pushvf(L, err2msg(em), argp); + va_end(argp); + if (tokstr) + msg = lj_strfmt_pushf(L, err2msg(LJ_ERR_XNEAR), msg, tokstr); + if (cp->linenumber > 1) + msg = lj_strfmt_pushf(L, "%s at line %d", msg, cp->linenumber); + lj_err_callermsg(L, msg); +} + +LJ_NORET LJ_NOINLINE static void cp_err_token(CPState *cp, CPToken tok) +{ + cp_errmsg(cp, cp->tok, LJ_ERR_XTOKEN, cp_tok2str(cp, tok)); +} + +LJ_NORET LJ_NOINLINE static void cp_err_badidx(CPState *cp, CType *ct) +{ + GCstr *s = lj_ctype_repr(cp->cts->L, ctype_typeid(cp->cts, ct), NULL); + cp_errmsg(cp, 0, LJ_ERR_FFI_BADIDX, strdata(s)); +} + +LJ_NORET LJ_NOINLINE static void cp_err(CPState *cp, ErrMsg em) +{ + cp_errmsg(cp, 0, em); +} + +/* -- Main lexical scanner ------------------------------------------------ */ + +/* Parse number literal. Only handles int32_t/uint32_t right now. */ +static CPToken cp_number(CPState *cp) +{ + StrScanFmt fmt; + TValue o; + do { cp_save(cp, cp->c); } while (lj_char_isident(cp_get(cp))); + cp_save(cp, '\0'); + fmt = lj_strscan_scan((const uint8_t *)sbufB(&cp->sb), &o, STRSCAN_OPT_C); + if (fmt == STRSCAN_INT) cp->val.id = CTID_INT32; + else if (fmt == STRSCAN_U32) cp->val.id = CTID_UINT32; + else if (!(cp->mode & CPARSE_MODE_SKIP)) + cp_errmsg(cp, CTOK_INTEGER, LJ_ERR_XNUMBER); + cp->val.u32 = (uint32_t)o.i; + return CTOK_INTEGER; +} + +/* Parse identifier or keyword. */ +static CPToken cp_ident(CPState *cp) +{ + do { cp_save(cp, cp->c); } while (lj_char_isident(cp_get(cp))); + cp->str = lj_buf_str(cp->L, &cp->sb); + cp->val.id = lj_ctype_getname(cp->cts, &cp->ct, cp->str, cp->tmask); + if (ctype_type(cp->ct->info) == CT_KW) + return ctype_cid(cp->ct->info); + return CTOK_IDENT; +} + +/* Parse parameter. */ +static CPToken cp_param(CPState *cp) +{ + CPChar c = cp_get(cp); + TValue *o = cp->param; + if (lj_char_isident(c) || c == '$') /* Reserve $xyz for future extensions. */ + cp_errmsg(cp, c, LJ_ERR_XSYNTAX); + if (!o || o >= cp->L->top) + cp_err(cp, LJ_ERR_FFI_NUMPARAM); + cp->param = o+1; + if (tvisstr(o)) { + cp->str = strV(o); + cp->val.id = 0; + cp->ct = &cp->cts->tab[0]; + return CTOK_IDENT; + } else if (tvisnumber(o)) { + cp->val.i32 = numberVint(o); + cp->val.id = CTID_INT32; + return CTOK_INTEGER; + } else { + GCcdata *cd; + if (!tviscdata(o)) + lj_err_argtype(cp->L, (int)(o-cp->L->base)+1, "type parameter"); + cd = cdataV(o); + if (cd->ctypeid == CTID_CTYPEID) + cp->val.id = *(CTypeID *)cdataptr(cd); + else + cp->val.id = cd->ctypeid; + return '$'; + } +} + +/* Parse string or character constant. */ +static CPToken cp_string(CPState *cp) +{ + CPChar delim = cp->c; + cp_get(cp); + while (cp->c != delim) { + CPChar c = cp->c; + if (c == '\0') cp_errmsg(cp, CTOK_EOF, LJ_ERR_XSTR); + if (c == '\\') { + c = cp_get(cp); + switch (c) { + case '\0': cp_errmsg(cp, CTOK_EOF, LJ_ERR_XSTR); break; + case 'a': c = '\a'; break; + case 'b': c = '\b'; break; + case 'f': c = '\f'; break; + case 'n': c = '\n'; break; + case 'r': c = '\r'; break; + case 't': c = '\t'; break; + case 'v': c = '\v'; break; + case 'e': c = 27; break; + case 'x': + c = 0; + while (lj_char_isxdigit(cp_get(cp))) + c = (c<<4) + (lj_char_isdigit(cp->c) ? cp->c-'0' : (cp->c&15)+9); + cp_save(cp, (c & 0xff)); + continue; + default: + if (lj_char_isdigit(c)) { + c -= '0'; + if (lj_char_isdigit(cp_get(cp))) { + c = c*8 + (cp->c - '0'); + if (lj_char_isdigit(cp_get(cp))) { + c = c*8 + (cp->c - '0'); + cp_get(cp); + } + } + cp_save(cp, (c & 0xff)); + continue; + } + break; + } + } + cp_save(cp, c); + cp_get(cp); + } + cp_get(cp); + if (delim == '"') { + cp->str = lj_buf_str(cp->L, &cp->sb); + return CTOK_STRING; + } else { + if (sbuflen(&cp->sb) != 1) cp_err_token(cp, '\''); + cp->val.i32 = (int32_t)(char)*sbufB(&cp->sb); + cp->val.id = CTID_INT32; + return CTOK_INTEGER; + } +} + +/* Skip C comment. */ +static void cp_comment_c(CPState *cp) +{ + do { + if (cp_get(cp) == '*') { + do { + if (cp_get(cp) == '/') { cp_get(cp); return; } + } while (cp->c == '*'); + } + if (cp_iseol(cp->c)) cp_newline(cp); + } while (cp->c != '\0'); +} + +/* Skip C++ comment. */ +static void cp_comment_cpp(CPState *cp) +{ + while (!cp_iseol(cp_get(cp)) && cp->c != '\0') + ; +} + +/* Lexical scanner for C. Only a minimal subset is implemented. */ +static CPToken cp_next_(CPState *cp) +{ + lj_buf_reset(&cp->sb); + for (;;) { + if (lj_char_isident(cp->c)) + return lj_char_isdigit(cp->c) ? cp_number(cp) : cp_ident(cp); + switch (cp->c) { + case '\n': case '\r': cp_newline(cp); /* fallthrough. */ + case ' ': case '\t': case '\v': case '\f': cp_get(cp); break; + case '"': case '\'': return cp_string(cp); + case '/': + if (cp_get(cp) == '*') cp_comment_c(cp); + else if (cp->c == '/') cp_comment_cpp(cp); + else return '/'; + break; + case '|': + if (cp_get(cp) != '|') return '|'; + cp_get(cp); return CTOK_OROR; + case '&': + if (cp_get(cp) != '&') return '&'; + cp_get(cp); return CTOK_ANDAND; + case '=': + if (cp_get(cp) != '=') return '='; + cp_get(cp); return CTOK_EQ; + case '!': + if (cp_get(cp) != '=') return '!'; + cp_get(cp); return CTOK_NE; + case '<': + if (cp_get(cp) == '=') { cp_get(cp); return CTOK_LE; } + else if (cp->c == '<') { cp_get(cp); return CTOK_SHL; } + return '<'; + case '>': + if (cp_get(cp) == '=') { cp_get(cp); return CTOK_GE; } + else if (cp->c == '>') { cp_get(cp); return CTOK_SHR; } + return '>'; + case '-': + if (cp_get(cp) != '>') return '-'; + cp_get(cp); return CTOK_DEREF; + case '$': + return cp_param(cp); + case '\0': return CTOK_EOF; + default: { CPToken c = cp->c; cp_get(cp); return c; } + } + } +} + +static LJ_NOINLINE CPToken cp_next(CPState *cp) +{ + return (cp->tok = cp_next_(cp)); +} + +/* -- C parser ------------------------------------------------------------ */ + +/* Namespaces for resolving identifiers. */ +#define CPNS_DEFAULT \ + ((1u<linenumber = 1; + cp->depth = 0; + cp->curpack = 0; + cp->packstack[0] = 255; + lj_buf_init(cp->L, &cp->sb); + lua_assert(cp->p != NULL); + cp_get(cp); /* Read-ahead first char. */ + cp->tok = 0; + cp->tmask = CPNS_DEFAULT; + cp_next(cp); /* Read-ahead first token. */ +} + +/* Cleanup C parser state. */ +static void cp_cleanup(CPState *cp) +{ + global_State *g = G(cp->L); + lj_buf_free(g, &cp->sb); +} + +/* Check and consume optional token. */ +static int cp_opt(CPState *cp, CPToken tok) +{ + if (cp->tok == tok) { cp_next(cp); return 1; } + return 0; +} + +/* Check and consume token. */ +static void cp_check(CPState *cp, CPToken tok) +{ + if (cp->tok != tok) cp_err_token(cp, tok); + cp_next(cp); +} + +/* Check if the next token may start a type declaration. */ +static int cp_istypedecl(CPState *cp) +{ + if (cp->tok >= CTOK_FIRSTDECL && cp->tok <= CTOK_LASTDECL) return 1; + if (cp->tok == CTOK_IDENT && ctype_istypedef(cp->ct->info)) return 1; + if (cp->tok == '$') return 1; + return 0; +} + +/* -- Constant expression evaluator --------------------------------------- */ + +/* Forward declarations. */ +static void cp_expr_unary(CPState *cp, CPValue *k); +static void cp_expr_sub(CPState *cp, CPValue *k, int pri); + +/* Please note that type handling is very weak here. Most ops simply +** assume integer operands. Accessors are only needed to compute types and +** return synthetic values. The only purpose of the expression evaluator +** is to compute the values of constant expressions one would typically +** find in C header files. And again: this is NOT a validating C parser! +*/ + +/* Parse comma separated expression and return last result. */ +static void cp_expr_comma(CPState *cp, CPValue *k) +{ + do { cp_expr_sub(cp, k, 0); } while (cp_opt(cp, ',')); +} + +/* Parse sizeof/alignof operator. */ +static void cp_expr_sizeof(CPState *cp, CPValue *k, int wantsz) +{ + CTSize sz; + CTInfo info; + if (cp_opt(cp, '(')) { + if (cp_istypedecl(cp)) + k->id = cp_decl_abstract(cp); + else + cp_expr_comma(cp, k); + cp_check(cp, ')'); + } else { + cp_expr_unary(cp, k); + } + info = lj_ctype_info(cp->cts, k->id, &sz); + if (wantsz) { + if (sz != CTSIZE_INVALID) + k->u32 = sz; + else if (k->id != CTID_A_CCHAR) /* Special case for sizeof("string"). */ + cp_err(cp, LJ_ERR_FFI_INVSIZE); + } else { + k->u32 = 1u << ctype_align(info); + } + k->id = CTID_UINT32; /* Really size_t. */ +} + +/* Parse prefix operators. */ +static void cp_expr_prefix(CPState *cp, CPValue *k) +{ + if (cp->tok == CTOK_INTEGER) { + *k = cp->val; cp_next(cp); + } else if (cp_opt(cp, '+')) { + cp_expr_unary(cp, k); /* Nothing to do (well, integer promotion). */ + } else if (cp_opt(cp, '-')) { + cp_expr_unary(cp, k); k->i32 = -k->i32; + } else if (cp_opt(cp, '~')) { + cp_expr_unary(cp, k); k->i32 = ~k->i32; + } else if (cp_opt(cp, '!')) { + cp_expr_unary(cp, k); k->i32 = !k->i32; k->id = CTID_INT32; + } else if (cp_opt(cp, '(')) { + if (cp_istypedecl(cp)) { /* Cast operator. */ + CTypeID id = cp_decl_abstract(cp); + cp_check(cp, ')'); + cp_expr_unary(cp, k); + k->id = id; /* No conversion performed. */ + } else { /* Sub-expression. */ + cp_expr_comma(cp, k); + cp_check(cp, ')'); + } + } else if (cp_opt(cp, '*')) { /* Indirection. */ + CType *ct; + cp_expr_unary(cp, k); + ct = lj_ctype_rawref(cp->cts, k->id); + if (!ctype_ispointer(ct->info)) + cp_err_badidx(cp, ct); + k->u32 = 0; k->id = ctype_cid(ct->info); + } else if (cp_opt(cp, '&')) { /* Address operator. */ + cp_expr_unary(cp, k); + k->id = lj_ctype_intern(cp->cts, CTINFO(CT_PTR, CTALIGN_PTR+k->id), + CTSIZE_PTR); + } else if (cp_opt(cp, CTOK_SIZEOF)) { + cp_expr_sizeof(cp, k, 1); + } else if (cp_opt(cp, CTOK_ALIGNOF)) { + cp_expr_sizeof(cp, k, 0); + } else if (cp->tok == CTOK_IDENT) { + if (ctype_type(cp->ct->info) == CT_CONSTVAL) { + k->u32 = cp->ct->size; k->id = ctype_cid(cp->ct->info); + } else if (ctype_type(cp->ct->info) == CT_EXTERN) { + k->u32 = cp->val.id; k->id = ctype_cid(cp->ct->info); + } else if (ctype_type(cp->ct->info) == CT_FUNC) { + k->u32 = cp->val.id; k->id = cp->val.id; + } else { + goto err_expr; + } + cp_next(cp); + } else if (cp->tok == CTOK_STRING) { + CTSize sz = cp->str->len; + while (cp_next(cp) == CTOK_STRING) + sz += cp->str->len; + k->u32 = sz + 1; + k->id = CTID_A_CCHAR; + } else { + err_expr: + cp_errmsg(cp, cp->tok, LJ_ERR_XSYMBOL); + } +} + +/* Parse postfix operators. */ +static void cp_expr_postfix(CPState *cp, CPValue *k) +{ + for (;;) { + CType *ct; + if (cp_opt(cp, '[')) { /* Array/pointer index. */ + CPValue k2; + cp_expr_comma(cp, &k2); + ct = lj_ctype_rawref(cp->cts, k->id); + if (!ctype_ispointer(ct->info)) { + ct = lj_ctype_rawref(cp->cts, k2.id); + if (!ctype_ispointer(ct->info)) + cp_err_badidx(cp, ct); + } + cp_check(cp, ']'); + k->u32 = 0; + } else if (cp->tok == '.' || cp->tok == CTOK_DEREF) { /* Struct deref. */ + CTSize ofs; + CType *fct; + ct = lj_ctype_rawref(cp->cts, k->id); + if (cp->tok == CTOK_DEREF) { + if (!ctype_ispointer(ct->info)) + cp_err_badidx(cp, ct); + ct = lj_ctype_rawref(cp->cts, ctype_cid(ct->info)); + } + cp_next(cp); + if (cp->tok != CTOK_IDENT) cp_err_token(cp, CTOK_IDENT); + if (!ctype_isstruct(ct->info) || ct->size == CTSIZE_INVALID || + !(fct = lj_ctype_getfield(cp->cts, ct, cp->str, &ofs)) || + ctype_isbitfield(fct->info)) { + GCstr *s = lj_ctype_repr(cp->cts->L, ctype_typeid(cp->cts, ct), NULL); + cp_errmsg(cp, 0, LJ_ERR_FFI_BADMEMBER, strdata(s), strdata(cp->str)); + } + ct = fct; + k->u32 = ctype_isconstval(ct->info) ? ct->size : 0; + cp_next(cp); + } else { + return; + } + k->id = ctype_cid(ct->info); + } +} + +/* Parse infix operators. */ +static void cp_expr_infix(CPState *cp, CPValue *k, int pri) +{ + CPValue k2; + k2.u32 = 0; k2.id = 0; /* Silence the compiler. */ + for (;;) { + switch (pri) { + case 0: + if (cp_opt(cp, '?')) { + CPValue k3; + cp_expr_comma(cp, &k2); /* Right-associative. */ + cp_check(cp, ':'); + cp_expr_sub(cp, &k3, 0); + k->u32 = k->u32 ? k2.u32 : k3.u32; + k->id = k2.id > k3.id ? k2.id : k3.id; + continue; + } + /* fallthrough */ + case 1: + if (cp_opt(cp, CTOK_OROR)) { + cp_expr_sub(cp, &k2, 2); k->i32 = k->u32 || k2.u32; k->id = CTID_INT32; + continue; + } + /* fallthrough */ + case 2: + if (cp_opt(cp, CTOK_ANDAND)) { + cp_expr_sub(cp, &k2, 3); k->i32 = k->u32 && k2.u32; k->id = CTID_INT32; + continue; + } + /* fallthrough */ + case 3: + if (cp_opt(cp, '|')) { + cp_expr_sub(cp, &k2, 4); k->u32 = k->u32 | k2.u32; goto arith_result; + } + /* fallthrough */ + case 4: + if (cp_opt(cp, '^')) { + cp_expr_sub(cp, &k2, 5); k->u32 = k->u32 ^ k2.u32; goto arith_result; + } + /* fallthrough */ + case 5: + if (cp_opt(cp, '&')) { + cp_expr_sub(cp, &k2, 6); k->u32 = k->u32 & k2.u32; goto arith_result; + } + /* fallthrough */ + case 6: + if (cp_opt(cp, CTOK_EQ)) { + cp_expr_sub(cp, &k2, 7); k->i32 = k->u32 == k2.u32; k->id = CTID_INT32; + continue; + } else if (cp_opt(cp, CTOK_NE)) { + cp_expr_sub(cp, &k2, 7); k->i32 = k->u32 != k2.u32; k->id = CTID_INT32; + continue; + } + /* fallthrough */ + case 7: + if (cp_opt(cp, '<')) { + cp_expr_sub(cp, &k2, 8); + if (k->id == CTID_INT32 && k2.id == CTID_INT32) + k->i32 = k->i32 < k2.i32; + else + k->i32 = k->u32 < k2.u32; + k->id = CTID_INT32; + continue; + } else if (cp_opt(cp, '>')) { + cp_expr_sub(cp, &k2, 8); + if (k->id == CTID_INT32 && k2.id == CTID_INT32) + k->i32 = k->i32 > k2.i32; + else + k->i32 = k->u32 > k2.u32; + k->id = CTID_INT32; + continue; + } else if (cp_opt(cp, CTOK_LE)) { + cp_expr_sub(cp, &k2, 8); + if (k->id == CTID_INT32 && k2.id == CTID_INT32) + k->i32 = k->i32 <= k2.i32; + else + k->i32 = k->u32 <= k2.u32; + k->id = CTID_INT32; + continue; + } else if (cp_opt(cp, CTOK_GE)) { + cp_expr_sub(cp, &k2, 8); + if (k->id == CTID_INT32 && k2.id == CTID_INT32) + k->i32 = k->i32 >= k2.i32; + else + k->i32 = k->u32 >= k2.u32; + k->id = CTID_INT32; + continue; + } + /* fallthrough */ + case 8: + if (cp_opt(cp, CTOK_SHL)) { + cp_expr_sub(cp, &k2, 9); k->u32 = k->u32 << k2.u32; + continue; + } else if (cp_opt(cp, CTOK_SHR)) { + cp_expr_sub(cp, &k2, 9); + if (k->id == CTID_INT32) + k->i32 = k->i32 >> k2.i32; + else + k->u32 = k->u32 >> k2.u32; + continue; + } + /* fallthrough */ + case 9: + if (cp_opt(cp, '+')) { + cp_expr_sub(cp, &k2, 10); k->u32 = k->u32 + k2.u32; + arith_result: + if (k2.id > k->id) k->id = k2.id; /* Trivial promotion to unsigned. */ + continue; + } else if (cp_opt(cp, '-')) { + cp_expr_sub(cp, &k2, 10); k->u32 = k->u32 - k2.u32; goto arith_result; + } + /* fallthrough */ + case 10: + if (cp_opt(cp, '*')) { + cp_expr_unary(cp, &k2); k->u32 = k->u32 * k2.u32; goto arith_result; + } else if (cp_opt(cp, '/')) { + cp_expr_unary(cp, &k2); + if (k2.id > k->id) k->id = k2.id; /* Trivial promotion to unsigned. */ + if (k2.u32 == 0 || + (k->id == CTID_INT32 && k->u32 == 0x80000000u && k2.i32 == -1)) + cp_err(cp, LJ_ERR_BADVAL); + if (k->id == CTID_INT32) + k->i32 = k->i32 / k2.i32; + else + k->u32 = k->u32 / k2.u32; + continue; + } else if (cp_opt(cp, '%')) { + cp_expr_unary(cp, &k2); + if (k2.id > k->id) k->id = k2.id; /* Trivial promotion to unsigned. */ + if (k2.u32 == 0 || + (k->id == CTID_INT32 && k->u32 == 0x80000000u && k2.i32 == -1)) + cp_err(cp, LJ_ERR_BADVAL); + if (k->id == CTID_INT32) + k->i32 = k->i32 % k2.i32; + else + k->u32 = k->u32 % k2.u32; + continue; + } + default: + return; + } + } +} + +/* Parse and evaluate unary expression. */ +static void cp_expr_unary(CPState *cp, CPValue *k) +{ + if (++cp->depth > CPARSE_MAX_DECLDEPTH) cp_err(cp, LJ_ERR_XLEVELS); + cp_expr_prefix(cp, k); + cp_expr_postfix(cp, k); + cp->depth--; +} + +/* Parse and evaluate sub-expression. */ +static void cp_expr_sub(CPState *cp, CPValue *k, int pri) +{ + cp_expr_unary(cp, k); + cp_expr_infix(cp, k, pri); +} + +/* Parse constant integer expression. */ +static void cp_expr_kint(CPState *cp, CPValue *k) +{ + CType *ct; + cp_expr_sub(cp, k, 0); + ct = ctype_raw(cp->cts, k->id); + if (!ctype_isinteger(ct->info)) cp_err(cp, LJ_ERR_BADVAL); +} + +/* Parse (non-negative) size expression. */ +static CTSize cp_expr_ksize(CPState *cp) +{ + CPValue k; + cp_expr_kint(cp, &k); + if (k.u32 >= 0x80000000u) cp_err(cp, LJ_ERR_FFI_INVSIZE); + return k.u32; +} + +/* -- Type declaration stack management ----------------------------------- */ + +/* Add declaration element behind the insertion position. */ +static CPDeclIdx cp_add(CPDecl *decl, CTInfo info, CTSize size) +{ + CPDeclIdx top = decl->top; + if (top >= CPARSE_MAX_DECLSTACK) cp_err(decl->cp, LJ_ERR_XLEVELS); + decl->stack[top].info = info; + decl->stack[top].size = size; + decl->stack[top].sib = 0; + setgcrefnull(decl->stack[top].name); + decl->stack[top].next = decl->stack[decl->pos].next; + decl->stack[decl->pos].next = (CTypeID1)top; + decl->top = top+1; + return top; +} + +/* Push declaration element before the insertion position. */ +static CPDeclIdx cp_push(CPDecl *decl, CTInfo info, CTSize size) +{ + return (decl->pos = cp_add(decl, info, size)); +} + +/* Push or merge attributes. */ +static void cp_push_attributes(CPDecl *decl) +{ + CType *ct = &decl->stack[decl->pos]; + if (ctype_isfunc(ct->info)) { /* Ok to modify in-place. */ +#if LJ_TARGET_X86 + if ((decl->fattr & CTFP_CCONV)) + ct->info = (ct->info & (CTMASK_NUM|CTF_VARARG|CTMASK_CID)) + + (decl->fattr & ~CTMASK_CID); +#endif + } else { + if ((decl->attr & CTFP_ALIGNED) && !(decl->mode & CPARSE_MODE_FIELD)) + cp_push(decl, CTINFO(CT_ATTRIB, CTATTRIB(CTA_ALIGN)), + ctype_align(decl->attr)); + } +} + +/* Push unrolled type to declaration stack and merge qualifiers. */ +static void cp_push_type(CPDecl *decl, CTypeID id) +{ + CType *ct = ctype_get(decl->cp->cts, id); + CTInfo info = ct->info; + CTSize size = ct->size; + switch (ctype_type(info)) { + case CT_STRUCT: case CT_ENUM: + cp_push(decl, CTINFO(CT_TYPEDEF, id), 0); /* Don't copy unique types. */ + if ((decl->attr & CTF_QUAL)) { /* Push unmerged qualifiers. */ + cp_push(decl, CTINFO(CT_ATTRIB, CTATTRIB(CTA_QUAL)), + (decl->attr & CTF_QUAL)); + decl->attr &= ~CTF_QUAL; + } + break; + case CT_ATTRIB: + if (ctype_isxattrib(info, CTA_QUAL)) + decl->attr &= ~size; /* Remove redundant qualifiers. */ + cp_push_type(decl, ctype_cid(info)); /* Unroll. */ + cp_push(decl, info & ~CTMASK_CID, size); /* Copy type. */ + break; + case CT_ARRAY: + if ((ct->info & (CTF_VECTOR|CTF_COMPLEX))) { + info |= (decl->attr & CTF_QUAL); + decl->attr &= ~CTF_QUAL; + } + cp_push_type(decl, ctype_cid(info)); /* Unroll. */ + cp_push(decl, info & ~CTMASK_CID, size); /* Copy type. */ + decl->stack[decl->pos].sib = 1; /* Mark as already checked and sized. */ + /* Note: this is not copied to the ct->sib in the C type table. */ + break; + case CT_FUNC: + /* Copy type, link parameters (shared). */ + decl->stack[cp_push(decl, info, size)].sib = ct->sib; + break; + default: + /* Copy type, merge common qualifiers. */ + cp_push(decl, info|(decl->attr & CTF_QUAL), size); + decl->attr &= ~CTF_QUAL; + break; + } +} + +/* Consume the declaration element chain and intern the C type. */ +static CTypeID cp_decl_intern(CPState *cp, CPDecl *decl) +{ + CTypeID id = 0; + CPDeclIdx idx = 0; + CTSize csize = CTSIZE_INVALID; + CTSize cinfo = 0; + do { + CType *ct = &decl->stack[idx]; + CTInfo info = ct->info; + CTInfo size = ct->size; + /* The cid is already part of info for copies of pointers/functions. */ + idx = ct->next; + if (ctype_istypedef(info)) { + lua_assert(id == 0); + id = ctype_cid(info); + /* Always refetch info/size, since struct/enum may have been completed. */ + cinfo = ctype_get(cp->cts, id)->info; + csize = ctype_get(cp->cts, id)->size; + lua_assert(ctype_isstruct(cinfo) || ctype_isenum(cinfo)); + } else if (ctype_isfunc(info)) { /* Intern function. */ + CType *fct; + CTypeID fid; + CTypeID sib; + if (id) { + CType *refct = ctype_raw(cp->cts, id); + /* Reject function or refarray return types. */ + if (ctype_isfunc(refct->info) || ctype_isrefarray(refct->info)) + cp_err(cp, LJ_ERR_FFI_INVTYPE); + } + /* No intervening attributes allowed, skip forward. */ + while (idx) { + CType *ctn = &decl->stack[idx]; + if (!ctype_isattrib(ctn->info)) break; + idx = ctn->next; /* Skip attribute. */ + } + sib = ct->sib; /* Next line may reallocate the C type table. */ + fid = lj_ctype_new(cp->cts, &fct); + csize = CTSIZE_INVALID; + fct->info = cinfo = info + id; + fct->size = size; + fct->sib = sib; + id = fid; + } else if (ctype_isattrib(info)) { + if (ctype_isxattrib(info, CTA_QUAL)) + cinfo |= size; + else if (ctype_isxattrib(info, CTA_ALIGN)) + CTF_INSERT(cinfo, ALIGN, size); + id = lj_ctype_intern(cp->cts, info+id, size); + /* Inherit csize/cinfo from original type. */ + } else { + if (ctype_isnum(info)) { /* Handle mode/vector-size attributes. */ + lua_assert(id == 0); + if (!(info & CTF_BOOL)) { + CTSize msize = ctype_msizeP(decl->attr); + CTSize vsize = ctype_vsizeP(decl->attr); + if (msize && (!(info & CTF_FP) || (msize == 4 || msize == 8))) { + CTSize malign = lj_fls(msize); + if (malign > 4) malign = 4; /* Limit alignment. */ + CTF_INSERT(info, ALIGN, malign); + size = msize; /* Override size via mode. */ + } + if (vsize) { /* Vector size set? */ + CTSize esize = lj_fls(size); + if (vsize >= esize) { + /* Intern the element type first. */ + id = lj_ctype_intern(cp->cts, info, size); + /* Then create a vector (array) with vsize alignment. */ + size = (1u << vsize); + if (vsize > 4) vsize = 4; /* Limit alignment. */ + if (ctype_align(info) > vsize) vsize = ctype_align(info); + info = CTINFO(CT_ARRAY, (info & CTF_QUAL) + CTF_VECTOR + + CTALIGN(vsize)); + } + } + } + } else if (ctype_isptr(info)) { + /* Reject pointer/ref to ref. */ + if (id && ctype_isref(ctype_raw(cp->cts, id)->info)) + cp_err(cp, LJ_ERR_FFI_INVTYPE); + if (ctype_isref(info)) { + info &= ~CTF_VOLATILE; /* Refs are always const, never volatile. */ + /* No intervening attributes allowed, skip forward. */ + while (idx) { + CType *ctn = &decl->stack[idx]; + if (!ctype_isattrib(ctn->info)) break; + idx = ctn->next; /* Skip attribute. */ + } + } + } else if (ctype_isarray(info)) { /* Check for valid array size etc. */ + if (ct->sib == 0) { /* Only check/size arrays not copied by unroll. */ + if (ctype_isref(cinfo)) /* Reject arrays of refs. */ + cp_err(cp, LJ_ERR_FFI_INVTYPE); + /* Reject VLS or unknown-sized types. */ + if (ctype_isvltype(cinfo) || csize == CTSIZE_INVALID) + cp_err(cp, LJ_ERR_FFI_INVSIZE); + /* a[] and a[?] keep their invalid size. */ + if (size != CTSIZE_INVALID) { + uint64_t xsz = (uint64_t)size * csize; + if (xsz >= 0x80000000u) cp_err(cp, LJ_ERR_FFI_INVSIZE); + size = (CTSize)xsz; + } + } + if ((cinfo & CTF_ALIGN) > (info & CTF_ALIGN)) /* Find max. align. */ + info = (info & ~CTF_ALIGN) | (cinfo & CTF_ALIGN); + info |= (cinfo & CTF_QUAL); /* Inherit qual. */ + } else { + lua_assert(ctype_isvoid(info)); + } + csize = size; + cinfo = info+id; + id = lj_ctype_intern(cp->cts, info+id, size); + } + } while (idx); + return id; +} + +/* -- C declaration parser ------------------------------------------------ */ + +#define H_(le, be) LJ_ENDIAN_SELECT(0x##le, 0x##be) + +/* Reset declaration state to declaration specifier. */ +static void cp_decl_reset(CPDecl *decl) +{ + decl->pos = decl->specpos; + decl->top = decl->specpos+1; + decl->stack[decl->specpos].next = 0; + decl->attr = decl->specattr; + decl->fattr = decl->specfattr; + decl->name = NULL; + decl->redir = NULL; +} + +/* Parse constant initializer. */ +/* NYI: FP constants and strings as initializers. */ +static CTypeID cp_decl_constinit(CPState *cp, CType **ctp, CTypeID ctypeid) +{ + CType *ctt = ctype_get(cp->cts, ctypeid); + CTInfo info; + CTSize size; + CPValue k; + CTypeID constid; + while (ctype_isattrib(ctt->info)) { /* Skip attributes. */ + ctypeid = ctype_cid(ctt->info); /* Update ID, too. */ + ctt = ctype_get(cp->cts, ctypeid); + } + info = ctt->info; + size = ctt->size; + if (!ctype_isinteger(info) || !(info & CTF_CONST) || size > 4) + cp_err(cp, LJ_ERR_FFI_INVTYPE); + cp_check(cp, '='); + cp_expr_sub(cp, &k, 0); + constid = lj_ctype_new(cp->cts, ctp); + (*ctp)->info = CTINFO(CT_CONSTVAL, CTF_CONST|ctypeid); + k.u32 <<= 8*(4-size); + if ((info & CTF_UNSIGNED)) + k.u32 >>= 8*(4-size); + else + k.u32 = (uint32_t)((int32_t)k.u32 >> 8*(4-size)); + (*ctp)->size = k.u32; + return constid; +} + +/* Parse size in parentheses as part of attribute. */ +static CTSize cp_decl_sizeattr(CPState *cp) +{ + CTSize sz; + uint32_t oldtmask = cp->tmask; + cp->tmask = CPNS_DEFAULT; /* Required for expression evaluator. */ + cp_check(cp, '('); + sz = cp_expr_ksize(cp); + cp->tmask = oldtmask; + cp_check(cp, ')'); + return sz; +} + +/* Parse alignment attribute. */ +static void cp_decl_align(CPState *cp, CPDecl *decl) +{ + CTSize al = 4; /* Unspecified alignment is 16 bytes. */ + if (cp->tok == '(') { + al = cp_decl_sizeattr(cp); + al = al ? lj_fls(al) : 0; + } + CTF_INSERT(decl->attr, ALIGN, al); + decl->attr |= CTFP_ALIGNED; +} + +/* Parse GCC asm("name") redirect. */ +static void cp_decl_asm(CPState *cp, CPDecl *decl) +{ + UNUSED(decl); + cp_next(cp); + cp_check(cp, '('); + if (cp->tok == CTOK_STRING) { + GCstr *str = cp->str; + while (cp_next(cp) == CTOK_STRING) { + lj_strfmt_pushf(cp->L, "%s%s", strdata(str), strdata(cp->str)); + cp->L->top--; + str = strV(cp->L->top); + } + decl->redir = str; + } + cp_check(cp, ')'); +} + +/* Parse GCC __attribute__((mode(...))). */ +static void cp_decl_mode(CPState *cp, CPDecl *decl) +{ + cp_check(cp, '('); + if (cp->tok == CTOK_IDENT) { + const char *s = strdata(cp->str); + CTSize sz = 0, vlen = 0; + if (s[0] == '_' && s[1] == '_') s += 2; + if (*s == 'V') { + s++; + vlen = *s++ - '0'; + if (*s >= '0' && *s <= '9') + vlen = vlen*10 + (*s++ - '0'); + } + switch (*s++) { + case 'Q': sz = 1; break; + case 'H': sz = 2; break; + case 'S': sz = 4; break; + case 'D': sz = 8; break; + case 'T': sz = 16; break; + case 'O': sz = 32; break; + default: goto bad_size; + } + if (*s == 'I' || *s == 'F') { + CTF_INSERT(decl->attr, MSIZEP, sz); + if (vlen) CTF_INSERT(decl->attr, VSIZEP, lj_fls(vlen*sz)); + } + bad_size: + cp_next(cp); + } + cp_check(cp, ')'); +} + +/* Parse GCC __attribute__((...)). */ +static void cp_decl_gccattribute(CPState *cp, CPDecl *decl) +{ + cp_next(cp); + cp_check(cp, '('); + cp_check(cp, '('); + while (cp->tok != ')') { + if (cp->tok == CTOK_IDENT) { + GCstr *attrstr = cp->str; + cp_next(cp); + switch (attrstr->hash) { + case H_(64a9208e,8ce14319): case H_(8e6331b2,95a282af): /* aligned */ + cp_decl_align(cp, decl); + break; + case H_(42eb47de,f0ede26c): case H_(29f48a09,cf383e0c): /* packed */ + decl->attr |= CTFP_PACKED; + break; + case H_(0a84eef6,8dfab04c): case H_(995cf92c,d5696591): /* mode */ + cp_decl_mode(cp, decl); + break; + case H_(0ab31997,2d5213fa): case H_(bf875611,200e9990): /* vector_size */ + { + CTSize vsize = cp_decl_sizeattr(cp); + if (vsize) CTF_INSERT(decl->attr, VSIZEP, lj_fls(vsize)); + } + break; +#if LJ_TARGET_X86 + case H_(5ad22db8,c689b848): case H_(439150fa,65ea78cb): /* regparm */ + CTF_INSERT(decl->fattr, REGPARM, cp_decl_sizeattr(cp)); + decl->fattr |= CTFP_CCONV; + break; + case H_(18fc0b98,7ff4c074): case H_(4e62abed,0a747424): /* cdecl */ + CTF_INSERT(decl->fattr, CCONV, CTCC_CDECL); + decl->fattr |= CTFP_CCONV; + break; + case H_(72b2e41b,494c5a44): case H_(f2356d59,f25fc9bd): /* thiscall */ + CTF_INSERT(decl->fattr, CCONV, CTCC_THISCALL); + decl->fattr |= CTFP_CCONV; + break; + case H_(0d0ffc42,ab746f88): case H_(21c54ba1,7f0ca7e3): /* fastcall */ + CTF_INSERT(decl->fattr, CCONV, CTCC_FASTCALL); + decl->fattr |= CTFP_CCONV; + break; + case H_(ef76b040,9412e06a): case H_(de56697b,c750e6e1): /* stdcall */ + CTF_INSERT(decl->fattr, CCONV, CTCC_STDCALL); + decl->fattr |= CTFP_CCONV; + break; + case H_(ea78b622,f234bd8e): case H_(252ffb06,8d50f34b): /* sseregparm */ + decl->fattr |= CTF_SSEREGPARM; + decl->fattr |= CTFP_CCONV; + break; +#endif + default: /* Skip all other attributes. */ + goto skip_attr; + } + } else if (cp->tok >= CTOK_FIRSTDECL) { /* For __attribute((const)) etc. */ + cp_next(cp); + skip_attr: + if (cp_opt(cp, '(')) { + while (cp->tok != ')' && cp->tok != CTOK_EOF) cp_next(cp); + cp_check(cp, ')'); + } + } else { + break; + } + if (!cp_opt(cp, ',')) break; + } + cp_check(cp, ')'); + cp_check(cp, ')'); +} + +/* Parse MSVC __declspec(...). */ +static void cp_decl_msvcattribute(CPState *cp, CPDecl *decl) +{ + cp_next(cp); + cp_check(cp, '('); + while (cp->tok == CTOK_IDENT) { + GCstr *attrstr = cp->str; + cp_next(cp); + switch (attrstr->hash) { + case H_(bc2395fa,98f267f8): /* align */ + cp_decl_align(cp, decl); + break; + default: /* Ignore all other attributes. */ + if (cp_opt(cp, '(')) { + while (cp->tok != ')' && cp->tok != CTOK_EOF) cp_next(cp); + cp_check(cp, ')'); + } + break; + } + } + cp_check(cp, ')'); +} + +/* Parse declaration attributes (and common qualifiers). */ +static void cp_decl_attributes(CPState *cp, CPDecl *decl) +{ + for (;;) { + switch (cp->tok) { + case CTOK_CONST: decl->attr |= CTF_CONST; break; + case CTOK_VOLATILE: decl->attr |= CTF_VOLATILE; break; + case CTOK_RESTRICT: break; /* Ignore. */ + case CTOK_EXTENSION: break; /* Ignore. */ + case CTOK_ATTRIBUTE: cp_decl_gccattribute(cp, decl); continue; + case CTOK_ASM: cp_decl_asm(cp, decl); continue; + case CTOK_DECLSPEC: cp_decl_msvcattribute(cp, decl); continue; + case CTOK_CCDECL: +#if LJ_TARGET_X86 + CTF_INSERT(decl->fattr, CCONV, cp->ct->size); + decl->fattr |= CTFP_CCONV; +#endif + break; + case CTOK_PTRSZ: +#if LJ_64 + CTF_INSERT(decl->attr, MSIZEP, cp->ct->size); +#endif + break; + default: return; + } + cp_next(cp); + } +} + +/* Parse struct/union/enum name. */ +static CTypeID cp_struct_name(CPState *cp, CPDecl *sdecl, CTInfo info) +{ + CTypeID sid; + CType *ct; + cp->tmask = CPNS_STRUCT; + cp_next(cp); + cp_decl_attributes(cp, sdecl); + cp->tmask = CPNS_DEFAULT; + if (cp->tok != '{') { + if (cp->tok != CTOK_IDENT) cp_err_token(cp, CTOK_IDENT); + if (cp->val.id) { /* Name of existing struct/union/enum. */ + sid = cp->val.id; + ct = cp->ct; + if ((ct->info ^ info) & (CTMASK_NUM|CTF_UNION)) /* Wrong type. */ + cp_errmsg(cp, 0, LJ_ERR_FFI_REDEF, strdata(gco2str(gcref(ct->name)))); + } else { /* Create named, incomplete struct/union/enum. */ + if ((cp->mode & CPARSE_MODE_NOIMPLICIT)) + cp_errmsg(cp, 0, LJ_ERR_FFI_BADTAG, strdata(cp->str)); + sid = lj_ctype_new(cp->cts, &ct); + ct->info = info; + ct->size = CTSIZE_INVALID; + ctype_setname(ct, cp->str); + lj_ctype_addname(cp->cts, ct, sid); + } + cp_next(cp); + } else { /* Create anonymous, incomplete struct/union/enum. */ + sid = lj_ctype_new(cp->cts, &ct); + ct->info = info; + ct->size = CTSIZE_INVALID; + } + if (cp->tok == '{') { + if (ct->size != CTSIZE_INVALID || ct->sib) + cp_errmsg(cp, 0, LJ_ERR_FFI_REDEF, strdata(gco2str(gcref(ct->name)))); + ct->sib = 1; /* Indicate the type is currently being defined. */ + } + return sid; +} + +/* Determine field alignment. */ +static CTSize cp_field_align(CPState *cp, CType *ct, CTInfo info) +{ + CTSize align = ctype_align(info); + UNUSED(cp); UNUSED(ct); +#if (LJ_TARGET_X86 && !LJ_ABI_WIN) || (LJ_TARGET_ARM && __APPLE__) + /* The SYSV i386 and iOS ABIs limit alignment of non-vector fields to 2^2. */ + if (align > 2 && !(info & CTFP_ALIGNED)) { + if (ctype_isarray(info) && !(info & CTF_VECTOR)) { + do { + ct = ctype_rawchild(cp->cts, ct); + info = ct->info; + } while (ctype_isarray(info) && !(info & CTF_VECTOR)); + } + if (ctype_isnum(info) || ctype_isenum(info)) + align = 2; + } +#endif + return align; +} + +/* Layout struct/union fields. */ +static void cp_struct_layout(CPState *cp, CTypeID sid, CTInfo sattr) +{ + CTSize bofs = 0, bmaxofs = 0; /* Bit offset and max. bit offset. */ + CTSize maxalign = ctype_align(sattr); + CType *sct = ctype_get(cp->cts, sid); + CTInfo sinfo = sct->info; + CTypeID fieldid = sct->sib; + while (fieldid) { + CType *ct = ctype_get(cp->cts, fieldid); + CTInfo attr = ct->size; /* Field declaration attributes (temp.). */ + + if (ctype_isfield(ct->info) || + (ctype_isxattrib(ct->info, CTA_SUBTYPE) && attr)) { + CTSize align, amask; /* Alignment (pow2) and alignment mask (bits). */ + CTSize sz; + CTInfo info = lj_ctype_info(cp->cts, ctype_cid(ct->info), &sz); + CTSize bsz, csz = 8*sz; /* Field size and container size (in bits). */ + sinfo |= (info & (CTF_QUAL|CTF_VLA)); /* Merge pseudo-qualifiers. */ + + /* Check for size overflow and determine alignment. */ + if (sz >= 0x20000000u || bofs + csz < bofs || (info & CTF_VLA)) { + if (!(sz == CTSIZE_INVALID && ctype_isarray(info) && + !(sinfo & CTF_UNION))) + cp_err(cp, LJ_ERR_FFI_INVSIZE); + csz = sz = 0; /* Treat a[] and a[?] as zero-sized. */ + } + align = cp_field_align(cp, ct, info); + if (((attr|sattr) & CTFP_PACKED) || + ((attr & CTFP_ALIGNED) && ctype_align(attr) > align)) + align = ctype_align(attr); + if (cp->packstack[cp->curpack] < align) + align = cp->packstack[cp->curpack]; + if (align > maxalign) maxalign = align; + amask = (8u << align) - 1; + + bsz = ctype_bitcsz(ct->info); /* Bitfield size (temp.). */ + if (bsz == CTBSZ_FIELD || !ctype_isfield(ct->info)) { + bsz = csz; /* Regular fields or subtypes always fill the container. */ + bofs = (bofs + amask) & ~amask; /* Start new aligned field. */ + ct->size = (bofs >> 3); /* Store field offset. */ + } else { /* Bitfield. */ + if (bsz == 0 || (attr & CTFP_ALIGNED) || + (!((attr|sattr) & CTFP_PACKED) && (bofs & amask) + bsz > csz)) + bofs = (bofs + amask) & ~amask; /* Start new aligned field. */ + + /* Prefer regular field over bitfield. */ + if (bsz == csz && (bofs & amask) == 0) { + ct->info = CTINFO(CT_FIELD, ctype_cid(ct->info)); + ct->size = (bofs >> 3); /* Store field offset. */ + } else { + ct->info = CTINFO(CT_BITFIELD, + (info & (CTF_QUAL|CTF_UNSIGNED|CTF_BOOL)) + + (csz << (CTSHIFT_BITCSZ-3)) + (bsz << CTSHIFT_BITBSZ)); +#if LJ_BE + ct->info += ((csz - (bofs & (csz-1)) - bsz) << CTSHIFT_BITPOS); +#else + ct->info += ((bofs & (csz-1)) << CTSHIFT_BITPOS); +#endif + ct->size = ((bofs & ~(csz-1)) >> 3); /* Store container offset. */ + } + } + + /* Determine next offset or max. offset. */ + if ((sinfo & CTF_UNION)) { + if (bsz > bmaxofs) bmaxofs = bsz; + } else { + bofs += bsz; + } + } /* All other fields in the chain are already set up. */ + + fieldid = ct->sib; + } + + /* Complete struct/union. */ + sct->info = sinfo + CTALIGN(maxalign); + bofs = (sinfo & CTF_UNION) ? bmaxofs : bofs; + maxalign = (8u << maxalign) - 1; + sct->size = (((bofs + maxalign) & ~maxalign) >> 3); +} + +/* Parse struct/union declaration. */ +static CTypeID cp_decl_struct(CPState *cp, CPDecl *sdecl, CTInfo sinfo) +{ + CTypeID sid = cp_struct_name(cp, sdecl, sinfo); + if (cp_opt(cp, '{')) { /* Struct/union definition. */ + CTypeID lastid = sid; + int lastdecl = 0; + while (cp->tok != '}') { + CPDecl decl; + CPscl scl = cp_decl_spec(cp, &decl, CDF_STATIC); + decl.mode = scl ? CPARSE_MODE_DIRECT : + CPARSE_MODE_DIRECT|CPARSE_MODE_ABSTRACT|CPARSE_MODE_FIELD; + + for (;;) { + CTypeID ctypeid; + + if (lastdecl) cp_err_token(cp, '}'); + + /* Parse field declarator. */ + decl.bits = CTSIZE_INVALID; + cp_declarator(cp, &decl); + ctypeid = cp_decl_intern(cp, &decl); + + if ((scl & CDF_STATIC)) { /* Static constant in struct namespace. */ + CType *ct; + CTypeID fieldid = cp_decl_constinit(cp, &ct, ctypeid); + ctype_get(cp->cts, lastid)->sib = fieldid; + lastid = fieldid; + ctype_setname(ct, decl.name); + } else { + CTSize bsz = CTBSZ_FIELD; /* Temp. for layout phase. */ + CType *ct; + CTypeID fieldid = lj_ctype_new(cp->cts, &ct); /* Do this first. */ + CType *tct = ctype_raw(cp->cts, ctypeid); + + if (decl.bits == CTSIZE_INVALID) { /* Regular field. */ + if (ctype_isarray(tct->info) && tct->size == CTSIZE_INVALID) + lastdecl = 1; /* a[] or a[?] must be the last declared field. */ + + /* Accept transparent struct/union/enum. */ + if (!decl.name) { + if (!((ctype_isstruct(tct->info) && !(tct->info & CTF_VLA)) || + ctype_isenum(tct->info))) + cp_err_token(cp, CTOK_IDENT); + ct->info = CTINFO(CT_ATTRIB, CTATTRIB(CTA_SUBTYPE) + ctypeid); + ct->size = ctype_isstruct(tct->info) ? + (decl.attr|0x80000000u) : 0; /* For layout phase. */ + goto add_field; + } + } else { /* Bitfield. */ + bsz = decl.bits; + if (!ctype_isinteger_or_bool(tct->info) || + (bsz == 0 && decl.name) || 8*tct->size > CTBSZ_MAX || + bsz > ((tct->info & CTF_BOOL) ? 1 : 8*tct->size)) + cp_errmsg(cp, ':', LJ_ERR_BADVAL); + } + + /* Create temporary field for layout phase. */ + ct->info = CTINFO(CT_FIELD, ctypeid + (bsz << CTSHIFT_BITCSZ)); + ct->size = decl.attr; + if (decl.name) ctype_setname(ct, decl.name); + + add_field: + ctype_get(cp->cts, lastid)->sib = fieldid; + lastid = fieldid; + } + if (!cp_opt(cp, ',')) break; + cp_decl_reset(&decl); + } + cp_check(cp, ';'); + } + cp_check(cp, '}'); + ctype_get(cp->cts, lastid)->sib = 0; /* Drop sib = 1 for empty structs. */ + cp_decl_attributes(cp, sdecl); /* Layout phase needs postfix attributes. */ + cp_struct_layout(cp, sid, sdecl->attr); + } + return sid; +} + +/* Parse enum declaration. */ +static CTypeID cp_decl_enum(CPState *cp, CPDecl *sdecl) +{ + CTypeID eid = cp_struct_name(cp, sdecl, CTINFO(CT_ENUM, CTID_VOID)); + CTInfo einfo = CTINFO(CT_ENUM, CTALIGN(2) + CTID_UINT32); + CTSize esize = 4; /* Only 32 bit enums are supported. */ + if (cp_opt(cp, '{')) { /* Enum definition. */ + CPValue k; + CTypeID lastid = eid; + k.u32 = 0; + k.id = CTID_INT32; + do { + GCstr *name = cp->str; + if (cp->tok != CTOK_IDENT) cp_err_token(cp, CTOK_IDENT); + if (cp->val.id) cp_errmsg(cp, 0, LJ_ERR_FFI_REDEF, strdata(name)); + cp_next(cp); + if (cp_opt(cp, '=')) { + cp_expr_kint(cp, &k); + if (k.id == CTID_UINT32) { + /* C99 says that enum constants are always (signed) integers. + ** But since unsigned constants like 0x80000000 are quite common, + ** those are left as uint32_t. + */ + if (k.i32 >= 0) k.id = CTID_INT32; + } else { + /* OTOH it's common practice and even mandated by some ABIs + ** that the enum type itself is unsigned, unless there are any + ** negative constants. + */ + k.id = CTID_INT32; + if (k.i32 < 0) einfo = CTINFO(CT_ENUM, CTALIGN(2) + CTID_INT32); + } + } + /* Add named enum constant. */ + { + CType *ct; + CTypeID constid = lj_ctype_new(cp->cts, &ct); + ctype_get(cp->cts, lastid)->sib = constid; + lastid = constid; + ctype_setname(ct, name); + ct->info = CTINFO(CT_CONSTVAL, CTF_CONST|k.id); + ct->size = k.u32++; + if (k.u32 == 0x80000000u) k.id = CTID_UINT32; + lj_ctype_addname(cp->cts, ct, constid); + } + if (!cp_opt(cp, ',')) break; + } while (cp->tok != '}'); /* Trailing ',' is ok. */ + cp_check(cp, '}'); + /* Complete enum. */ + ctype_get(cp->cts, eid)->info = einfo; + ctype_get(cp->cts, eid)->size = esize; + } + return eid; +} + +/* Parse declaration specifiers. */ +static CPscl cp_decl_spec(CPState *cp, CPDecl *decl, CPscl scl) +{ + uint32_t cds = 0, sz = 0; + CTypeID tdef = 0; + + decl->cp = cp; + decl->mode = cp->mode; + decl->name = NULL; + decl->redir = NULL; + decl->attr = 0; + decl->fattr = 0; + decl->pos = decl->top = 0; + decl->stack[0].next = 0; + + for (;;) { /* Parse basic types. */ + cp_decl_attributes(cp, decl); + if (cp->tok >= CTOK_FIRSTDECL && cp->tok <= CTOK_LASTDECLFLAG) { + uint32_t cbit; + if (cp->ct->size) { + if (sz) goto end_decl; + sz = cp->ct->size; + } + cbit = (1u << (cp->tok - CTOK_FIRSTDECL)); + cds = cds | cbit | ((cbit & cds & CDF_LONG) << 1); + if (cp->tok >= CTOK_FIRSTSCL) { + if (!(scl & cbit)) cp_errmsg(cp, cp->tok, LJ_ERR_FFI_BADSCL); + } else if (tdef) { + goto end_decl; + } + cp_next(cp); + continue; + } + if (sz || tdef || + (cds & (CDF_SHORT|CDF_LONG|CDF_SIGNED|CDF_UNSIGNED|CDF_COMPLEX))) + break; + switch (cp->tok) { + case CTOK_STRUCT: + tdef = cp_decl_struct(cp, decl, CTINFO(CT_STRUCT, 0)); + continue; + case CTOK_UNION: + tdef = cp_decl_struct(cp, decl, CTINFO(CT_STRUCT, CTF_UNION)); + continue; + case CTOK_ENUM: + tdef = cp_decl_enum(cp, decl); + continue; + case CTOK_IDENT: + if (ctype_istypedef(cp->ct->info)) { + tdef = ctype_cid(cp->ct->info); /* Get typedef. */ + cp_next(cp); + continue; + } + break; + case '$': + tdef = cp->val.id; + cp_next(cp); + continue; + default: + break; + } + break; + } +end_decl: + + if ((cds & CDF_COMPLEX)) /* Use predefined complex types. */ + tdef = sz == 4 ? CTID_COMPLEX_FLOAT : CTID_COMPLEX_DOUBLE; + + if (tdef) { + cp_push_type(decl, tdef); + } else if ((cds & CDF_VOID)) { + cp_push(decl, CTINFO(CT_VOID, (decl->attr & CTF_QUAL)), CTSIZE_INVALID); + decl->attr &= ~CTF_QUAL; + } else { + /* Determine type info and size. */ + CTInfo info = CTINFO(CT_NUM, (cds & CDF_UNSIGNED) ? CTF_UNSIGNED : 0); + if ((cds & CDF_BOOL)) { + if ((cds & ~(CDF_SCL|CDF_BOOL|CDF_INT|CDF_SIGNED|CDF_UNSIGNED))) + cp_errmsg(cp, 0, LJ_ERR_FFI_INVTYPE); + info |= CTF_BOOL; + if (!(cds & CDF_SIGNED)) info |= CTF_UNSIGNED; + if (!sz) { + sz = 1; + } + } else if ((cds & CDF_FP)) { + info = CTINFO(CT_NUM, CTF_FP); + if ((cds & CDF_LONG)) sz = sizeof(long double); + } else if ((cds & CDF_CHAR)) { + if ((cds & (CDF_CHAR|CDF_SIGNED|CDF_UNSIGNED)) == CDF_CHAR) + info |= CTF_UCHAR; /* Handle platforms where char is unsigned. */ + } else if ((cds & CDF_SHORT)) { + sz = sizeof(short); + } else if ((cds & CDF_LONGLONG)) { + sz = 8; + } else if ((cds & CDF_LONG)) { + info |= CTF_LONG; + sz = sizeof(long); + } else if (!sz) { + if (!(cds & (CDF_SIGNED|CDF_UNSIGNED))) + cp_errmsg(cp, cp->tok, LJ_ERR_FFI_DECLSPEC); + sz = sizeof(int); + } + lua_assert(sz != 0); + info += CTALIGN(lj_fls(sz)); /* Use natural alignment. */ + info += (decl->attr & CTF_QUAL); /* Merge qualifiers. */ + cp_push(decl, info, sz); + decl->attr &= ~CTF_QUAL; + } + decl->specpos = decl->pos; + decl->specattr = decl->attr; + decl->specfattr = decl->fattr; + return (cds & CDF_SCL); /* Return storage class. */ +} + +/* Parse array declaration. */ +static void cp_decl_array(CPState *cp, CPDecl *decl) +{ + CTInfo info = CTINFO(CT_ARRAY, 0); + CTSize nelem = CTSIZE_INVALID; /* Default size for a[] or a[?]. */ + cp_decl_attributes(cp, decl); + if (cp_opt(cp, '?')) + info |= CTF_VLA; /* Create variable-length array a[?]. */ + else if (cp->tok != ']') + nelem = cp_expr_ksize(cp); + cp_check(cp, ']'); + cp_add(decl, info, nelem); +} + +/* Parse function declaration. */ +static void cp_decl_func(CPState *cp, CPDecl *fdecl) +{ + CTSize nargs = 0; + CTInfo info = CTINFO(CT_FUNC, 0); + CTypeID lastid = 0, anchor = 0; + if (cp->tok != ')') { + do { + CPDecl decl; + CTypeID ctypeid, fieldid; + CType *ct; + if (cp_opt(cp, '.')) { /* Vararg function. */ + cp_check(cp, '.'); /* Workaround for the minimalistic lexer. */ + cp_check(cp, '.'); + info |= CTF_VARARG; + break; + } + cp_decl_spec(cp, &decl, CDF_REGISTER); + decl.mode = CPARSE_MODE_DIRECT|CPARSE_MODE_ABSTRACT; + cp_declarator(cp, &decl); + ctypeid = cp_decl_intern(cp, &decl); + ct = ctype_raw(cp->cts, ctypeid); + if (ctype_isvoid(ct->info)) + break; + else if (ctype_isrefarray(ct->info)) + ctypeid = lj_ctype_intern(cp->cts, + CTINFO(CT_PTR, CTALIGN_PTR|ctype_cid(ct->info)), CTSIZE_PTR); + else if (ctype_isfunc(ct->info)) + ctypeid = lj_ctype_intern(cp->cts, + CTINFO(CT_PTR, CTALIGN_PTR|ctypeid), CTSIZE_PTR); + /* Add new parameter. */ + fieldid = lj_ctype_new(cp->cts, &ct); + if (anchor) + ctype_get(cp->cts, lastid)->sib = fieldid; + else + anchor = fieldid; + lastid = fieldid; + if (decl.name) ctype_setname(ct, decl.name); + ct->info = CTINFO(CT_FIELD, ctypeid); + ct->size = nargs++; + } while (cp_opt(cp, ',')); + } + cp_check(cp, ')'); + if (cp_opt(cp, '{')) { /* Skip function definition. */ + int level = 1; + cp->mode |= CPARSE_MODE_SKIP; + for (;;) { + if (cp->tok == '{') level++; + else if (cp->tok == '}' && --level == 0) break; + else if (cp->tok == CTOK_EOF) cp_err_token(cp, '}'); + cp_next(cp); + } + cp->mode &= ~CPARSE_MODE_SKIP; + cp->tok = ';'; /* Ok for cp_decl_multi(), error in cp_decl_single(). */ + } + info |= (fdecl->fattr & ~CTMASK_CID); + fdecl->fattr = 0; + fdecl->stack[cp_add(fdecl, info, nargs)].sib = anchor; +} + +/* Parse declarator. */ +static void cp_declarator(CPState *cp, CPDecl *decl) +{ + if (++cp->depth > CPARSE_MAX_DECLDEPTH) cp_err(cp, LJ_ERR_XLEVELS); + + for (;;) { /* Head of declarator. */ + if (cp_opt(cp, '*')) { /* Pointer. */ + CTSize sz; + CTInfo info; + cp_decl_attributes(cp, decl); + sz = CTSIZE_PTR; + info = CTINFO(CT_PTR, CTALIGN_PTR); +#if LJ_64 + if (ctype_msizeP(decl->attr) == 4) { + sz = 4; + info = CTINFO(CT_PTR, CTALIGN(2)); + } +#endif + info += (decl->attr & (CTF_QUAL|CTF_REF)); + decl->attr &= ~(CTF_QUAL|(CTMASK_MSIZEP<attr &= ~(CTF_QUAL|(CTMASK_MSIZEP<mode & CPARSE_MODE_ABSTRACT) && + (cp->tok == ')' || cp_istypedecl(cp))) goto func_decl; + pos = decl->pos; + cp_declarator(cp, decl); + cp_check(cp, ')'); + decl->pos = pos; + } else if (cp->tok == CTOK_IDENT) { /* Direct declarator. */ + if (!(decl->mode & CPARSE_MODE_DIRECT)) cp_err_token(cp, CTOK_EOF); + decl->name = cp->str; + decl->nameid = cp->val.id; + cp_next(cp); + } else { /* Abstract declarator. */ + if (!(decl->mode & CPARSE_MODE_ABSTRACT)) cp_err_token(cp, CTOK_IDENT); + } + + for (;;) { /* Tail of declarator. */ + if (cp_opt(cp, '[')) { /* Array. */ + cp_decl_array(cp, decl); + } else if (cp_opt(cp, '(')) { /* Function. */ + func_decl: + cp_decl_func(cp, decl); + } else { + break; + } + } + + if ((decl->mode & CPARSE_MODE_FIELD) && cp_opt(cp, ':')) /* Field width. */ + decl->bits = cp_expr_ksize(cp); + + /* Process postfix attributes. */ + cp_decl_attributes(cp, decl); + cp_push_attributes(decl); + + cp->depth--; +} + +/* Parse an abstract type declaration and return it's C type ID. */ +static CTypeID cp_decl_abstract(CPState *cp) +{ + CPDecl decl; + cp_decl_spec(cp, &decl, 0); + decl.mode = CPARSE_MODE_ABSTRACT; + cp_declarator(cp, &decl); + return cp_decl_intern(cp, &decl); +} + +/* Handle pragmas. */ +static void cp_pragma(CPState *cp, BCLine pragmaline) +{ + cp_next(cp); + if (cp->tok == CTOK_IDENT && + cp->str->hash == H_(e79b999f,42ca3e85)) { /* pack */ + cp_next(cp); + cp_check(cp, '('); + if (cp->tok == CTOK_IDENT) { + if (cp->str->hash == H_(738e923c,a1b65954)) { /* push */ + if (cp->curpack < CPARSE_MAX_PACKSTACK) { + cp->packstack[cp->curpack+1] = cp->packstack[cp->curpack]; + cp->curpack++; + } + } else if (cp->str->hash == H_(6c71cf27,6c71cf27)) { /* pop */ + if (cp->curpack > 0) cp->curpack--; + } else { + cp_errmsg(cp, cp->tok, LJ_ERR_XSYMBOL); + } + cp_next(cp); + if (!cp_opt(cp, ',')) goto end_pack; + } + if (cp->tok == CTOK_INTEGER) { + cp->packstack[cp->curpack] = cp->val.u32 ? lj_fls(cp->val.u32) : 0; + cp_next(cp); + } else { + cp->packstack[cp->curpack] = 255; + } + end_pack: + cp_check(cp, ')'); + } else { /* Ignore all other pragmas. */ + while (cp->tok != CTOK_EOF && cp->linenumber == pragmaline) + cp_next(cp); + } +} + +/* Handle line number. */ +static void cp_line(CPState *cp, BCLine hashline) +{ + BCLine newline = cp->val.u32; + /* TODO: Handle file name and include it in error messages. */ + while (cp->tok != CTOK_EOF && cp->linenumber == hashline) + cp_next(cp); + cp->linenumber = newline; +} + +/* Parse multiple C declarations of types or extern identifiers. */ +static void cp_decl_multi(CPState *cp) +{ + int first = 1; + while (cp->tok != CTOK_EOF) { + CPDecl decl; + CPscl scl; + if (cp_opt(cp, ';')) { /* Skip empty statements. */ + first = 0; + continue; + } + if (cp->tok == '#') { /* Workaround, since we have no preprocessor, yet. */ + BCLine hashline = cp->linenumber; + CPToken tok = cp_next(cp); + if (tok == CTOK_INTEGER) { + cp_line(cp, hashline); + continue; + } else if (tok == CTOK_IDENT && + cp->str->hash == H_(187aab88,fcb60b42)) { /* line */ + if (cp_next(cp) != CTOK_INTEGER) cp_err_token(cp, tok); + cp_line(cp, hashline); + continue; + } else if (tok == CTOK_IDENT && + cp->str->hash == H_(f5e6b4f8,1d509107)) { /* pragma */ + cp_pragma(cp, hashline); + continue; + } else { + cp_errmsg(cp, cp->tok, LJ_ERR_XSYMBOL); + } + } + scl = cp_decl_spec(cp, &decl, CDF_TYPEDEF|CDF_EXTERN|CDF_STATIC); + if ((cp->tok == ';' || cp->tok == CTOK_EOF) && + ctype_istypedef(decl.stack[0].info)) { + CTInfo info = ctype_rawchild(cp->cts, &decl.stack[0])->info; + if (ctype_isstruct(info) || ctype_isenum(info)) + goto decl_end; /* Accept empty declaration of struct/union/enum. */ + } + for (;;) { + CTypeID ctypeid; + cp_declarator(cp, &decl); + ctypeid = cp_decl_intern(cp, &decl); + if (decl.name && !decl.nameid) { /* NYI: redeclarations are ignored. */ + CType *ct; + CTypeID id; + if ((scl & CDF_TYPEDEF)) { /* Create new typedef. */ + id = lj_ctype_new(cp->cts, &ct); + ct->info = CTINFO(CT_TYPEDEF, ctypeid); + goto noredir; + } else if (ctype_isfunc(ctype_get(cp->cts, ctypeid)->info)) { + /* Treat both static and extern function declarations as extern. */ + ct = ctype_get(cp->cts, ctypeid); + /* We always get new anonymous functions (typedefs are copied). */ + lua_assert(gcref(ct->name) == NULL); + id = ctypeid; /* Just name it. */ + } else if ((scl & CDF_STATIC)) { /* Accept static constants. */ + id = cp_decl_constinit(cp, &ct, ctypeid); + goto noredir; + } else { /* External references have extern or no storage class. */ + id = lj_ctype_new(cp->cts, &ct); + ct->info = CTINFO(CT_EXTERN, ctypeid); + } + if (decl.redir) { /* Add attribute for redirected symbol name. */ + CType *cta; + CTypeID aid = lj_ctype_new(cp->cts, &cta); + ct = ctype_get(cp->cts, id); /* Table may have been reallocated. */ + cta->info = CTINFO(CT_ATTRIB, CTATTRIB(CTA_REDIR)); + cta->sib = ct->sib; + ct->sib = aid; + ctype_setname(cta, decl.redir); + } + noredir: + ctype_setname(ct, decl.name); + lj_ctype_addname(cp->cts, ct, id); + } + if (!cp_opt(cp, ',')) break; + cp_decl_reset(&decl); + } + decl_end: + if (cp->tok == CTOK_EOF && first) break; /* May omit ';' for 1 decl. */ + first = 0; + cp_check(cp, ';'); + } +} + +/* Parse a single C type declaration. */ +static void cp_decl_single(CPState *cp) +{ + CPDecl decl; + cp_decl_spec(cp, &decl, 0); + cp_declarator(cp, &decl); + cp->val.id = cp_decl_intern(cp, &decl); + if (cp->tok != CTOK_EOF) cp_err_token(cp, CTOK_EOF); +} + +#undef H_ + +/* ------------------------------------------------------------------------ */ + +/* Protected callback for C parser. */ +static TValue *cpcparser(lua_State *L, lua_CFunction dummy, void *ud) +{ + CPState *cp = (CPState *)ud; + UNUSED(dummy); + cframe_errfunc(L->cframe) = -1; /* Inherit error function. */ + cp_init(cp); + if ((cp->mode & CPARSE_MODE_MULTI)) + cp_decl_multi(cp); + else + cp_decl_single(cp); + if (cp->param && cp->param != cp->L->top) + cp_err(cp, LJ_ERR_FFI_NUMPARAM); + lua_assert(cp->depth == 0); + return NULL; +} + +/* C parser. */ +int lj_cparse(CPState *cp) +{ + LJ_CTYPE_SAVE(cp->cts); + int errcode = lj_vm_cpcall(cp->L, NULL, cp, cpcparser); + if (errcode) + LJ_CTYPE_RESTORE(cp->cts); + cp_cleanup(cp); + return errcode; +} + +#endif diff --git a/lib/LuaJIT/lj_cparse.h b/lib/LuaJIT/lj_cparse.h new file mode 100644 index 0000000..bad1060 --- /dev/null +++ b/lib/LuaJIT/lj_cparse.h @@ -0,0 +1,65 @@ +/* +** C declaration parser. +** Copyright (C) 2005-2017 Mike Pall. See Copyright Notice in luajit.h +*/ + +#ifndef _LJ_CPARSE_H +#define _LJ_CPARSE_H + +#include "lj_obj.h" +#include "lj_ctype.h" + +#if LJ_HASFFI + +/* C parser limits. */ +#define CPARSE_MAX_BUF 32768 /* Max. token buffer size. */ +#define CPARSE_MAX_DECLSTACK 100 /* Max. declaration stack depth. */ +#define CPARSE_MAX_DECLDEPTH 20 /* Max. recursive declaration depth. */ +#define CPARSE_MAX_PACKSTACK 7 /* Max. pack pragma stack depth. */ + +/* Flags for C parser mode. */ +#define CPARSE_MODE_MULTI 1 /* Process multiple declarations. */ +#define CPARSE_MODE_ABSTRACT 2 /* Accept abstract declarators. */ +#define CPARSE_MODE_DIRECT 4 /* Accept direct declarators. */ +#define CPARSE_MODE_FIELD 8 /* Accept field width in bits, too. */ +#define CPARSE_MODE_NOIMPLICIT 16 /* Reject implicit declarations. */ +#define CPARSE_MODE_SKIP 32 /* Skip definitions, ignore errors. */ + +typedef int CPChar; /* C parser character. Unsigned ext. from char. */ +typedef int CPToken; /* C parser token. */ + +/* C parser internal value representation. */ +typedef struct CPValue { + union { + int32_t i32; /* Value for CTID_INT32. */ + uint32_t u32; /* Value for CTID_UINT32. */ + }; + CTypeID id; /* C Type ID of the value. */ +} CPValue; + +/* C parser state. */ +typedef struct CPState { + CPChar c; /* Current character. */ + CPToken tok; /* Current token. */ + CPValue val; /* Token value. */ + GCstr *str; /* Interned string of identifier/keyword. */ + CType *ct; /* C type table entry. */ + const char *p; /* Current position in input buffer. */ + SBuf sb; /* String buffer for tokens. */ + lua_State *L; /* Lua state. */ + CTState *cts; /* C type state. */ + TValue *param; /* C type parameters. */ + const char *srcname; /* Current source name. */ + BCLine linenumber; /* Input line counter. */ + int depth; /* Recursive declaration depth. */ + uint32_t tmask; /* Type mask for next identifier. */ + uint32_t mode; /* C parser mode. */ + uint8_t packstack[CPARSE_MAX_PACKSTACK]; /* Stack for pack pragmas. */ + uint8_t curpack; /* Current position in pack pragma stack. */ +} CPState; + +LJ_FUNC int lj_cparse(CPState *cp); + +#endif + +#endif diff --git a/lib/LuaJIT/lj_cparse.o b/lib/LuaJIT/lj_cparse.o new file mode 100644 index 0000000000000000000000000000000000000000..42d8a8f6dfd39cd5ea6620375357aea96d329c1c GIT binary patch literal 31520 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.ir[(ref)]) + +/* Pass IR on to next optimization in chain (FOLD). */ +#define emitir(ot, a, b) (lj_ir_set(J, (ot), (a), (b)), lj_opt_fold(J)) + +#define emitconv(a, dt, st, flags) \ + emitir(IRT(IR_CONV, (dt)), (a), (st)|((dt) << 5)|(flags)) + +/* -- C type checks ------------------------------------------------------- */ + +static GCcdata *argv2cdata(jit_State *J, TRef tr, cTValue *o) +{ + GCcdata *cd; + TRef trtypeid; + if (!tref_iscdata(tr)) + lj_trace_err(J, LJ_TRERR_BADTYPE); + cd = cdataV(o); + /* Specialize to the CTypeID. */ + trtypeid = emitir(IRT(IR_FLOAD, IRT_U16), tr, IRFL_CDATA_CTYPEID); + emitir(IRTG(IR_EQ, IRT_INT), trtypeid, lj_ir_kint(J, (int32_t)cd->ctypeid)); + return cd; +} + +/* Specialize to the CTypeID held by a cdata constructor. */ +static CTypeID crec_constructor(jit_State *J, GCcdata *cd, TRef tr) +{ + CTypeID id; + lua_assert(tref_iscdata(tr) && cd->ctypeid == CTID_CTYPEID); + id = *(CTypeID *)cdataptr(cd); + tr = emitir(IRT(IR_FLOAD, IRT_INT), tr, IRFL_CDATA_INT); + emitir(IRTG(IR_EQ, IRT_INT), tr, lj_ir_kint(J, (int32_t)id)); + return id; +} + +static CTypeID argv2ctype(jit_State *J, TRef tr, cTValue *o) +{ + if (tref_isstr(tr)) { + GCstr *s = strV(o); + CPState cp; + CTypeID oldtop; + /* Specialize to the string containing the C type declaration. */ + emitir(IRTG(IR_EQ, IRT_STR), tr, lj_ir_kstr(J, s)); + cp.L = J->L; + cp.cts = ctype_ctsG(J2G(J)); + oldtop = cp.cts->top; + cp.srcname = strdata(s); + cp.p = strdata(s); + cp.param = NULL; + cp.mode = CPARSE_MODE_ABSTRACT|CPARSE_MODE_NOIMPLICIT; + if (lj_cparse(&cp) || cp.cts->top > oldtop) /* Avoid new struct defs. */ + lj_trace_err(J, LJ_TRERR_BADTYPE); + return cp.val.id; + } else { + GCcdata *cd = argv2cdata(J, tr, o); + return cd->ctypeid == CTID_CTYPEID ? crec_constructor(J, cd, tr) : + cd->ctypeid; + } +} + +/* Convert CType to IRType (if possible). */ +static IRType crec_ct2irt(CTState *cts, CType *ct) +{ + if (ctype_isenum(ct->info)) ct = ctype_child(cts, ct); + if (LJ_LIKELY(ctype_isnum(ct->info))) { + if ((ct->info & CTF_FP)) { + if (ct->size == sizeof(double)) + return IRT_NUM; + else if (ct->size == sizeof(float)) + return IRT_FLOAT; + } else { + uint32_t b = lj_fls(ct->size); + if (b <= 3) + return IRT_I8 + 2*b + ((ct->info & CTF_UNSIGNED) ? 1 : 0); + } + } else if (ctype_isptr(ct->info)) { + return (LJ_64 && ct->size == 8) ? IRT_P64 : IRT_P32; + } else if (ctype_iscomplex(ct->info)) { + if (ct->size == 2*sizeof(double)) + return IRT_NUM; + else if (ct->size == 2*sizeof(float)) + return IRT_FLOAT; + } + return IRT_CDATA; +} + +/* -- Optimized memory fill and copy -------------------------------------- */ + +/* Maximum length and unroll of inlined copy/fill. */ +#define CREC_COPY_MAXUNROLL 16 +#define CREC_COPY_MAXLEN 128 + +#define CREC_FILL_MAXUNROLL 16 + +/* Number of windowed registers used for optimized memory copy. */ +#if LJ_TARGET_X86 +#define CREC_COPY_REGWIN 2 +#elif LJ_TARGET_PPC || LJ_TARGET_MIPS +#define CREC_COPY_REGWIN 8 +#else +#define CREC_COPY_REGWIN 4 +#endif + +/* List of memory offsets for copy/fill. */ +typedef struct CRecMemList { + CTSize ofs; /* Offset in bytes. */ + IRType tp; /* Type of load/store. */ + TRef trofs; /* TRef of interned offset. */ + TRef trval; /* TRef of load value. */ +} CRecMemList; + +/* Generate copy list for element-wise struct copy. */ +static MSize crec_copy_struct(CRecMemList *ml, CTState *cts, CType *ct) +{ + CTypeID fid = ct->sib; + MSize mlp = 0; + while (fid) { + CType *df = ctype_get(cts, fid); + fid = df->sib; + if (ctype_isfield(df->info)) { + CType *cct; + IRType tp; + if (!gcref(df->name)) continue; /* Ignore unnamed fields. */ + cct = ctype_rawchild(cts, df); /* Field type. */ + tp = crec_ct2irt(cts, cct); + if (tp == IRT_CDATA) return 0; /* NYI: aggregates. */ + if (mlp >= CREC_COPY_MAXUNROLL) return 0; + ml[mlp].ofs = df->size; + ml[mlp].tp = tp; + mlp++; + if (ctype_iscomplex(cct->info)) { + if (mlp >= CREC_COPY_MAXUNROLL) return 0; + ml[mlp].ofs = df->size + (cct->size >> 1); + ml[mlp].tp = tp; + mlp++; + } + } else if (!ctype_isconstval(df->info)) { + /* NYI: bitfields and sub-structures. */ + return 0; + } + } + return mlp; +} + +/* Generate unrolled copy list, from highest to lowest step size/alignment. */ +static MSize crec_copy_unroll(CRecMemList *ml, CTSize len, CTSize step, + IRType tp) +{ + CTSize ofs = 0; + MSize mlp = 0; + if (tp == IRT_CDATA) tp = IRT_U8 + 2*lj_fls(step); + do { + while (ofs + step <= len) { + if (mlp >= CREC_COPY_MAXUNROLL) return 0; + ml[mlp].ofs = ofs; + ml[mlp].tp = tp; + mlp++; + ofs += step; + } + step >>= 1; + tp -= 2; + } while (ofs < len); + return mlp; +} + +/* +** Emit copy list with windowed loads/stores. +** LJ_TARGET_UNALIGNED: may emit unaligned loads/stores (not marked as such). +*/ +static void crec_copy_emit(jit_State *J, CRecMemList *ml, MSize mlp, + TRef trdst, TRef trsrc) +{ + MSize i, j, rwin = 0; + for (i = 0, j = 0; i < mlp; ) { + TRef trofs = lj_ir_kintp(J, ml[i].ofs); + TRef trsptr = emitir(IRT(IR_ADD, IRT_PTR), trsrc, trofs); + ml[i].trval = emitir(IRT(IR_XLOAD, ml[i].tp), trsptr, 0); + ml[i].trofs = trofs; + i++; + rwin += (LJ_SOFTFP32 && ml[i].tp == IRT_NUM) ? 2 : 1; + if (rwin >= CREC_COPY_REGWIN || i >= mlp) { /* Flush buffered stores. */ + rwin = 0; + for ( ; j < i; j++) { + TRef trdptr = emitir(IRT(IR_ADD, IRT_PTR), trdst, ml[j].trofs); + emitir(IRT(IR_XSTORE, ml[j].tp), trdptr, ml[j].trval); + } + } + } +} + +/* Optimized memory copy. */ +static void crec_copy(jit_State *J, TRef trdst, TRef trsrc, TRef trlen, + CType *ct) +{ + if (tref_isk(trlen)) { /* Length must be constant. */ + CRecMemList ml[CREC_COPY_MAXUNROLL]; + MSize mlp = 0; + CTSize step = 1, len = (CTSize)IR(tref_ref(trlen))->i; + IRType tp = IRT_CDATA; + int needxbar = 0; + if (len == 0) return; /* Shortcut. */ + if (len > CREC_COPY_MAXLEN) goto fallback; + if (ct) { + CTState *cts = ctype_ctsG(J2G(J)); + lua_assert(ctype_isarray(ct->info) || ctype_isstruct(ct->info)); + if (ctype_isarray(ct->info)) { + CType *cct = ctype_rawchild(cts, ct); + tp = crec_ct2irt(cts, cct); + if (tp == IRT_CDATA) goto rawcopy; + step = lj_ir_type_size[tp]; + lua_assert((len & (step-1)) == 0); + } else if ((ct->info & CTF_UNION)) { + step = (1u << ctype_align(ct->info)); + goto rawcopy; + } else { + mlp = crec_copy_struct(ml, cts, ct); + goto emitcopy; + } + } else { + rawcopy: + needxbar = 1; + if (LJ_TARGET_UNALIGNED || step >= CTSIZE_PTR) + step = CTSIZE_PTR; + } + mlp = crec_copy_unroll(ml, len, step, tp); + emitcopy: + if (mlp) { + crec_copy_emit(J, ml, mlp, trdst, trsrc); + if (needxbar) + emitir(IRT(IR_XBAR, IRT_NIL), 0, 0); + return; + } + } +fallback: + /* Call memcpy. Always needs a barrier to disable alias analysis. */ + lj_ir_call(J, IRCALL_memcpy, trdst, trsrc, trlen); + emitir(IRT(IR_XBAR, IRT_NIL), 0, 0); +} + +/* Generate unrolled fill list, from highest to lowest step size/alignment. */ +static MSize crec_fill_unroll(CRecMemList *ml, CTSize len, CTSize step) +{ + CTSize ofs = 0; + MSize mlp = 0; + IRType tp = IRT_U8 + 2*lj_fls(step); + do { + while (ofs + step <= len) { + if (mlp >= CREC_COPY_MAXUNROLL) return 0; + ml[mlp].ofs = ofs; + ml[mlp].tp = tp; + mlp++; + ofs += step; + } + step >>= 1; + tp -= 2; + } while (ofs < len); + return mlp; +} + +/* +** Emit stores for fill list. +** LJ_TARGET_UNALIGNED: may emit unaligned stores (not marked as such). +*/ +static void crec_fill_emit(jit_State *J, CRecMemList *ml, MSize mlp, + TRef trdst, TRef trfill) +{ + MSize i; + for (i = 0; i < mlp; i++) { + TRef trofs = lj_ir_kintp(J, ml[i].ofs); + TRef trdptr = emitir(IRT(IR_ADD, IRT_PTR), trdst, trofs); + emitir(IRT(IR_XSTORE, ml[i].tp), trdptr, trfill); + } +} + +/* Optimized memory fill. */ +static void crec_fill(jit_State *J, TRef trdst, TRef trlen, TRef trfill, + CTSize step) +{ + if (tref_isk(trlen)) { /* Length must be constant. */ + CRecMemList ml[CREC_FILL_MAXUNROLL]; + MSize mlp; + CTSize len = (CTSize)IR(tref_ref(trlen))->i; + if (len == 0) return; /* Shortcut. */ + if (LJ_TARGET_UNALIGNED || step >= CTSIZE_PTR) + step = CTSIZE_PTR; + if (step * CREC_FILL_MAXUNROLL < len) goto fallback; + mlp = crec_fill_unroll(ml, len, step); + if (!mlp) goto fallback; + if (tref_isk(trfill) || ml[0].tp != IRT_U8) + trfill = emitconv(trfill, IRT_INT, IRT_U8, 0); + if (ml[0].tp != IRT_U8) { /* Scatter U8 to U16/U32/U64. */ + if (CTSIZE_PTR == 8 && ml[0].tp == IRT_U64) { + if (tref_isk(trfill)) /* Pointless on x64 with zero-extended regs. */ + trfill = emitconv(trfill, IRT_U64, IRT_U32, 0); + trfill = emitir(IRT(IR_MUL, IRT_U64), trfill, + lj_ir_kint64(J, U64x(01010101,01010101))); + } else { + trfill = emitir(IRTI(IR_MUL), trfill, + lj_ir_kint(J, ml[0].tp == IRT_U16 ? 0x0101 : 0x01010101)); + } + } + crec_fill_emit(J, ml, mlp, trdst, trfill); + } else { +fallback: + /* Call memset. Always needs a barrier to disable alias analysis. */ + lj_ir_call(J, IRCALL_memset, trdst, trfill, trlen); /* Note: arg order! */ + } + emitir(IRT(IR_XBAR, IRT_NIL), 0, 0); +} + +/* -- Convert C type to C type -------------------------------------------- */ + +/* +** This code mirrors the code in lj_cconv.c. It performs the same steps +** for the trace recorder that lj_cconv.c does for the interpreter. +** +** One major difference is that we can get away with much fewer checks +** here. E.g. checks for casts, constness or correct types can often be +** omitted, even if they might fail. The interpreter subsequently throws +** an error, which aborts the trace. +** +** All operations are specialized to their C types, so the on-trace +** outcome must be the same as the outcome in the interpreter. If the +** interpreter doesn't throw an error, then the trace is correct, too. +** Care must be taken not to generate invalid (temporary) IR or to +** trigger asserts. +*/ + +/* Determine whether a passed number or cdata number is non-zero. */ +static int crec_isnonzero(CType *s, void *p) +{ + if (p == (void *)0) + return 0; + if (p == (void *)1) + return 1; + if ((s->info & CTF_FP)) { + if (s->size == sizeof(float)) + return (*(float *)p != 0); + else + return (*(double *)p != 0); + } else { + if (s->size == 1) + return (*(uint8_t *)p != 0); + else if (s->size == 2) + return (*(uint16_t *)p != 0); + else if (s->size == 4) + return (*(uint32_t *)p != 0); + else + return (*(uint64_t *)p != 0); + } +} + +static TRef crec_ct_ct(jit_State *J, CType *d, CType *s, TRef dp, TRef sp, + void *svisnz) +{ + IRType dt = crec_ct2irt(ctype_ctsG(J2G(J)), d); + IRType st = crec_ct2irt(ctype_ctsG(J2G(J)), s); + CTSize dsize = d->size, ssize = s->size; + CTInfo dinfo = d->info, sinfo = s->info; + + if (ctype_type(dinfo) > CT_MAYCONVERT || ctype_type(sinfo) > CT_MAYCONVERT) + goto err_conv; + + /* + ** Note: Unlike lj_cconv_ct_ct(), sp holds the _value_ of pointers and + ** numbers up to 8 bytes. Otherwise sp holds a pointer. + */ + + switch (cconv_idx2(dinfo, sinfo)) { + /* Destination is a bool. */ + case CCX(B, B): + goto xstore; /* Source operand is already normalized. */ + case CCX(B, I): + case CCX(B, F): + if (st != IRT_CDATA) { + /* Specialize to the result of a comparison against 0. */ + TRef zero = (st == IRT_NUM || st == IRT_FLOAT) ? lj_ir_knum(J, 0) : + (st == IRT_I64 || st == IRT_U64) ? lj_ir_kint64(J, 0) : + lj_ir_kint(J, 0); + int isnz = crec_isnonzero(s, svisnz); + emitir(IRTG(isnz ? IR_NE : IR_EQ, st), sp, zero); + sp = lj_ir_kint(J, isnz); + goto xstore; + } + goto err_nyi; + + /* Destination is an integer. */ + case CCX(I, B): + case CCX(I, I): + conv_I_I: + if (dt == IRT_CDATA || st == IRT_CDATA) goto err_nyi; + /* Extend 32 to 64 bit integer. */ + if (dsize == 8 && ssize < 8 && !(LJ_64 && (sinfo & CTF_UNSIGNED))) + sp = emitconv(sp, dt, ssize < 4 ? IRT_INT : st, + (sinfo & CTF_UNSIGNED) ? 0 : IRCONV_SEXT); + else if (dsize < 8 && ssize == 8) /* Truncate from 64 bit integer. */ + sp = emitconv(sp, dsize < 4 ? IRT_INT : dt, st, 0); + else if (st == IRT_INT) + sp = lj_opt_narrow_toint(J, sp); + xstore: + if (dt == IRT_I64 || dt == IRT_U64) lj_needsplit(J); + if (dp == 0) return sp; + emitir(IRT(IR_XSTORE, dt), dp, sp); + break; + case CCX(I, C): + sp = emitir(IRT(IR_XLOAD, st), sp, 0); /* Load re. */ + /* fallthrough */ + case CCX(I, F): + if (dt == IRT_CDATA || st == IRT_CDATA) goto err_nyi; + sp = emitconv(sp, dsize < 4 ? IRT_INT : dt, st, IRCONV_ANY); + goto xstore; + case CCX(I, P): + case CCX(I, A): + sinfo = CTINFO(CT_NUM, CTF_UNSIGNED); + ssize = CTSIZE_PTR; + st = IRT_UINTP; + if (((dsize ^ ssize) & 8) == 0) { /* Must insert no-op type conversion. */ + sp = emitconv(sp, dsize < 4 ? IRT_INT : dt, IRT_PTR, 0); + goto xstore; + } + goto conv_I_I; + + /* Destination is a floating-point number. */ + case CCX(F, B): + case CCX(F, I): + conv_F_I: + if (dt == IRT_CDATA || st == IRT_CDATA) goto err_nyi; + sp = emitconv(sp, dt, ssize < 4 ? IRT_INT : st, 0); + goto xstore; + case CCX(F, C): + sp = emitir(IRT(IR_XLOAD, st), sp, 0); /* Load re. */ + /* fallthrough */ + case CCX(F, F): + conv_F_F: + if (dt == IRT_CDATA || st == IRT_CDATA) goto err_nyi; + if (dt != st) sp = emitconv(sp, dt, st, 0); + goto xstore; + + /* Destination is a complex number. */ + case CCX(C, I): + case CCX(C, F): + { /* Clear im. */ + TRef ptr = emitir(IRT(IR_ADD, IRT_PTR), dp, lj_ir_kintp(J, (dsize >> 1))); + emitir(IRT(IR_XSTORE, dt), ptr, lj_ir_knum(J, 0)); + } + /* Convert to re. */ + if ((sinfo & CTF_FP)) goto conv_F_F; else goto conv_F_I; + + case CCX(C, C): + if (dt == IRT_CDATA || st == IRT_CDATA) goto err_nyi; + { + TRef re, im, ptr; + re = emitir(IRT(IR_XLOAD, st), sp, 0); + ptr = emitir(IRT(IR_ADD, IRT_PTR), sp, lj_ir_kintp(J, (ssize >> 1))); + im = emitir(IRT(IR_XLOAD, st), ptr, 0); + if (dt != st) { + re = emitconv(re, dt, st, 0); + im = emitconv(im, dt, st, 0); + } + emitir(IRT(IR_XSTORE, dt), dp, re); + ptr = emitir(IRT(IR_ADD, IRT_PTR), dp, lj_ir_kintp(J, (dsize >> 1))); + emitir(IRT(IR_XSTORE, dt), ptr, im); + } + break; + + /* Destination is a vector. */ + case CCX(V, I): + case CCX(V, F): + case CCX(V, C): + case CCX(V, V): + goto err_nyi; + + /* Destination is a pointer. */ + case CCX(P, P): + case CCX(P, A): + case CCX(P, S): + /* There are only 32 bit pointers/addresses on 32 bit machines. + ** Also ok on x64, since all 32 bit ops clear the upper part of the reg. + */ + goto xstore; + case CCX(P, I): + if (st == IRT_CDATA) goto err_nyi; + if (!LJ_64 && ssize == 8) /* Truncate from 64 bit integer. */ + sp = emitconv(sp, IRT_U32, st, 0); + goto xstore; + case CCX(P, F): + if (st == IRT_CDATA) goto err_nyi; + /* The signed conversion is cheaper. x64 really has 47 bit pointers. */ + sp = emitconv(sp, (LJ_64 && dsize == 8) ? IRT_I64 : IRT_U32, + st, IRCONV_ANY); + goto xstore; + + /* Destination is an array. */ + case CCX(A, A): + /* Destination is a struct/union. */ + case CCX(S, S): + if (dp == 0) goto err_conv; + crec_copy(J, dp, sp, lj_ir_kint(J, dsize), d); + break; + + default: + err_conv: + err_nyi: + lj_trace_err(J, LJ_TRERR_NYICONV); + break; + } + return 0; +} + +/* -- Convert C type to TValue (load) ------------------------------------- */ + +static TRef crec_tv_ct(jit_State *J, CType *s, CTypeID sid, TRef sp) +{ + CTState *cts = ctype_ctsG(J2G(J)); + IRType t = crec_ct2irt(cts, s); + CTInfo sinfo = s->info; + if (ctype_isnum(sinfo)) { + TRef tr; + if (t == IRT_CDATA) + goto err_nyi; /* NYI: copyval of >64 bit integers. */ + tr = emitir(IRT(IR_XLOAD, t), sp, 0); + if (t == IRT_FLOAT || t == IRT_U32) { /* Keep uint32_t/float as numbers. */ + return emitconv(tr, IRT_NUM, t, 0); + } else if (t == IRT_I64 || t == IRT_U64) { /* Box 64 bit integer. */ + sp = tr; + lj_needsplit(J); + } else if ((sinfo & CTF_BOOL)) { + /* Assume not equal to zero. Fixup and emit pending guard later. */ + lj_ir_set(J, IRTGI(IR_NE), tr, lj_ir_kint(J, 0)); + J->postproc = LJ_POST_FIXGUARD; + return TREF_TRUE; + } else { + return tr; + } + } else if (ctype_isptr(sinfo) || ctype_isenum(sinfo)) { + sp = emitir(IRT(IR_XLOAD, t), sp, 0); /* Box pointers and enums. */ + } else if (ctype_isrefarray(sinfo) || ctype_isstruct(sinfo)) { + cts->L = J->L; + sid = lj_ctype_intern(cts, CTINFO_REF(sid), CTSIZE_PTR); /* Create ref. */ + } else if (ctype_iscomplex(sinfo)) { /* Unbox/box complex. */ + ptrdiff_t esz = (ptrdiff_t)(s->size >> 1); + TRef ptr, tr1, tr2, dp; + dp = emitir(IRTG(IR_CNEW, IRT_CDATA), lj_ir_kint(J, sid), TREF_NIL); + tr1 = emitir(IRT(IR_XLOAD, t), sp, 0); + ptr = emitir(IRT(IR_ADD, IRT_PTR), sp, lj_ir_kintp(J, esz)); + tr2 = emitir(IRT(IR_XLOAD, t), ptr, 0); + ptr = emitir(IRT(IR_ADD, IRT_PTR), dp, lj_ir_kintp(J, sizeof(GCcdata))); + emitir(IRT(IR_XSTORE, t), ptr, tr1); + ptr = emitir(IRT(IR_ADD, IRT_PTR), dp, lj_ir_kintp(J, sizeof(GCcdata)+esz)); + emitir(IRT(IR_XSTORE, t), ptr, tr2); + return dp; + } else { + /* NYI: copyval of vectors. */ + err_nyi: + lj_trace_err(J, LJ_TRERR_NYICONV); + } + /* Box pointer, ref, enum or 64 bit integer. */ + return emitir(IRTG(IR_CNEWI, IRT_CDATA), lj_ir_kint(J, sid), sp); +} + +/* -- Convert TValue to C type (store) ------------------------------------ */ + +static TRef crec_ct_tv(jit_State *J, CType *d, TRef dp, TRef sp, cTValue *sval) +{ + CTState *cts = ctype_ctsG(J2G(J)); + CTypeID sid = CTID_P_VOID; + void *svisnz = 0; + CType *s; + if (LJ_LIKELY(tref_isinteger(sp))) { + sid = CTID_INT32; + svisnz = (void *)(intptr_t)(tvisint(sval)?(intV(sval)!=0):!tviszero(sval)); + } else if (tref_isnum(sp)) { + sid = CTID_DOUBLE; + svisnz = (void *)(intptr_t)(tvisint(sval)?(intV(sval)!=0):!tviszero(sval)); + } else if (tref_isbool(sp)) { + sp = lj_ir_kint(J, tref_istrue(sp) ? 1 : 0); + sid = CTID_BOOL; + } else if (tref_isnil(sp)) { + sp = lj_ir_kptr(J, NULL); + } else if (tref_isudata(sp)) { + GCudata *ud = udataV(sval); + if (ud->udtype == UDTYPE_IO_FILE) { + TRef tr = emitir(IRT(IR_FLOAD, IRT_U8), sp, IRFL_UDATA_UDTYPE); + emitir(IRTGI(IR_EQ), tr, lj_ir_kint(J, UDTYPE_IO_FILE)); + sp = emitir(IRT(IR_FLOAD, IRT_PTR), sp, IRFL_UDATA_FILE); + } else { + sp = emitir(IRT(IR_ADD, IRT_PTR), sp, lj_ir_kintp(J, sizeof(GCudata))); + } + } else if (tref_isstr(sp)) { + if (ctype_isenum(d->info)) { /* Match string against enum constant. */ + GCstr *str = strV(sval); + CTSize ofs; + CType *cct = lj_ctype_getfield(cts, d, str, &ofs); + /* Specialize to the name of the enum constant. */ + emitir(IRTG(IR_EQ, IRT_STR), sp, lj_ir_kstr(J, str)); + if (cct && ctype_isconstval(cct->info)) { + lua_assert(ctype_child(cts, cct)->size == 4); + svisnz = (void *)(intptr_t)(ofs != 0); + sp = lj_ir_kint(J, (int32_t)ofs); + sid = ctype_cid(cct->info); + } /* else: interpreter will throw. */ + } else if (ctype_isrefarray(d->info)) { /* Copy string to array. */ + lj_trace_err(J, LJ_TRERR_BADTYPE); /* NYI */ + } else { /* Otherwise pass the string data as a const char[]. */ + /* Don't use STRREF. It folds with SNEW, which loses the trailing NUL. */ + sp = emitir(IRT(IR_ADD, IRT_PTR), sp, lj_ir_kintp(J, sizeof(GCstr))); + sid = CTID_A_CCHAR; + } + } else if (tref_islightud(sp)) { +#if LJ_64 + sp = emitir(IRT(IR_BAND, IRT_P64), sp, + lj_ir_kint64(J, U64x(00007fff,ffffffff))); +#endif + } else { /* NYI: tref_istab(sp). */ + IRType t; + sid = argv2cdata(J, sp, sval)->ctypeid; + s = ctype_raw(cts, sid); + svisnz = cdataptr(cdataV(sval)); + if (ctype_isfunc(s->info)) { + sid = lj_ctype_intern(cts, CTINFO(CT_PTR, CTALIGN_PTR|sid), CTSIZE_PTR); + s = ctype_get(cts, sid); + t = IRT_PTR; + } else { + t = crec_ct2irt(cts, s); + } + if (ctype_isptr(s->info)) { + sp = emitir(IRT(IR_FLOAD, t), sp, IRFL_CDATA_PTR); + if (ctype_isref(s->info)) { + svisnz = *(void **)svisnz; + s = ctype_rawchild(cts, s); + if (ctype_isenum(s->info)) s = ctype_child(cts, s); + t = crec_ct2irt(cts, s); + } else { + goto doconv; + } + } else if (t == IRT_I64 || t == IRT_U64) { + sp = emitir(IRT(IR_FLOAD, t), sp, IRFL_CDATA_INT64); + lj_needsplit(J); + goto doconv; + } else if (t == IRT_INT || t == IRT_U32) { + if (ctype_isenum(s->info)) s = ctype_child(cts, s); + sp = emitir(IRT(IR_FLOAD, t), sp, IRFL_CDATA_INT); + goto doconv; + } else { + sp = emitir(IRT(IR_ADD, IRT_PTR), sp, lj_ir_kintp(J, sizeof(GCcdata))); + } + if (ctype_isnum(s->info) && t != IRT_CDATA) + sp = emitir(IRT(IR_XLOAD, t), sp, 0); /* Load number value. */ + goto doconv; + } + s = ctype_get(cts, sid); +doconv: + if (ctype_isenum(d->info)) d = ctype_child(cts, d); + return crec_ct_ct(J, d, s, dp, sp, svisnz); +} + +/* -- C data metamethods -------------------------------------------------- */ + +/* This would be rather difficult in FOLD, so do it here: +** (base+k)+(idx*sz)+ofs ==> (base+idx*sz)+(ofs+k) +** (base+(idx+k)*sz)+ofs ==> (base+idx*sz)+(ofs+k*sz) +*/ +static TRef crec_reassoc_ofs(jit_State *J, TRef tr, ptrdiff_t *ofsp, MSize sz) +{ + IRIns *ir = IR(tref_ref(tr)); + if (LJ_LIKELY(J->flags & JIT_F_OPT_FOLD) && irref_isk(ir->op2) && + (ir->o == IR_ADD || ir->o == IR_ADDOV || ir->o == IR_SUBOV)) { + IRIns *irk = IR(ir->op2); + ptrdiff_t k; + if (LJ_64 && irk->o == IR_KINT64) + k = (ptrdiff_t)ir_kint64(irk)->u64 * sz; + else + k = (ptrdiff_t)irk->i * sz; + if (ir->o == IR_SUBOV) *ofsp -= k; else *ofsp += k; + tr = ir->op1; /* Not a TRef, but the caller doesn't care. */ + } + return tr; +} + +/* Tailcall to function. */ +static void crec_tailcall(jit_State *J, RecordFFData *rd, cTValue *tv) +{ + TRef kfunc = lj_ir_kfunc(J, funcV(tv)); +#if LJ_FR2 + J->base[-2] = kfunc; + J->base[-1] = TREF_FRAME; +#else + J->base[-1] = kfunc | TREF_FRAME; +#endif + rd->nres = -1; /* Pending tailcall. */ +} + +/* Record ctype __index/__newindex metamethods. */ +static void crec_index_meta(jit_State *J, CTState *cts, CType *ct, + RecordFFData *rd) +{ + CTypeID id = ctype_typeid(cts, ct); + cTValue *tv = lj_ctype_meta(cts, id, rd->data ? MM_newindex : MM_index); + if (!tv) + lj_trace_err(J, LJ_TRERR_BADTYPE); + if (tvisfunc(tv)) { + crec_tailcall(J, rd, tv); + } else if (rd->data == 0 && tvistab(tv) && tref_isstr(J->base[1])) { + /* Specialize to result of __index lookup. */ + cTValue *o = lj_tab_get(J->L, tabV(tv), &rd->argv[1]); + J->base[0] = lj_record_constify(J, o); + if (!J->base[0]) + lj_trace_err(J, LJ_TRERR_BADTYPE); + /* Always specialize to the key. */ + emitir(IRTG(IR_EQ, IRT_STR), J->base[1], lj_ir_kstr(J, strV(&rd->argv[1]))); + } else { + /* NYI: resolving of non-function metamethods. */ + /* NYI: non-string keys for __index table. */ + /* NYI: stores to __newindex table. */ + lj_trace_err(J, LJ_TRERR_BADTYPE); + } +} + +/* Record bitfield load/store. */ +static void crec_index_bf(jit_State *J, RecordFFData *rd, TRef ptr, CTInfo info) +{ + IRType t = IRT_I8 + 2*lj_fls(ctype_bitcsz(info)) + ((info&CTF_UNSIGNED)?1:0); + TRef tr = emitir(IRT(IR_XLOAD, t), ptr, 0); + CTSize pos = ctype_bitpos(info), bsz = ctype_bitbsz(info), shift = 32 - bsz; + lua_assert(t <= IRT_U32); /* NYI: 64 bit bitfields. */ + if (rd->data == 0) { /* __index metamethod. */ + if ((info & CTF_BOOL)) { + tr = emitir(IRTI(IR_BAND), tr, lj_ir_kint(J, (int32_t)((1u << pos)))); + /* Assume not equal to zero. Fixup and emit pending guard later. */ + lj_ir_set(J, IRTGI(IR_NE), tr, lj_ir_kint(J, 0)); + J->postproc = LJ_POST_FIXGUARD; + tr = TREF_TRUE; + } else if (!(info & CTF_UNSIGNED)) { + tr = emitir(IRTI(IR_BSHL), tr, lj_ir_kint(J, shift - pos)); + tr = emitir(IRTI(IR_BSAR), tr, lj_ir_kint(J, shift)); + } else { + lua_assert(bsz < 32); /* Full-size fields cannot end up here. */ + tr = emitir(IRTI(IR_BSHR), tr, lj_ir_kint(J, pos)); + tr = emitir(IRTI(IR_BAND), tr, lj_ir_kint(J, (int32_t)((1u << bsz)-1))); + /* We can omit the U32 to NUM conversion, since bsz < 32. */ + } + J->base[0] = tr; + } else { /* __newindex metamethod. */ + CTState *cts = ctype_ctsG(J2G(J)); + CType *ct = ctype_get(cts, + (info & CTF_BOOL) ? CTID_BOOL : + (info & CTF_UNSIGNED) ? CTID_UINT32 : CTID_INT32); + int32_t mask = (int32_t)(((1u << bsz)-1) << pos); + TRef sp = crec_ct_tv(J, ct, 0, J->base[2], &rd->argv[2]); + sp = emitir(IRTI(IR_BSHL), sp, lj_ir_kint(J, pos)); + /* Use of the target type avoids forwarding conversions. */ + sp = emitir(IRT(IR_BAND, t), sp, lj_ir_kint(J, mask)); + tr = emitir(IRT(IR_BAND, t), tr, lj_ir_kint(J, (int32_t)~mask)); + tr = emitir(IRT(IR_BOR, t), tr, sp); + emitir(IRT(IR_XSTORE, t), ptr, tr); + rd->nres = 0; + J->needsnap = 1; + } +} + +void LJ_FASTCALL recff_cdata_index(jit_State *J, RecordFFData *rd) +{ + TRef idx, ptr = J->base[0]; + ptrdiff_t ofs = sizeof(GCcdata); + GCcdata *cd = argv2cdata(J, ptr, &rd->argv[0]); + CTState *cts = ctype_ctsG(J2G(J)); + CType *ct = ctype_raw(cts, cd->ctypeid); + CTypeID sid = 0; + + /* Resolve pointer or reference for cdata object. */ + if (ctype_isptr(ct->info)) { + IRType t = (LJ_64 && ct->size == 8) ? IRT_P64 : IRT_P32; + if (ctype_isref(ct->info)) ct = ctype_rawchild(cts, ct); + ptr = emitir(IRT(IR_FLOAD, t), ptr, IRFL_CDATA_PTR); + ofs = 0; + ptr = crec_reassoc_ofs(J, ptr, &ofs, 1); + } + +again: + idx = J->base[1]; + if (tref_isnumber(idx)) { + idx = lj_opt_narrow_cindex(J, idx); + if (ctype_ispointer(ct->info)) { + CTSize sz; + integer_key: + if ((ct->info & CTF_COMPLEX)) + idx = emitir(IRT(IR_BAND, IRT_INTP), idx, lj_ir_kintp(J, 1)); + sz = lj_ctype_size(cts, (sid = ctype_cid(ct->info))); + idx = crec_reassoc_ofs(J, idx, &ofs, sz); +#if LJ_TARGET_ARM || LJ_TARGET_PPC + /* Hoist base add to allow fusion of index/shift into operands. */ + if (LJ_LIKELY(J->flags & JIT_F_OPT_LOOP) && ofs +#if LJ_TARGET_ARM + && (sz == 1 || sz == 4) +#endif + ) { + ptr = emitir(IRT(IR_ADD, IRT_PTR), ptr, lj_ir_kintp(J, ofs)); + ofs = 0; + } +#endif + idx = emitir(IRT(IR_MUL, IRT_INTP), idx, lj_ir_kintp(J, sz)); + ptr = emitir(IRT(IR_ADD, IRT_PTR), idx, ptr); + } + } else if (tref_iscdata(idx)) { + GCcdata *cdk = cdataV(&rd->argv[1]); + CType *ctk = ctype_raw(cts, cdk->ctypeid); + IRType t = crec_ct2irt(cts, ctk); + if (ctype_ispointer(ct->info) && t >= IRT_I8 && t <= IRT_U64) { + if (ctk->size == 8) { + idx = emitir(IRT(IR_FLOAD, t), idx, IRFL_CDATA_INT64); + } else if (ctk->size == 4) { + idx = emitir(IRT(IR_FLOAD, t), idx, IRFL_CDATA_INT); + } else { + idx = emitir(IRT(IR_ADD, IRT_PTR), idx, + lj_ir_kintp(J, sizeof(GCcdata))); + idx = emitir(IRT(IR_XLOAD, t), idx, 0); + } + if (LJ_64 && ctk->size < sizeof(intptr_t) && !(ctk->info & CTF_UNSIGNED)) + idx = emitconv(idx, IRT_INTP, IRT_INT, IRCONV_SEXT); + if (!LJ_64 && ctk->size > sizeof(intptr_t)) { + idx = emitconv(idx, IRT_INTP, t, 0); + lj_needsplit(J); + } + goto integer_key; + } + } else if (tref_isstr(idx)) { + GCstr *name = strV(&rd->argv[1]); + if (cd && cd->ctypeid == CTID_CTYPEID) + ct = ctype_raw(cts, crec_constructor(J, cd, ptr)); + if (ctype_isstruct(ct->info)) { + CTSize fofs; + CType *fct; + fct = lj_ctype_getfield(cts, ct, name, &fofs); + if (fct) { + ofs += (ptrdiff_t)fofs; + /* Always specialize to the field name. */ + emitir(IRTG(IR_EQ, IRT_STR), idx, lj_ir_kstr(J, name)); + if (ctype_isconstval(fct->info)) { + if (fct->size >= 0x80000000u && + (ctype_child(cts, fct)->info & CTF_UNSIGNED)) { + J->base[0] = lj_ir_knum(J, (lua_Number)(uint32_t)fct->size); + return; + } + J->base[0] = lj_ir_kint(J, (int32_t)fct->size); + return; /* Interpreter will throw for newindex. */ + } else if (ctype_isbitfield(fct->info)) { + if (ofs) + ptr = emitir(IRT(IR_ADD, IRT_PTR), ptr, lj_ir_kintp(J, ofs)); + crec_index_bf(J, rd, ptr, fct->info); + return; + } else { + lua_assert(ctype_isfield(fct->info)); + sid = ctype_cid(fct->info); + } + } + } else if (ctype_iscomplex(ct->info)) { + if (name->len == 2 && + ((strdata(name)[0] == 'r' && strdata(name)[1] == 'e') || + (strdata(name)[0] == 'i' && strdata(name)[1] == 'm'))) { + /* Always specialize to the field name. */ + emitir(IRTG(IR_EQ, IRT_STR), idx, lj_ir_kstr(J, name)); + if (strdata(name)[0] == 'i') ofs += (ct->size >> 1); + sid = ctype_cid(ct->info); + } + } + } + if (!sid) { + if (ctype_isptr(ct->info)) { /* Automatically perform '->'. */ + CType *cct = ctype_rawchild(cts, ct); + if (ctype_isstruct(cct->info)) { + ct = cct; + cd = NULL; + if (tref_isstr(idx)) goto again; + } + } + crec_index_meta(J, cts, ct, rd); + return; + } + + if (ofs) + ptr = emitir(IRT(IR_ADD, IRT_PTR), ptr, lj_ir_kintp(J, ofs)); + + /* Resolve reference for field. */ + ct = ctype_get(cts, sid); + if (ctype_isref(ct->info)) { + ptr = emitir(IRT(IR_XLOAD, IRT_PTR), ptr, 0); + sid = ctype_cid(ct->info); + ct = ctype_get(cts, sid); + } + + while (ctype_isattrib(ct->info)) + ct = ctype_child(cts, ct); /* Skip attributes. */ + + if (rd->data == 0) { /* __index metamethod. */ + J->base[0] = crec_tv_ct(J, ct, sid, ptr); + } else { /* __newindex metamethod. */ + rd->nres = 0; + J->needsnap = 1; + crec_ct_tv(J, ct, ptr, J->base[2], &rd->argv[2]); + } +} + +/* Record setting a finalizer. */ +static void crec_finalizer(jit_State *J, TRef trcd, TRef trfin, cTValue *fin) +{ + if (tvisgcv(fin)) { + if (!trfin) trfin = lj_ir_kptr(J, gcval(fin)); + } else if (tvisnil(fin)) { + trfin = lj_ir_kptr(J, NULL); + } else { + lj_trace_err(J, LJ_TRERR_BADTYPE); + } + lj_ir_call(J, IRCALL_lj_cdata_setfin, trcd, + trfin, lj_ir_kint(J, (int32_t)itype(fin))); + J->needsnap = 1; +} + +/* Record cdata allocation. */ +static void crec_alloc(jit_State *J, RecordFFData *rd, CTypeID id) +{ + CTState *cts = ctype_ctsG(J2G(J)); + CTSize sz; + CTInfo info = lj_ctype_info(cts, id, &sz); + CType *d = ctype_raw(cts, id); + TRef trcd, trid = lj_ir_kint(J, id); + cTValue *fin; + /* Use special instruction to box pointer or 32/64 bit integer. */ + if (ctype_isptr(info) || (ctype_isinteger(info) && (sz == 4 || sz == 8))) { + TRef sp = J->base[1] ? crec_ct_tv(J, d, 0, J->base[1], &rd->argv[1]) : + ctype_isptr(info) ? lj_ir_kptr(J, NULL) : + sz == 4 ? lj_ir_kint(J, 0) : + (lj_needsplit(J), lj_ir_kint64(J, 0)); + J->base[0] = emitir(IRTG(IR_CNEWI, IRT_CDATA), trid, sp); + return; + } else { + TRef trsz = TREF_NIL; + if ((info & CTF_VLA)) { /* Calculate VLA/VLS size at runtime. */ + CTSize sz0, sz1; + if (!J->base[1] || J->base[2]) + lj_trace_err(J, LJ_TRERR_NYICONV); /* NYI: init VLA/VLS. */ + trsz = crec_ct_tv(J, ctype_get(cts, CTID_INT32), 0, + J->base[1], &rd->argv[1]); + sz0 = lj_ctype_vlsize(cts, d, 0); + sz1 = lj_ctype_vlsize(cts, d, 1); + trsz = emitir(IRTGI(IR_MULOV), trsz, lj_ir_kint(J, (int32_t)(sz1-sz0))); + trsz = emitir(IRTGI(IR_ADDOV), trsz, lj_ir_kint(J, (int32_t)sz0)); + J->base[1] = 0; /* Simplify logic below. */ + } else if (ctype_align(info) > CT_MEMALIGN) { + trsz = lj_ir_kint(J, sz); + } + trcd = emitir(IRTG(IR_CNEW, IRT_CDATA), trid, trsz); + if (sz > 128 || (info & CTF_VLA)) { + TRef dp; + CTSize align; + special: /* Only handle bulk zero-fill for large/VLA/VLS types. */ + if (J->base[1]) + lj_trace_err(J, LJ_TRERR_NYICONV); /* NYI: init large/VLA/VLS types. */ + dp = emitir(IRT(IR_ADD, IRT_PTR), trcd, lj_ir_kintp(J, sizeof(GCcdata))); + if (trsz == TREF_NIL) trsz = lj_ir_kint(J, sz); + align = ctype_align(info); + if (align < CT_MEMALIGN) align = CT_MEMALIGN; + crec_fill(J, dp, trsz, lj_ir_kint(J, 0), (1u << align)); + } else if (J->base[1] && !J->base[2] && + !lj_cconv_multi_init(cts, d, &rd->argv[1])) { + goto single_init; + } else if (ctype_isarray(d->info)) { + CType *dc = ctype_rawchild(cts, d); /* Array element type. */ + CTSize ofs, esize = dc->size; + TRef sp = 0; + TValue tv; + TValue *sval = &tv; + MSize i; + tv.u64 = 0; + if (!(ctype_isnum(dc->info) || ctype_isptr(dc->info)) || + esize * CREC_FILL_MAXUNROLL < sz) + goto special; + for (i = 1, ofs = 0; ofs < sz; ofs += esize) { + TRef dp = emitir(IRT(IR_ADD, IRT_PTR), trcd, + lj_ir_kintp(J, ofs + sizeof(GCcdata))); + if (J->base[i]) { + sp = J->base[i]; + sval = &rd->argv[i]; + i++; + } else if (i != 2) { + sp = ctype_isnum(dc->info) ? lj_ir_kint(J, 0) : TREF_NIL; + } + crec_ct_tv(J, dc, dp, sp, sval); + } + } else if (ctype_isstruct(d->info)) { + CTypeID fid = d->sib; + MSize i = 1; + while (fid) { + CType *df = ctype_get(cts, fid); + fid = df->sib; + if (ctype_isfield(df->info)) { + CType *dc; + TRef sp, dp; + TValue tv; + TValue *sval = &tv; + setintV(&tv, 0); + if (!gcref(df->name)) continue; /* Ignore unnamed fields. */ + dc = ctype_rawchild(cts, df); /* Field type. */ + if (!(ctype_isnum(dc->info) || ctype_isptr(dc->info) || + ctype_isenum(dc->info))) + lj_trace_err(J, LJ_TRERR_NYICONV); /* NYI: init aggregates. */ + if (J->base[i]) { + sp = J->base[i]; + sval = &rd->argv[i]; + i++; + } else { + sp = ctype_isptr(dc->info) ? TREF_NIL : lj_ir_kint(J, 0); + } + dp = emitir(IRT(IR_ADD, IRT_PTR), trcd, + lj_ir_kintp(J, df->size + sizeof(GCcdata))); + crec_ct_tv(J, dc, dp, sp, sval); + } else if (!ctype_isconstval(df->info)) { + /* NYI: init bitfields and sub-structures. */ + lj_trace_err(J, LJ_TRERR_NYICONV); + } + } + } else { + TRef dp; + single_init: + dp = emitir(IRT(IR_ADD, IRT_PTR), trcd, lj_ir_kintp(J, sizeof(GCcdata))); + if (J->base[1]) { + crec_ct_tv(J, d, dp, J->base[1], &rd->argv[1]); + } else { + TValue tv; + tv.u64 = 0; + crec_ct_tv(J, d, dp, lj_ir_kint(J, 0), &tv); + } + } + } + J->base[0] = trcd; + /* Handle __gc metamethod. */ + fin = lj_ctype_meta(cts, id, MM_gc); + if (fin) + crec_finalizer(J, trcd, 0, fin); +} + +/* Record argument conversions. */ +static TRef crec_call_args(jit_State *J, RecordFFData *rd, + CTState *cts, CType *ct) +{ + TRef args[CCI_NARGS_MAX]; + CTypeID fid; + MSize i, n; + TRef tr, *base; + cTValue *o; +#if LJ_TARGET_X86 +#if LJ_ABI_WIN + TRef *arg0 = NULL, *arg1 = NULL; +#endif + int ngpr = 0; + if (ctype_cconv(ct->info) == CTCC_THISCALL) + ngpr = 1; + else if (ctype_cconv(ct->info) == CTCC_FASTCALL) + ngpr = 2; +#endif + + /* Skip initial attributes. */ + fid = ct->sib; + while (fid) { + CType *ctf = ctype_get(cts, fid); + if (!ctype_isattrib(ctf->info)) break; + fid = ctf->sib; + } + args[0] = TREF_NIL; + for (n = 0, base = J->base+1, o = rd->argv+1; *base; n++, base++, o++) { + CTypeID did; + CType *d; + + if (n >= CCI_NARGS_MAX) + lj_trace_err(J, LJ_TRERR_NYICALL); + + if (fid) { /* Get argument type from field. */ + CType *ctf = ctype_get(cts, fid); + fid = ctf->sib; + lua_assert(ctype_isfield(ctf->info)); + did = ctype_cid(ctf->info); + } else { + if (!(ct->info & CTF_VARARG)) + lj_trace_err(J, LJ_TRERR_NYICALL); /* Too many arguments. */ + did = lj_ccall_ctid_vararg(cts, o); /* Infer vararg type. */ + } + d = ctype_raw(cts, did); + if (!(ctype_isnum(d->info) || ctype_isptr(d->info) || + ctype_isenum(d->info))) + lj_trace_err(J, LJ_TRERR_NYICALL); + tr = crec_ct_tv(J, d, 0, *base, o); + if (ctype_isinteger_or_bool(d->info)) { + if (d->size < 4) { + if ((d->info & CTF_UNSIGNED)) + tr = emitconv(tr, IRT_INT, d->size==1 ? IRT_U8 : IRT_U16, 0); + else + tr = emitconv(tr, IRT_INT, d->size==1 ? IRT_I8 : IRT_I16,IRCONV_SEXT); + } + } else if (LJ_SOFTFP32 && ctype_isfp(d->info) && d->size > 4) { + lj_needsplit(J); + } +#if LJ_TARGET_X86 + /* 64 bit args must not end up in registers for fastcall/thiscall. */ +#if LJ_ABI_WIN + if (!ctype_isfp(d->info)) { + /* Sigh, the Windows/x86 ABI allows reordering across 64 bit args. */ + if (tref_typerange(tr, IRT_I64, IRT_U64)) { + if (ngpr) { + arg0 = &args[n]; args[n++] = TREF_NIL; ngpr--; + if (ngpr) { + arg1 = &args[n]; args[n++] = TREF_NIL; ngpr--; + } + } + } else { + if (arg0) { *arg0 = tr; arg0 = NULL; n--; continue; } + if (arg1) { *arg1 = tr; arg1 = NULL; n--; continue; } + if (ngpr) ngpr--; + } + } +#else + if (!ctype_isfp(d->info) && ngpr) { + if (tref_typerange(tr, IRT_I64, IRT_U64)) { + /* No reordering for other x86 ABIs. Simply add alignment args. */ + do { args[n++] = TREF_NIL; } while (--ngpr); + } else { + ngpr--; + } + } +#endif +#endif + args[n] = tr; + } + tr = args[0]; + for (i = 1; i < n; i++) + tr = emitir(IRT(IR_CARG, IRT_NIL), tr, args[i]); + return tr; +} + +/* Create a snapshot for the caller, simulating a 'false' return value. */ +static void crec_snap_caller(jit_State *J) +{ + lua_State *L = J->L; + TValue *base = L->base, *top = L->top; + const BCIns *pc = J->pc; + TRef ftr = J->base[-1-LJ_FR2]; + ptrdiff_t delta; + if (!frame_islua(base-1) || J->framedepth <= 0) + lj_trace_err(J, LJ_TRERR_NYICALL); + J->pc = frame_pc(base-1); delta = 1+LJ_FR2+bc_a(J->pc[-1]); + L->top = base; L->base = base - delta; + J->base[-1-LJ_FR2] = TREF_FALSE; + J->base -= delta; J->baseslot -= (BCReg)delta; + J->maxslot = (BCReg)delta-LJ_FR2; J->framedepth--; + lj_snap_add(J); + L->base = base; L->top = top; + J->framedepth++; J->maxslot = 1; + J->base += delta; J->baseslot += (BCReg)delta; + J->base[-1-LJ_FR2] = ftr; J->pc = pc; +} + +/* Record function call. */ +static int crec_call(jit_State *J, RecordFFData *rd, GCcdata *cd) +{ + CTState *cts = ctype_ctsG(J2G(J)); + CType *ct = ctype_raw(cts, cd->ctypeid); + IRType tp = IRT_PTR; + if (ctype_isptr(ct->info)) { + tp = (LJ_64 && ct->size == 8) ? IRT_P64 : IRT_P32; + ct = ctype_rawchild(cts, ct); + } + if (ctype_isfunc(ct->info)) { + TRef func = emitir(IRT(IR_FLOAD, tp), J->base[0], IRFL_CDATA_PTR); + CType *ctr = ctype_rawchild(cts, ct); + IRType t = crec_ct2irt(cts, ctr); + TRef tr; + TValue tv; + /* Check for blacklisted C functions that might call a callback. */ + setlightudV(&tv, + cdata_getptr(cdataptr(cd), (LJ_64 && tp == IRT_P64) ? 8 : 4)); + if (tvistrue(lj_tab_get(J->L, cts->miscmap, &tv))) + lj_trace_err(J, LJ_TRERR_BLACKL); + if (ctype_isvoid(ctr->info)) { + t = IRT_NIL; + rd->nres = 0; + } else if (!(ctype_isnum(ctr->info) || ctype_isptr(ctr->info) || + ctype_isenum(ctr->info)) || t == IRT_CDATA) { + lj_trace_err(J, LJ_TRERR_NYICALL); + } + if ((ct->info & CTF_VARARG) +#if LJ_TARGET_X86 + || ctype_cconv(ct->info) != CTCC_CDECL +#endif + ) + func = emitir(IRT(IR_CARG, IRT_NIL), func, + lj_ir_kint(J, ctype_typeid(cts, ct))); + tr = emitir(IRT(IR_CALLXS, t), crec_call_args(J, rd, cts, ct), func); + if (ctype_isbool(ctr->info)) { + if (frame_islua(J->L->base-1) && bc_b(frame_pc(J->L->base-1)[-1]) == 1) { + /* Don't check result if ignored. */ + tr = TREF_NIL; + } else { + crec_snap_caller(J); +#if LJ_TARGET_X86ORX64 + /* Note: only the x86/x64 backend supports U8 and only for EQ(tr, 0). */ + lj_ir_set(J, IRTG(IR_NE, IRT_U8), tr, lj_ir_kint(J, 0)); +#else + lj_ir_set(J, IRTGI(IR_NE), tr, lj_ir_kint(J, 0)); +#endif + J->postproc = LJ_POST_FIXGUARDSNAP; + tr = TREF_TRUE; + } + } else if (t == IRT_PTR || (LJ_64 && t == IRT_P32) || + t == IRT_I64 || t == IRT_U64 || ctype_isenum(ctr->info)) { + TRef trid = lj_ir_kint(J, ctype_cid(ct->info)); + tr = emitir(IRTG(IR_CNEWI, IRT_CDATA), trid, tr); + if (t == IRT_I64 || t == IRT_U64) lj_needsplit(J); + } else if (t == IRT_FLOAT || t == IRT_U32) { + tr = emitconv(tr, IRT_NUM, t, 0); + } else if (t == IRT_I8 || t == IRT_I16) { + tr = emitconv(tr, IRT_INT, t, IRCONV_SEXT); + } else if (t == IRT_U8 || t == IRT_U16) { + tr = emitconv(tr, IRT_INT, t, 0); + } + J->base[0] = tr; + J->needsnap = 1; + return 1; + } + return 0; +} + +void LJ_FASTCALL recff_cdata_call(jit_State *J, RecordFFData *rd) +{ + CTState *cts = ctype_ctsG(J2G(J)); + GCcdata *cd = argv2cdata(J, J->base[0], &rd->argv[0]); + CTypeID id = cd->ctypeid; + CType *ct; + cTValue *tv; + MMS mm = MM_call; + if (id == CTID_CTYPEID) { + id = crec_constructor(J, cd, J->base[0]); + mm = MM_new; + } else if (crec_call(J, rd, cd)) { + return; + } + /* Record ctype __call/__new metamethod. */ + ct = ctype_raw(cts, id); + tv = lj_ctype_meta(cts, ctype_isptr(ct->info) ? ctype_cid(ct->info) : id, mm); + if (tv) { + if (tvisfunc(tv)) { + crec_tailcall(J, rd, tv); + return; + } + } else if (mm == MM_new) { + crec_alloc(J, rd, id); + return; + } + /* No metamethod or NYI: non-function metamethods. */ + lj_trace_err(J, LJ_TRERR_BADTYPE); +} + +static TRef crec_arith_int64(jit_State *J, TRef *sp, CType **s, MMS mm) +{ + if (sp[0] && sp[1] && ctype_isnum(s[0]->info) && ctype_isnum(s[1]->info)) { + IRType dt; + CTypeID id; + TRef tr; + MSize i; + IROp op; + lj_needsplit(J); + if (((s[0]->info & CTF_UNSIGNED) && s[0]->size == 8) || + ((s[1]->info & CTF_UNSIGNED) && s[1]->size == 8)) { + dt = IRT_U64; id = CTID_UINT64; + } else { + dt = IRT_I64; id = CTID_INT64; + if (mm < MM_add && + !((s[0]->info | s[1]->info) & CTF_FP) && + s[0]->size == 4 && s[1]->size == 4) { /* Try to narrow comparison. */ + if (!((s[0]->info ^ s[1]->info) & CTF_UNSIGNED) || + (tref_isk(sp[1]) && IR(tref_ref(sp[1]))->i >= 0)) { + dt = (s[0]->info & CTF_UNSIGNED) ? IRT_U32 : IRT_INT; + goto comp; + } else if (tref_isk(sp[0]) && IR(tref_ref(sp[0]))->i >= 0) { + dt = (s[1]->info & CTF_UNSIGNED) ? IRT_U32 : IRT_INT; + goto comp; + } + } + } + for (i = 0; i < 2; i++) { + IRType st = tref_type(sp[i]); + if (st == IRT_NUM || st == IRT_FLOAT) + sp[i] = emitconv(sp[i], dt, st, IRCONV_ANY); + else if (!(st == IRT_I64 || st == IRT_U64)) + sp[i] = emitconv(sp[i], dt, IRT_INT, + (s[i]->info & CTF_UNSIGNED) ? 0 : IRCONV_SEXT); + } + if (mm < MM_add) { + comp: + /* Assume true comparison. Fixup and emit pending guard later. */ + if (mm == MM_eq) { + op = IR_EQ; + } else { + op = mm == MM_lt ? IR_LT : IR_LE; + if (dt == IRT_U32 || dt == IRT_U64) + op += (IR_ULT-IR_LT); + } + lj_ir_set(J, IRTG(op, dt), sp[0], sp[1]); + J->postproc = LJ_POST_FIXGUARD; + return TREF_TRUE; + } else { + tr = emitir(IRT(mm+(int)IR_ADD-(int)MM_add, dt), sp[0], sp[1]); + } + return emitir(IRTG(IR_CNEWI, IRT_CDATA), lj_ir_kint(J, id), tr); + } + return 0; +} + +static TRef crec_arith_ptr(jit_State *J, TRef *sp, CType **s, MMS mm) +{ + CTState *cts = ctype_ctsG(J2G(J)); + CType *ctp = s[0]; + if (!(sp[0] && sp[1])) return 0; + if (ctype_isptr(ctp->info) || ctype_isrefarray(ctp->info)) { + if ((mm == MM_sub || mm == MM_eq || mm == MM_lt || mm == MM_le) && + (ctype_isptr(s[1]->info) || ctype_isrefarray(s[1]->info))) { + if (mm == MM_sub) { /* Pointer difference. */ + TRef tr; + CTSize sz = lj_ctype_size(cts, ctype_cid(ctp->info)); + if (sz == 0 || (sz & (sz-1)) != 0) + return 0; /* NYI: integer division. */ + tr = emitir(IRT(IR_SUB, IRT_INTP), sp[0], sp[1]); + tr = emitir(IRT(IR_BSAR, IRT_INTP), tr, lj_ir_kint(J, lj_fls(sz))); +#if LJ_64 + tr = emitconv(tr, IRT_NUM, IRT_INTP, 0); +#endif + return tr; + } else { /* Pointer comparison (unsigned). */ + /* Assume true comparison. Fixup and emit pending guard later. */ + IROp op = mm == MM_eq ? IR_EQ : mm == MM_lt ? IR_ULT : IR_ULE; + lj_ir_set(J, IRTG(op, IRT_PTR), sp[0], sp[1]); + J->postproc = LJ_POST_FIXGUARD; + return TREF_TRUE; + } + } + if (!((mm == MM_add || mm == MM_sub) && ctype_isnum(s[1]->info))) + return 0; + } else if (mm == MM_add && ctype_isnum(ctp->info) && + (ctype_isptr(s[1]->info) || ctype_isrefarray(s[1]->info))) { + TRef tr = sp[0]; sp[0] = sp[1]; sp[1] = tr; /* Swap pointer and index. */ + ctp = s[1]; + } else { + return 0; + } + { + TRef tr = sp[1]; + IRType t = tref_type(tr); + CTSize sz = lj_ctype_size(cts, ctype_cid(ctp->info)); + CTypeID id; +#if LJ_64 + if (t == IRT_NUM || t == IRT_FLOAT) + tr = emitconv(tr, IRT_INTP, t, IRCONV_ANY); + else if (!(t == IRT_I64 || t == IRT_U64)) + tr = emitconv(tr, IRT_INTP, IRT_INT, + ((t - IRT_I8) & 1) ? 0 : IRCONV_SEXT); +#else + if (!tref_typerange(sp[1], IRT_I8, IRT_U32)) { + tr = emitconv(tr, IRT_INTP, t, + (t == IRT_NUM || t == IRT_FLOAT) ? IRCONV_ANY : 0); + } +#endif + tr = emitir(IRT(IR_MUL, IRT_INTP), tr, lj_ir_kintp(J, sz)); + tr = emitir(IRT(mm+(int)IR_ADD-(int)MM_add, IRT_PTR), sp[0], tr); + id = lj_ctype_intern(cts, CTINFO(CT_PTR, CTALIGN_PTR|ctype_cid(ctp->info)), + CTSIZE_PTR); + return emitir(IRTG(IR_CNEWI, IRT_CDATA), lj_ir_kint(J, id), tr); + } +} + +/* Record ctype arithmetic metamethods. */ +static TRef crec_arith_meta(jit_State *J, TRef *sp, CType **s, CTState *cts, + RecordFFData *rd) +{ + cTValue *tv = NULL; + if (J->base[0]) { + if (tviscdata(&rd->argv[0])) { + CTypeID id = argv2cdata(J, J->base[0], &rd->argv[0])->ctypeid; + CType *ct = ctype_raw(cts, id); + if (ctype_isptr(ct->info)) id = ctype_cid(ct->info); + tv = lj_ctype_meta(cts, id, (MMS)rd->data); + } + if (!tv && J->base[1] && tviscdata(&rd->argv[1])) { + CTypeID id = argv2cdata(J, J->base[1], &rd->argv[1])->ctypeid; + CType *ct = ctype_raw(cts, id); + if (ctype_isptr(ct->info)) id = ctype_cid(ct->info); + tv = lj_ctype_meta(cts, id, (MMS)rd->data); + } + } + if (tv) { + if (tvisfunc(tv)) { + crec_tailcall(J, rd, tv); + return 0; + } /* NYI: non-function metamethods. */ + } else if ((MMS)rd->data == MM_eq) { /* Fallback cdata pointer comparison. */ + if (sp[0] && sp[1] && ctype_isnum(s[0]->info) == ctype_isnum(s[1]->info)) { + /* Assume true comparison. Fixup and emit pending guard later. */ + lj_ir_set(J, IRTG(IR_EQ, IRT_PTR), sp[0], sp[1]); + J->postproc = LJ_POST_FIXGUARD; + return TREF_TRUE; + } else { + return TREF_FALSE; + } + } + lj_trace_err(J, LJ_TRERR_BADTYPE); + return 0; +} + +void LJ_FASTCALL recff_cdata_arith(jit_State *J, RecordFFData *rd) +{ + CTState *cts = ctype_ctsG(J2G(J)); + TRef sp[2]; + CType *s[2]; + MSize i; + for (i = 0; i < 2; i++) { + TRef tr = J->base[i]; + CType *ct = ctype_get(cts, CTID_DOUBLE); + if (!tr) { + lj_trace_err(J, LJ_TRERR_BADTYPE); + } else if (tref_iscdata(tr)) { + CTypeID id = argv2cdata(J, tr, &rd->argv[i])->ctypeid; + IRType t; + ct = ctype_raw(cts, id); + t = crec_ct2irt(cts, ct); + if (ctype_isptr(ct->info)) { /* Resolve pointer or reference. */ + tr = emitir(IRT(IR_FLOAD, t), tr, IRFL_CDATA_PTR); + if (ctype_isref(ct->info)) { + ct = ctype_rawchild(cts, ct); + t = crec_ct2irt(cts, ct); + } + } else if (t == IRT_I64 || t == IRT_U64) { + tr = emitir(IRT(IR_FLOAD, t), tr, IRFL_CDATA_INT64); + lj_needsplit(J); + goto ok; + } else if (t == IRT_INT || t == IRT_U32) { + tr = emitir(IRT(IR_FLOAD, t), tr, IRFL_CDATA_INT); + if (ctype_isenum(ct->info)) ct = ctype_child(cts, ct); + goto ok; + } else if (ctype_isfunc(ct->info)) { + tr = emitir(IRT(IR_FLOAD, IRT_PTR), tr, IRFL_CDATA_PTR); + ct = ctype_get(cts, + lj_ctype_intern(cts, CTINFO(CT_PTR, CTALIGN_PTR|id), CTSIZE_PTR)); + goto ok; + } else { + tr = emitir(IRT(IR_ADD, IRT_PTR), tr, lj_ir_kintp(J, sizeof(GCcdata))); + } + if (ctype_isenum(ct->info)) ct = ctype_child(cts, ct); + if (ctype_isnum(ct->info)) { + if (t == IRT_CDATA) { + tr = 0; + } else { + if (t == IRT_I64 || t == IRT_U64) lj_needsplit(J); + tr = emitir(IRT(IR_XLOAD, t), tr, 0); + } + } + } else if (tref_isnil(tr)) { + tr = lj_ir_kptr(J, NULL); + ct = ctype_get(cts, CTID_P_VOID); + } else if (tref_isinteger(tr)) { + ct = ctype_get(cts, CTID_INT32); + } else if (tref_isstr(tr)) { + TRef tr2 = J->base[1-i]; + CTypeID id = argv2cdata(J, tr2, &rd->argv[1-i])->ctypeid; + ct = ctype_raw(cts, id); + if (ctype_isenum(ct->info)) { /* Match string against enum constant. */ + GCstr *str = strV(&rd->argv[i]); + CTSize ofs; + CType *cct = lj_ctype_getfield(cts, ct, str, &ofs); + if (cct && ctype_isconstval(cct->info)) { + /* Specialize to the name of the enum constant. */ + emitir(IRTG(IR_EQ, IRT_STR), tr, lj_ir_kstr(J, str)); + ct = ctype_child(cts, cct); + tr = lj_ir_kint(J, (int32_t)ofs); + } else { /* Interpreter will throw or return false. */ + ct = ctype_get(cts, CTID_P_VOID); + } + } else if (ctype_isptr(ct->info)) { + tr = emitir(IRT(IR_ADD, IRT_PTR), tr, lj_ir_kintp(J, sizeof(GCstr))); + } else { + ct = ctype_get(cts, CTID_P_VOID); + } + } else if (!tref_isnum(tr)) { + tr = 0; + ct = ctype_get(cts, CTID_P_VOID); + } + ok: + s[i] = ct; + sp[i] = tr; + } + { + TRef tr; + if (!(tr = crec_arith_int64(J, sp, s, (MMS)rd->data)) && + !(tr = crec_arith_ptr(J, sp, s, (MMS)rd->data)) && + !(tr = crec_arith_meta(J, sp, s, cts, rd))) + return; + J->base[0] = tr; + /* Fixup cdata comparisons, too. Avoids some cdata escapes. */ + if (J->postproc == LJ_POST_FIXGUARD && frame_iscont(J->L->base-1) && + !irt_isguard(J->guardemit)) { + const BCIns *pc = frame_contpc(J->L->base-1) - 1; + if (bc_op(*pc) <= BC_ISNEP) { + J2G(J)->tmptv.u64 = (uint64_t)(uintptr_t)pc; + J->postproc = LJ_POST_FIXCOMP; + } + } + } +} + +/* -- C library namespace metamethods ------------------------------------- */ + +void LJ_FASTCALL recff_clib_index(jit_State *J, RecordFFData *rd) +{ + CTState *cts = ctype_ctsG(J2G(J)); + if (tref_isudata(J->base[0]) && tref_isstr(J->base[1]) && + udataV(&rd->argv[0])->udtype == UDTYPE_FFI_CLIB) { + CLibrary *cl = (CLibrary *)uddata(udataV(&rd->argv[0])); + GCstr *name = strV(&rd->argv[1]); + CType *ct; + CTypeID id = lj_ctype_getname(cts, &ct, name, CLNS_INDEX); + cTValue *tv = lj_tab_getstr(cl->cache, name); + rd->nres = rd->data; + if (id && tv && !tvisnil(tv)) { + /* Specialize to the symbol name and make the result a constant. */ + emitir(IRTG(IR_EQ, IRT_STR), J->base[1], lj_ir_kstr(J, name)); + if (ctype_isconstval(ct->info)) { + if (ct->size >= 0x80000000u && + (ctype_child(cts, ct)->info & CTF_UNSIGNED)) + J->base[0] = lj_ir_knum(J, (lua_Number)(uint32_t)ct->size); + else + J->base[0] = lj_ir_kint(J, (int32_t)ct->size); + } else if (ctype_isextern(ct->info)) { + CTypeID sid = ctype_cid(ct->info); + void *sp = *(void **)cdataptr(cdataV(tv)); + TRef ptr; + ct = ctype_raw(cts, sid); + if (LJ_64 && !checkptr32(sp)) + ptr = lj_ir_kintp(J, (uintptr_t)sp); + else + ptr = lj_ir_kptr(J, sp); + if (rd->data) { + J->base[0] = crec_tv_ct(J, ct, sid, ptr); + } else { + J->needsnap = 1; + crec_ct_tv(J, ct, ptr, J->base[2], &rd->argv[2]); + } + } else { + J->base[0] = lj_ir_kgc(J, obj2gco(cdataV(tv)), IRT_CDATA); + } + } else { + lj_trace_err(J, LJ_TRERR_NOCACHE); + } + } /* else: interpreter will throw. */ +} + +/* -- FFI library functions ----------------------------------------------- */ + +static TRef crec_toint(jit_State *J, CTState *cts, TRef sp, TValue *sval) +{ + return crec_ct_tv(J, ctype_get(cts, CTID_INT32), 0, sp, sval); +} + +void LJ_FASTCALL recff_ffi_new(jit_State *J, RecordFFData *rd) +{ + crec_alloc(J, rd, argv2ctype(J, J->base[0], &rd->argv[0])); +} + +void LJ_FASTCALL recff_ffi_errno(jit_State *J, RecordFFData *rd) +{ + UNUSED(rd); + if (J->base[0]) + lj_trace_err(J, LJ_TRERR_NYICALL); + J->base[0] = lj_ir_call(J, IRCALL_lj_vm_errno); +} + +void LJ_FASTCALL recff_ffi_string(jit_State *J, RecordFFData *rd) +{ + CTState *cts = ctype_ctsG(J2G(J)); + TRef tr = J->base[0]; + if (tr) { + TRef trlen = J->base[1]; + if (!tref_isnil(trlen)) { + trlen = crec_toint(J, cts, trlen, &rd->argv[1]); + tr = crec_ct_tv(J, ctype_get(cts, CTID_P_CVOID), 0, tr, &rd->argv[0]); + } else { + tr = crec_ct_tv(J, ctype_get(cts, CTID_P_CCHAR), 0, tr, &rd->argv[0]); + trlen = lj_ir_call(J, IRCALL_strlen, tr); + } + J->base[0] = emitir(IRT(IR_XSNEW, IRT_STR), tr, trlen); + } /* else: interpreter will throw. */ +} + +void LJ_FASTCALL recff_ffi_copy(jit_State *J, RecordFFData *rd) +{ + CTState *cts = ctype_ctsG(J2G(J)); + TRef trdst = J->base[0], trsrc = J->base[1], trlen = J->base[2]; + if (trdst && trsrc && (trlen || tref_isstr(trsrc))) { + trdst = crec_ct_tv(J, ctype_get(cts, CTID_P_VOID), 0, trdst, &rd->argv[0]); + trsrc = crec_ct_tv(J, ctype_get(cts, CTID_P_CVOID), 0, trsrc, &rd->argv[1]); + if (trlen) { + trlen = crec_toint(J, cts, trlen, &rd->argv[2]); + } else { + trlen = emitir(IRTI(IR_FLOAD), J->base[1], IRFL_STR_LEN); + trlen = emitir(IRTI(IR_ADD), trlen, lj_ir_kint(J, 1)); + } + rd->nres = 0; + crec_copy(J, trdst, trsrc, trlen, NULL); + } /* else: interpreter will throw. */ +} + +void LJ_FASTCALL recff_ffi_fill(jit_State *J, RecordFFData *rd) +{ + CTState *cts = ctype_ctsG(J2G(J)); + TRef trdst = J->base[0], trlen = J->base[1], trfill = J->base[2]; + if (trdst && trlen) { + CTSize step = 1; + if (tviscdata(&rd->argv[0])) { /* Get alignment of original destination. */ + CTSize sz; + CType *ct = ctype_raw(cts, cdataV(&rd->argv[0])->ctypeid); + if (ctype_isptr(ct->info)) + ct = ctype_rawchild(cts, ct); + step = (1u<argv[0]); + trlen = crec_toint(J, cts, trlen, &rd->argv[1]); + if (trfill) + trfill = crec_toint(J, cts, trfill, &rd->argv[2]); + else + trfill = lj_ir_kint(J, 0); + rd->nres = 0; + crec_fill(J, trdst, trlen, trfill, step); + } /* else: interpreter will throw. */ +} + +void LJ_FASTCALL recff_ffi_typeof(jit_State *J, RecordFFData *rd) +{ + if (tref_iscdata(J->base[0])) { + TRef trid = lj_ir_kint(J, argv2ctype(J, J->base[0], &rd->argv[0])); + J->base[0] = emitir(IRTG(IR_CNEWI, IRT_CDATA), + lj_ir_kint(J, CTID_CTYPEID), trid); + } else { + setfuncV(J->L, &J->errinfo, J->fn); + lj_trace_err_info(J, LJ_TRERR_NYIFFU); + } +} + +void LJ_FASTCALL recff_ffi_istype(jit_State *J, RecordFFData *rd) +{ + argv2ctype(J, J->base[0], &rd->argv[0]); + if (tref_iscdata(J->base[1])) { + argv2ctype(J, J->base[1], &rd->argv[1]); + J->postproc = LJ_POST_FIXBOOL; + J->base[0] = TREF_TRUE; + } else { + J->base[0] = TREF_FALSE; + } +} + +void LJ_FASTCALL recff_ffi_abi(jit_State *J, RecordFFData *rd) +{ + if (tref_isstr(J->base[0])) { + /* Specialize to the ABI string to make the boolean result a constant. */ + emitir(IRTG(IR_EQ, IRT_STR), J->base[0], lj_ir_kstr(J, strV(&rd->argv[0]))); + J->postproc = LJ_POST_FIXBOOL; + J->base[0] = TREF_TRUE; + } else { + lj_trace_err(J, LJ_TRERR_BADTYPE); + } +} + +/* Record ffi.sizeof(), ffi.alignof(), ffi.offsetof(). */ +void LJ_FASTCALL recff_ffi_xof(jit_State *J, RecordFFData *rd) +{ + CTypeID id = argv2ctype(J, J->base[0], &rd->argv[0]); + if (rd->data == FF_ffi_sizeof) { + CType *ct = lj_ctype_rawref(ctype_ctsG(J2G(J)), id); + if (ctype_isvltype(ct->info)) + lj_trace_err(J, LJ_TRERR_BADTYPE); + } else if (rd->data == FF_ffi_offsetof) { /* Specialize to the field name. */ + if (!tref_isstr(J->base[1])) + lj_trace_err(J, LJ_TRERR_BADTYPE); + emitir(IRTG(IR_EQ, IRT_STR), J->base[1], lj_ir_kstr(J, strV(&rd->argv[1]))); + rd->nres = 3; /* Just in case. */ + } + J->postproc = LJ_POST_FIXCONST; + J->base[0] = J->base[1] = J->base[2] = TREF_NIL; +} + +void LJ_FASTCALL recff_ffi_gc(jit_State *J, RecordFFData *rd) +{ + argv2cdata(J, J->base[0], &rd->argv[0]); + if (!J->base[1]) + lj_trace_err(J, LJ_TRERR_BADTYPE); + crec_finalizer(J, J->base[0], J->base[1], &rd->argv[1]); +} + +/* -- 64 bit bit.* library functions -------------------------------------- */ + +/* Determine bit operation type from argument type. */ +static CTypeID crec_bit64_type(CTState *cts, cTValue *tv) +{ + if (tviscdata(tv)) { + CType *ct = lj_ctype_rawref(cts, cdataV(tv)->ctypeid); + if (ctype_isenum(ct->info)) ct = ctype_child(cts, ct); + if ((ct->info & (CTMASK_NUM|CTF_BOOL|CTF_FP|CTF_UNSIGNED)) == + CTINFO(CT_NUM, CTF_UNSIGNED) && ct->size == 8) + return CTID_UINT64; /* Use uint64_t, since it has the highest rank. */ + return CTID_INT64; /* Otherwise use int64_t. */ + } + return 0; /* Use regular 32 bit ops. */ +} + +void LJ_FASTCALL recff_bit64_tobit(jit_State *J, RecordFFData *rd) +{ + CTState *cts = ctype_ctsG(J2G(J)); + TRef tr = crec_ct_tv(J, ctype_get(cts, CTID_INT64), 0, + J->base[0], &rd->argv[0]); + if (!tref_isinteger(tr)) + tr = emitconv(tr, IRT_INT, tref_type(tr), 0); + J->base[0] = tr; +} + +int LJ_FASTCALL recff_bit64_unary(jit_State *J, RecordFFData *rd) +{ + CTState *cts = ctype_ctsG(J2G(J)); + CTypeID id = crec_bit64_type(cts, &rd->argv[0]); + if (id) { + TRef tr = crec_ct_tv(J, ctype_get(cts, id), 0, J->base[0], &rd->argv[0]); + tr = emitir(IRT(rd->data, id-CTID_INT64+IRT_I64), tr, 0); + J->base[0] = emitir(IRTG(IR_CNEWI, IRT_CDATA), lj_ir_kint(J, id), tr); + return 1; + } + return 0; +} + +int LJ_FASTCALL recff_bit64_nary(jit_State *J, RecordFFData *rd) +{ + CTState *cts = ctype_ctsG(J2G(J)); + CTypeID id = 0; + MSize i; + for (i = 0; J->base[i] != 0; i++) { + CTypeID aid = crec_bit64_type(cts, &rd->argv[i]); + if (id < aid) id = aid; /* Determine highest type rank of all arguments. */ + } + if (id) { + CType *ct = ctype_get(cts, id); + uint32_t ot = IRT(rd->data, id-CTID_INT64+IRT_I64); + TRef tr = crec_ct_tv(J, ct, 0, J->base[0], &rd->argv[0]); + for (i = 1; J->base[i] != 0; i++) { + TRef tr2 = crec_ct_tv(J, ct, 0, J->base[i], &rd->argv[i]); + tr = emitir(ot, tr, tr2); + } + J->base[0] = emitir(IRTG(IR_CNEWI, IRT_CDATA), lj_ir_kint(J, id), tr); + return 1; + } + return 0; +} + +int LJ_FASTCALL recff_bit64_shift(jit_State *J, RecordFFData *rd) +{ + CTState *cts = ctype_ctsG(J2G(J)); + CTypeID id; + TRef tsh = 0; + if (J->base[0] && tref_iscdata(J->base[1])) { + tsh = crec_ct_tv(J, ctype_get(cts, CTID_INT64), 0, + J->base[1], &rd->argv[1]); + if (!tref_isinteger(tsh)) + tsh = emitconv(tsh, IRT_INT, tref_type(tsh), 0); + J->base[1] = tsh; + } + id = crec_bit64_type(cts, &rd->argv[0]); + if (id) { + TRef tr = crec_ct_tv(J, ctype_get(cts, id), 0, J->base[0], &rd->argv[0]); + uint32_t op = rd->data; + if (!tsh) tsh = lj_opt_narrow_tobit(J, J->base[1]); + if (!(op < IR_BROL ? LJ_TARGET_MASKSHIFT : LJ_TARGET_MASKROT) && + !tref_isk(tsh)) + tsh = emitir(IRTI(IR_BAND), tsh, lj_ir_kint(J, 63)); +#ifdef LJ_TARGET_UNIFYROT + if (op == (LJ_TARGET_UNIFYROT == 1 ? IR_BROR : IR_BROL)) { + op = LJ_TARGET_UNIFYROT == 1 ? IR_BROL : IR_BROR; + tsh = emitir(IRTI(IR_NEG), tsh, tsh); + } +#endif + tr = emitir(IRT(op, id-CTID_INT64+IRT_I64), tr, tsh); + J->base[0] = emitir(IRTG(IR_CNEWI, IRT_CDATA), lj_ir_kint(J, id), tr); + return 1; + } + return 0; +} + +TRef recff_bit64_tohex(jit_State *J, RecordFFData *rd, TRef hdr) +{ + CTState *cts = ctype_ctsG(J2G(J)); + CTypeID id = crec_bit64_type(cts, &rd->argv[0]); + TRef tr, trsf = J->base[1]; + SFormat sf = (STRFMT_UINT|STRFMT_T_HEX); + int32_t n; + if (trsf) { + CTypeID id2 = 0; + n = (int32_t)lj_carith_check64(J->L, 2, &id2); + if (id2) + trsf = crec_ct_tv(J, ctype_get(cts, CTID_INT32), 0, trsf, &rd->argv[1]); + else + trsf = lj_opt_narrow_tobit(J, trsf); + emitir(IRTGI(IR_EQ), trsf, lj_ir_kint(J, n)); /* Specialize to n. */ + } else { + n = id ? 16 : 8; + } + if (n < 0) { n = -n; sf |= STRFMT_F_UPPER; } + sf |= ((SFormat)((n+1)&255) << STRFMT_SH_PREC); + if (id) { + tr = crec_ct_tv(J, ctype_get(cts, id), 0, J->base[0], &rd->argv[0]); + if (n < 16) + tr = emitir(IRT(IR_BAND, IRT_U64), tr, + lj_ir_kint64(J, ((uint64_t)1 << 4*n)-1)); + } else { + tr = lj_opt_narrow_tobit(J, J->base[0]); + if (n < 8) + tr = emitir(IRTI(IR_BAND), tr, lj_ir_kint(J, (int32_t)((1u << 4*n)-1))); + tr = emitconv(tr, IRT_U64, IRT_INT, 0); /* No sign-extension. */ + lj_needsplit(J); + } + return lj_ir_call(J, IRCALL_lj_strfmt_putfxint, hdr, lj_ir_kint(J, sf), tr); +} + +/* -- Miscellaneous library functions ------------------------------------- */ + +void LJ_FASTCALL lj_crecord_tonumber(jit_State *J, RecordFFData *rd) +{ + CTState *cts = ctype_ctsG(J2G(J)); + CType *d, *ct = lj_ctype_rawref(cts, cdataV(&rd->argv[0])->ctypeid); + if (ctype_isenum(ct->info)) ct = ctype_child(cts, ct); + if (ctype_isnum(ct->info) || ctype_iscomplex(ct->info)) { + if (ctype_isinteger_or_bool(ct->info) && ct->size <= 4 && + !(ct->size == 4 && (ct->info & CTF_UNSIGNED))) + d = ctype_get(cts, CTID_INT32); + else + d = ctype_get(cts, CTID_DOUBLE); + J->base[0] = crec_ct_tv(J, d, 0, J->base[0], &rd->argv[0]); + } else { + /* Specialize to the ctype that couldn't be converted. */ + argv2cdata(J, J->base[0], &rd->argv[0]); + J->base[0] = TREF_NIL; + } +} + +#undef IR +#undef emitir +#undef emitconv + +#endif diff --git a/lib/LuaJIT/lj_crecord.h b/lib/LuaJIT/lj_crecord.h new file mode 100644 index 0000000..c165def --- /dev/null +++ b/lib/LuaJIT/lj_crecord.h @@ -0,0 +1,38 @@ +/* +** Trace recorder for C data operations. +** Copyright (C) 2005-2017 Mike Pall. See Copyright Notice in luajit.h +*/ + +#ifndef _LJ_CRECORD_H +#define _LJ_CRECORD_H + +#include "lj_obj.h" +#include "lj_jit.h" +#include "lj_ffrecord.h" + +#if LJ_HASJIT && LJ_HASFFI +LJ_FUNC void LJ_FASTCALL recff_cdata_index(jit_State *J, RecordFFData *rd); +LJ_FUNC void LJ_FASTCALL recff_cdata_call(jit_State *J, RecordFFData *rd); +LJ_FUNC void LJ_FASTCALL recff_cdata_arith(jit_State *J, RecordFFData *rd); +LJ_FUNC void LJ_FASTCALL recff_clib_index(jit_State *J, RecordFFData *rd); +LJ_FUNC void LJ_FASTCALL recff_ffi_new(jit_State *J, RecordFFData *rd); +LJ_FUNC void LJ_FASTCALL recff_ffi_errno(jit_State *J, RecordFFData *rd); +LJ_FUNC void LJ_FASTCALL recff_ffi_string(jit_State *J, RecordFFData *rd); +LJ_FUNC void LJ_FASTCALL recff_ffi_copy(jit_State *J, RecordFFData *rd); +LJ_FUNC void LJ_FASTCALL recff_ffi_fill(jit_State *J, RecordFFData *rd); +LJ_FUNC void LJ_FASTCALL recff_ffi_typeof(jit_State *J, RecordFFData *rd); +LJ_FUNC void LJ_FASTCALL recff_ffi_istype(jit_State *J, RecordFFData *rd); +LJ_FUNC void LJ_FASTCALL recff_ffi_abi(jit_State *J, RecordFFData *rd); +LJ_FUNC void LJ_FASTCALL recff_ffi_xof(jit_State *J, RecordFFData *rd); +LJ_FUNC void LJ_FASTCALL recff_ffi_gc(jit_State *J, RecordFFData *rd); + +LJ_FUNC void LJ_FASTCALL recff_bit64_tobit(jit_State *J, RecordFFData *rd); +LJ_FUNC int LJ_FASTCALL recff_bit64_unary(jit_State *J, RecordFFData *rd); +LJ_FUNC int LJ_FASTCALL recff_bit64_nary(jit_State *J, RecordFFData *rd); +LJ_FUNC int LJ_FASTCALL recff_bit64_shift(jit_State *J, RecordFFData *rd); +LJ_FUNC TRef recff_bit64_tohex(jit_State *J, RecordFFData *rd, TRef hdr); + +LJ_FUNC void LJ_FASTCALL lj_crecord_tonumber(jit_State *J, RecordFFData *rd); +#endif + +#endif diff --git a/lib/LuaJIT/lj_crecord.o b/lib/LuaJIT/lj_crecord.o new file mode 100644 index 0000000000000000000000000000000000000000..8e826204bceebf7594d2f4e65759fdcbef5bcfe3 GIT binary patch literal 38208 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