From 39fb273c41d7d855be399f5c9d7896963ac9555b Mon Sep 17 00:00:00 2001 From: GrieferAtWork Date: Mon, 18 Dec 2023 18:10:37 +0100 Subject: [PATCH] Further work on the deemon-to-x86 compiler --- .vs/dex/_hostasm.vcxproj | 1 + .vs/dex/_hostasm.vcxproj.filters | 1 + src/dex/_hostasm/assembler.c | 14 +- src/dex/_hostasm/common.c | 190 ++++++- src/dex/_hostasm/generator-arch.c | 740 +++++++++++++++++++++++---- src/dex/_hostasm/generator-common.c | 622 ++++++++++++++++++---- src/dex/_hostasm/host.h | 262 ++++++++++ src/dex/_hostasm/libgen86/gen.h | 21 +- src/dex/_hostasm/libgen86/register.h | 4 +- src/dex/_hostasm/libhostasm.h | 511 +++++++++--------- src/dex/_hostasm/loader.c | 23 +- src/dex/_hostasm/memstate.c | 80 ++- 12 files changed, 1955 insertions(+), 514 deletions(-) create mode 100644 src/dex/_hostasm/host.h diff --git a/.vs/dex/_hostasm.vcxproj b/.vs/dex/_hostasm.vcxproj index eede4ae85..5f7f831e1 100644 --- a/.vs/dex/_hostasm.vcxproj +++ b/.vs/dex/_hostasm.vcxproj @@ -35,6 +35,7 @@ + diff --git a/.vs/dex/_hostasm.vcxproj.filters b/.vs/dex/_hostasm.vcxproj.filters index 923712bca..b834f1d56 100644 --- a/.vs/dex/_hostasm.vcxproj.filters +++ b/.vs/dex/_hostasm.vcxproj.filters @@ -6,6 +6,7 @@ + libgen86 diff --git a/src/dex/_hostasm/assembler.c b/src/dex/_hostasm/assembler.c index b2b8d8e98..b18057264 100644 --- a/src/dex/_hostasm/assembler.c +++ b/src/dex/_hostasm/assembler.c @@ -83,7 +83,8 @@ find_not_compiled_block_with_most_defined_entry_points(struct Dee_function_assem result_index = i; } } - ASSERTF(result_missing_entries < self->fa_blockv[result_index]->bb_entries.jds_size, + ASSERTF(result_index == 0 || + result_missing_entries < self->fa_blockv[result_index]->bb_entries.jds_size, "There must be at least 1 present entry, else the block itself would " "be completely unreachable, which is impossible because the loader created " "it because it found a label that points to it."); @@ -136,17 +137,6 @@ Dee_function_assembler_compileblocks(struct Dee_function_assembler *__restrict s /* Save caller-provided arguments onto stack. */ #ifdef HOSTASM_X86_64 { -#ifdef HOSTASM_X86_64_MSABI -#define HOST_REGISTER_R_ARG0 HOST_REGISTER_RCX -#define HOST_REGISTER_R_ARG1 HOST_REGISTER_RDX -#define HOST_REGISTER_R_ARG2 HOST_REGISTER_R8 -#define HOST_REGISTER_R_ARG3 HOST_REGISTER_R9 -#elif defined(HOSTASM_X86_64_SYSVABI) -#define HOST_REGISTER_R_ARG0 HOST_REGISTER_RDI -#define HOST_REGISTER_R_ARG1 HOST_REGISTER_RSI -#define HOST_REGISTER_R_ARG2 HOST_REGISTER_RDX -#define HOST_REGISTER_R_ARG3 HOST_REGISTER_RCX -#endif /* ... */ PRIVATE Dee_host_register_t const truearg_regno[4] = { HOST_REGISTER_R_ARG0, HOST_REGISTER_R_ARG1, diff --git a/src/dex/_hostasm/common.c b/src/dex/_hostasm/common.c index c5e1cfaba..14647aafc 100644 --- a/src/dex/_hostasm/common.c +++ b/src/dex/_hostasm/common.c @@ -25,11 +25,18 @@ /**/ #ifdef CONFIG_HAVE_LIBHOSTASM +#include #include #include DECL_BEGIN +#ifndef NDEBUG +#define DBG_memset (void)memset +#else /* !NDEBUG */ +#define DBG_memset(dst, byte, n_bytes) (void)0 +#endif /* NDEBUG */ + INTERN NONNULL((1)) void DCALL Dee_memstate_destroy(struct Dee_memstate *__restrict self) { Dee_Free(self->ms_stackv); @@ -191,6 +198,7 @@ Dee_jump_descriptors_remove(struct Dee_jump_descriptors *__restrict self, +/* Destroy the given basic block `self'. */ INTERN NONNULL((1)) void DCALL Dee_basic_block_destroy(struct Dee_basic_block *__restrict self) { size_t i; @@ -205,6 +213,7 @@ Dee_basic_block_destroy(struct Dee_basic_block *__restrict self) { Dee_memstate_decref(self->bb_mem_start); if (self->bb_mem_end) Dee_memstate_decref(self->bb_mem_end); + Dee_Free(self->bb_host_relv); Dee_Free(self->bb_host_start); Dee_basic_block_free(self); } @@ -277,13 +286,7 @@ Dee_basic_block_splitat(struct Dee_basic_block *__restrict self, } /* Fill in remaining fields and adjust caller-given block bounds. */ - Dee_jump_descriptors_init(&result->bb_entries); - result->bb_next = NULL; - result->bb_mem_start = NULL; - result->bb_mem_end = NULL; - result->bb_host_start = NULL; - result->bb_host_end = NULL; - result->bb_host_free = 0; + Dee_basic_block_init_common(result); result->bb_deemon_start = addr; result->bb_deemon_end = self->bb_deemon_end; self->bb_deemon_end = addr; @@ -302,8 +305,8 @@ INTERN WUNUSED NONNULL((1)) int DCALL _Dee_basic_block_reqhost(struct Dee_basic_block *__restrict self, size_t num_bytes) { byte_t *new_blob; - size_t old_used = self->bb_host_end - self->bb_host_start; - size_t old_alloc = old_used + self->bb_host_free; + size_t old_used = (size_t)(self->bb_host_end - self->bb_host_start); + size_t old_alloc = (size_t)(self->bb_host_alend - self->bb_host_start); size_t min_alloc = old_used + num_bytes; size_t new_alloc = old_alloc << 1; if (new_alloc < min_alloc) @@ -317,21 +320,66 @@ _Dee_basic_block_reqhost(struct Dee_basic_block *__restrict self, } self->bb_host_start = new_blob; self->bb_host_end = new_blob + old_used; - self->bb_host_free = new_alloc - old_used; - ASSERT(self->bb_host_free >= num_bytes); + self->bb_host_alend = new_blob + new_alloc; + ASSERT(self->bb_host_alend >= self->bb_host_end); return 0; err: return -1; } +/* Allocate and return a new host relocation. The caller is responsible + * for filling in said relocation, and the returned pointer only remains + * valid until the next call to this function with the same `self'. + * @return: * : The (uninitialized) host relocation + * @return: NULL: Error */ +INTERN WUNUSED NONNULL((1)) struct Dee_host_reloc *DCALL +Dee_basic_block_newhostrel(struct Dee_basic_block *__restrict self) { + struct Dee_host_reloc *result; + ASSERT(self->bb_host_relc <= self->bb_host_rela); + if unlikely(self->bb_host_relc >= self->bb_host_rela) { + size_t min_alloc = self->bb_host_relc + 1; + size_t new_alloc = self->bb_host_rela * 2; + struct Dee_host_reloc *new_list; + if (new_alloc < 4) + new_alloc = 4; + if (new_alloc < min_alloc) + new_alloc = min_alloc; + new_list = (struct Dee_host_reloc *)Dee_TryReallocc(self->bb_host_relv, + new_alloc, + sizeof(struct Dee_host_reloc)); + if unlikely(!new_list) { + new_alloc = min_alloc; + new_list = (struct Dee_host_reloc *)Dee_Reallocc(self->bb_host_relv, + new_alloc, + sizeof(struct Dee_host_reloc)); + if unlikely(!new_list) + goto err; + } + self->bb_host_relv = new_list; + self->bb_host_rela = new_alloc; + } + result = &self->bb_host_relv[self->bb_host_relc]; + ++self->bb_host_relc; + DBG_memset(result, 0xcc, sizeof(*result)); + return result; +err: + return NULL; +} + + + + INTERN NONNULL((1)) void DCALL Dee_function_assembler_fini(struct Dee_function_assembler *__restrict self) { size_t i; for (i = 0; i < self->fa_blockc; ++i) Dee_basic_block_destroy(self->fa_blockv[i]); + for (i = 0; i < self->fa_except_exitc; ++i) + Dee_except_exitinfo_destroy(self->fa_except_exitv[i]); Dee_Free(self->fa_blockv); + Dee_Free(self->fa_except_exitv); } @@ -424,6 +472,126 @@ Dee_function_assembler_locateblock(struct Dee_function_assembler const *__restri return NULL; } +/* Lookup/allocate an exception-exit basic block that to clean up `state' + * and then return `NULL' to the caller of the generated function. + * @return: * : The basic block to which to jump in order to clean up `state'. + * @return: NULL: Error. */ +INTERN WUNUSED NONNULL((1, 2)) struct Dee_basic_block *DCALL +Dee_function_assembler_except_exit(struct Dee_function_assembler *__restrict self, + struct Dee_memstate *__restrict state) { + size_t lo, hi; + size_t infosize = Dee_except_exitinfo_cmp_sizeof(state->ms_host_cfa_offset); + struct Dee_basic_block *result; + struct Dee_except_exitinfo *info; +#ifdef Dee_Alloca + info = (struct Dee_except_exitinfo *)Dee_Alloca(infosize); +#else /* Dee_Alloca */ + info = Dee_except_exitinfo_alloc(infosize); + if unlikely(!info) + goto err; +#endif /* !Dee_Alloca */ + + /* Fill in info. */ + if unlikely(Dee_except_exitinfo_init(info, state)) + goto err_info_alloca; + + /* Check if we already have a block for this state. */ + lo = 0; + hi = self->fa_except_exitc; + while (lo < hi) { + int diff; + struct Dee_except_exitinfo *oldinfo; + size_t mid = (lo + hi) / 2; + oldinfo = self->fa_except_exitv[mid]; + ASSERT(oldinfo); + ASSERT(oldinfo->exi_block); + diff = Dee_except_exitinfo_cmp(info, oldinfo); + if (diff < 0) { + hi = mid; + } else if (diff > 0) { + lo = mid + 1; + } else { + /* Found it! */ +#ifndef Dee_Alloca + Dee_except_exitinfo_free(info); +#endif /* !Dee_Alloca */ + return oldinfo->exi_block; + } + } + ASSERT(lo == hi); + + /* Need to insert a new exit information descriptor. */ +#ifdef Dee_Alloca + { + struct Dee_except_exitinfo *heapinfo; + heapinfo = Dee_except_exitinfo_alloc(infosize); + if unlikely(!heapinfo) + goto err_info_alloca; + info = (struct Dee_except_exitinfo *)memcpy(heapinfo, info, infosize); + } +#endif /* Dee_Alloca */ + + /* Allocate a basic block for `info'. */ + result = Dee_basic_block_alloc(); + if unlikely(!result) + goto err_info; + info->exi_block = result; + Dee_basic_block_init_common(result); + Dee_jump_descriptors_init(&result->bb_exits); + result->bb_deemon_start = NULL; /* No deemon code here */ + result->bb_deemon_end = NULL; + result->bb_mem_start = state; /* Remember (some) state when jumping to this block. */ + Dee_memstate_incref(state); + + /* Make sure there is enough space in the sorted list of exit information descriptors. */ + ASSERT(self->fa_except_exitc <= self->fa_except_exita); + if unlikely(self->fa_except_exitc >= self->fa_except_exita) { + struct Dee_except_exitinfo **new_list; + size_t new_alloc = self->fa_except_exita * 2; + size_t min_alloc = self->fa_except_exitc + 1; + if (new_alloc < 8) + new_alloc = 8; + if (new_alloc < min_alloc) + new_alloc = min_alloc; + new_list = (struct Dee_except_exitinfo **)Dee_TryReallocc(self->fa_except_exitv, + new_alloc, + sizeof(struct Dee_except_exitinfo *)); + if unlikely(!new_list) { + new_alloc = min_alloc; + new_list = (struct Dee_except_exitinfo **)Dee_Reallocc(self->fa_except_exitv, + new_alloc, + sizeof(struct Dee_except_exitinfo *)); + if unlikely(!new_list) + goto err_info_result; + } + self->fa_except_exitv = new_list; + self->fa_except_exita = new_alloc; + } + + /* Insert the new info-descriptor at the appropriate location (`lo'). */ + memmoveupc(&self->fa_except_exitv[lo + 1], + &self->fa_except_exitv[lo], + self->fa_except_exitc - lo, + sizeof(struct Dee_except_exitinfo *)); + self->fa_except_exitv[lo] = info; + ++self->fa_except_exitc; + + return result; +err_info_result: + Dee_basic_block_free(result); +err_info: +#ifdef Dee_Alloca + Dee_except_exitinfo_free(info); +#endif /* Dee_Alloca */ +err_info_alloca: +#ifndef Dee_Alloca + Dee_except_exitinfo_free(info); +err: +#endif /* !Dee_Alloca */ + return NULL; +} + + /* Error throwing helper functions. */ diff --git a/src/dex/_hostasm/generator-arch.c b/src/dex/_hostasm/generator-arch.c index 1c9f5bd50..ed2d87649 100644 --- a/src/dex/_hostasm/generator-arch.c +++ b/src/dex/_hostasm/generator-arch.c @@ -27,7 +27,10 @@ #ifdef CONFIG_HAVE_LIBHOSTASM #include #include +#include +#include +#include #include #ifdef HOSTASM_X86 @@ -66,80 +69,278 @@ PRIVATE uint8_t const gen86_registers[HOST_REGISTER_COUNT] = { #endif /* ... */ }; -#define p_pc(block) (&(block)->bb_host_end) +#define p_pc(block) (&(block)->bb_host_end) +#define p_off(block) (uintptr_t)((block)->bb_host_end - (block)->bb_host_start) -/* Code generators. */ -INTERN WUNUSED NONNULL((1, 2)) int DCALL -Dee_function_generator_gincref(struct Dee_function_generator *__restrict self, - struct Dee_memloc *__restrict loc) { - /* TODO: - * #IF(loc->ml_where == MEMLOC_TYPE_CONST && FIT32) - * >> lock decP ml_value.ml_const->ob_refcnt> - * #ELIF(loc->ml_where == MEMLOC_TYPE_CONST && !FIT32) - * >> movabs $ml_value.ml_const->ob_refcnt>, %Pax - * >> lock incP (%Pax) - * #ELSE - * >> movP , %Pax - * >> lock incP ob_refcnt(%Pax) - * #ENDIF - */ - (void)self; - (void)loc; - return DeeError_NOTIMPLEMENTED(); +/* Object reference count incref/decref */ +INTERN WUNUSED NONNULL((1)) int DCALL +_Dee_basic_block_gincref_reg(struct Dee_basic_block *__restrict self, + Dee_host_register_t regno) { + if unlikely(Dee_basic_block_reqx86(self, 2)) + goto err; + gen86_lock(p_pc(self)); + gen86_incP_mod(p_pc(self), gen86_modrm_db, + offsetof(DeeObject, ob_refcnt), + gen86_registers[regno]); + return 0; +err: + return -1; } -INTERN WUNUSED NONNULL((1, 2)) int DCALL -Dee_function_generator_gdecref(struct Dee_function_generator *__restrict self, - struct Dee_memloc *__restrict loc) { - /* TODO: - * #IF(loc->ml_where == MEMLOC_TYPE_CONST && FIT32) - * >> lock decP ml_value.ml_const->ob_refcnt> - * #ELIF(loc->ml_where == MEMLOC_TYPE_CONST && !FIT32) - * >> movabs $ml_value.ml_const->ob_refcnt>, %Pax - * >> lock decP (%Pax) - * #ELIF_OPTION_1 - * >> - * >> movP , %Pax - * >> lock subP $1, ob_refcnt(%Pax) - * >> jnz 1f - * >> pushl %Pax - * >> call DeeObject_Destroy@4 - * >> 1: - * #ELSE - * >> movP , %Pax - * >> lock subP $1, ob_refcnt(%Pax) - * >> jnz 1f - * >> - * >> pushl %Pax - * >> call DeeObject_Destroy@4 - * >> - * >> 1: - * #ENDIF - */ - (void)self; - (void)loc; - return DeeError_NOTIMPLEMENTED(); +INTERN WUNUSED NONNULL((1)) int DCALL +_Dee_basic_block_gdecref_reg_nokill(struct Dee_basic_block *__restrict self, + Dee_host_register_t regno) { + if unlikely(Dee_basic_block_reqx86(self, 2)) + goto err; + gen86_lock(p_pc(self)); + gen86_decP_mod(p_pc(self), gen86_modrm_db, + offsetof(DeeObject, ob_refcnt), + gen86_registers[regno]); + return 0; +err: + return -1; } -INTERN WUNUSED NONNULL((1, 2)) int DCALL -Dee_function_generator_gxincref(struct Dee_function_generator *__restrict self, - struct Dee_memloc *__restrict loc) { - /* TODO: Like `Dee_function_generator_gincref()', but generate a NULL-check */ - (void)self; - (void)loc; - return DeeError_NOTIMPLEMENTED(); +#if !defined(CONFIG_NO_BADREFCNT_CHECKS) || defined(CONFIG_TRACE_REFCHANGES) +DFUNDEF NONNULL((1)) void (DCALL DeeObject_Destroy)(DeeObject *__restrict self); +#endif /* !CONFIG_NO_BADREFCNT_CHECKS || CONFIG_TRACE_REFCHANGES */ + +PRIVATE WUNUSED NONNULL((1, 2)) int DCALL +_Dee_function_generator_gdestroy_reg(struct Dee_function_generator *__restrict self, + struct Dee_basic_block *block, + Dee_host_register_t regno) { + struct Dee_memstate *state = self->fg_state; + BITSET(HOST_REGISTER_COUNT) used_regs; + Dee_host_register_t save_regno; + uint16_t i; + + /* Figure out all registers that are currently in use (because + * the call to `DeeObject_Destory()' might clobber them). Note + * that we push and then later pop those registers! */ + bzero(&used_regs, sizeof(used_regs)); + for (i = 0; i < state->ms_localc; ++i) { + struct Dee_memloc const *loc = &state->ms_localv[i]; + if (loc->ml_where == MEMLOC_TYPE_HREG) { + ASSERT(loc->ml_value.ml_hreg < HOST_REGISTER_COUNT); + BITSET_TURNON(&used_regs, loc->ml_value.ml_hreg); + } + } + for (i = 0; i < state->ms_stackc; ++i) { + struct Dee_memloc const *loc = &state->ms_stackv[i]; + if (loc->ml_where == MEMLOC_TYPE_HREG) { + ASSERT(loc->ml_value.ml_hreg < HOST_REGISTER_COUNT); + BITSET_TURNON(&used_regs, loc->ml_value.ml_hreg); + } + } + + /* The caller-given register never needs to be preserved. + * Since this is the path where the object gets destroyed, + * there isn't any reason to save its address somewhere! */ + BITSET_TURNOFF(&used_regs, regno); + + /* Save registers. */ + for (save_regno = 0; save_regno < HOST_REGISTER_COUNT; ++save_regno) { + if (!BITSET_GET(&used_regs, save_regno)) + continue; + if unlikely(Dee_basic_block_reqx86(block, 1)) + goto err; + gen86_pushP_r(p_pc(block), gen86_registers[save_regno]); + Dee_function_generator_gadjust_cfa_offset(self, HOST_SIZEOF_POINTER); + } + + /* Load `regno' as argument for the call to `DeeObject_Destroy()' */ +#ifdef HOST_REGISTER_R_ARG0 + if (regno != HOST_REGISTER_R_ARG0) { + if unlikely(Dee_basic_block_reqx86(block, 1)) + goto err; + gen86_movP_r_r(p_pc(block), + gen86_registers[regno], + gen86_registers[HOST_REGISTER_R_ARG0]); + } +#else /* HOST_REGISTER_R_ARG0 */ + if unlikely(Dee_basic_block_reqx86(block, 1)) + goto err; + gen86_pushP_r(p_pc(block), gen86_registers[regno]); + Dee_function_generator_gadjust_cfa_offset(self, HOST_SIZEOF_POINTER); +#endif /* !HOST_REGISTER_R_ARG0 */ + + /* Make the call to `DeeObject_Destroy()' */ + { + struct Dee_host_reloc *rel; +#ifdef HOSTASM_X86_64_MSABI + if unlikely(Dee_basic_block_reqx86(block, 3)) + goto err; + gen86_subP_imm_r(p_pc(block), 32, GEN86_R_PSP); +#else /* HOSTASM_X86_64_MSABI */ + if unlikely(Dee_basic_block_reqx86(block, 1)) + goto err; +#endif /* !HOSTASM_X86_64_MSABI */ + rel = Dee_basic_block_newhostrel(block); + if unlikely(!rel) + goto err; + gen86_calll_offset(p_pc(block), -4); + rel->hr_offset = p_off(block) - 4; + rel->hr_type = DEE_HOST_RELOC_PTREL32; + rel->hr_value.hr_pt = (void *)&DeeObject_Destroy; +#ifdef HOSTASM_X86_64_MSABI + gen86_addP_imm_r(p_pc(block), 32, GEN86_R_PSP); +#endif /* !HOSTASM_X86_64_MSABI */ + } + + /* Account for the fact that `DeeObject_Destroy()' pops its own arguments on i386 */ +#ifndef HOST_REGISTER_R_ARG0 + Dee_function_generator_gadjust_cfa_offset(self, -HOST_SIZEOF_POINTER); +#endif /* !HOST_REGISTER_R_ARG0 */ + + /* After calling an external function, usage registers may have gotten clobbered. */ + Dee_memstate_hregs_clear_usage(self->fg_state); + + /* Restore registers (in reverse order). */ + save_regno = HOST_REGISTER_COUNT; + while (save_regno) { + --save_regno; + if (!BITSET_GET(&used_regs, save_regno)) + continue; + if unlikely(Dee_basic_block_reqx86(block, 1)) + goto err; + gen86_popP_r(p_pc(block), gen86_registers[save_regno]); + Dee_function_generator_gadjust_cfa_offset(self, -HOST_SIZEOF_POINTER); + } + + return 0; +err: + return -1; } -INTERN WUNUSED NONNULL((1, 2)) int DCALL -Dee_function_generator_gxdecref(struct Dee_function_generator *__restrict self, - struct Dee_memloc *__restrict loc) { - /* TODO: Like `Dee_function_generator_gdecref()', but generate a NULL-check */ - (void)self; - (void)loc; - return DeeError_NOTIMPLEMENTED(); +INTERN WUNUSED NONNULL((1)) int DCALL +_Dee_function_generator_gdecref_reg(struct Dee_function_generator *__restrict self, + Dee_host_register_t regno) { + struct Dee_basic_block *cold_block = self->fg_assembler->fa_cold_block; + struct Dee_basic_block *block = self->fg_block; + if unlikely(Dee_basic_block_reqx86(block, 2)) + goto err; + gen86_lock(p_pc(block)); + gen86_decP_mod(p_pc(block), gen86_modrm_db, + offsetof(DeeObject, ob_refcnt), + gen86_registers[regno]); + + /* NOTE: decP sets FLAGS.Z=1 when the reference counter becomes `0'. */ + if (cold_block != NULL) { + struct Dee_host_reloc *enter_rel; + struct Dee_host_reloc *leave_rel; + int32_t delta; + + /* Generate code that jumps into the cold block when the reference counter became `0'. */ + delta = -4 + p_off(cold_block); + gen86_jccl_offset(p_pc(block), GEN86_CC_Z, delta); + enter_rel = Dee_basic_block_newhostrel(block); + if unlikely(!enter_rel) + goto err; + enter_rel->hr_offset = p_off(block); + enter_rel->hr_type = DEE_HOST_RELOC_BBREL32; + enter_rel->hr_value.hr_bb = cold_block; + + /* Generate the destroy-code *within* the cold block. */ + if unlikely(_Dee_function_generator_gdestroy_reg(self, cold_block, regno)) + goto err; + + /* Generate code to jump from the cold block back to the normal block. */ + if unlikely(Dee_basic_block_reqx86(cold_block, 1)) + goto err; + delta = -4 + p_off(block); + gen86_jmpl_offset(p_pc(cold_block), delta); + leave_rel = Dee_basic_block_newhostrel(cold_block); + if unlikely(!leave_rel) + goto err; + leave_rel->hr_offset = p_off(cold_block); + leave_rel->hr_type = DEE_HOST_RELOC_BBREL32; + leave_rel->hr_value.hr_bb = block; + } else { + uintptr_t jcc8_offset; + gen86_jcc8_offset(p_pc(block), GEN86_CC_NZ, -1); + jcc8_offset = p_off(block) - 1; + if unlikely(_Dee_function_generator_gdestroy_reg(self, block, regno)) + goto err; + /* Fill in the delta for the non-zero-jump above. */ + *(int8_t *)(block->bb_host_start + jcc8_offset) += (int8_t)(p_off(block) - jcc8_offset); + } + return 0; +err: + return -1; } +INTERN WUNUSED NONNULL((1)) int DCALL +_Dee_basic_block_gincref_const(struct Dee_basic_block *__restrict self, + DeeObject *value) { + if unlikely(Dee_basic_block_reqx86(self, 2)) + goto err; + gen86_lock(p_pc(self)); + gen86_incP_mod(p_pc(self), gen86_modrm_d, (intptr_t)(uintptr_t)&value->ob_refcnt); /* TODO: movabs */ + return 0; +err: + return -1; +} + +INTERN WUNUSED NONNULL((1)) int DCALL +_Dee_basic_block_gdecref_const(struct Dee_basic_block *__restrict self, + DeeObject *value) { + if unlikely(Dee_basic_block_reqx86(self, 2)) + goto err; + /* Constants can never be destroyed, so decref'ing one is + * like `Dee_DecrefNoKill()' (iow: doesn't need a zero-check) */ + gen86_lock(p_pc(self)); + gen86_decP_mod(p_pc(self), gen86_modrm_d, (intptr_t)(uintptr_t)&value->ob_refcnt); /* TODO: movabs */ + return 0; +err: + return -1; +} + + +INTERN WUNUSED NONNULL((1)) int DCALL +_Dee_basic_block_gxincref_reg(struct Dee_basic_block *__restrict self, + Dee_host_register_t regno) { + uint8_t reg86; + uintptr_t jcc8_offset; + if unlikely(Dee_basic_block_reqx86(self, 2)) + goto err; + reg86 = gen86_registers[regno]; + gen86_testP_r_r(p_pc(self), reg86, reg86); + gen86_jcc8_offset(p_pc(self), GEN86_CC_Z, -1); + jcc8_offset = p_off(self) - 1; + if unlikely(_Dee_basic_block_gincref_reg(self, regno)) + goto err; + /* Fix-up the jump */ + *(int8_t *)(self->bb_host_start + jcc8_offset) += (int8_t)(p_off(self) - jcc8_offset); + return 0; +err: + return -1; +} + +INTERN WUNUSED NONNULL((1)) int DCALL +_Dee_function_generator_gxdecref_reg(struct Dee_function_generator *__restrict self, + Dee_host_register_t regno) { + struct Dee_basic_block *block = self->fg_block; + uint8_t reg86; + uintptr_t jcc8_offset; + if unlikely(Dee_basic_block_reqx86(block, 2)) + goto err; + reg86 = gen86_registers[regno]; + gen86_testP_r_r(p_pc(block), reg86, reg86); + gen86_jcc8_offset(p_pc(block), GEN86_CC_Z, -1); + jcc8_offset = p_off(block) - 1; + if unlikely(_Dee_function_generator_gdecref_reg(self, regno)) + goto err; + /* Fix-up the jump */ + *(int8_t *)(block->bb_host_start + jcc8_offset) += (int8_t)(p_off(block) - jcc8_offset); + return 0; +err: + return -1; +} + + + +/* Controls for operating with R/W-locks (as needed for accessing global/extern variables) */ INTERN WUNUSED NONNULL((1, 2)) int DCALL _Dee_function_generator_grwlock_read(struct Dee_function_generator *__restrict self, Dee_atomic_rwlock_t *__restrict lock) { @@ -226,6 +427,28 @@ _Dee_basic_block_ghstack_pushconst(struct Dee_basic_block *__restrict self, return -1; } +INTERN WUNUSED NONNULL((1)) int DCALL +_Dee_basic_block_ghstack_pushhstack(struct Dee_basic_block *__restrict self, + ptrdiff_t sp_offset) { + if unlikely(Dee_basic_block_reqx86(self, 1)) + goto err; + gen86_pushP_mod(p_pc(self), gen86_modrm_db, sp_offset, GEN86_R_PSP); + return 0; +err: + return -1; +} + +INTERN WUNUSED NONNULL((1)) int DCALL +_Dee_basic_block_ghstack_popreg(struct Dee_basic_block *__restrict self, + Dee_host_register_t dst_regno) { + if unlikely(Dee_basic_block_reqx86(self, 1)) + goto err; + gen86_popP_r(p_pc(self), gen86_registers[dst_regno]); + return 0; +err: + return -1; +} + INTERN WUNUSED NONNULL((1)) int DCALL _Dee_basic_block_gmov_reg2hstack(struct Dee_basic_block *__restrict self, Dee_host_register_t src_regno, ptrdiff_t sp_offset) { @@ -435,45 +658,386 @@ _Dee_function_generator_gret(struct Dee_function_generator *__restrict self) { /* Generate a call to a C-function `c_function' with `argc' - * pointer-sized arguments whose values are taken from `argv'. */ + * pointer-sized arguments whose values are taken from `argv'. + * NOTE: The given `c_function' is assumed to use the `DCALL' calling convention. */ INTERN WUNUSED NONNULL((1)) int DCALL -Dee_function_generator_gcall_c_function(struct Dee_function_generator *__restrict self, - void *c_function, size_t argc, - struct Dee_memloc const *argv) { +_Dee_function_generator_gcall_c_function(struct Dee_function_generator *__restrict self, + void *c_function, size_t argc, + struct Dee_memloc const *argv) { +#ifdef HOSTASM_X86_64 /* TODO */ (void)self; (void)c_function; (void)argc; (void)argv; return DeeError_NOTIMPLEMENTED(); +#else /* HOSTASM_X86_64 */ + struct Dee_basic_block *block = self->fg_block; + size_t argi; + /* MEMLOC_TYPE_ARG-type arguments with index >= this must be filled after-the-fact */ + size_t fill_arg_args_later; + + /* Push arguments onto the host stack in reverse order. */ + argi = argc; + fill_arg_args_later = argc; + while (argi) { + struct Dee_memloc const *arg; + --argi; + arg = &argv[argi]; + switch (arg->ml_where) { + case MEMLOC_TYPE_HSTACK: + if unlikely(Dee_function_generator_ghstack_pushhstack(self, arg->ml_value.ml_hstack)) + goto err; + break; + case MEMLOC_TYPE_HREG: + if unlikely(Dee_function_generator_ghstack_pushreg(self, arg->ml_value.ml_hreg)) + goto err; + break; + case MEMLOC_TYPE_ARG: { + /* Must load the `argv' vector, for which we need a temp register. + * However, we mustn't use registers needed by later argument pushs for this! */ + uint16_t aid = arg->ml_value.ml_harg; + struct Dee_memstate *state = self->fg_state; + Dee_host_register_t temp_regno; + BITSET(HOST_REGISTER_COUNT) used_regs; + size_t future_argi; + if unlikely(Dee_basic_block_reqx86(block, 1)) + goto err; + temp_regno = Dee_memstate_hregs_find_usage(state, REGISTER_USAGE_ARGV); + if (temp_regno < HOST_REGISTER_COUNT) { + ptrdiff_t offset; +do_load_arg_with_argv_regno: + offset = (ptrdiff_t)aid * HOST_SIZEOF_POINTER; + gen86_pushP_mod(p_pc(block), gen86_modrm_db, offset, gen86_registers[temp_regno]); + Dee_function_generator_gadjust_cfa_offset(self, HOST_SIZEOF_POINTER); + break; + } + temp_regno = Dee_memstate_hregs_find_usage(state, REGISTER_USAGE_ARGS); + if (temp_regno < HOST_REGISTER_COUNT) { + ptrdiff_t offset; +do_load_arg_with_args_regno: + offset = offsetof(DeeTupleObject, t_elem) + ((ptrdiff_t)aid * HOST_SIZEOF_POINTER); + gen86_pushP_mod(p_pc(block), gen86_modrm_db, offset, gen86_registers[temp_regno]); + Dee_function_generator_gadjust_cfa_offset(self, HOST_SIZEOF_POINTER); + break; + } + + /* Figure out which registers are used by later pushs. */ + bzero(&used_regs, sizeof(used_regs)); + for (future_argi = 0; future_argi < argi; ++future_argi) { + struct Dee_memloc const *future_arg = &argv[future_argi]; + if (future_arg->ml_where == MEMLOC_TYPE_HREG) { + ASSERT(future_arg->ml_value.ml_hreg < HOST_REGISTER_COUNT); + BITSET_TURNON(&used_regs, future_arg->ml_value.ml_hreg); + } + } + for (temp_regno = 0; temp_regno < HOST_REGISTER_COUNT; ++temp_regno) { + if (!BITSET_GET(&used_regs, temp_regno)) { + /* Found an empty register! */ + if (self->fg_assembler->fa_cc & HOSTFUNC_CC_F_TUPLE) { + if unlikely(_Dee_function_generator_gmov_usage2reg(self, REGISTER_USAGE_ARGS, temp_regno)) + goto err; + state->ms_regs[temp_regno] = REGISTER_USAGE_ARGS; + goto do_load_arg_with_args_regno; + } + if unlikely(_Dee_function_generator_gmov_usage2reg(self, REGISTER_USAGE_ARGV, temp_regno)) + goto err; + state->ms_regs[temp_regno] = REGISTER_USAGE_ARGV; + goto do_load_arg_with_argv_regno; + } + } + + /* Must fill in this argument later */ + fill_arg_args_later = argi; + gen86_pushP_imm(p_pc(block), 0); + Dee_function_generator_gadjust_cfa_offset(self, HOST_SIZEOF_POINTER); + } break; + + case MEMLOC_TYPE_CONST: + if unlikely(Dee_function_generator_ghstack_pushconst(self, arg->ml_value.ml_const)) + goto err; + break; + default: + return DeeError_Throwf(&DeeError_IllegalInstruction, + "Cannot push memory location with type %#" PRFx16, + arg->ml_where); + break; + } + } + if (fill_arg_args_later < argc) { + /* Fill in missing arg-arguments */ + ptrdiff_t extra_offset = 0; + Dee_host_register_t temp_regno, temp2_regno; + temp_regno = Dee_memstate_hregs_find_usage(self->fg_state, REGISTER_USAGE_ARGV); + if (temp_regno >= HOST_REGISTER_COUNT) { + temp_regno = Dee_memstate_hregs_find_usage(self->fg_state, REGISTER_USAGE_ARGS); + if (temp_regno < HOST_REGISTER_COUNT) { + extra_offset = offsetof(DeeTupleObject, t_elem); + } else { + temp_regno = HOST_REGISTER_RETURN; + if (self->fg_assembler->fa_cc & HOSTFUNC_CC_F_TUPLE) { + extra_offset = offsetof(DeeTupleObject, t_elem); + if unlikely(_Dee_function_generator_gmov_usage2reg(self, REGISTER_USAGE_ARGS, temp_regno)) + goto err; + } else { + if unlikely(_Dee_function_generator_gmov_usage2reg(self, REGISTER_USAGE_ARGV, temp_regno)) + goto err; + } + self->fg_state->ms_regs[temp_regno] = REGISTER_USAGE_ARGV; + } + } + temp2_regno = temp_regno + 1; + if (temp2_regno >= HOST_REGISTER_COUNT) + temp2_regno = 0; + for (argi = fill_arg_args_later; argi < argc; ++argi) { + ptrdiff_t offset; + ptrdiff_t sp_offset; + struct Dee_memloc const *arg = &argv[argi]; + if (arg->ml_where != MEMLOC_TYPE_ARG) + continue; + if unlikely(Dee_basic_block_reqx86(block, 2)) + goto err; + offset = (ptrdiff_t)arg->ml_value.ml_harg * HOST_SIZEOF_POINTER; + offset += extra_offset; + sp_offset = (ptrdiff_t)argi * HOST_SIZEOF_POINTER; + gen86_movP_db_r(p_pc(block), offset, gen86_registers[temp_regno], gen86_registers[temp2_regno]); + gen86_movP_r_db(p_pc(block), gen86_registers[temp2_regno], sp_offset, GEN86_R_PSP); + } + } + + /* With everything pushed onto the stack and in the correct position, generate the call. + * Note that because deemon API functions use DCALL (STDCALL), the called function does + * the stack cleanup. */ + { + struct Dee_host_reloc *rel; + if unlikely(Dee_basic_block_reqx86(block, 1)) + goto err; + rel = Dee_basic_block_newhostrel(block); + if unlikely(!rel) + goto err; + gen86_calll_offset(p_pc(block), -4); + rel->hr_offset = p_off(block) - 4; + rel->hr_type = DEE_HOST_RELOC_PTREL32; + rel->hr_value.hr_pt = c_function; + } + + /* Adjust the CFA offset to account for the called function having cleaned up stack arguments. */ + Dee_function_generator_gadjust_cfa_offset(self, -(ptrdiff_t)(argc * HOST_SIZEOF_POINTER)); + + /* After calling an external function, usage registers may have gotten clobbered. */ + Dee_memstate_hregs_clear_usage(self->fg_state); + return 0; +err: + return -1; +#endif /* !HOSTASM_X86_64 */ } -/* Generate checks to enter exception handling mode. */ -INTERN WUNUSED NONNULL((1, 2)) int DCALL -Dee_function_generator_gexcept_if_zero(struct Dee_function_generator *__restrict self, - struct Dee_memloc *__restrict loc) { - /* TODO */ - (void)self; - (void)loc; - return DeeError_NOTIMPLEMENTED(); + + + +PRIVATE WUNUSED NONNULL((1, 2, 3)) int DCALL +_Dee_function_generator_gtest_and_jcc(struct Dee_function_generator *__restrict self, + struct Dee_basic_block *dst, + struct Dee_memloc *test_loc, uint8_t cc) { + struct Dee_basic_block *block = self->fg_block; + struct Dee_host_reloc *rel; + if unlikely(Dee_basic_block_reqx86(block, 2)) + goto err; + switch (test_loc->ml_where) { + + case MEMLOC_TYPE_HSTACK: { + ptrdiff_t sp_offset = Dee_memstate_hstack_cfa2sp(self->fg_state, test_loc->ml_value.ml_hstack); + gen86_cmpP_imm_mod(p_pc(block), gen86_modrm_db, 0, sp_offset, GEN86_R_PSP); + } break; + + case MEMLOC_TYPE_HREG: { + uint8_t reg86 = gen86_registers[test_loc->ml_value.ml_hreg]; + gen86_testP_r_r(p_pc(block), reg86, reg86); + } break; + + case MEMLOC_TYPE_ARG: + /* Arguments are always non-NULL */ + if (cc == GEN86_CC_Z) + return 0; /* Don't jump */ + goto always_jump; + case MEMLOC_TYPE_CONST: + if ((test_loc->ml_value.ml_const == NULL) != (cc == GEN86_CC_Z)) + return 0; /* Don't jump */ +always_jump: + return _Dee_basic_block_gjmp(block, dst); + default: + return DeeError_Throwf(&DeeError_IllegalInstruction, + "Cannot test memory location with type %#" PRFx16, + test_loc->ml_where); + } + gen86_jccl_offset(p_pc(block), cc, -4); + rel = Dee_basic_block_newhostrel(block); + if unlikely(!rel) + goto err; + rel->hr_offset = p_off(block) - 4; + rel->hr_type = DEE_HOST_RELOC_BBREL32; + rel->hr_value.hr_bb = dst; + return 0; +err: + return -1; } -INTERN WUNUSED NONNULL((1, 2)) int DCALL -Dee_function_generator_gexcept_if_nonzero(struct Dee_function_generator *__restrict self, - struct Dee_memloc *__restrict loc) { - /* TODO */ - (void)self; - (void)loc; - return DeeError_NOTIMPLEMENTED(); +/* Generate jumps. */ +INTERN WUNUSED NONNULL((1, 2, 3)) int DCALL +_Dee_function_generator_gjz(struct Dee_function_generator *__restrict self, + struct Dee_basic_block *dst, struct Dee_memloc *test_loc) { + return _Dee_function_generator_gtest_and_jcc(self, dst, test_loc, GEN86_CC_Z); } -INTERN WUNUSED NONNULL((1)) int DCALL -Dee_function_generator_gexcept(struct Dee_function_generator *__restrict self) { - /* TODO */ - (void)self; - return DeeError_NOTIMPLEMENTED(); +INTERN WUNUSED NONNULL((1, 2, 3)) int DCALL +_Dee_function_generator_gjnz(struct Dee_function_generator *__restrict self, + struct Dee_basic_block *dst, struct Dee_memloc *test_loc) { + return _Dee_function_generator_gtest_and_jcc(self, dst, test_loc, GEN86_CC_NZ); } +/* Emit conditional jump(s) based on ` <=> 0' */ +INTERN WUNUSED NONNULL((1, 5)) int DCALL +_Dee_function_generator_gjcmp0(struct Dee_function_generator *__restrict self, + struct Dee_basic_block *dst_lo_0, /* Jump here if ` < 0' */ + struct Dee_basic_block *dst_eq_0, /* Jump here if ` == 0' */ + struct Dee_basic_block *dst_gr_0, /* Jump here if ` > 0' */ + struct Dee_memloc *test_loc) { + struct Dee_basic_block *block = self->fg_block; + /* Check for some special cases. */ + if (!dst_lo_0 && !dst_eq_0 && !dst_gr_0) + return 0; + if (!dst_lo_0 && dst_eq_0 && !dst_gr_0) + return _Dee_function_generator_gjz(self, dst_eq_0, test_loc); + if (!dst_eq_0 && dst_lo_0 == dst_gr_0) + return _Dee_function_generator_gjnz(self, dst_lo_0, test_loc); + if unlikely(Dee_basic_block_reqx86(block, 2)) + goto err; + switch (test_loc->ml_where) { + + case MEMLOC_TYPE_HSTACK: { + ptrdiff_t sp_offset = Dee_memstate_hstack_cfa2sp(self->fg_state, test_loc->ml_value.ml_hstack); + gen86_cmpP_imm_mod(p_pc(block), gen86_modrm_db, 0, sp_offset, GEN86_R_PSP); + } break; + + case MEMLOC_TYPE_HREG: { + uint8_t reg86 = gen86_registers[test_loc->ml_value.ml_hreg]; + gen86_cmpP_imm_r(p_pc(block), 0, reg86); + } break; + + case MEMLOC_TYPE_CONST: { + struct Dee_basic_block *dst; + if ((intptr_t)test_loc->ml_value.ml_const < 0) { + dst = dst_lo_0; + } else if ((intptr_t)test_loc->ml_value.ml_const > 0) { + dst = dst_gr_0; + } else { + dst = NULL; + } + return dst ? _Dee_basic_block_gjmp(block, dst) : 0; + } break; + + case MEMLOC_TYPE_ARG: + /* Always non-NULL, and sign-bit shouldn't be + * set because that's where the kernel is at! */ + return dst_gr_0 ? _Dee_basic_block_gjmp(block, dst_gr_0) : 0; + + default: + return DeeError_Throwf(&DeeError_IllegalInstruction, + "Cannot test memory location with type %#" PRFx16, + test_loc->ml_where); + } + + /* Emit jumps to different basic blocks (while trying to combine shared destinations). */ + if (dst_lo_0) { + struct Dee_host_reloc *rel; + uint8_t cc = GEN86_CC_L; + if (dst_lo_0 == dst_eq_0) { + cc = GEN86_CC_LE; + dst_eq_0 = NULL; + } else if (dst_lo_0 == dst_gr_0) { + cc = GEN86_CC_NE; + dst_gr_0 = NULL; + } + gen86_jccl_offset(p_pc(block), cc, -4); + rel = Dee_basic_block_newhostrel(block); + if unlikely(!rel) + goto err; + rel->hr_offset = p_off(block) - 4; + rel->hr_type = DEE_HOST_RELOC_BBREL32; + rel->hr_value.hr_bb = dst_lo_0; + if (cc == GEN86_CC_LE) { + if (dst_gr_0) { + dst_eq_0 = dst_gr_0; +emit_unconditional_jump: + if unlikely(Dee_basic_block_reqx86(block, 1)) + goto err; + goto do_emit_unconditional_jump; + } + return 0; + } else if (cc == GEN86_CC_NE) { + if (dst_eq_0) + goto emit_unconditional_jump; + return 0; + } + } + if (dst_gr_0) { + struct Dee_host_reloc *rel; + uint8_t cc = GEN86_CC_G; + if (dst_eq_0 == dst_gr_0) { + if (dst_lo_0) + goto emit_unconditional_jump; + cc = GEN86_CC_GE; + dst_eq_0 = NULL; + } + if unlikely(Dee_basic_block_reqx86(block, 1)) + goto err; + gen86_jccl_offset(p_pc(block), cc, -4); + rel = Dee_basic_block_newhostrel(block); + if unlikely(!rel) + goto err; + rel->hr_offset = p_off(block) - 4; + rel->hr_type = DEE_HOST_RELOC_BBREL32; + rel->hr_value.hr_bb = dst_gr_0; + } + if (dst_eq_0) { + struct Dee_host_reloc *rel; + if unlikely(Dee_basic_block_reqx86(block, 1)) + goto err; + if (dst_lo_0 && dst_gr_0) { +do_emit_unconditional_jump: + gen86_jmpl_offset(p_pc(block), -4); + } else { + gen86_jccl_offset(p_pc(block), GEN86_CC_E, -4); + } + rel = Dee_basic_block_newhostrel(block); + if unlikely(!rel) + goto err; + rel->hr_offset = p_off(block) - 4; + rel->hr_type = DEE_HOST_RELOC_BBREL32; + rel->hr_value.hr_bb = dst_eq_0; + } + return 0; +err: + return -1; +} + +INTERN WUNUSED NONNULL((1, 2)) int DCALL +_Dee_basic_block_gjmp(struct Dee_basic_block *__restrict self, + struct Dee_basic_block *dst) { + struct Dee_host_reloc *rel; + if unlikely(Dee_basic_block_reqx86(self, 1)) + goto err; + gen86_jmpl_offset(p_pc(self), -4); + rel = Dee_basic_block_newhostrel(self); + if unlikely(!rel) + goto err; + rel->hr_offset = p_off(self) - 4; + rel->hr_type = DEE_HOST_RELOC_BBREL32; + rel->hr_value.hr_bb = dst; + return 0; +err: + return -1; +} DECL_END diff --git a/src/dex/_hostasm/generator-common.c b/src/dex/_hostasm/generator-common.c index b501b6c6f..50d15033a 100644 --- a/src/dex/_hostasm/generator-common.c +++ b/src/dex/_hostasm/generator-common.c @@ -45,6 +45,10 @@ DECL_BEGIN +/************************************************************************/ +/* CODE GENERATION */ +/************************************************************************/ + INTERN WUNUSED NONNULL((1)) int DCALL Dee_function_generator_ghstack_adjust(struct Dee_function_generator *__restrict self, ptrdiff_t cfa_delta) { @@ -61,7 +65,7 @@ Dee_function_generator_ghstack_adjust(struct Dee_function_generator *__restrict result = Dee_function_generator_state_unshare(self); if unlikely(result != 0) goto done; - self->fg_state->ms_host_cfa_offset += cfa_delta; + Dee_function_generator_gadjust_cfa_offset(self, cfa_delta); done: return result; } @@ -75,7 +79,7 @@ Dee_function_generator_ghstack_pushreg(struct Dee_function_generator *__restrict result = Dee_function_generator_state_unshare(self); if unlikely(result != 0) goto done; - self->fg_state->ms_host_cfa_offset += HOST_SIZEOF_POINTER; + Dee_function_generator_gadjust_cfa_offset(self, HOST_SIZEOF_POINTER); done: return result; } @@ -89,7 +93,24 @@ Dee_function_generator_ghstack_pushconst(struct Dee_function_generator *__restri result = Dee_function_generator_state_unshare(self); if unlikely(result != 0) goto done; - self->fg_state->ms_host_cfa_offset += HOST_SIZEOF_POINTER; + Dee_function_generator_gadjust_cfa_offset(self, HOST_SIZEOF_POINTER); +done: + return result; +} + +INTERN WUNUSED NONNULL((1)) int DCALL +Dee_function_generator_ghstack_pushhstack(struct Dee_function_generator *__restrict self, + uintptr_t cfa_offset) { + int result; + ptrdiff_t sp_offset; + sp_offset = Dee_memstate_hstack_cfa2sp(self->fg_state, cfa_offset); + result = _Dee_function_generator_ghstack_pushhstack(self, sp_offset); + if unlikely(result != 0) + goto done; + result = Dee_function_generator_state_unshare(self); + if unlikely(result != 0) + goto done; + Dee_function_generator_gadjust_cfa_offset(self, HOST_SIZEOF_POINTER); done: return result; } @@ -138,9 +159,23 @@ Dee_function_generator_gmov_loc2reg(struct Dee_function_generator *__restrict se Dee_host_register_t dst_regno) { int result; switch (src_loc->ml_where) { - case MEMLOC_TYPE_HSTACK: - result = _Dee_function_generator_gmov_hstack2reg(self, src_loc->ml_value.ml_hstack, dst_regno); - break; + case MEMLOC_TYPE_HSTACK: { + uintptr_t cfa_offset = src_loc->ml_value.ml_hstack; + ptrdiff_t sp_offset = Dee_memstate_hstack_cfa2sp(self->fg_state, cfa_offset); +#ifdef HOSTASM_STACK_GROWS_DOWN + if (sp_offset == 0) +#else /* HOSTASM_STACK_GROWS_DOWN */ + if (sp_offset == -HOST_SIZEOF_POINTER) +#endif /* !HOSTASM_STACK_GROWS_DOWN */ + { + /* Special case: if the value lies on-top of the host stack, then pop it instead of move it. */ + result = _Dee_function_generator_ghstack_popreg(self, dst_regno); + if likely(result == 0) + Dee_function_generator_gadjust_cfa_offset(self, -HOST_SIZEOF_POINTER); + } else { + result = _Dee_function_generator_gmov_hstack2reg(self, sp_offset, dst_regno); + } + } break; case MEMLOC_TYPE_HREG: result = _Dee_function_generator_gmov_reg2reg(self, src_loc->ml_value.ml_hreg, dst_regno); break; @@ -323,12 +358,13 @@ Dee_function_generator_gflushreg(struct Dee_function_generator *__restrict self, } /* Generate code to flush all registers used by the deemon stack/locals into the host stack. + * NOTE: Usage-registers are cleared by arch-specific code (e.g. `_Dee_function_generator_gcall_c_function()') * @param: ignore_top_n_stack_if_not_ref: From the top-most N stack locations, ignore any * that don't contain object references. */ INTERN WUNUSED NONNULL((1)) int DCALL Dee_function_generator_gflushregs(struct Dee_function_generator *__restrict self, - size_t ignore_top_n_stack_if_not_ref) { - size_t i; + uint16_t ignore_top_n_stack_if_not_ref) { + uint16_t i; uintptr_t register_cfa[HOST_REGISTER_COUNT]; struct Dee_memstate *state = self->fg_state; ASSERT(!Dee_memstate_isshared(state)); @@ -368,16 +404,7 @@ Dee_function_generator_gflushregs(struct Dee_function_generator *__restrict self } } - /* Clear usage-registers. */ -#if REGISTER_USAGE_GENERIC == 0 - bzero(state->ms_regs, sizeof(state->ms_regs)); -#else /* REGISTER_USAGE_GENERIC == 0 */ - { - Dee_host_regusage_t regno; - for (regno = 0; regno < HOST_REGISTER_COUNT; ++regno) - state->ms_regs[regno] = REGISTER_USAGE_GENERIC; - } -#endif /* REGISTER_USAGE_GENERIC != 0 */ + /* NOTE: Usage-registers must be cleared by the caller! */ return 0; err: return -1; @@ -491,7 +518,7 @@ Dee_function_generator_gassert_bound(struct Dee_function_generator *__restrict s * an exception handler. */ INTERN WUNUSED NONNULL((1)) int DCALL Dee_function_generator_gthrow_local_unbound(struct Dee_function_generator *__restrict self, uint16_t lid) { - /* TODO: Implement using `Dee_function_generator_gcall_c_function()'+`Dee_function_generator_gexcept()' */ + /* TODO: Implement using `_Dee_function_generator_gcall_c_function()'+`Dee_function_generator_gjmp_except()' */ (void)self; (void)lid; return DeeError_NOTIMPLEMENTED(); @@ -499,7 +526,7 @@ Dee_function_generator_gthrow_local_unbound(struct Dee_function_generator *__res INTERN WUNUSED NONNULL((1)) int DCALL Dee_function_generator_gthrow_global_unbound(struct Dee_function_generator *__restrict self, uint16_t gid) { - /* TODO: Implement using `Dee_function_generator_gcall_c_function()'+`Dee_function_generator_gexcept()' */ + /* TODO: Implement using `_Dee_function_generator_gcall_c_function()'+`Dee_function_generator_gjmp_except()' */ (void)self; (void)gid; return DeeError_NOTIMPLEMENTED(); @@ -507,7 +534,7 @@ Dee_function_generator_gthrow_global_unbound(struct Dee_function_generator *__re INTERN WUNUSED NONNULL((1)) int DCALL Dee_function_generator_gthrow_extern_unbound(struct Dee_function_generator *__restrict self, uint16_t mid, uint16_t gid) { - /* TODO: Implement using `Dee_function_generator_gcall_c_function()'+`Dee_function_generator_gexcept()' */ + /* TODO: Implement using `_Dee_function_generator_gcall_c_function()'+`Dee_function_generator_gjmp_except()' */ (void)self; (void)mid; (void)gid; @@ -515,13 +542,191 @@ Dee_function_generator_gthrow_extern_unbound(struct Dee_function_generator *__re } +/* Generate checks to enter exception handling mode. */ +INTERN WUNUSED NONNULL((1, 2)) int DCALL +Dee_function_generator_gjz_except(struct Dee_function_generator *__restrict self, + struct Dee_memloc *loc) { + struct Dee_basic_block *bb; + bb = Dee_function_generator_except_exit(self); + if unlikely(!bb) + goto err; + return _Dee_function_generator_gjz(self, bb, loc); +err: + return -1; +} + +INTERN WUNUSED NONNULL((1, 2)) int DCALL +Dee_function_generator_gjnz_except(struct Dee_function_generator *__restrict self, + struct Dee_memloc *loc) { + struct Dee_basic_block *bb; + bb = Dee_function_generator_except_exit(self); + if unlikely(!bb) + goto err; + return _Dee_function_generator_gjnz(self, bb, loc); +err: + return -1; +} + +INTERN WUNUSED NONNULL((1)) int DCALL +Dee_function_generator_gjmp_except(struct Dee_function_generator *__restrict self) { + struct Dee_basic_block *bb; + bb = Dee_function_generator_except_exit(self); + if unlikely(!bb) + goto err; + return _Dee_function_generator_gjmp(self, bb); +err: + return -1; +} + + +INTERN WUNUSED NONNULL((1, 2)) int DCALL +Dee_function_generator_gincref(struct Dee_function_generator *__restrict self, + struct Dee_memloc *__restrict loc) { + switch (loc->ml_where) { + case MEMLOC_TYPE_HSTACK: + case MEMLOC_TYPE_ARG: + if unlikely(Dee_function_generator_greg(self, loc)) + goto err; + ATTR_FALLTHROUGH + case MEMLOC_TYPE_HREG: + return _Dee_function_generator_gincref_reg(self, loc->ml_value.ml_hreg); + case MEMLOC_TYPE_CONST: + return _Dee_function_generator_gincref_const(self, loc->ml_value.ml_const); + default: + return DeeError_Throwf(&DeeError_IllegalInstruction, + "Cannot incref memory location type %#" PRFx16, + loc->ml_where); + } + __builtin_unreachable(); +err: + return -1; +} + +INTERN WUNUSED NONNULL((1, 2)) int DCALL +Dee_function_generator_gdecref(struct Dee_function_generator *__restrict self, + struct Dee_memloc *__restrict loc) { + switch (loc->ml_where) { + case MEMLOC_TYPE_HSTACK: + case MEMLOC_TYPE_ARG: + if unlikely(Dee_function_generator_greg(self, loc)) + goto err; + ATTR_FALLTHROUGH + case MEMLOC_TYPE_HREG: + return _Dee_function_generator_gdecref_reg(self, loc->ml_value.ml_hreg); + case MEMLOC_TYPE_CONST: + return _Dee_function_generator_gdecref_const(self, loc->ml_value.ml_const); + default: + return DeeError_Throwf(&DeeError_IllegalInstruction, + "Cannot decref memory location type %#" PRFx16, + loc->ml_where); + } + __builtin_unreachable(); +err: + return -1; +} + +INTERN WUNUSED NONNULL((1, 2)) int DCALL +Dee_function_generator_gxincref(struct Dee_function_generator *__restrict self, + struct Dee_memloc *__restrict loc) { + switch (loc->ml_where) { + case MEMLOC_TYPE_HSTACK: + case MEMLOC_TYPE_ARG: + if unlikely(Dee_function_generator_greg(self, loc)) + goto err; + ATTR_FALLTHROUGH + case MEMLOC_TYPE_HREG: + return _Dee_function_generator_gxincref_reg(self, loc->ml_value.ml_hreg); + case MEMLOC_TYPE_CONST: + if (!loc->ml_value.ml_const) + return 0; + return _Dee_function_generator_gincref_const(self, loc->ml_value.ml_const); + default: + return DeeError_Throwf(&DeeError_IllegalInstruction, + "Cannot incref memory location type %#" PRFx16, + loc->ml_where); + } + __builtin_unreachable(); +err: + return -1; +} + +INTERN WUNUSED NONNULL((1, 2)) int DCALL +Dee_function_generator_gxdecref(struct Dee_function_generator *__restrict self, + struct Dee_memloc *__restrict loc) { + switch (loc->ml_where) { + case MEMLOC_TYPE_HSTACK: + case MEMLOC_TYPE_ARG: + if unlikely(Dee_function_generator_greg(self, loc)) + goto err; + ATTR_FALLTHROUGH + case MEMLOC_TYPE_HREG: + return _Dee_function_generator_gxdecref_reg(self, loc->ml_value.ml_hreg); + case MEMLOC_TYPE_CONST: + if (!loc->ml_value.ml_const) + return 0; + return _Dee_function_generator_gdecref_const(self, loc->ml_value.ml_const); + default: + return DeeError_Throwf(&DeeError_IllegalInstruction, + "Cannot incref memory location type %#" PRFx16, + loc->ml_where); + } + __builtin_unreachable(); +err: + return -1; +} + +/* Force `loc' to become a register (`MEMLOC_TYPE_HREG'). */ +INTERN WUNUSED NONNULL((1, 2)) int DCALL +Dee_function_generator_greg(struct Dee_function_generator *__restrict self, + struct Dee_memloc *__restrict loc) { + Dee_host_register_t regno; + if (loc->ml_where == MEMLOC_TYPE_HREG) + return 0; /* Already in a register! */ + + /* Allocate a register. */ + regno = Dee_function_generator_gallocreg(self, NULL); + if unlikely(regno >= HOST_REGISTER_COUNT) + goto err; + + /* Move value into register. */ + if unlikely(Dee_function_generator_gmov_loc2reg(self, loc, regno)) + goto err; + + /* Remember that `loc' now lies in a register. */ + loc->ml_where = MEMLOC_TYPE_HREG; + loc->ml_value.ml_hreg = regno; + return 0; +err: + return -1; +} + + + + + + + + + + + + + + + + + +/************************************************************************/ +/* VSTACK CONTROLS */ +/************************************************************************/ + /* Code generator helpers to manipulate the V-stack. */ INTERN WUNUSED NONNULL((1)) int DCALL Dee_function_generator_vswap(struct Dee_function_generator *__restrict self) { - int result = Dee_memstate_unshare(&self->fg_state); + int result = Dee_function_generator_state_unshare(self); if likely(result == 0) result = Dee_memstate_vswap(self->fg_state); return result; @@ -532,7 +737,7 @@ Dee_function_generator_vlrot(struct Dee_function_generator *__restrict self, siz int result; if unlikely(n <= 1) return 0; - result = Dee_memstate_unshare(&self->fg_state); + result = Dee_function_generator_state_unshare(self); if likely(result == 0) result = Dee_memstate_vlrot(self->fg_state, n); return result; @@ -543,7 +748,7 @@ Dee_function_generator_vrrot(struct Dee_function_generator *__restrict self, siz int result; if unlikely(n <= 1) return 0; - result = Dee_memstate_unshare(&self->fg_state); + result = Dee_function_generator_state_unshare(self); if likely(result == 0) result = Dee_memstate_vrrot(self->fg_state, n); return result; @@ -551,26 +756,37 @@ Dee_function_generator_vrrot(struct Dee_function_generator *__restrict self, siz INTERN WUNUSED NONNULL((1, 2)) int DCALL Dee_function_generator_vpush(struct Dee_function_generator *__restrict self, struct Dee_memloc *loc) { - int result = Dee_memstate_unshare(&self->fg_state); + int result = Dee_function_generator_state_unshare(self); if likely(result == 0) result = Dee_memstate_vpush(self->fg_state, loc); return result; } -INTERN WUNUSED NONNULL((1, 2)) int DCALL +INTERN WUNUSED NONNULL((1)) int DCALL Dee_function_generator_vpush_const(struct Dee_function_generator *__restrict self, DeeObject *value) { - int result = Dee_memstate_unshare(&self->fg_state); + int result = Dee_function_generator_state_unshare(self); if likely(result == 0) result = Dee_memstate_vpush_const(self->fg_state, value); return result; } + +/* Sets the `MEMLOC_F_NOREF' flag */ +INTERN WUNUSED NONNULL((1)) int DCALL +Dee_function_generator_vpush_reg(struct Dee_function_generator *__restrict self, + Dee_host_register_t regno) { + int result = Dee_function_generator_state_unshare(self); + if likely(result == 0) + result = Dee_memstate_vpush_reg(self->fg_state, regno); + return result; +} + INTERN WUNUSED NONNULL((1)) int DCALL Dee_function_generator_vpush_arg(struct Dee_function_generator *__restrict self, uint16_t aid) { int result; if unlikely(aid >= self->fg_assembler->fa_code->co_argc_max) return err_illegal_aid(aid); - result = Dee_memstate_unshare(&self->fg_state); + result = Dee_function_generator_state_unshare(self); if likely(result == 0) result = Dee_memstate_vpush_arg(self->fg_state, aid); return result; @@ -580,7 +796,7 @@ INTERN WUNUSED NONNULL((1)) int DCALL Dee_function_generator_vpush_local(struct Dee_function_generator *__restrict self, uint16_t lid) { struct Dee_memstate *state; struct Dee_memloc *loc; - if unlikely(Dee_memstate_unshare(&self->fg_state)) + if unlikely(Dee_function_generator_state_unshare(self)) goto err; state = self->fg_state; if unlikely(lid >= state->ms_localc) @@ -612,7 +828,7 @@ Dee_function_generator_vpush_local(struct Dee_function_generator *__restrict sel INTERN WUNUSED NONNULL((1)) int DCALL Dee_function_generator_vdup_n(struct Dee_function_generator *__restrict self, size_t n) { - int result = Dee_memstate_unshare(&self->fg_state); + int result = Dee_function_generator_state_unshare(self); if likely(result == 0) result = Dee_memstate_vdup_n(self->fg_state, n); return result; @@ -622,12 +838,12 @@ INTERN WUNUSED NONNULL((1)) int DCALL Dee_function_generator_vpop(struct Dee_function_generator *__restrict self) { struct Dee_memstate *state; struct Dee_memloc *loc; - if unlikely(Dee_memstate_unshare(&self->fg_state)) + if unlikely(Dee_function_generator_state_unshare(self)) goto err; state = self->fg_state; if unlikely(state->ms_stackc < 1) return err_illegal_stack_effect(); - loc = &state->ms_stackv[state->ms_stackc - 1]; + loc = Dee_memstate_vtop(state); --state->ms_stackc; if (!(loc->ml_flags & MEMLOC_F_NOREF)) { uint16_t i; @@ -681,7 +897,7 @@ Dee_function_generator_vpush_usage(struct Dee_function_generator *__restrict sel Dee_host_regusage_t usage) { struct Dee_memstate *state; Dee_host_register_t regno; - if unlikely(Dee_memstate_unshare(&self->fg_state)) + if unlikely(Dee_function_generator_state_unshare(self)) goto err; state = self->fg_state; for (regno = 0; regno < HOST_REGISTER_COUNT; ++regno) { @@ -713,12 +929,12 @@ Dee_function_generator_vpop_local(struct Dee_function_generator *__restrict self uint16_t lid) { struct Dee_memstate *state; struct Dee_memloc *src, *dst; - if unlikely(Dee_memstate_unshare(&self->fg_state)) + if unlikely(Dee_function_generator_state_unshare(self)) goto err; state = self->fg_state; if unlikely(lid >= state->ms_localc) return err_illegal_lid(lid); - src = &state->ms_stackv[state->ms_stackc - 1]; + src = Dee_memstate_vtop(state); dst = &state->ms_localv[lid]; if ((dst->ml_where == MEMLOC_TYPE_UNALLOC) || (dst->ml_flags & MEMLOC_F_NOREF)) { @@ -752,7 +968,7 @@ INTERN WUNUSED NONNULL((1)) int DCALL Dee_function_generator_vdel_local(struct Dee_function_generator *__restrict self, uint16_t lid) { struct Dee_memstate *state; struct Dee_memloc *loc; - if unlikely(Dee_memstate_unshare(&self->fg_state)) + if unlikely(Dee_function_generator_state_unshare(self)) goto err; state = self->fg_state; if unlikely(lid >= state->ms_localc) @@ -809,7 +1025,7 @@ PRIVATE struct host_operator_specs const operator_apis[] = { /* [OPERATOR_REPR] = */ { (void *)&DeeObject_Repr, 1, VCALLOP_CC_OBJECT }, /* [OPERATOR_BOOL] = */ { (void *)NULL }, /* Special handling */ /* [OPERATOR_ITERNEXT] = */ { (void *)&DeeObject_IterNext, 1, VCALLOP_CC_OBJECT }, - /* [OPERATOR_CALL] = */ { (void *)&DeeObject_CallTuple, 2, VCALLOP_CC_OBJECT }, + /* [OPERATOR_CALL] = */ { (void *)NULL }, /* Special handling */ /* [OPERATOR_INT] = */ { (void *)&DeeObject_Int, 1, VCALLOP_CC_OBJECT }, #ifdef CONFIG_HAVE_FPU /* [OPERATOR_FLOAT] = */ { (void *)&api_object_as_float, 1, VCALLOP_CC_OBJECT }, @@ -871,12 +1087,26 @@ INTERN WUNUSED NONNULL((1)) int DCALL Dee_function_generator_vop(struct Dee_function_generator *__restrict self, uint16_t operator_name, uint16_t argc) { struct Dee_memstate *state; - if unlikely(Dee_memstate_unshare(&self->fg_state)) + if unlikely(Dee_function_generator_state_unshare(self)) goto err; state = self->fg_state; if unlikely(argc > state->ms_stackc) return err_illegal_stack_effect(); + /* Check if there is a dedicated API function. */ + if (operator_name < COMPILER_LENOF(operator_apis)) { + struct host_operator_specs const *specs = &operator_apis[operator_name]; + if (specs->hos_apifunc != NULL && specs->hos_argc == argc) { + if unlikely(Dee_function_generator_vcallop(self, specs->hos_apifunc, + specs->hos_cc, argc)) + goto err; + /* Always make sure to return some value on-stack. */ + if (specs->hos_cc != VCALLOP_CC_OBJECT) + return Dee_function_generator_vpush_const(self, Dee_None); + return 0; + } + } + switch (operator_name) { case OPERATOR_CALL: @@ -889,17 +1119,98 @@ Dee_function_generator_vop(struct Dee_function_generator *__restrict self, default: break; } + /* TODO: make a call to `DeeObject_InvokeOperator()' */ + return DeeError_NOTIMPLEMENTED(); +err: + return -1; +} + +/* doesn't leave result on-stack */ +INTERN WUNUSED NONNULL((1)) int DCALL +Dee_function_generator_vopv(struct Dee_function_generator *__restrict self, + uint16_t operator_name, uint16_t argc) { +#ifndef __OPTIMIZE_SIZE__ /* Check if there is a dedicated API function. */ if (operator_name < COMPILER_LENOF(operator_apis)) { struct host_operator_specs const *specs = &operator_apis[operator_name]; if (specs->hos_apifunc != NULL && specs->hos_argc == argc) { - return Dee_function_generator_vcallop(self, specs->hos_apifunc, - specs->hos_cc, argc); + if unlikely(argc > self->fg_state->ms_stackc) + return err_illegal_stack_effect(); + if unlikely(Dee_function_generator_vcallop(self, specs->hos_apifunc, + specs->hos_cc, argc)) + goto err; + /* Pop the result if there was one. */ + if (specs->hos_cc == VCALLOP_CC_OBJECT) + goto done_pop; + return 0; } } +#endif /* !__OPTIMIZE_SIZE__ */ + if unlikely(Dee_function_generator_vop(self, operator_name, argc)) + goto err; +#ifndef __OPTIMIZE_SIZE__ +done_pop: +#endif /* !__OPTIMIZE_SIZE__ */ + return Dee_function_generator_vpop(self); +err: + return -1; +} - /* TODO: make a call to `DeeObject_InvokeOperator()' */ - return DeeError_NOTIMPLEMENTED(); +INTERN WUNUSED NONNULL((1, 2)) int DCALL +Dee_function_generator_vjcc(struct Dee_function_generator *__restrict self, + struct Dee_basic_block *target, bool jump_if_true) { + struct Dee_basic_block *except_exit; + struct Dee_memstate *state; + struct Dee_memloc *loc, arg_loc; + if unlikely(Dee_function_generator_state_unshare(self)) + goto err; + state = self->fg_state; + if unlikely(state->ms_stackc < 1) + return err_illegal_stack_effect(); + arg_loc = *Dee_memstate_vtop(state); + + /* Special case for when the top-element is a constant. */ + if (arg_loc.ml_where == MEMLOC_TYPE_CONST) { + int temp = DeeObject_Bool(arg_loc.ml_value.ml_const); + if unlikely(temp < 0) { + DeeError_Handled(Dee_ERROR_HANDLED_RESTORE); + } else { + bool should_jump = (temp > 0) == jump_if_true; + if (should_jump) { + if unlikely(Dee_function_generator_gjmp(self, target)) + goto err; + } + return Dee_function_generator_vpop(self); + } + } + + /* Evaluate the top stack-object to a boolean (via `DeeObject_Bool'). */ + if unlikely(Dee_function_generator_gflushregs(self, 1)) + goto err; + if unlikely(_Dee_function_generator_gcall_c_function(self, (void *)&DeeObject_Bool, 1, &arg_loc)) + goto err; + if unlikely(Dee_function_generator_vpush_reg(self, HOST_REGISTER_RETURN)) + goto err; + if unlikely(Dee_function_generator_vswap(self)) + goto err; + if unlikely(Dee_function_generator_vpop(self)) + goto err; + + /* At this point, the stack-top location */ + loc = Dee_function_generator_vtop(self); + ASSERT(loc->ml_flags & MEMLOC_F_NOREF); + except_exit = Dee_function_generator_except_exit(self); + if unlikely(!except_exit) + goto err; + if unlikely(Dee_function_generator_state_unshare(self)) + goto err; + if unlikely(_Dee_function_generator_gjcmp0(self, + except_exit, + jump_if_true ? NULL : target, + jump_if_true ? target : NULL, + loc)) + goto err; + return Dee_function_generator_vpop(self); err: return -1; } @@ -916,7 +1227,7 @@ Dee_function_generator_vind(struct Dee_function_generator *__restrict self, ptrd struct Dee_memloc *loc; if unlikely(state->ms_stackc < 1) return err_illegal_stack_effect(); - loc = &state->ms_stackv[state->ms_stackc - 1]; + loc = Dee_memstate_vtop(state); ASSERTF(loc->ml_flags & MEMLOC_F_NOREF, "Dee_function_generator_vind() called on reference"); if (loc->ml_where == MEMLOC_TYPE_HREG) { @@ -965,18 +1276,18 @@ Dee_function_generator_vref(struct Dee_function_generator *__restrict self) { struct Dee_memloc *loc; if unlikely(state->ms_stackc < 1) return err_illegal_stack_effect(); - loc = &state->ms_stackv[state->ms_stackc - 1]; + loc = Dee_memstate_vtop(state); if (loc->ml_flags & MEMLOC_F_NOREF) { - if unlikely(Dee_memstate_unshare(&self->fg_state)) + if unlikely(Dee_function_generator_state_unshare(self)) goto err; state = self->fg_state; - loc = &state->ms_stackv[state->ms_stackc - 1]; + loc = Dee_memstate_vtop(state); ASSERT(loc->ml_flags & MEMLOC_F_NOREF); if unlikely(Dee_function_generator_gincref(self, loc)) goto err; - loc = &state->ms_stackv[state->ms_stackc - 1]; + loc = Dee_memstate_vtop(state); ASSERT(loc->ml_flags & MEMLOC_F_NOREF); - state->ms_stackv[state->ms_stackc - 1].ml_flags &= ~MEMLOC_F_NOREF; + loc->ml_flags &= ~MEMLOC_F_NOREF; } return 0; err: @@ -1009,17 +1320,17 @@ vpush_global_or_extern(struct Dee_function_generator *__restrict self, goto err; if unlikely(Dee_function_generator_vind(self, 0)) goto err; - loc = &self->fg_state->ms_stackv[self->fg_state->ms_stackc - 1]; + loc = Dee_function_generator_vtop(self); ASSERT(loc->ml_flags & MEMLOC_F_NOREF); if unlikely(Dee_function_generator_gassert_bound(self, loc, kind, id1, id2, &mod->mo_lock)) goto err; - loc = &self->fg_state->ms_stackv[self->fg_state->ms_stackc - 1]; + loc = Dee_function_generator_vtop(self); ASSERT(loc->ml_flags & MEMLOC_F_NOREF); if unlikely(Dee_function_generator_gincref(self, loc)) goto err; if unlikely(Dee_function_generator_grwlock_endread(self, &mod->mo_lock)) goto err; - loc = &self->fg_state->ms_stackv[self->fg_state->ms_stackc - 1]; + loc = Dee_function_generator_vtop(self); ASSERT(loc->ml_flags & MEMLOC_F_NOREF); loc->ml_flags &= ~MEMLOC_F_NOREF; return 0; @@ -1061,7 +1372,7 @@ vpop_global_or_extern(struct Dee_function_generator *__restrict self, if unlikely(Dee_function_generator_grwlock_endwrite(self, &mod->mo_lock)) goto err; ASSERT(self->fg_state->ms_stackc >= 2); - loc = &self->fg_state->ms_stackv[self->fg_state->ms_stackc - 1]; + loc = Dee_function_generator_vtop(self); loc->ml_flags |= MEMLOC_F_NOREF; if unlikely(Dee_function_generator_gxdecref(self, loc)) /* xdecref in case global wasn't bound before. */ goto err; @@ -1130,7 +1441,7 @@ Dee_function_generator_vpush_static(struct Dee_function_generator *__restrict se goto err; if unlikely(Dee_function_generator_vind(self, 0)) goto err; - loc = &self->fg_state->ms_stackv[self->fg_state->ms_stackc - 1]; + loc = Dee_function_generator_vtop(self); ASSERT(loc->ml_flags & MEMLOC_F_NOREF); loc->ml_flags &= ~MEMLOC_F_NOREF; if unlikely(Dee_function_generator_gincref(self, loc)) @@ -1147,7 +1458,7 @@ Dee_function_generator_vpop_static(struct Dee_function_generator *__restrict sel return err_illegal_sid(sid); if unlikely(Dee_function_generator_vref(self)) goto err; - ASSERT(!(self->fg_state->ms_stackv[self->fg_state->ms_stackc - 1].ml_flags & MEMLOC_F_NOREF)); + ASSERT(!(Dee_function_generator_vtop(self)->ml_flags & MEMLOC_F_NOREF)); if unlikely(Dee_function_generator_vpush_addr(self, &code->co_staticv[sid])) goto err; if unlikely(Dee_function_generator_vswap(self)) @@ -1158,10 +1469,10 @@ Dee_function_generator_vpop_static(struct Dee_function_generator *__restrict sel goto err; if unlikely(Dee_function_generator_grwlock_endwrite(self, &code->co_static_lock)) goto err; - ASSERT(!(self->fg_state->ms_stackv[self->fg_state->ms_stackc - 1].ml_flags & MEMLOC_F_NOREF)); + ASSERT(!(Dee_function_generator_vtop(self)->ml_flags & MEMLOC_F_NOREF)); if unlikely(Dee_function_generator_vpop(self)) goto err; - ASSERT(self->fg_state->ms_stackv[self->fg_state->ms_stackc - 1].ml_flags & MEMLOC_F_NOREF); + ASSERT(Dee_function_generator_vtop(self)->ml_flags & MEMLOC_F_NOREF); if unlikely(Dee_function_generator_vpop(self)) goto err; return 0; @@ -1231,9 +1542,9 @@ Dee_function_generator_vthrow(struct Dee_function_generator *__restrict self) { loc = self->fg_state->ms_stackv[stackc - 1]; ASSERT(!(loc.ml_flags & MEMLOC_F_NOREF)); --self->fg_state->ms_stackc; - if unlikely(Dee_function_generator_gcall_c_function(self, &DeeError_Throw_inherited, 1, &loc)) + if unlikely(_Dee_function_generator_gcall_c_function(self, &DeeError_Throw_inherited, 1, &loc)) goto err; - return Dee_function_generator_gexcept(self); + return Dee_function_generator_gjmp_except(self); err: return -1; } @@ -1244,13 +1555,64 @@ Dee_function_generator_vthrow(struct Dee_function_generator *__restrict self) { * @param: cc: One of `VCALLOP_CC_*', describing the calling-convention of `api_function' */ INTERN WUNUSED NONNULL((1)) int DCALL Dee_function_generator_vcallop(struct Dee_function_generator *__restrict self, - void *api_function, unsigned int cc, size_t argc) { - /* TODO: Implement portably once arch-specific jumps and labels can be created */ - (void)self; - (void)api_function; - (void)cc; - (void)argc; - return DeeError_NOTIMPLEMENTED(); + void *api_function, unsigned int cc, uint16_t argc) { + struct Dee_memloc *loc, *argv; + if unlikely(Dee_function_generator_state_unshare(self)) + goto err; + if unlikely(argc > self->fg_state->ms_stackc) + return err_illegal_stack_effect(); + + /* Save argument memory locations from before the flush. This is because after the + * flush, registers are written to the stack, and if we were to pass the then-current + * `Dee_memloc' to `_Dee_function_generator_gcall_c_function', it would have to load + * those values from the stack (when they can also be found in registers) */ + loc = self->fg_state->ms_stackv; + loc += self->fg_state->ms_stackc; + loc -= argc; + argv = (struct Dee_memloc *)Dee_Mallocac(argc, sizeof(struct Dee_memloc)); + if unlikely(!argv) + goto err; + argv = (struct Dee_memloc *)memcpyc(argv, loc, argc, sizeof(struct Dee_memloc)); + + /* Flush registers that don't appear in the top `argc' stack locations. */ + if unlikely(Dee_function_generator_gflushregs(self, argc)) + goto err_argv; + + /* Call the actual C function */ + if unlikely(_Dee_function_generator_gcall_c_function(self, api_function, argc, argv)) + goto err_argv; + Dee_Freea(argv); + if unlikely(Dee_function_generator_vpush_reg(self, HOST_REGISTER_RETURN)) + goto err; + loc = Dee_function_generator_vtop(self); + if (cc == VCALLOP_CC_OBJECT) { + if unlikely(Dee_function_generator_gjz_except(self, loc)) + goto err; + /* Clear the NOREF flag now that we know the return value to be non-NULL */ + loc = Dee_function_generator_vtop(self); + ASSERT(loc->ml_flags & MEMLOC_F_NOREF); + loc->ml_flags &= ~MEMLOC_F_NOREF; + + /* Rotate the return value to be located *below* arguments (which get popped below) */ + if unlikely(Dee_function_generator_vrrot(self, argc + 1)) + goto err; + } else { + if unlikely(Dee_function_generator_gjnz_except(self, loc)) + goto err; + ASSERT(Dee_function_generator_vtop(self)->ml_flags & MEMLOC_F_NOREF); + } + + /* Pop function arguments. */ + while (argc) { + --argc; + if unlikely(Dee_function_generator_vpop(self)) + goto err; + } + return 0; +err_argv: + Dee_Freea(argv); +err: + return -1; } @@ -1479,12 +1841,67 @@ Dee_function_generator_geninstr(struct Dee_function_generator *__restrict self, //TODO: case ASM16_PUSH_BND_GLOBAL: //TODO: case ASM_PUSH_BND_LOCAL: //TODO: case ASM16_PUSH_BND_LOCAL: - //TODO: case ASM_JF: - //TODO: case ASM_JF16: - //TODO: case ASM_JT: - //TODO: case ASM_JT16: - //TODO: case ASM_JMP: - //TODO: case ASM_JMP16: + + case ASM_JF: + case ASM_JF16: + case ASM_JT: + case ASM_JT16: + case ASM_JMP: + case ASM_JMP16: + case ASM32_JMP: { + int temp; + struct Dee_jump_descriptor *desc; + struct Dee_basic_block *target; +do_jcc: + desc = Dee_jump_descriptors_lookup(&self->fg_block->bb_exits, instr); + ASSERTF(desc, "Jump at +%.4" PRFx32 " should have been found by the loader", + Dee_function_assembler_addrof(self->fg_assembler, instr)); + target = desc->jd_to; + switch (opcode & 0xff) { + case ASM_JF: + temp = Dee_function_generator_vjf(self, target); + break; + case ASM_JT: + temp = Dee_function_generator_vjt(self, target); + break; + case ASM_JMP: + case ASM32_JMP: { + /* Check for special case: if the jump happens at the end of the + * block (which it always should be due to the loader trimming + * anything that happens after a noreturn instruction), then don't + * generate any additional code but instead set the block's fallthru + * next-pointer to point at the target-block. + * + * That way, the jump will be generated during the block-stitching + * phase, at which point any additional memory transformation that + * might be necessary can be appended to the original block (while + * also allowing blocks to seamlessly flow into each other) */ + Dee_instruction_t const *after_jmp; + after_jmp = DeeAsm_NextInstr(instr); + if likely(after_jmp >= self->fg_block->bb_deemon_end) { + self->fg_block->bb_next = target; + self->fg_block->bb_deemon_end = instr; /* The jump doesn't exist anymore! */ + /* Get rid of the jump */ + Dee_jump_descriptors_remove(&self->fg_block->bb_exits, desc); + Dee_jump_descriptors_remove(&target->bb_entries, desc); + Dee_jump_descriptor_destroy(desc); + return 0; + } + temp = Dee_function_generator_gjmp(self, target); + } break; + + default: __builtin_unreachable(); + } + if unlikely(temp) + goto err; + + /* Remember the memory-state as it is when the jump is made. */ + ASSERTF(!desc->jd_stat, "Who assigned this? Doing that is *my* job!"); + desc->jd_stat = self->fg_state; + Dee_memstate_incref(desc->jd_stat); + return 0; + } break; + //TODO: case ASM_FOREACH: //TODO: case ASM_FOREACH16: //TODO: case ASM_JMP_POP: @@ -1582,32 +1999,9 @@ Dee_function_generator_geninstr(struct Dee_function_generator *__restrict self, //TODO: case ASM_NOT: case ASM_ASSIGN: + return Dee_function_generator_vopv(self, OPERATOR_ASSIGN, 2); case ASM_MOVE_ASSIGN: - case ASM_DELATTR: - case ASM_SETATTR: - case ASM_DELITEM: - case ASM_SETITEM: - case ASM_DELRANGE: - case ASM_SETRANGE: - case ASM_LEAVE: { - uint16_t opname; - uint16_t argc; - switch (opcode) { - case ASM_ASSIGN: opname = OPERATOR_ASSIGN, argc = 2; break; - case ASM_MOVE_ASSIGN: opname = OPERATOR_MOVEASSIGN, argc = 2; break; - case ASM_DELATTR: opname = OPERATOR_DELATTR, argc = 2; break; - case ASM_SETATTR: opname = OPERATOR_SETATTR, argc = 3; break; - case ASM_DELITEM: opname = OPERATOR_DELITEM, argc = 2; break; - case ASM_SETITEM: opname = OPERATOR_SETITEM, argc = 3; break; - case ASM_DELRANGE: opname = OPERATOR_DELRANGE, argc = 3; break; - case ASM_SETRANGE: opname = OPERATOR_SETRANGE, argc = 4; break; - case ASM_LEAVE: opname = OPERATOR_LEAVE, argc = 1; break; - default: __builtin_unreachable(); - } - if unlikely(Dee_function_generator_vop(self, opname, argc)) - goto err; - return Dee_function_generator_vpop(self); - } break; + return Dee_function_generator_vopv(self, OPERATOR_MOVEASSIGN, 2); case ASM_COPY: return Dee_function_generator_vop(self, OPERATOR_COPY, 1); @@ -1615,6 +2009,10 @@ Dee_function_generator_geninstr(struct Dee_function_generator *__restrict self, return Dee_function_generator_vop(self, OPERATOR_DEEPCOPY, 1); case ASM_GETATTR: return Dee_function_generator_vop(self, OPERATOR_GETATTR, 2); + case ASM_DELATTR: + return Dee_function_generator_vopv(self, OPERATOR_DELATTR, 2); + case ASM_SETATTR: + return Dee_function_generator_vopv(self, OPERATOR_SETATTR, 3); //TODO: case ASM_BOUNDATTR: @@ -1861,13 +2259,12 @@ Dee_function_generator_geninstr(struct Dee_function_generator *__restrict self, //TODO: case ASM_PRINT_C_NL: //TODO: case ASM_RANGE_0_I16: - case ASM_ENTER: { + case ASM_ENTER: if unlikely(Dee_function_generator_vdup(self)) goto err; - if unlikely(Dee_function_generator_vop(self, OPERATOR_ENTER, 1)) - goto err; - return Dee_function_generator_vpop(self); - } break; + return Dee_function_generator_vopv(self, OPERATOR_ENTER, 1); + case ASM_LEAVE: + return Dee_function_generator_vopv(self, OPERATOR_LEAVE, 1); //TODO: case ASM_FPRINT_C: //TODO: case ASM_FPRINT_C_SP: @@ -1937,6 +2334,10 @@ Dee_function_generator_geninstr(struct Dee_function_generator *__restrict self, case ASM_ITERSELF: return Dee_function_generator_vop(self, OPERATOR_ITERSELF, 1); + case ASM_DELITEM: + return Dee_function_generator_vopv(self, OPERATOR_DELITEM, 2); + case ASM_SETITEM: + return Dee_function_generator_vopv(self, OPERATOR_SETITEM, 3); case ASM_GETRANGE: return Dee_function_generator_vop(self, OPERATOR_GETRANGE, 3); case ASM_GETRANGE_PN: @@ -1956,6 +2357,10 @@ Dee_function_generator_geninstr(struct Dee_function_generator *__restrict self, //TODO: case ASM_GETRANGE_IN: //TODO: case ASM_GETRANGE_II: + case ASM_DELRANGE: + return Dee_function_generator_vopv(self, OPERATOR_DELRANGE, 3); + case ASM_SETRANGE: + return Dee_function_generator_vopv(self, OPERATOR_SETRANGE, 4); case ASM_SETRANGE_PN: if unlikely(Dee_function_generator_vpush_const(self, Dee_None)) goto err; @@ -2075,7 +2480,6 @@ Dee_function_generator_geninstr(struct Dee_function_generator *__restrict self, //TODO: case ASM_CALL_LOCAL: //TODO: case ASM16_CALL_LOCAL: - //TODO: case ASM32_JMP: //TODO: case ASM_CALL_SEQ: //TODO: case ASM_CALL_MAP: @@ -2218,13 +2622,19 @@ Dee_function_generator_geninstr(struct Dee_function_generator *__restrict self, prefix_opcode = (prefix_opcode << 8) | prefix_instr[1]; switch (prefix_opcode) { + case ASM_JF: /* jf PREFIX, */ + case ASM_JF16: /* jf PREFIX, */ + case ASM_JT: /* jt PREFIX, */ + case ASM_JT16: /* jt PREFIX, */ + if unlikely(Dee_function_generator_vpush_prefix(self, prefix_type, id1, id2)) + goto err; + opcode = prefix_opcode; + /* Need to do this in a special way because `instr' must not become `prefix_instr' here. */ + goto do_jcc; + case ASM_RET: /* ret PREFIX */ case ASM_THROW: /* throw PREFIX */ case ASM_SETRET: /* setret PREFIX */ - case ASM_JF: /* jf PREFIX, */ - case ASM_JF16: /* jf PREFIX, */ - case ASM_JT: /* jt PREFIX, */ - case ASM_JT16: /* jt PREFIX, */ case ASM_POP_STATIC: /* mov static , PREFIX */ case ASM16_POP_STATIC: /* mov static , PREFIX */ case ASM_POP_EXTERN: /* mov extern :, PREFIX */ diff --git a/src/dex/_hostasm/host.h b/src/dex/_hostasm/host.h new file mode 100644 index 000000000..0d84fe636 --- /dev/null +++ b/src/dex/_hostasm/host.h @@ -0,0 +1,262 @@ +/* Copyright (c) 2018-2023 Griefer@Work * + * * + * This software is provided 'as-is', without any express or implied * + * warranty. In no event will the authors be held liable for any damages * + * arising from the use of this software. * + * * + * Permission is granted to anyone to use this software for any purpose, * + * including commercial applications, and to alter it and redistribute it * + * freely, subject to the following restrictions: * + * * + * 1. The origin of this software must not be misrepresented; you must not * + * claim that you wrote the original software. If you use this software * + * in a product, an acknowledgement (see the following) in the product * + * documentation is required: * + * Portions Copyright (c) 2018-2023 Griefer@Work * + * 2. Altered source versions must be plainly marked as such, and must not be * + * misrepresented as being the original software. * + * 3. This notice may not be removed or altered from any source distribution. * + */ +#ifndef GUARD_DEX_HOSTASM_HOST_H +#define GUARD_DEX_HOSTASM_HOST_H 1 + +#include +#include +#include + +#include +#include +#include + +#include + +#ifdef CONFIG_HOST_WINDOWS +#include +#endif /* CONFIG_HOST_WINDOWS */ + +#undef byte_t +#define byte_t __BYTE_TYPE__ + +#ifdef CONFIG_NO_LIBHOSTASM +#undef CONFIG_NO_LIBHOSTASM +#undef CONFIG_HAVE_LIBHOSTASM +#else /* CONFIG_NO_LIBHOSTASM */ +#define CONFIG_HAVE_LIBHOSTASM + +/* Check for ARCH support */ +#if !(defined(__i386__) || defined(__x86_64__)) +#undef CONFIG_HAVE_LIBHOSTASM +#endif /* ... */ + +/* Check for OS support */ +#undef CONFIG_hostfunc_USES_VirtualAlloc +#undef CONFIG_hostfunc_USES_mmap +#if defined(CONFIG_HOST_WINDOWS) +#define CONFIG_hostfunc_USES_VirtualAlloc +#elif ((defined(CONFIG_HAVE_mmap64) || defined(CONFIG_HAVE_mmap)) && \ + defined(CONFIG_HAVE_mprotect) && defined(CONFIG_HAVE_MAP_ANON) && \ + defined(CONFIG_HAVE_PROT_READ) && defined(CONFIG_HAVE_PROT_WRITE) && \ + defined(CONFIG_HAVE_PROT_EXEC)) +#ifndef CONFIG_HAVE_mmap +#define CONFIG_HAVE_mmap +#undef mmap +#define mmap mmap64 +#endif /* !CONFIG_HAVE_mmap */ +#define CONFIG_hostfunc_USES_mmap +#else /* ... */ +#undef CONFIG_HAVE_LIBHOSTASM +#endif /* !... */ +#endif /* !CONFIG_NO_LIBHOSTASM */ + +#ifdef CONFIG_HAVE_LIBHOSTASM + +/* Select the arch for which to generate code. */ +#if defined(__i386__) || defined(__x86_64__) +#define HOSTASM_X86 +#ifdef __x86_64__ +#define HOSTASM_X86_64 +#ifdef CONFIG_HOST_WINDOWS +#define HOSTASM_X86_64_MSABI +#else /* CONFIG_HOST_WINDOWS */ +#define HOSTASM_X86_64_SYSVABI +#endif /* !CONFIG_HOST_WINDOWS */ +#endif /* __x86_64__ */ +#else /* ... */ +#error "Unsupported architecture" +#endif /* !... */ + +#ifdef HOSTASM_X86_64 +#define HOST_SIZEOF_POINTER 8 +#elif defined(HOSTASM_X86) +#define HOST_SIZEOF_POINTER 4 +#endif /* ... */ + + +#ifdef HOSTASM_X86 +#undef LIBGEN86_TARGET_BITS +#define LIBGEN86_TARGET_BITS (HOST_SIZEOF_POINTER * 8) +#include "libgen86/register.h" +#endif /* HOSTASM_X86 */ + +#define HOSTASM_STACK_GROWS_DOWN + + +/* Figure out how many scratch registers this host gives us. */ +#ifdef HOSTASM_X86_64_MSABI +#define HOST_REGISTER_RETURN HOST_REGISTER_RAX +#define HOST_REGISTER_RAX 0 +#define HOST_REGISTER_RCX 1 +#define HOST_REGISTER_RDX 2 +#define HOST_REGISTER_R8 3 +#define HOST_REGISTER_R9 4 +#define HOST_REGISTER_R10 5 +#define HOST_REGISTER_R11 6 +#define HOST_REGISTER_COUNT 7 /* %rax, %rcx, %rdx, %r8, %r9, %r10, %r11 */ +#elif defined(HOSTASM_X86_64_SYSVABI) +#define HOST_REGISTER_RETURN HOST_REGISTER_RAX +#define HOST_REGISTER_RAX 0 +#define HOST_REGISTER_RCX 1 +#define HOST_REGISTER_RDX 2 +#define HOST_REGISTER_RDI 3 +#define HOST_REGISTER_RSI 4 +#define HOST_REGISTER_R8 5 +#define HOST_REGISTER_R9 6 +#define HOST_REGISTER_R10 7 +#define HOST_REGISTER_R11 8 +#define HOST_REGISTER_COUNT 9 /* %rax, %rcx, %rdx, %rdi, %rsi, %r8, %r9, %r10, %r11 */ +#elif defined(HOSTASM_X86) +#define HOST_REGISTER_RETURN HOST_REGISTER_EAX +#define HOST_REGISTER_EAX 0 +#define HOST_REGISTER_ECX 1 +#define HOST_REGISTER_EDX 2 +#define HOST_REGISTER_COUNT 3 /* %eax, %ecx, %edx */ +#endif /* ... */ + + +/* Arguments registers in the `DCALL' ABI */ +#ifdef HOSTASM_X86_64_MSABI +#define HOST_REGISTER_R_ARG0 HOST_REGISTER_RCX +#define HOST_REGISTER_R_ARG1 HOST_REGISTER_RDX +#define HOST_REGISTER_R_ARG2 HOST_REGISTER_R8 +#define HOST_REGISTER_R_ARG3 HOST_REGISTER_R9 +#elif defined(HOSTASM_X86_64_SYSVABI) +#define HOST_REGISTER_R_ARG0 HOST_REGISTER_RDI +#define HOST_REGISTER_R_ARG1 HOST_REGISTER_RSI +#define HOST_REGISTER_R_ARG2 HOST_REGISTER_RDX +#define HOST_REGISTER_R_ARG3 HOST_REGISTER_RCX +#endif /* ... */ + + +DECL_BEGIN + +typedef uint8_t Dee_host_register_t; + +/* Host function assembly calling convention (one of `HOSTFUNC_CC_*'). */ +typedef uint8_t Dee_hostfunc_cc_t; + +/* Possible calling convention flags. */ +#define HOSTFUNC_CC_F_KW 1 +#define HOSTFUNC_CC_F_THIS 2 +#define HOSTFUNC_CC_F_TUPLE 4 + +/* Possible calling conventions. */ +#define HOSTFUNC_CC_CALL 0 /* DREF DeeObject *(DCALL *)(size_t argc, DeeObject *const *argv); */ +#define HOSTFUNC_CC_CALL_KW 1 /* DREF DeeObject *(DCALL *)(size_t argc, DeeObject *const *argv, DeeObject *kw); */ +#define HOSTFUNC_CC_THISCALL 2 /* DREF DeeObject *(DCALL *)(DeeObject *self, size_t argc, DeeObject *const *argv); */ +#define HOSTFUNC_CC_THISCALL_KW 3 /* DREF DeeObject *(DCALL *)(DeeObject *self, size_t argc, DeeObject *const *argv, DeeObject *kw); */ +#define HOSTFUNC_CC_CALL_TUPLE 4 /* DREF DeeObject *(DCALL *)(DeeObject *args); */ +#define HOSTFUNC_CC_CALL_TUPLE_KW 5 /* DREF DeeObject *(DCALL *)(DeeObject *args, DeeObject *kw); */ +#define HOSTFUNC_CC_THISCALL_TUPLE 6 /* DREF DeeObject *(DCALL *)(DeeObject *self, DeeObject *args); */ +#define HOSTFUNC_CC_THISCALL_TUPLE_KW 7 /* DREF DeeObject *(DCALL *)(DeeObject *self, DeeObject *args, DeeObject *kw); */ +#define HOSTFUNC_CC_COUNT 8 +union Dee_hostfunc_entry { + DREF DeeObject *(DCALL *hfe_call)(size_t argc, DeeObject *const *argv); /* HOSTFUNC_CC_CALL */ + DREF DeeObject *(DCALL *hfe_call_kw)(size_t argc, DeeObject *const *argv, DeeObject *kw); /* HOSTFUNC_CC_CALL_KW */ + DREF DeeObject *(DCALL *hfe_thiscall)(DeeObject *self, size_t argc, DeeObject *const *argv); /* HOSTFUNC_CC_THISCALL */ + DREF DeeObject *(DCALL *hfe_thiscall_kw)(DeeObject *self, size_t argc, DeeObject *const *argv, DeeObject *kw); /* HOSTFUNC_CC_THISCALL_KW */ + DREF DeeObject *(DCALL *hfe_call_tuple)(DeeObject *args); /* HOSTFUNC_CC_CALL_TUPLE */ + DREF DeeObject *(DCALL *hfe_call_tuple_kw)(DeeObject *args, DeeObject *kw); /* HOSTFUNC_CC_CALL_TUPLE_KW */ + DREF DeeObject *(DCALL *hfe_thiscall_tuple)(DeeObject *self, DeeObject *args); /* HOSTFUNC_CC_THISCALL_TUPLE */ + DREF DeeObject *(DCALL *hfe_thiscall_tuple_kw)(DeeObject *self, DeeObject *args, DeeObject *kw); /* HOSTFUNC_CC_THISCALL_TUPLE_KW */ +}; + + +struct Dee_hostfunc { + union Dee_hostfunc_entry hf_entry; /* Function entry point. */ + void *_hf_base; /* Mmap base address. */ + size_t _hf_size; /* Mmap size address. */ +}; + +#ifdef __CYGWIN__ +/* Cygwin's `getpagesize' is broken in that it returns the + * allocation granularity instead of the actual page-size. */ +#undef getpagesize +#define getpagesize() 4096 +#elif !defined(CONFIG_HAVE_getpagesize) +#ifdef __ARCH_PAGESIZE +#define getpagesize() __ARCH_PAGESIZE +#elif defined(PAGESIZE) +#define getpagesize() PAGESIZE +#elif defined(PAGE_SIZE) +#define getpagesize() PAGE_SIZE +#elif defined(EXEC_PAGESIZE) +#define getpagesize() EXEC_PAGESIZE +#elif defined(NBPG) && defined(CLSIZE) +#define getpagesize() (NBPG * CLSIZE) +#elif defined(NBPG) +#define getpagesize() NBPG +#elif defined(_SC_PAGESIZE) +#define getpagesize() sysconf(_SC_PAGESIZE) +#elif defined(_SC_PAGE_SIZE) +#define getpagesize() sysconf(_SC_PAGE_SIZE) +#elif defined(CONFIG_HOST_WINDOWS) +#define getpagesize() dee_nt_getpagesize() +PRIVATE ATTR_CONST size_t DCALL dee_nt_getpagesize(void) { + static size_t ps = 0; + if (ps == 0) { + SYSTEM_INFO system_info; + GetSystemInfo(&system_info); + ps = system_info.dwPageSize; + if unlikely(ps == 0) + ps = 1; + } + return ps; +} +#else /* ... */ +#define getpagesize() 4096 /* Just guess... */ +#endif /* !... */ +#endif /* !CONFIG_HAVE_getpagesize */ + + +#if defined(CONFIG_hostfunc_USES_VirtualAlloc) +#define Dee_hostfunc_init(self, size) \ + (((self)->_hf_size = CEIL_ALIGN(size, getpagesize())), \ + ((self)->_hf_base = VirtualAlloc(NULL, (self)->_hf_size, MEM_COMMIT, PAGE_READWRITE)) != NULL ? 0 : -1) +#define Dee_hostfunc_fini(self) (void)VirtualFree((self)->_hf_base, 0, MEM_RELEASE) +#define Dee_hostfunc_mkexec(self) Dee_hostfunc_mkexec(self) +LOCAL WUNUSED NONNULL((1)) int (DCALL Dee_hostfunc_mkexec)(struct Dee_hostfunc *__restrict self) { + DWORD dwTemp; + return VirtualProtect(self->_hf_base, self->_hf_size, PAGE_EXECUTE_READ, &dwTemp) ? 0 : -1; +} +#elif defined(CONFIG_hostfunc_USES_mmap) +#ifndef MAP_FAILED +#define MAP_FAILED ((void *)(uintptr_t)-1) +#endif /* !MAP_FAILED */ +#define Dee_hostfunc_init(self, size) \ + (((self)->_hf_size = CEIL_ALIGN(size, getpagesize())), \ + ((self)->_hf_base = mmap(NULL, (self)->_hf_size, PROT_READ | PROT_WRITE, \ + MAP_PRIVATE | MAP_ANON, -1, 0)) != MAP_FAILED ? 0 : -1) +#define Dee_hostfunc_fini(self) (void)munmap((self)->_hf_base, (self)->_hf_size) +#define Dee_hostfunc_mkexec(self) mprotect((self)->_hf_base, (self)->_hf_size, PROT_READ | PROT_EXEC) +#else /* ... */ +#define Dee_hostfunc_init(self, size) \ + (((self)->_hf_size = CEIL_ALIGN(size, getpagesize())), \ + ((self)->_hf_base = Dee_TryMalloc((self)->_hf_size)) != NULL ? 0 : -1) +#define Dee_hostfunc_fini(self) Dee_Free((self)->_hf_base) +#define Dee_hostfunc_mkexec(self) 0 +#endif /* !... */ + +DECL_END +#endif /* CONFIG_HAVE_LIBHOSTASM */ + +#endif /* !GUARD_DEX_HOSTASM_HOST_H */ diff --git a/src/dex/_hostasm/libgen86/gen.h b/src/dex/_hostasm/libgen86/gen.h index c314057d4..e72916a72 100644 --- a/src/dex/_hostasm/libgen86/gen.h +++ b/src/dex/_hostasm/libgen86/gen.h @@ -431,6 +431,7 @@ genArithBlock("cmp", 0x38); #define GEN86_CC_NG 14 /* Not Greater (signed) */ #define GEN86_CC_G 15 /* Greater (signed) */ #define GEN86_CC_NLE 15 /* Not Less or Equal (signed) */ +#define GEN86_CC_COUNT 16 /* Conditional jumps * @param: cc: One of `GEN86_CC_*' */ @@ -834,19 +835,19 @@ genShift("sar", 7); #define gen86_outl_eax_imm(p_pc, imm) /* outl %eax, $imm */ (_gen86_pfx32_(p_pc) _gen86_putb(p_pc, 0xe7), _gen86_putb(p_pc, imm)) #if LIBGEN86_TARGET_BITS == 16 -#define gen86_callP(p_pc, addr) /* call addr */ (_gen86_putb(p_pc, 0xe8), _gen86_putsw_pcrel(p_pc, addr)) -#define gen86_jmpP(p_pc, addr) /* jmp addr */ (_gen86_putb(p_pc, 0xe9), _gen86_putsw_pcrel(p_pc, addr)) -#define gen86_callP_offset(p_pc, offset) /* call 1f+offset; 1: */ (_gen86_putb(p_pc, 0xe8), _gen86_putsw(p_pc, offset)) -#define gen86_jmpP_offset(p_pc, offset) /* jmp 1f+offset; 1: */ (_gen86_putb(p_pc, 0xe9), _gen86_putsw(p_pc, offset)) +#define gen86_callP(p_pc, addr) /* call addr */ (_gen86_putb(p_pc, 0xe8), _gen86_putsw_pcrel(p_pc, addr)) +#define gen86_jmpP(p_pc, addr) /* jmp addr */ (_gen86_putb(p_pc, 0xe9), _gen86_putsw_pcrel(p_pc, addr)) +#define gen86_callP_offset(p_pc, offset) /* call 1f+offset-2; 1: */ (_gen86_putb(p_pc, 0xe8), _gen86_putsw(p_pc, offset)) +#define gen86_jmpP_offset(p_pc, offset) /* jmp 1f+offset-2; 1: */ (_gen86_putb(p_pc, 0xe9), _gen86_putsw(p_pc, offset)) #define gen86_callw gen86_callP #define gen86_jmpw gen86_jmpP #define gen86_callw_offset gen86_callP_offset #define gen86_jmpw_offset gen86_jmpP_offset #else /* LIBGEN86_TARGET_BITS == ... */ -#define gen86_callP(p_pc, addr) /* call addr */ (_gen86_putb(p_pc, 0xe8), _gen86_putsl_pcrel(p_pc, addr)) -#define gen86_jmpP(p_pc, addr) /* jmp addr */ (_gen86_putb(p_pc, 0xe9), _gen86_putsl_pcrel(p_pc, addr)) -#define gen86_callP_offset(p_pc, offset) /* call 1f+offset; 1: */ (_gen86_putb(p_pc, 0xe8), _gen86_putsl(p_pc, offset)) -#define gen86_jmpP_offset(p_pc, offset) /* jmp 1f+offset; 1: */ (_gen86_putb(p_pc, 0xe9), _gen86_putsl(p_pc, offset)) +#define gen86_callP(p_pc, addr) /* call addr */ (_gen86_putb(p_pc, 0xe8), _gen86_putsl_pcrel(p_pc, addr)) +#define gen86_jmpP(p_pc, addr) /* jmp addr */ (_gen86_putb(p_pc, 0xe9), _gen86_putsl_pcrel(p_pc, addr)) +#define gen86_callP_offset(p_pc, offset) /* call 1f+offset-4; 1: */ (_gen86_putb(p_pc, 0xe8), _gen86_putsl(p_pc, offset)) +#define gen86_jmpP_offset(p_pc, offset) /* jmp 1f+offset-4; 1: */ (_gen86_putb(p_pc, 0xe9), _gen86_putsl(p_pc, offset)) #define gen86_calll gen86_callP #define gen86_jmpl gen86_jmpP #define gen86_calll_offset gen86_callP_offset @@ -1186,8 +1187,8 @@ genShift("sar", 7); #endif /* LIBGEN86_TARGET_BITS == 64 */ #define gen86_lfence(p_pc) /* lfence */ _gen86_putb3(p_pc, 0x0f, 0xae, 0xe8) -#define gen86_mfence(p_pc) /* lfence */ _gen86_putb3(p_pc, 0x0f, 0xae, 0xf0) -#define gen86_sfence(p_pc) /* lfence */ _gen86_putb3(p_pc, 0x0f, 0xae, 0xf8) +#define gen86_mfence(p_pc) /* mfence */ _gen86_putb3(p_pc, 0x0f, 0xae, 0xf0) +#define gen86_sfence(p_pc) /* sfence */ _gen86_putb3(p_pc, 0x0f, 0xae, 0xf8) #define gen86_imulw_mod_r(p_pc, gen_modrm, reg, ...) /* imulw ..., %reg */ (_gen86_pfx16_(p_pc) gen_modrm((p_pc, 0, _gen86_putb2(p_pc, 0x0f, 0xaf), reg, __VA_ARGS__, (void)0))) #define gen86_imull_mod_r(p_pc, gen_modrm, reg, ...) /* imull ..., %reg */ (_gen86_pfx32_(p_pc) gen_modrm((p_pc, 0, _gen86_putb2(p_pc, 0x0f, 0xaf), reg, __VA_ARGS__, (void)0))) diff --git a/src/dex/_hostasm/libgen86/register.h b/src/dex/_hostasm/libgen86/register.h index 417cf1bcc..6d7861c6d 100644 --- a/src/dex/_hostasm/libgen86/register.h +++ b/src/dex/_hostasm/libgen86/register.h @@ -197,8 +197,8 @@ (__INT64_TYPE__)(__UINT64_TYPE__)(value)) #endif /* LIBGEN86_TARGET_BITS != 64 */ -#define _gen86_putb(p_pc, b) (void)(*(*(p_pc))++ = (__UINT8_TYPE__)(b)) -#define _gen86_putsb(p_pc, b) (void)(*(*(p_pc))++ = (__UINT8_TYPE__)(__INT8_TYPE__)(b)) +#define _gen86_putb(p_pc, b) (void)(**(p_pc) = (__UINT8_TYPE__)(b), ++*(p_pc)) +#define _gen86_putsb(p_pc, b) (void)(**(p_pc) = (__UINT8_TYPE__)(__INT8_TYPE__)(b), ++*(p_pc)) #define _gen86_putw(p_pc, w) (void)(__hybrid_unaligned_setle16(*(p_pc), (__UINT16_TYPE__)(w)), *(p_pc) += 2) #define _gen86_putsw(p_pc, w) (void)(__hybrid_unaligned_setle16(*(p_pc), (__UINT16_TYPE__)(__INT16_TYPE__)(w)), *(p_pc) += 2) #define _gen86_putl(p_pc, l) (void)(__hybrid_unaligned_setle32(*(p_pc), (__UINT32_TYPE__)(l)), *(p_pc) += 4) diff --git a/src/dex/_hostasm/libhostasm.h b/src/dex/_hostasm/libhostasm.h index 8306b621a..33b23832f 100644 --- a/src/dex/_hostasm/libhostasm.h +++ b/src/dex/_hostasm/libhostasm.h @@ -20,6 +20,9 @@ #ifndef GUARD_DEX_HOSTASM_LIBHOSTASM_H #define GUARD_DEX_HOSTASM_LIBHOSTASM_H 1 +#include "host.h" +/**/ + #include #include #include @@ -32,77 +35,6 @@ #include #include -#ifdef CONFIG_HOST_WINDOWS -#include -#endif /* CONFIG_HOST_WINDOWS */ - -#undef byte_t -#define byte_t __BYTE_TYPE__ - -#ifdef CONFIG_NO_LIBHOSTASM -#undef CONFIG_NO_LIBHOSTASM -#undef CONFIG_HAVE_LIBHOSTASM -#else /* CONFIG_NO_LIBHOSTASM */ -#define CONFIG_HAVE_LIBHOSTASM - -/* Check for ARCH support */ -#if !(defined(__i386__) || defined(__x86_64__)) -#undef CONFIG_HAVE_LIBHOSTASM -#endif /* ... */ - -/* Check for OS support */ -#undef CONFIG_hostfunc_USES_VirtualAlloc -#undef CONFIG_hostfunc_USES_mmap -#if defined(CONFIG_HOST_WINDOWS) -#define CONFIG_hostfunc_USES_VirtualAlloc -#elif ((defined(CONFIG_HAVE_mmap64) || defined(CONFIG_HAVE_mmap)) && \ - defined(CONFIG_HAVE_mprotect) && defined(CONFIG_HAVE_MAP_ANON) && \ - defined(CONFIG_HAVE_PROT_READ) && defined(CONFIG_HAVE_PROT_WRITE) && \ - defined(CONFIG_HAVE_PROT_EXEC)) -#ifndef CONFIG_HAVE_mmap -#define CONFIG_HAVE_mmap -#undef mmap -#define mmap mmap64 -#endif /* !CONFIG_HAVE_mmap */ -#define CONFIG_hostfunc_USES_mmap -#else /* ... */ -#undef CONFIG_HAVE_LIBHOSTASM -#endif /* !... */ -#endif /* !CONFIG_NO_LIBHOSTASM */ - -#ifdef CONFIG_HAVE_LIBHOSTASM - -/* Select the arch for which to generate code. */ -#if defined(__i386__) || defined(__x86_64__) -#define HOSTASM_X86 -#ifdef __x86_64__ -#define HOSTASM_X86_64 -#ifdef CONFIG_HOST_WINDOWS -#define HOSTASM_X86_64_MSABI -#else /* CONFIG_HOST_WINDOWS */ -#define HOSTASM_X86_64_SYSVABI -#endif /* !CONFIG_HOST_WINDOWS */ -#endif /* __x86_64__ */ -#else /* ... */ -#error "Unsupported architecture" -#endif /* !... */ - -#ifdef HOSTASM_X86_64 -#define HOST_SIZEOF_POINTER 8 -#elif defined(HOSTASM_X86) -#define HOST_SIZEOF_POINTER 4 -#endif /* ... */ - - -#ifdef HOSTASM_X86 -#undef LIBGEN86_TARGET_BITS -#define LIBGEN86_TARGET_BITS (HOST_SIZEOF_POINTER * 8) -#include "libgen86/register.h" -#endif /* HOSTASM_X86 */ - -#define HOSTASM_STACK_GROWS_DOWN - - /* Convert compiled deemon code to host machine assembly (currently: only x86) * * @@ -169,6 +101,7 @@ * * ============= FUTURE DIRECTIONS ============= * + * - Turn `libgen86' into a git module that is shared between deemon and KOS * - Add a heuristic to DeeFunctionObject that keeps track of how often * the function has been called. If that count exceeds some limit, * automatically replace the function with its hostasm version. @@ -185,159 +118,28 @@ * - Generating code differently such that the deemon stack and locals * aren't used %Psp-relative, but (e.g.) %Pbp. * - Calculate the max stack/locals blob size at (re-)compile-time - * - When a yield function returns, it gives back 2 values + * - When a yield function yields, it first saves all registers to the blob + * - When a yield function yields, it gives back 2 values * - DREF DeeObject * -- The yielded value * - void * -- The "resume PC" * The "resume PC" must be loaded by: * - Loading the stack/locals blob into %Pbp * - Pushing the return PC onto %Psp * - Jumping to the "resume PC" - * - The stack/locals blob is allocated/owned by - * + * - Code at the "resume PC" then loads saved registers from the blob + * - The stack/locals blob is then allocated/owned by the caller + * - When code does a call to an extern/global symbol (that is already + * assigned, is final, *and* points to another Code object), it may + * be possible to actually hard-code calls between deemon functions + * without any overhead normally related to dynamic calls. Actually, + * this same thing goes for any operator invocation where the object + * the object gets invoked on is assigned at compile-time, and its + * container is final/immutable. */ - +#ifdef CONFIG_HAVE_LIBHOSTASM DECL_BEGIN -/* Figure out how many scratch registers this host gives us. */ -#ifdef HOSTASM_X86_64_MSABI -#define HOST_REGISTER_RETURN HOST_REGISTER_RAX -#define HOST_REGISTER_RAX 0 -#define HOST_REGISTER_RCX 1 -#define HOST_REGISTER_RDX 2 -#define HOST_REGISTER_R8 3 -#define HOST_REGISTER_R9 4 -#define HOST_REGISTER_R10 5 -#define HOST_REGISTER_R11 6 -#define HOST_REGISTER_COUNT 7 /* %rax, %rcx, %rdx, %r8, %r9, %r10, %r11 */ -#elif defined(HOSTASM_X86_64_SYSVABI) -#define HOST_REGISTER_RETURN HOST_REGISTER_RAX -#define HOST_REGISTER_RAX 0 -#define HOST_REGISTER_RCX 1 -#define HOST_REGISTER_RDX 2 -#define HOST_REGISTER_RDI 3 -#define HOST_REGISTER_RSI 4 -#define HOST_REGISTER_R8 5 -#define HOST_REGISTER_R9 6 -#define HOST_REGISTER_R10 7 -#define HOST_REGISTER_R11 8 -#define HOST_REGISTER_COUNT 9 /* %rax, %rcx, %rdx, %rdi, %rsi, %r8, %r9, %r10, %r11 */ -#elif defined(HOSTASM_X86) -#define HOST_REGISTER_RETURN HOST_REGISTER_EAX -#define HOST_REGISTER_EAX 0 -#define HOST_REGISTER_ECX 1 -#define HOST_REGISTER_EDX 2 -#define HOST_REGISTER_COUNT 3 /* %eax, %ecx, %edx */ -#endif /* ... */ - -typedef uint8_t Dee_host_register_t; - -/* Host function assembly calling convention (one of `HOSTFUNC_CC_*'). */ -typedef uint8_t Dee_hostfunc_cc_t; - -/* Possible calling convention flags. */ -#define HOSTFUNC_CC_F_KW 1 -#define HOSTFUNC_CC_F_THIS 2 -#define HOSTFUNC_CC_F_TUPLE 4 - -/* Possible calling conventions. */ -#define HOSTFUNC_CC_CALL 0 /* DREF DeeObject *(DCALL *)(size_t argc, DeeObject *const *argv); */ -#define HOSTFUNC_CC_CALL_KW 1 /* DREF DeeObject *(DCALL *)(size_t argc, DeeObject *const *argv, DeeObject *kw); */ -#define HOSTFUNC_CC_THISCALL 2 /* DREF DeeObject *(DCALL *)(DeeObject *self, size_t argc, DeeObject *const *argv); */ -#define HOSTFUNC_CC_THISCALL_KW 3 /* DREF DeeObject *(DCALL *)(DeeObject *self, size_t argc, DeeObject *const *argv, DeeObject *kw); */ -#define HOSTFUNC_CC_CALL_TUPLE 4 /* DREF DeeObject *(DCALL *)(DeeObject *args); */ -#define HOSTFUNC_CC_CALL_TUPLE_KW 5 /* DREF DeeObject *(DCALL *)(DeeObject *args, DeeObject *kw); */ -#define HOSTFUNC_CC_THISCALL_TUPLE 6 /* DREF DeeObject *(DCALL *)(DeeObject *self, DeeObject *args); */ -#define HOSTFUNC_CC_THISCALL_TUPLE_KW 7 /* DREF DeeObject *(DCALL *)(DeeObject *self, DeeObject *args, DeeObject *kw); */ -#define HOSTFUNC_CC_COUNT 8 -union Dee_hostfunc_entry { - DREF DeeObject *(DCALL *hfe_call)(size_t argc, DeeObject *const *argv); /* HOSTFUNC_CC_CALL */ - DREF DeeObject *(DCALL *hfe_call_kw)(size_t argc, DeeObject *const *argv, DeeObject *kw); /* HOSTFUNC_CC_CALL_KW */ - DREF DeeObject *(DCALL *hfe_thiscall)(DeeObject *self, size_t argc, DeeObject *const *argv); /* HOSTFUNC_CC_THISCALL */ - DREF DeeObject *(DCALL *hfe_thiscall_kw)(DeeObject *self, size_t argc, DeeObject *const *argv, DeeObject *kw); /* HOSTFUNC_CC_THISCALL_KW */ - DREF DeeObject *(DCALL *hfe_call_tuple)(DeeObject *args); /* HOSTFUNC_CC_CALL_TUPLE */ - DREF DeeObject *(DCALL *hfe_call_tuple_kw)(DeeObject *args, DeeObject *kw); /* HOSTFUNC_CC_CALL_TUPLE_KW */ - DREF DeeObject *(DCALL *hfe_thiscall_tuple)(DeeObject *self, DeeObject *args); /* HOSTFUNC_CC_THISCALL_TUPLE */ - DREF DeeObject *(DCALL *hfe_thiscall_tuple_kw)(DeeObject *self, DeeObject *args, DeeObject *kw); /* HOSTFUNC_CC_THISCALL_TUPLE_KW */ -}; - - -struct Dee_hostfunc { - union Dee_hostfunc_entry hf_entry; /* Function entry point. */ - void *_hf_base; /* Mmap base address. */ - size_t _hf_size; /* Mmap size address. */ -}; - -#ifdef __CYGWIN__ -/* Cygwin's `getpagesize' is broken in that it returns the - * allocation granularity instead of the actual page-size. */ -#undef getpagesize -#define getpagesize() 4096 -#elif !defined(CONFIG_HAVE_getpagesize) -#ifdef __ARCH_PAGESIZE -#define getpagesize() __ARCH_PAGESIZE -#elif defined(PAGESIZE) -#define getpagesize() PAGESIZE -#elif defined(PAGE_SIZE) -#define getpagesize() PAGE_SIZE -#elif defined(EXEC_PAGESIZE) -#define getpagesize() EXEC_PAGESIZE -#elif defined(NBPG) && defined(CLSIZE) -#define getpagesize() (NBPG * CLSIZE) -#elif defined(NBPG) -#define getpagesize() NBPG -#elif defined(_SC_PAGESIZE) -#define getpagesize() sysconf(_SC_PAGESIZE) -#elif defined(_SC_PAGE_SIZE) -#define getpagesize() sysconf(_SC_PAGE_SIZE) -#elif defined(CONFIG_HOST_WINDOWS) -#define getpagesize() dee_nt_getpagesize() -PRIVATE ATTR_CONST size_t DCALL dee_nt_getpagesize(void) { - static size_t ps = 0; - if (ps == 0) { - SYSTEM_INFO system_info; - GetSystemInfo(&system_info); - ps = system_info.dwPageSize; - if unlikely(ps == 0) - ps = 1; - } - return ps; -} -#else /* ... */ -#define getpagesize() 4096 /* Just guess... */ -#endif /* !... */ -#endif /* !CONFIG_HAVE_getpagesize */ - - -#if defined(CONFIG_hostfunc_USES_VirtualAlloc) -#define Dee_hostfunc_init(self, size) \ - (((self)->_hf_size = CEIL_ALIGN(size, getpagesize())), \ - ((self)->_hf_base = VirtualAlloc(NULL, (self)->_hf_size, MEM_COMMIT, PAGE_READWRITE)) != NULL ? 0 : -1) -#define Dee_hostfunc_fini(self) (void)VirtualFree((self)->_hf_base, 0, MEM_RELEASE) -#define Dee_hostfunc_mkexec(self) Dee_hostfunc_mkexec(self) -LOCAL WUNUSED NONNULL((1)) int (DCALL Dee_hostfunc_mkexec)(struct Dee_hostfunc *__restrict self) { - DWORD dwTemp; - return VirtualProtect(self->_hf_base, self->_hf_size, PAGE_EXECUTE_READ, &dwTemp) ? 0 : -1; -} -#elif defined(CONFIG_hostfunc_USES_mmap) -#ifndef MAP_FAILED -#define MAP_FAILED ((void *)(uintptr_t)-1) -#endif /* !MAP_FAILED */ -#define Dee_hostfunc_init(self, size) \ - (((self)->_hf_size = CEIL_ALIGN(size, getpagesize())), \ - ((self)->_hf_base = mmap(NULL, (self)->_hf_size, PROT_READ | PROT_WRITE, \ - MAP_PRIVATE | MAP_ANON, -1, 0)) != MAP_FAILED ? 0 : -1) -#define Dee_hostfunc_fini(self) (void)munmap((self)->_hf_base, (self)->_hf_size) -#define Dee_hostfunc_mkexec(self) mprotect((self)->_hf_base, (self)->_hf_size, PROT_READ | PROT_EXEC) -#else /* ... */ -#define Dee_hostfunc_init(self, size) \ - (((self)->_hf_size = CEIL_ALIGN(size, getpagesize())), \ - ((self)->_hf_base = Dee_TryMalloc((self)->_hf_size)) != NULL ? 0 : -1) -#define Dee_hostfunc_fini(self) Dee_Free((self)->_hf_base) -#define Dee_hostfunc_mkexec(self) 0 -#endif /* !... */ - - struct Dee_memloc { #define MEMLOC_F_NORMAL 0x0000 #define MEMLOC_F_NOREF 0x0001 /* Slot contains no reference */ @@ -356,7 +158,7 @@ struct Dee_memloc { uintptr_t _ml_data; Dee_host_register_t ml_hreg; /* [valid_if(ml_where == MEMLOC_TYPE_HREG)] Host register number (< HOST_REGISTER_COUNT) */ uintptr_t ml_hstack; /* [valid_if(ml_where == MEMLOC_TYPE_HSTACK)][ALIGNED(HOST_SIZEOF_POINTER)] Host stack CFA offset */ - uint16_t ml_harg; /* [valid_if(ml_where == MEMLOC_TYPE_ARG)] Function argument number */ + uint16_t ml_harg; /* [valid_if(ml_where == MEMLOC_TYPE_ARG)] Function argument number (only when `ml_harg < :fa_code->co_argc_min') */ DeeObject *ml_const; /* [valid_if(ml_where == MEMLOC_TYPE_CONST)][1..1] Constant object */ } ml_value; }; @@ -456,6 +258,21 @@ INTDEF WUNUSED NONNULL((1)) Dee_host_register_t DCALL Dee_memstate_hregs_find_unused_ex(struct Dee_memstate *__restrict self, Dee_host_register_t const *not_these); +/* Set all members of `self->ms_regs' to `REGISTER_USAGE_GENERIC' */ +#if REGISTER_USAGE_GENERIC == 0 +#define Dee_memstate_hregs_clear_usage(self) \ + bzero((self)->ms_regs, sizeof((self)->ms_regs)) +#else /* REGISTER_USAGE_GENERIC == 0 */ +#define Dee_memstate_hregs_clear_usage(self) \ + do { \ + Dee_host_regusage_t _mhrcu_regno; \ + for (_mhrcu_regno = 0; _mhrcu_regno < HOST_REGISTER_COUNT; ++_mhrcu_regno) \ + (self)->ms_regs[_mhrcu_regno] = REGISTER_USAGE_GENERIC; \ + } __WHILE0 +#endif /* REGISTER_USAGE_GENERIC != 0 */ + + + /* Try to find a `n_bytes'-large free section of host stack memory. * @return: * : The base-CFA offset of the free section of memory * @return: (uintptr_t)-1: There is no free section of at least `n_bytes' bytes. @@ -477,24 +294,18 @@ Dee_memstate_constrainwith(struct Dee_memstate *__restrict self, struct Dee_memstate const *__restrict other); /* Functions to manipulate the virtual deemon object stack. */ +#define Dee_memstate_vtop(self) (&(self)->ms_stackv[(self)->ms_stackc - 1]) INTDEF WUNUSED NONNULL((1)) int DCALL Dee_memstate_vswap(struct Dee_memstate *__restrict self); /* ASM_SWAP */ INTDEF WUNUSED NONNULL((1)) int DCALL Dee_memstate_vlrot(struct Dee_memstate *__restrict self, size_t n); INTDEF WUNUSED NONNULL((1)) int DCALL Dee_memstate_vrrot(struct Dee_memstate *__restrict self, size_t n); INTDEF WUNUSED NONNULL((1, 2)) int DCALL Dee_memstate_vpush(struct Dee_memstate *__restrict self, struct Dee_memloc *loc); -INTDEF WUNUSED NONNULL((1, 2)) int DCALL Dee_memstate_vpush_const(struct Dee_memstate *__restrict self, DeeObject *value); +INTDEF WUNUSED NONNULL((1)) int DCALL Dee_memstate_vpush_const(struct Dee_memstate *__restrict self, DeeObject *value); +INTDEF WUNUSED NONNULL((1)) int DCALL Dee_memstate_vpush_reg(struct Dee_memstate *__restrict self, Dee_host_register_t regno); /* Sets the `MEMLOC_F_NOREF' flag */ INTDEF WUNUSED NONNULL((1)) int DCALL Dee_memstate_vpush_arg(struct Dee_memstate *__restrict self, uint16_t aid); INTDEF WUNUSED NONNULL((1)) int DCALL Dee_memstate_vdup_n(struct Dee_memstate *__restrict self, size_t n); #define Dee_memstate_vdup(self) Dee_memstate_vdup_n(self, 1) -struct Dee_host_jmpdesc { - uintptr_t hjd_bb_offset; /* Offset from the start of generated code to where this host-jmp-instruction resides. - * If this points to some delta, or some other part of the instruction is arch-specific, - * and the only portable guaranty is that it points somewhere into [instr-start,instr-end) - * - * X86: Points to the start of the signed 32-bit delta of the jcc or jmp instruction. */ -}; - struct Dee_jump_descriptor { Dee_instruction_t const *jd_from; /* [1..1][const] Deemon instruction where the jump originates from. */ struct Dee_basic_block *jd_to; /* [1..1][const] Basic block that this jump goes to. */ @@ -503,7 +314,6 @@ struct Dee_jump_descriptor { #else /* __INTELLISENSE__ */ DREF struct Dee_memstate *jd_stat; /* [0..1] Memory state at the point where `jd_from' performs its jump (or NULL if not yet generated). */ #endif /* !__INTELLISENSE__ */ - struct Dee_host_jmpdesc jd_host; /* Host-specific jmp data, including where the jmp is located in generated code. */ }; #define Dee_jump_descriptor_alloc() ((struct Dee_jump_descriptor *)Dee_Malloc(sizeof(struct Dee_jump_descriptor))) @@ -544,11 +354,29 @@ Dee_jump_descriptors_remove(struct Dee_jump_descriptors *__restrict self, struct Dee_jump_descriptor *__restrict descriptor); +/* Host relocation types. */ +#define DEE_HOST_RELOC_NONE 0 +#ifdef HOSTASM_X86 +#define DEE_HOST_RELOC_BBREL32 1 /* *(int32_t *) += (FINAL(hr_bb->bb_host_start) - ) */ +#define DEE_HOST_RELOC_BBABS32 2 /* *(int32_t *) += FINAL(hr_bb->bb_host_start) */ +#define DEE_HOST_RELOC_PTREL32 3 /* *(int32_t *) += (hr_pt - ) */ +#endif /* HOSTASM_X86 */ + +struct Dee_host_reloc { + uintptr_t hr_offset; /* Offset from `bb_host_start' to where the relocation takes place. */ + uintptr_t hr_type; /* Relocation type (one of `DEE_HOST_RELOC_*') */ + union { + struct Dee_basic_block *hr_bb; /* Basic block target */ + void *hr_pt; /* Absolute memory location */ + } hr_value; +}; + + struct Dee_basic_block { Dee_instruction_t const *bb_deemon_start; /* [1..1][<= bb_deemon_end][const] Start of deemon assembly */ Dee_instruction_t const *bb_deemon_end; /* [1..1][>= bb_deemon_start][const] End of deemon assembly */ struct Dee_jump_descriptors bb_entries; /* All of the possible ways this basic block can be entered (at `bb_deemon_start' / `bb_host_start'; this one owns descriptors). */ - struct Dee_jump_descriptors bb_exits; /* All of the possible ways this basic block can be exited. */ + struct Dee_jump_descriptors bb_exits; /* All of the possible ways this basic block can be exited (via deemon code). */ struct Dee_basic_block *bb_next; /* [0..1] Fallthru exit of this basic block (or NULL if there is none, which happens for the last block and blocks that end with NORETURN instructions) */ #ifdef __INTELLISENSE__ struct Dee_memstate *bb_mem_start; /* [0..1] Memory state at start of basic block (or NULL if not yet assembled) */ @@ -558,14 +386,33 @@ struct Dee_basic_block { DREF struct Dee_memstate *bb_mem_end; /* [0..1] Memory state at end of basic block (or NULL if not yet assembled) */ #endif /* !__INTELLISENSE__ */ byte_t *bb_host_start; /* [0..bb_host_size][owned] Start of host assembly */ - byte_t *bb_host_end; /* [0..bb_host_size] End of host assembly */ - size_t bb_host_free; /* Amount of unused, trailing memory in `bb_host_start' */ - /* TODO: Host text relocations (for API calls). */ + byte_t *bb_host_end; /* [>= bb_host_start && <= bb_host_alend] End of host assembly */ + byte_t *bb_host_alend; /* [>= bb_host_start] End of allocated host assembly */ + struct Dee_host_reloc *bb_host_relv; /* [0..bb_host_relc][owned] Vector of host relocations. */ + size_t bb_host_relc; /* Number of host relocations. */ + size_t bb_host_rela; /* Allocated number of host relocations. */ }; #define Dee_basic_block_alloc() ((struct Dee_basic_block *)Dee_Malloc(sizeof(struct Dee_basic_block))) #define Dee_basic_block_free(self) Dee_Free(self) +/* Initialize common fields of `self'. The caller must still initialize: + * - self->bb_deemon_start + * - self->bb_deemon_end + * - self->bb_exits */ +#define Dee_basic_block_init_common(self) \ + (Dee_jump_descriptors_init(&(self)->bb_entries), \ + (self)->bb_next = NULL, \ + (self)->bb_mem_start = NULL, \ + (self)->bb_mem_end = NULL, \ + (self)->bb_host_start = NULL, \ + (self)->bb_host_end = NULL, \ + (self)->bb_host_alend = NULL, \ + (self)->bb_host_relv = NULL, \ + (self)->bb_host_relc = 0, \ + (self)->bb_host_rela = 0) + +/* Destroy the given basic block `self'. */ INTDEF NONNULL((1)) void DCALL Dee_basic_block_destroy(struct Dee_basic_block *__restrict self); @@ -583,18 +430,79 @@ Dee_basic_block_splitat(struct Dee_basic_block *__restrict self, INTDEF WUNUSED NONNULL((1)) int DCALL _Dee_basic_block_reqhost(struct Dee_basic_block *__restrict self, size_t num_bytes); +#define _Dee_basic_block_hostavail(self) \ + ((size_t)((self)->bb_host_alend - (self)->bb_host_end)) #define Dee_basic_block_reqhost(self, num_bytes) \ - ((self)->bb_host_free >= (num_bytes) ? 0 : _Dee_basic_block_reqhost(self, num_bytes)) + (_Dee_basic_block_hostavail(self) >= (num_bytes) ? 0 : _Dee_basic_block_reqhost(self, num_bytes)) + +/* Allocate and return a new host relocation. The caller is responsible + * for filling in said relocation, and the returned pointer only remains + * valid until the next call to this function with the same `self'. + * @return: * : The (uninitialized) host relocation + * @return: NULL: Error */ +INTDEF WUNUSED NONNULL((1)) struct Dee_host_reloc *DCALL +Dee_basic_block_newhostrel(struct Dee_basic_block *__restrict self); + + + +/* Small descriptor for what needs to be cleaned up in a `struct Dee_memstate' */ +struct Dee_except_exitinfo { + struct Dee_basic_block *exi_block; /* [1..1][owned] Block implementing this exit state. */ + uintptr_t exi_cfa_offset; /* [== exi_block->bb_mem_start->ms_host_cfa_offset]. */ + uint16_t exi_regs[HOST_REGISTER_COUNT]; /* How often each register needs to be decref'd */ + COMPILER_FLEXIBLE_ARRAY(uint16_t, exi_stack); /* [exi_cfa_offset / HOST_SIZEOF_POINTER] + * How often CFA offsets need to be decref'd */ +}; + +/* Convert CFA offsets <==> index into `struct Dee_except_exitinfo::exi_stack' */ +#ifdef HOSTASM_STACK_GROWS_DOWN +#define Dee_except_exitinfo_cfa2index(cfa_offset) (((cfa_offset)-HOST_SIZEOF_POINTER) / HOST_SIZEOF_POINTER) +#define Dee_except_exitinfo_index2cfa(index) (((index)*HOST_SIZEOF_POINTER) + HOST_SIZEOF_POINTER) +#else /* HOSTASM_STACK_GROWS_DOWN */ +#define Dee_except_exitinfo_cfa2index(cfa_offset) ((cfa_offset) / HOST_SIZEOF_POINTER) +#define Dee_except_exitinfo_index2cfa(index) ((index)*HOST_SIZEOF_POINTER) +#endif /* !HOSTASM_STACK_GROWS_DOWN */ + +#define Dee_except_exitinfo_alloc(sizeof) ((struct Dee_except_exitinfo *)Dee_Malloc(sizeof)) +#define Dee_except_exitinfo_free(self) Dee_Free(self) +#define Dee_except_exitinfo_destroy(self) (Dee_basic_block_destroy((self)->exi_block), Dee_except_exitinfo_free(self)) +#define Dee_except_exitinfo_cmp_sizeof(cfa_offset) \ + (offsetof(struct Dee_except_exitinfo, exi_stack) + ((cfa_offset) / HOST_SIZEOF_POINTER) * sizeof(uint16_t)) +#define _Dee_except_exitinfo_cmp_baseof(x) (&(x)->exi_cfa_offset) +#define _Dee_except_exitinfo_cmp_sizeof(x) \ + ((offsetof(struct Dee_except_exitinfo, exi_stack) - \ + offsetof(struct Dee_except_exitinfo, exi_cfa_offset)) + \ + ((x)->exi_cfa_offset / HOST_SIZEOF_POINTER) * sizeof(uint16_t)) +#define Dee_except_exitinfo_cmp(a, b) \ + ((a)->exi_cfa_offset < (b)->exi_cfa_offset \ + ? -1 \ + : (a)->exi_cfa_offset > (b)->exi_cfa_offset \ + ? 1 \ + : memcmp(_Dee_except_exitinfo_cmp_baseof(a), \ + _Dee_except_exitinfo_cmp_baseof(b), \ + _Dee_except_exitinfo_cmp_sizeof(a))) + +/* Initialize `self' from `state' (with the exception of `self->exi_block') + * @return: 0 : Success + * @return: -1: Error (you're holding a reference to an argument/constant; why?) */ +INTDEF WUNUSED NONNULL((1, 2)) int DCALL +Dee_except_exitinfo_init(struct Dee_except_exitinfo *__restrict self, + struct Dee_memstate *__restrict state); struct Dee_function_assembler { - DeeFunctionObject *fa_function; /* [1..1][const] The function being assembled */ - DeeCodeObject *fa_code; /* [1..1][const][== fa_function->fo_code] The code being assembled */ - struct Dee_basic_block **fa_blockv; /* [owned][0..fa_blockc][owned] Vector of basic blocks (stored by `bb_deemon_start'). */ - size_t fa_blockc; /* Number of basic blocks. */ - size_t fa_blocka; /* Allocated number of basic blocks. */ - Dee_hostfunc_cc_t fa_cc; /* [const] Calling convention. */ + DeeFunctionObject *fa_function; /* [1..1][const] The function being assembled */ + DeeCodeObject *fa_code; /* [1..1][const][== fa_function->fo_code] The code being assembled */ + struct Dee_basic_block **fa_blockv; /* [owned][0..fa_blockc][owned] Vector of basic blocks (sorted by `bb_deemon_start'). */ + size_t fa_blockc; /* Number of basic blocks. */ + size_t fa_blocka; /* Allocated number of basic blocks. */ + /* TODO: Transition basic blocks (to insert between basic blocks whenever the memory state needs to be transformed) */ + struct Dee_except_exitinfo **fa_except_exitv; /* [owned][0..fa_except_exitc][owned] Vector of exception exit basic blocks (sorted by `Dee_except_exitinfo_cmp()') */ + size_t fa_except_exitc; /* Number of exception exit basic blocks. */ + size_t fa_except_exita; /* Allocated number of exception exit basic blocks. */ + struct Dee_basic_block *fa_cold_block; /* [0..1][const] Block where "cold" parts stubs should be written (e.g. `DeeObject_Destroy' calls) */ + Dee_hostfunc_cc_t fa_cc; /* [const] Calling convention. */ }; /* Return the extra CFA addend that needs to be freed on function return */ @@ -610,13 +518,17 @@ INTDEF size_t const _Dee_function_assembler_cfa_addend[HOSTFUNC_CC_COUNT]; #define Dee_function_assembler_addrof(self, addr) \ ((Dee_code_addr_t)((addr) - (self)->fa_code->co_code)) -#define Dee_function_assembler_init(self, function, cc) \ - (void)((self)->fa_function = (function), \ - (self)->fa_code = (self)->fa_function->fo_code, \ - (self)->fa_blockv = NULL, \ - (self)->fa_blockc = 0, \ - (self)->fa_blocka = 0, \ - (self)->fa_cc = (cc)) +#define Dee_function_assembler_init(self, function, cc) \ + (void)((self)->fa_function = (function), \ + (self)->fa_code = (self)->fa_function->fo_code, \ + (self)->fa_blockv = NULL, \ + (self)->fa_blockc = 0, \ + (self)->fa_blocka = 0, \ + (self)->fa_except_exitv = NULL, \ + (self)->fa_except_exitc = 0, \ + (self)->fa_except_exita = 0, \ + (self)->fa_cold_block = NULL, \ + (self)->fa_cc = (cc)) INTDEF NONNULL((1)) void DCALL Dee_function_assembler_fini(struct Dee_function_assembler *__restrict self); @@ -638,25 +550,42 @@ INTDEF WUNUSED NONNULL((1)) struct Dee_basic_block *DCALL Dee_function_assembler_locateblock(struct Dee_function_assembler const *__restrict self, Dee_instruction_t const *deemon_addr); +/* Lookup/allocate an exception-exit basic block that to clean up `state' + * and then return `NULL' to the caller of the generated function. + * @return: * : The basic block to which to jump in order to clean up `state'. + * @return: NULL: Error. */ +INTDEF WUNUSED NONNULL((1, 2)) struct Dee_basic_block *DCALL +Dee_function_assembler_except_exit(struct Dee_function_assembler *__restrict self, + struct Dee_memstate *__restrict state); /* ================ Loaders ================ */ struct Dee_function_generator { - struct Dee_function_assembler *fg_assembler; /* [1..1][const] Assembler. */ - struct Dee_basic_block *fg_block; /* [1..1][const] Output basic block. */ - DREF struct Dee_memstate *fg_state; /* [1..1] Current memory state. */ + struct Dee_function_assembler *fg_assembler; /* [1..1][const] Assembler. */ + struct Dee_basic_block *fg_block; /* [1..1][const] Output basic block. */ + DREF struct Dee_memstate *fg_state; /* [1..1] Current memory state. */ }; #define Dee_function_generator_state_unshare(self) Dee_memstate_unshare(&(self)->fg_state) +/* Return a basic block that should be jumped to in order to handle a exception. */ +#define Dee_function_generator_except_exit(self) \ + Dee_function_assembler_except_exit((self)->fg_assembler, (self)->fg_state) + +#define Dee_function_generator_gadjust_cfa_offset(self, delta) \ + (void)((self)->fg_state->ms_host_cfa_offset += (uintptr_t)(ptrdiff_t)(delta)) + + /* Code generator helpers to manipulate the V-stack. */ +#define Dee_function_generator_vtop(self) Dee_memstate_vtop((self)->fg_state) INTDEF WUNUSED NONNULL((1)) int DCALL Dee_function_generator_vswap(struct Dee_function_generator *__restrict self); /* ASM_SWAP */ INTDEF WUNUSED NONNULL((1)) int DCALL Dee_function_generator_vlrot(struct Dee_function_generator *__restrict self, size_t n); INTDEF WUNUSED NONNULL((1)) int DCALL Dee_function_generator_vrrot(struct Dee_function_generator *__restrict self, size_t n); INTDEF WUNUSED NONNULL((1, 2)) int DCALL Dee_function_generator_vpush(struct Dee_function_generator *__restrict self, struct Dee_memloc *loc); -INTDEF WUNUSED NONNULL((1, 2)) int DCALL Dee_function_generator_vpush_const(struct Dee_function_generator *__restrict self, DeeObject *value); +INTDEF WUNUSED NONNULL((1)) int DCALL Dee_function_generator_vpush_const(struct Dee_function_generator *__restrict self, DeeObject *value); +INTDEF WUNUSED NONNULL((1)) int DCALL Dee_function_generator_vpush_reg(struct Dee_function_generator *__restrict self, Dee_host_register_t regno); /* Sets the `MEMLOC_F_NOREF' flag */ #define Dee_function_generator_vpush_addr(self, addr) Dee_function_generator_vpush_const(self, (DeeObject *)(void *)(addr)) INTDEF WUNUSED NONNULL((1)) int DCALL Dee_function_generator_vpush_arg(struct Dee_function_generator *__restrict self, uint16_t aid); INTDEF WUNUSED NONNULL((1)) int DCALL Dee_function_generator_vpush_local(struct Dee_function_generator *__restrict self, uint16_t lid); @@ -668,6 +597,10 @@ INTDEF WUNUSED NONNULL((1, 2)) int DCALL Dee_function_generator_vpush_usage(stru INTDEF WUNUSED NONNULL((1)) int DCALL Dee_function_generator_vpop_local(struct Dee_function_generator *__restrict self, uint16_t lid); INTDEF WUNUSED NONNULL((1)) int DCALL Dee_function_generator_vdel_local(struct Dee_function_generator *__restrict self, uint16_t lid); INTDEF WUNUSED NONNULL((1)) int DCALL Dee_function_generator_vop(struct Dee_function_generator *__restrict self, uint16_t operator_name, uint16_t argc); +INTDEF WUNUSED NONNULL((1)) int DCALL Dee_function_generator_vopv(struct Dee_function_generator *__restrict self, uint16_t operator_name, uint16_t argc); /* doesn't leave result on-stack */ +INTDEF WUNUSED NONNULL((1, 2)) int DCALL Dee_function_generator_vjcc(struct Dee_function_generator *__restrict self, struct Dee_basic_block *target, bool jump_if_true); +#define Dee_function_generator_vjt(self, target) Dee_function_generator_vjcc(self, target, true) +#define Dee_function_generator_vjf(self, target) Dee_function_generator_vjcc(self, target, false) /* Take the base address of the top-most `DeeObject' from the object-stack, add `offset' to that * base address (possibly at runtime), then re-interpret that address as `(DeeObject **)' @@ -701,25 +634,42 @@ INTDEF WUNUSED NONNULL((1, 2)) int DCALL Dee_function_generator_vthrow(struct De * @param: cc: One of `VCALLOP_CC_*', describing the calling-convention of `api_function' */ INTDEF WUNUSED NONNULL((1)) int DCALL Dee_function_generator_vcallop(struct Dee_function_generator *__restrict self, - void *api_function, unsigned int cc, size_t argc); + void *api_function, unsigned int cc, uint16_t argc); #define VCALLOP_CC_OBJECT 0 /* DREF DeeObject *(DCALL *api_function)(DeeObject *, [DeeObject *, [DeeObject *, [...]]]); */ -#define VCALLOP_CC_INT 1 /* int (DCALL *api_function)(DeeObject *, [DeeObject *, [DeeObject *, [...]]]); (leaving an implicit `Dee_None') */ -#define VCALLOP_CC_INPLACE 2 /* int (DCALL *api_function)(DeeObject **, [DeeObject *, [DeeObject *, [...]]]); */ +#define VCALLOP_CC_INT 1 /* int (DCALL *api_function)(DeeObject *, [DeeObject *, [DeeObject *, [...]]]); (doesn't push anything onto the stack) */ +#define VCALLOP_CC_INPLACE 2 /* int (DCALL *api_function)(DeeObject **, [DeeObject *, [DeeObject *, [...]]]); (doesn't push anything onto the stack) */ /* Generate a call to a C-function `c_function' with `argc' - * pointer-sized arguments whose values are taken from `argv'. */ + * pointer-sized arguments whose values are taken from `argv'. + * NOTE: The given `c_function' is assumed to use the `DCALL' calling convention. */ INTDEF WUNUSED NONNULL((1)) int DCALL -Dee_function_generator_gcall_c_function(struct Dee_function_generator *__restrict self, - void *c_function, size_t argc, - struct Dee_memloc const *argv); - - -/* Code generators. */ -INTDEF WUNUSED NONNULL((1, 2)) int DCALL Dee_function_generator_gincref(struct Dee_function_generator *__restrict self, struct Dee_memloc *__restrict loc); -INTDEF WUNUSED NONNULL((1, 2)) int DCALL Dee_function_generator_gdecref(struct Dee_function_generator *__restrict self, struct Dee_memloc *__restrict loc); -INTDEF WUNUSED NONNULL((1, 2)) int DCALL Dee_function_generator_gxincref(struct Dee_function_generator *__restrict self, struct Dee_memloc *__restrict loc); -INTDEF WUNUSED NONNULL((1, 2)) int DCALL Dee_function_generator_gxdecref(struct Dee_function_generator *__restrict self, struct Dee_memloc *__restrict loc); - +_Dee_function_generator_gcall_c_function(struct Dee_function_generator *__restrict self, + void *c_function, size_t argc, + struct Dee_memloc const *argv); + + +/* Object reference count incref/decref */ +INTDEF WUNUSED NONNULL((1, 2)) int DCALL Dee_function_generator_gincref(struct Dee_function_generator *__restrict self, struct Dee_memloc *__restrict loc); /* NOTE: Might alter `loc' into a register! */ +INTDEF WUNUSED NONNULL((1, 2)) int DCALL Dee_function_generator_gdecref(struct Dee_function_generator *__restrict self, struct Dee_memloc *__restrict loc); /* NOTE: Might alter `loc' into a register! */ +INTDEF WUNUSED NONNULL((1, 2)) int DCALL Dee_function_generator_gxincref(struct Dee_function_generator *__restrict self, struct Dee_memloc *__restrict loc); /* NOTE: Might alter `loc' into a register! */ +INTDEF WUNUSED NONNULL((1, 2)) int DCALL Dee_function_generator_gxdecref(struct Dee_function_generator *__restrict self, struct Dee_memloc *__restrict loc); /* NOTE: Might alter `loc' into a register! */ +#define _Dee_function_generator_gincref_reg(self, regno) _Dee_basic_block_gincref_reg((self)->fg_block, regno) +#define _Dee_function_generator_gdecref_reg_nokill(self, regno) _Dee_basic_block_gdecref_reg_nokill((self)->fg_block, regno) +#define _Dee_function_generator_gxincref_reg(self, regno) _Dee_basic_block_gxincref_reg((self)->fg_block, regno) +#define _Dee_function_generator_gincref_const(self, value) _Dee_basic_block_gincref_const((self)->fg_block, value) +#define _Dee_function_generator_gdecref_const(self, value) _Dee_basic_block_gdecref_const((self)->fg_block, value) +INTDEF WUNUSED NONNULL((1)) int DCALL _Dee_basic_block_gincref_reg(struct Dee_basic_block *__restrict self, Dee_host_register_t regno); +INTDEF WUNUSED NONNULL((1)) int DCALL _Dee_basic_block_gdecref_reg_nokill(struct Dee_basic_block *__restrict self, Dee_host_register_t regno); +INTDEF WUNUSED NONNULL((1)) int DCALL _Dee_basic_block_gxincref_reg(struct Dee_basic_block *__restrict self, Dee_host_register_t regno); +INTDEF WUNUSED NONNULL((1)) int DCALL _Dee_function_generator_gdecref_reg(struct Dee_function_generator *__restrict self, Dee_host_register_t regno); +INTDEF WUNUSED NONNULL((1)) int DCALL _Dee_function_generator_gxdecref_reg(struct Dee_function_generator *__restrict self, Dee_host_register_t regno); +INTDEF WUNUSED NONNULL((1)) int DCALL _Dee_basic_block_gincref_const(struct Dee_basic_block *__restrict self, DeeObject *value); +INTDEF WUNUSED NONNULL((1)) int DCALL _Dee_basic_block_gdecref_const(struct Dee_basic_block *__restrict self, DeeObject *value); + +/* Force `loc' to become a register (`MEMLOC_TYPE_HREG'). */ +INTDEF WUNUSED NONNULL((1, 2)) int DCALL Dee_function_generator_greg(struct Dee_function_generator *__restrict self, struct Dee_memloc *__restrict loc); + +/* Controls for operating with R/W-locks (as needed for accessing global/extern variables) */ #define Dee_function_generator_grwlock_read(self, lock) _Dee_function_generator_grwlock_read(self, lock) #define Dee_function_generator_grwlock_write(self, lock) _Dee_function_generator_grwlock_write(self, lock) #define Dee_function_generator_grwlock_endwrite(self, lock) _Dee_function_generator_grwlock_endwrite(self, lock) @@ -734,13 +684,18 @@ INTDEF WUNUSED NONNULL((1, 2)) int DCALL _Dee_function_generator_grwlock_endread INTDEF WUNUSED NONNULL((1)) int DCALL Dee_function_generator_ghstack_adjust(struct Dee_function_generator *__restrict self, ptrdiff_t cfa_delta); INTDEF WUNUSED NONNULL((1)) int DCALL Dee_function_generator_ghstack_pushreg(struct Dee_function_generator *__restrict self, Dee_host_register_t src_regno); INTDEF WUNUSED NONNULL((1)) int DCALL Dee_function_generator_ghstack_pushconst(struct Dee_function_generator *__restrict self, DeeObject *value); -#define _Dee_function_generator_ghstack_adjust(self, sp_delta) _Dee_basic_block_ghstack_adjust((self)->fg_block, sp_delta) -#define _Dee_function_generator_ghstack_pushreg(self, src_regno) _Dee_basic_block_ghstack_pushreg((self)->fg_block, src_regno) -#define _Dee_function_generator_ghstack_pushconst(self, value) _Dee_basic_block_ghstack_pushconst((self)->fg_block, value) +INTDEF WUNUSED NONNULL((1)) int DCALL Dee_function_generator_ghstack_pushhstack(struct Dee_function_generator *__restrict self, uintptr_t cfa_offset); +#define _Dee_function_generator_ghstack_adjust(self, sp_delta) _Dee_basic_block_ghstack_adjust((self)->fg_block, sp_delta) +#define _Dee_function_generator_ghstack_pushreg(self, src_regno) _Dee_basic_block_ghstack_pushreg((self)->fg_block, src_regno) +#define _Dee_function_generator_ghstack_pushconst(self, value) _Dee_basic_block_ghstack_pushconst((self)->fg_block, value) +#define _Dee_function_generator_ghstack_pushhstack(self, sp_offset) _Dee_basic_block_ghstack_pushhstack((self)->fg_block, sp_offset) /* `sp_offset' is as it would be *before* the push */ +#define _Dee_function_generator_ghstack_popreg(self, dst_regno) _Dee_basic_block_ghstack_popreg((self)->fg_block, dst_regno) INTDEF WUNUSED NONNULL((1)) int DCALL _Dee_basic_block_ghstack_adjust(struct Dee_basic_block *__restrict self, ptrdiff_t sp_delta); INTDEF WUNUSED NONNULL((1)) int DCALL _Dee_basic_block_ghstack_pushreg(struct Dee_basic_block *__restrict self, Dee_host_register_t src_regno); INTDEF WUNUSED NONNULL((1)) int DCALL _Dee_basic_block_ghstack_pushconst(struct Dee_basic_block *__restrict self, DeeObject *value); -#define Dee_function_generator_gmov_reg2hstack(self, src_regno, cfa_offset) _Dee_function_generator_gmov_reg2hstack(self, src_regno, cfa_offset) +INTDEF WUNUSED NONNULL((1)) int DCALL _Dee_basic_block_ghstack_pushhstack(struct Dee_basic_block *__restrict self, ptrdiff_t sp_offset); /* `sp_offset' is as it would be *before* the push */ +INTDEF WUNUSED NONNULL((1)) int DCALL _Dee_basic_block_ghstack_popreg(struct Dee_basic_block *__restrict self, Dee_host_register_t dst_regno); +#define Dee_function_generator_gmov_reg2hstack(self, src_regno, cfa_offset) _Dee_function_generator_gmov_reg2hstack(self, src_regno, Dee_memstate_hstack_cfa2sp((self)->fg_state, cfa_offset)) #define Dee_function_generator_gmov_hstack2reg(self, cfa_offset, dst_regno) (unlikely(_Dee_function_generator_gmov_hstack2reg(self, Dee_memstate_hstack_cfa2sp((self)->fg_state, cfa_offset), dst_regno)) ? -1 : ((self)->fg_state->ms_regs[dst_regno] = REGISTER_USAGE_GENERIC, 0)) #define Dee_function_generator_gmov_const2reg(self, value, dst_regno) (unlikely(_Dee_function_generator_gmov_const2reg(self, value, dst_regno)) ? -1 : ((self)->fg_state->ms_regs[dst_regno] = REGISTER_USAGE_GENERIC, 0)) #define Dee_function_generator_gmov_reg2reg(self, src_regno, dst_regno) (unlikely(_Dee_function_generator_gmov_reg2reg(self, src_regno, dst_regno)) ? -1 : ((self)->fg_state->ms_regs[dst_regno] = (self)->fg_state->ms_regs[src_regno], 0)) @@ -792,11 +747,12 @@ Dee_function_generator_gallocreg(struct Dee_function_generator *__restrict self, /* Generate code to flush all registers used by the deemon stack/locals into the host stack. + * NOTE: Usage-registers are cleared by arch-specific code (e.g. `_Dee_function_generator_gcall_c_function()') * @param: ignore_top_n_stack_if_not_ref: From the top-most N stack locations, ignore any * that don't contain object references. */ INTDEF WUNUSED NONNULL((1)) int DCALL Dee_function_generator_gflushregs(struct Dee_function_generator *__restrict self, - size_t ignore_top_n_stack_if_not_ref); + uint16_t ignore_top_n_stack_if_not_ref); /* Generate code to assert that location `loc' is non-NULL: * >> Dee_function_generator_gassert_bound(self, loc, ASM_LOCAL, lid, , NULL); @@ -828,10 +784,25 @@ INTDEF WUNUSED NONNULL((1)) int DCALL Dee_function_generator_gthrow_local_unboun INTDEF WUNUSED NONNULL((1)) int DCALL Dee_function_generator_gthrow_global_unbound(struct Dee_function_generator *__restrict self, uint16_t gid); INTDEF WUNUSED NONNULL((1)) int DCALL Dee_function_generator_gthrow_extern_unbound(struct Dee_function_generator *__restrict self, uint16_t mid, uint16_t gid); +/* Generate jumps. */ +INTDEF WUNUSED NONNULL((1, 2, 3)) int DCALL _Dee_function_generator_gjz(struct Dee_function_generator *__restrict self, struct Dee_basic_block *dst, struct Dee_memloc *test_loc); +INTDEF WUNUSED NONNULL((1, 2, 3)) int DCALL _Dee_function_generator_gjnz(struct Dee_function_generator *__restrict self, struct Dee_basic_block *dst, struct Dee_memloc *test_loc); +INTDEF WUNUSED NONNULL((1, 2)) int DCALL _Dee_basic_block_gjmp(struct Dee_basic_block *__restrict self, struct Dee_basic_block *dst); +#define _Dee_function_generator_gjmp(self, dst) _Dee_basic_block_gjmp((self)->fg_block, dst) +#define Dee_function_generator_gjmp(self, dst) _Dee_function_generator_gjmp(self, dst) + +/* Emit conditional jump(s) based on ` <=> 0' */ +INTDEF WUNUSED NONNULL((1, 5)) int DCALL +_Dee_function_generator_gjcmp0(struct Dee_function_generator *__restrict self, + struct Dee_basic_block *dst_lo_0, /* Jump here if `(signed) < 0' */ + struct Dee_basic_block *dst_eq_0, /* Jump here if `(signed) == 0' */ + struct Dee_basic_block *dst_gr_0, /* Jump here if `(signed) > 0' */ + struct Dee_memloc *test_loc); + /* Generate checks to enter exception handling mode. */ -INTDEF WUNUSED NONNULL((1, 2)) int DCALL Dee_function_generator_gexcept_if_zero(struct Dee_function_generator *__restrict self, struct Dee_memloc *__restrict loc); -INTDEF WUNUSED NONNULL((1, 2)) int DCALL Dee_function_generator_gexcept_if_nonzero(struct Dee_function_generator *__restrict self, struct Dee_memloc *__restrict loc); -INTDEF WUNUSED NONNULL((1)) int DCALL Dee_function_generator_gexcept(struct Dee_function_generator *__restrict self); +INTDEF WUNUSED NONNULL((1, 2)) int DCALL Dee_function_generator_gjexceptz(struct Dee_function_generator *__restrict self, struct Dee_memloc *loc); +INTDEF WUNUSED NONNULL((1, 2)) int DCALL Dee_function_generator_gjexceptnz(struct Dee_function_generator *__restrict self, struct Dee_memloc *loc); +INTDEF WUNUSED NONNULL((1)) int DCALL Dee_function_generator_gjexcept(struct Dee_function_generator *__restrict self); /* Convert a single deemon instruction `instr' to host assembly and adjust the host memory * state according to the instruction in question. This is the core function to parse deemon diff --git a/src/dex/_hostasm/loader.c b/src/dex/_hostasm/loader.c index 2ed0d80d1..6f53c8113 100644 --- a/src/dex/_hostasm/loader.c +++ b/src/dex/_hostasm/loader.c @@ -213,16 +213,10 @@ Dee_function_assembler_loadblocks(struct Dee_function_assembler *__restrict self block = Dee_basic_block_alloc(); if unlikely(!block) goto err; + Dee_basic_block_init_common(block); block->bb_deemon_start = self->fa_code->co_code; block->bb_deemon_end = self->fa_code->co_code + self->fa_code->co_codebytes; - Dee_jump_descriptors_init(&block->bb_entries); Dee_jump_descriptors_init(&block->bb_exits); - block->bb_next = NULL; - block->bb_mem_start = NULL; - block->bb_mem_end = NULL; - block->bb_host_start = NULL; - block->bb_host_end = NULL; - block->bb_host_free = 0; /* Set the initial block. */ self->fa_blockv[0] = block; @@ -716,8 +710,9 @@ Dee_function_assembler_loadboundlocals(struct Dee_function_assembler *__restrict state->ms_localc = self->fa_code->co_localc; state->ms_stackc = 0; state->ms_stacka = 0; - bzero(state->ms_regs, sizeof(state->ms_regs)); + Dee_memstate_hregs_clear_usage(state); state->ms_stackv = NULL; + /* Initially, all variables are unbound */ for (lid = 0; lid < state->ms_localc; ++lid) { state->ms_localv[lid].ml_flags = MEMLOC_F_NOREF | MEMLOC_F_LOCAL_UNBOUND; @@ -734,7 +729,7 @@ Dee_function_assembler_loadboundlocals(struct Dee_function_assembler *__restrict if unlikely(!state) goto err; if (state == (struct Dee_memstate *)ITER_DONE) { - block->bb_host_free = 1; + block->bb_host_relc = 1; has_unreachable_code = true; break; } @@ -775,12 +770,12 @@ Dee_function_assembler_loadboundlocals(struct Dee_function_assembler *__restrict if (block_changed) { changed = true; /* (Re-)scan the block for changes */ - block->bb_host_free = 0; + block->bb_host_relc = 0; state = Dee_basic_block_scan_boundlocals(block, primary_state, false); if unlikely(!state) goto err; if (state == (struct Dee_memstate *)ITER_DONE) { - block->bb_host_free = 1; + block->bb_host_relc = 1; has_unreachable_code = true; } else { /* Merge the block's exit status with the block it fall into. */ @@ -809,8 +804,8 @@ Dee_function_assembler_loadboundlocals(struct Dee_function_assembler *__restrict for (i = 0; i < self->fa_blockc; ++i) { block = self->fa_blockv[i]; ASSERT(block); - if (block->bb_host_free) { - block->bb_host_free = 0; + if (block->bb_host_relc) { + block->bb_host_relc = 0; state = Dee_basic_block_scan_boundlocals(block, primary_state, true); if unlikely(!state) goto err; @@ -823,7 +818,7 @@ Dee_function_assembler_loadboundlocals(struct Dee_function_assembler *__restrict for (i = 0; i < self->fa_blockc; ++i) { block = self->fa_blockv[i]; ASSERT(block); - block->bb_host_free = 0; + block->bb_host_relc = 0; } return -1; } diff --git a/src/dex/_hostasm/memstate.c b/src/dex/_hostasm/memstate.c index 16f4a1c69..7472b6092 100644 --- a/src/dex/_hostasm/memstate.c +++ b/src/dex/_hostasm/memstate.c @@ -362,7 +362,7 @@ Dee_memstate_vpush(struct Dee_memstate *__restrict self, struct Dee_memloc *loc) return -1; } -INTERN WUNUSED NONNULL((1, 2)) int DCALL +INTERN WUNUSED NONNULL((1)) int DCALL Dee_memstate_vpush_const(struct Dee_memstate *__restrict self, DeeObject *value) { if unlikely(self->ms_stackc >= self->ms_stacka && Dee_memstate_reqvstack(self, self->ms_stackc + 1)) @@ -376,6 +376,23 @@ Dee_memstate_vpush_const(struct Dee_memstate *__restrict self, DeeObject *value) return -1; } + +/* Sets the `MEMLOC_F_NOREF' flag */ +INTERN WUNUSED NONNULL((1)) int DCALL +Dee_memstate_vpush_reg(struct Dee_memstate *__restrict self, + Dee_host_register_t regno) { + if unlikely(self->ms_stackc >= self->ms_stacka && + Dee_memstate_reqvstack(self, self->ms_stackc + 1)) + goto err; + self->ms_stackv[self->ms_stackc].ml_flags = MEMLOC_F_NOREF; + self->ms_stackv[self->ms_stackc].ml_where = MEMLOC_TYPE_HREG; + self->ms_stackv[self->ms_stackc].ml_value.ml_hreg = regno; + ++self->ms_stackc; + return 0; +err: + return -1; +} + INTERN WUNUSED NONNULL((1, 2)) int DCALL Dee_memstate_vpush_arg(struct Dee_memstate *__restrict self, uint16_t aid) { if unlikely(self->ms_stackc >= self->ms_stacka && @@ -407,6 +424,67 @@ Dee_memstate_vdup_n(struct Dee_memstate *__restrict self, size_t n) { return -1; } + + + +/* Initialize `self' from `state' (with the exception of `self->exi_block') + * @return: 0 : Success + * @return: -1: Error (you're holding a reference to an argument/constant; why?) */ +INTERN WUNUSED NONNULL((1, 2)) int DCALL +Dee_except_exitinfo_init(struct Dee_except_exitinfo *__restrict self, + struct Dee_memstate *__restrict state) { + uint16_t i; + self->exi_cfa_offset = state->ms_host_cfa_offset; + bzero(&self->exi_regs, + ((offsetof(struct Dee_except_exitinfo, exi_stack) - + offsetof(struct Dee_except_exitinfo, exi_regs)) + + (self->exi_cfa_offset / HOST_SIZEOF_POINTER) * sizeof(uint16_t))); + for (i = 0; i < state->ms_localc; ++i) { + struct Dee_memloc *loc = &state->ms_localv[i]; + if (loc->ml_flags & (MEMLOC_F_NOREF | MEMLOC_F_LOCAL_UNBOUND)) + continue; + switch (loc->ml_where) { + case MEMLOC_TYPE_HSTACK: { + size_t index = Dee_except_exitinfo_cfa2index(loc->ml_value.ml_hstack); + ASSERT(index < (self->exi_cfa_offset / HOST_SIZEOF_POINTER)); + ++self->exi_stack[index]; + } break; + case MEMLOC_TYPE_HREG: + ASSERT(loc->ml_value.ml_hreg < HOST_REGISTER_COUNT); + ++self->exi_regs[loc->ml_value.ml_hreg]; + break; + case MEMLOC_TYPE_UNALLOC: + break; + default: + return DeeError_Throwf(&DeeError_IllegalInstruction, + "Cannot jump to exception handler while holding " + "a reference to an argument or a constant"); + } + } + for (i = 0; i < state->ms_stackc; ++i) { + struct Dee_memloc *loc = &state->ms_stackv[i]; + if (loc->ml_flags & MEMLOC_F_NOREF) + continue; + switch (loc->ml_where) { + case MEMLOC_TYPE_HSTACK: { + size_t index = Dee_except_exitinfo_cfa2index(loc->ml_value.ml_hstack); + ASSERT(index < (self->exi_cfa_offset / HOST_SIZEOF_POINTER)); + ++self->exi_stack[index]; + } break; + case MEMLOC_TYPE_HREG: + ASSERT(loc->ml_value.ml_hreg < HOST_REGISTER_COUNT); + ++self->exi_regs[loc->ml_value.ml_hreg]; + break; + default: + return DeeError_Throwf(&DeeError_IllegalInstruction, + "Cannot jump to exception handler while holding " + "a reference to an argument or a constant"); + } + } + return 0; +} + + DECL_END #endif /* CONFIG_HAVE_LIBHOSTASM */