From ea6e1e0c9a5c20847ac257a4c06c96412b2a0a7d Mon Sep 17 00:00:00 2001 From: GrieferAtWork Date: Fri, 22 Dec 2023 16:23:48 +0100 Subject: [PATCH] Redesign how caller-args are tracked in _hostasm for x86 With this, there doesn't need to be an unconditional prolog to save argument registers on x86_64 --- src/dex/_hostasm/assembler.c | 267 ++++++++++++++++++++-------- src/dex/_hostasm/common.c | 2 + src/dex/_hostasm/generator-arch.c | 87 +-------- src/dex/_hostasm/generator-common.c | 211 ++++++++++++++-------- src/dex/_hostasm/generator-deemon.c | 32 ++-- src/dex/_hostasm/generator-vstack.c | 219 +++++++++++------------ src/dex/_hostasm/libhostasm.h | 88 +++++---- src/dex/_hostasm/memstate.c | 20 +++ 8 files changed, 525 insertions(+), 401 deletions(-) diff --git a/src/dex/_hostasm/assembler.c b/src/dex/_hostasm/assembler.c index 965dc5a15..f825f3488 100644 --- a/src/dex/_hostasm/assembler.c +++ b/src/dex/_hostasm/assembler.c @@ -113,34 +113,22 @@ INTERN_CONST size_t const _Dee_function_assembler_cfa_addend[HOSTFUNC_CC_COUNT] }; #endif /* !HOSTASM_X86_64_SYSVABI */ -/* Step #4: Compile basic blocks and determine memory states. Fills in: - * - self->fa_blockv[*]->bb_entries.jds_list[*]->jd_stat - * - self->fa_blockv[*]->bb_mem_start->* (everything not already done by `Dee_function_assembler_loadboundlocals()') - * - self->fa_blockv[*]->bb_mem_end - * - self->fa_blockv[*]->bb_host_start - * - self->fa_blockv[*]->bb_host_end - * Also makes sure that memory states at start/end of basic blocks are - * always identical (or compatible; i.e.: MEMLOC_F_LOCAL_UNKNOWN can - * be set at the start of a block, but doesn't need to be set at the - * end of a preceding block). When not compatible, extra block(s) are - * inserted with `bb_deemon_start==bb_deemon_end', but non-empty host - * assembly, which serves the purpose of transforming memory states. - * @return: 0 : Success - * @return: -1: Error */ -INTERN WUNUSED NONNULL((1)) int DCALL -Dee_function_assembler_compileblocks(struct Dee_function_assembler *__restrict self) { - size_t block_i; +/* Allocate and return the initial memory-state when the + * generated function is entered at the start of the prolog. + * @return: * : The initial memory-state + * @return: NULL: Error */ +PRIVATE WUNUSED NONNULL((1)) DREF struct Dee_memstate *DCALL +Dee_function_assembler_alloc_init_memstate(struct Dee_function_assembler const *__restrict self) { + Dee_hostfunc_cc_t cc = self->fa_cc; struct Dee_memstate *state; - struct Dee_basic_block *block; size_t local_count; + size_t extra_base; ASSERT(self->fa_blockc >= 1); - block = self->fa_blockv[0]; - ASSERT(block); - ASSERT(block->bb_mem_start == NULL); /* Figure out how many locals we need. */ - local_count = self->fa_code->co_localc; - local_count += DEE_MEMSTATE_EXTRA_LOCALS_MINCOUNT; + extra_base = self->fa_localc; + local_count = extra_base; + local_count += DEE_MEMSTATE_EXTRA_LOCAL_MINCOUNT; local_count += self->fa_code->co_argc_max; local_count -= self->fa_code->co_argc_max; @@ -162,75 +150,141 @@ Dee_function_assembler_compileblocks(struct Dee_function_assembler *__restrict s size_t lid; for (lid = 0; lid < state->ms_localc; ++lid) { state->ms_localv[lid].ml_flags = MEMLOC_F_NOREF | MEMLOC_F_LOCAL_UNBOUND; - state->ms_localv[lid].ml_type = MEMLOC_TYPE_UNALLOC; + state->ms_localv[lid].ml_type = MEMLOC_TYPE_UNALLOC; } } - /* Set the mem-state for the initial block. */ - block->bb_mem_start = state; /* Inherit reference */ - - /* Save caller-provided arguments onto stack. */ -#ifdef HOSTASM_X86_64 + /* Set-up the mem-state to indicate where arguments are stored at */ +#ifdef HOSTASM_X86 { +#ifdef HOSTASM_X86_64 +#define Dee_memloc_set_x86_arg(self, argi) \ + ((self)->ml_flags = MEMLOC_F_NOREF | MEMLOC_F_LOCAL_BOUND, \ + (self)->ml_type = MEMLOC_TYPE_HREG, \ + (self)->ml_value.v_hreg.r_regno = truearg_regno[argi], \ + (self)->ml_value.v_hreg.r_off = 0) PRIVATE Dee_host_register_t const truearg_regno[4] = { HOST_REGISTER_R_ARG0, HOST_REGISTER_R_ARG1, HOST_REGISTER_R_ARG2, HOST_REGISTER_R_ARG0, }; - - Dee_hostfunc_cc_t cc = self->fa_cc; - size_t trueargc = 0; +#else /* HOSTASM_X86_64 */ +#define Dee_memloc_set_x86_arg(self, argi) \ + ((self)->ml_flags = MEMLOC_F_NOREF | MEMLOC_F_LOCAL_BOUND, \ + (self)->ml_type = MEMLOC_TYPE_HSTACKIND, \ + (self)->ml_value.v_hstack.s_cfa = (uintptr_t)(-(ptrdiff_t)(((argi) + 1) * 4)), \ + (self)->ml_value.v_hstack.s_off = 0) +#endif /* !HOSTASM_X86_64 */ + size_t argi = 0; if (cc & HOSTFUNC_CC_F_THIS) { - state->ms_rusage[HOST_REGISTER_R_ARG0] = DEE_HOST_REGUSAGE_THIS; - trueargc += 1; + Dee_memloc_set_x86_arg(&state->ms_localv[extra_base + DEE_MEMSTATE_EXTRA_LOCAL_A_THIS], argi); + ++argi; } if (cc & HOSTFUNC_CC_F_TUPLE) { - state->ms_rusage[truearg_regno[trueargc]] = DEE_HOST_REGUSAGE_ARGS; - trueargc += 1; + Dee_memloc_set_x86_arg(&state->ms_localv[extra_base + DEE_MEMSTATE_EXTRA_LOCAL_A_ARGS], argi); + ++argi; } else { - state->ms_rusage[truearg_regno[trueargc]] = DEE_HOST_REGUSAGE_ARGC; - trueargc += 1; - state->ms_rusage[truearg_regno[trueargc]] = DEE_HOST_REGUSAGE_ARGV; - trueargc += 1; - } - if (cc & HOSTFUNC_CC_F_KW) { - state->ms_rusage[truearg_regno[trueargc]] = DEE_HOST_REGUSAGE_KW; - trueargc += 1; + Dee_memloc_set_x86_arg(&state->ms_localv[extra_base + DEE_MEMSTATE_EXTRA_LOCAL_A_ARGC], argi); + ++argi; + Dee_memloc_set_x86_arg(&state->ms_localv[extra_base + DEE_MEMSTATE_EXTRA_LOCAL_A_ARGV], argi); + ++argi; } + if (cc & HOSTFUNC_CC_F_KW) + Dee_memloc_set_x86_arg(&state->ms_localv[extra_base + DEE_MEMSTATE_EXTRA_LOCAL_A_KW], argi); +#undef Dee_memloc_set_x86_arg + } +#else /* ... */ +#error "Initial register state not implemented for this architecture" +#endif /* !... */ -#if 0 /* TODO: This can't go in the first basic block; this needs to go into - * a special prolog section that is generated during linking! */ -#ifdef HOSTASM_X86_64_SYSVABI - /* Push arguments onto stack */ - if (trueargc >= 4 && _Dee_basic_block_ghstack_pushreg(block, HOST_REGISTER_R_ARG3)) - goto err; - if (trueargc >= 3 && _Dee_basic_block_ghstack_pushreg(block, HOST_REGISTER_R_ARG2)) - goto err; - if (trueargc >= 2 && _Dee_basic_block_ghstack_pushreg(block, HOST_REGISTER_R_ARG1)) - goto err; - if (_Dee_basic_block_ghstack_pushreg(block, HOST_REGISTER_R_ARG0)) - goto err; - ASSERT(_Dee_function_assembler_cfa_addend[cc] == trueargc * HOST_SIZEOF_POINTER); -#elif defined(HOSTASM_X86_64_MSABI) - /* Save register arguments in caller-allocated locations */ - if (_Dee_basic_block_gmov_reg2hstack(block, HOST_REGISTER_R_ARG0, HOST_SIZEOF_POINTER * 1)) - goto err; - if (trueargc >= 2 && _Dee_basic_block_gmov_reg2hstack(block, HOST_REGISTER_R_ARG1, HOST_SIZEOF_POINTER * 2)) - goto err; - if (trueargc >= 3 && _Dee_basic_block_gmov_reg2hstack(block, HOST_REGISTER_R_ARG2, HOST_SIZEOF_POINTER * 3)) - goto err; - if (trueargc >= 4 && _Dee_basic_block_gmov_reg2hstack(block, HOST_REGISTER_R_ARG3, HOST_SIZEOF_POINTER * 4)) - goto err; -#endif /* ... */ -#endif + return state; +err: + return NULL; +} + +PRIVATE WUNUSED NONNULL((1)) int DCALL +Dee_function_generator_makeprolog(struct Dee_function_generator *__restrict self) { + /* TODO: Check argc */ + /* TODO: Unpack keywords */ + (void)self; + return 0; +} + + +/* Generate the prolog of the function (check argc, unpack keywords, etc...). + * This fills in: + * - self->fa_prolog + * - self->fa_prolog_end + * @return: 0 : Success + * @return: -1: Error */ +PRIVATE WUNUSED NONNULL((1)) int DCALL +Dee_function_assembler_makeprolog(struct Dee_function_assembler *__restrict self, + /*inherit(always)*/ DREF struct Dee_memstate *__restrict state) { + int result; + struct Dee_function_generator gen; + gen.fg_assembler = self; + gen.fg_block = self->fa_blockv[0]; + gen.fg_sect = &self->fa_prolog; + gen.fg_state = state; /* Inherit reference */ + ASSERT(gen.fg_block); + ASSERT(self->fa_prolog_end == NULL); + ASSERT(gen.fg_state != NULL); + result = Dee_function_generator_makeprolog(&gen); + ASSERT(gen.fg_state != NULL); + ASSERT(self->fa_prolog_end == NULL); + if likely(result == 0) { + self->fa_prolog_end = gen.fg_state; /* Inherit reference */ + } else { + Dee_memstate_decref(gen.fg_state); } -#endif /* HOSTASM_X86_64 */ + return result; +} + +/* Step #2: Compile basic blocks and determine memory states. Fills in: + * - self->fa_prolog + * - self->fa_prolog_end + * - self->fa_blockv[*]->bb_entries.jds_list[*]->jd_stat + * - self->fa_blockv[*]->bb_mem_start + * - self->fa_blockv[*]->bb_mem_end + * - self->fa_blockv[*]->bb_host_start + * - self->fa_blockv[*]->bb_host_end + * Also makes sure that memory states at start/end of basic blocks are + * always identical (or compatible; i.e.: MEMLOC_F_LOCAL_UNKNOWN can + * be set at the start of a block, but doesn't need to be set at the + * end of a preceding block). When not compatible, extra block(s) are + * inserted with `bb_deemon_start==bb_deemon_end', but non-empty host + * assembly, which serves the purpose of transforming memory states. + * @return: 0 : Success + * @return: -1: Error */ +INTERN WUNUSED NONNULL((1)) int DCALL +Dee_function_assembler_compileblocks(struct Dee_function_assembler *__restrict self) { + size_t block_i; + struct Dee_memstate *state; + struct Dee_basic_block *block; + ASSERT(self->fa_blockc >= 1); + + /* Setup the state of the function's first (entry) block. */ + state = Dee_function_assembler_alloc_init_memstate(self); + if unlikely(!state) + goto err; + + /* Generate the function prolog. */ + if unlikely(Dee_function_assembler_makeprolog(self, state)) + goto err; + + /* Set the mem-state for the initial block. */ + ASSERT(self->fa_prolog_end); + block = self->fa_blockv[0]; + ASSERT(block); + ASSERT(block->bb_mem_start == NULL); + Dee_memstate_incref(self->fa_prolog_end); + block->bb_mem_start = self->fa_prolog_end; /* Inherit reference */ /* Compile all basic blocks until everything has been compiled and everyone is happy. */ while ((block_i = find_next_block_to_compile(self)) != (size_t)-1) { block = self->fa_blockv[block_i]; - Dee_DPRINTF("Dee_function_assembler_compileblocks: %" PRFuSIZ " [%.4" PRFx32 "-%.4" PRFx32 "]\n", + Dee_DPRINTF("Dee_basic_block_compile: %" PRFuSIZ " [%.4" PRFx32 "-%.4" PRFx32 "]\n", block_i, Dee_function_assembler_addrof(self, block->bb_deemon_start), Dee_function_assembler_addrof(self, block->bb_deemon_end - 1)); @@ -445,6 +499,43 @@ Dee_function_assembler_trimdead(struct Dee_function_assembler *__restrict self) return -1; } + +/* Append morphing code for `from_state' to `to_state' to `sect' */ +#undef assemble_morph +PRIVATE WUNUSED NONNULL((1, 2, 3, 4)) int DCALL +assemble_morph(struct Dee_function_assembler *__restrict assembler, + struct Dee_host_section *sect, + struct Dee_memstate *from_state, + struct Dee_basic_block *to_block +#ifndef Dee_DPRINT_IS_NOOP + , Dee_instruction_t const *from_instr +#endif /* !Dee_DPRINT_IS_NOOP */ + ) { +#ifdef Dee_DPRINT_IS_NOOP +#define assemble_morph(a, b, c, d, e) assemble_morph(a, b, c, d) +#endif /* Dee_DPRINT_IS_NOOP */ + int result; + struct Dee_function_generator gen; + ASSERT(to_block->bb_mem_start); + Dee_DPRINTF("Dee_function_generator_gmorph: %.4" PRFx32 " -> %.4" PRFx32 "\n", + Dee_function_assembler_addrof(assembler, from_instr), + to_block->bb_deemon_start < to_block->bb_deemon_end + ? Dee_function_assembler_addrof(assembler, to_block->bb_deemon_start) + : 0xffff); +#ifndef NO_HOSTASM_DEBUG_PRINT + _Dee_memstate_debug_print(from_state); + _Dee_memstate_debug_print(to_block->bb_mem_start); +#endif /* !NO_HOSTASM_DEBUG_PRINT */ + gen.fg_assembler = assembler; + gen.fg_block = to_block; + gen.fg_sect = sect; + gen.fg_state = from_state; + Dee_memstate_incref(gen.fg_state); + result = Dee_function_generator_gmorph(&gen, to_block->bb_mem_start); + Dee_memstate_decref(gen.fg_state); + return result; +} + /* Step #4: Generate morph instruction sequences to perform memory state transitions. * This also extends the host text of basic blocks that fall through to some * other basic block with an extra instructions needed for morphing: @@ -457,9 +548,41 @@ Dee_function_assembler_trimdead(struct Dee_function_assembler *__restrict self) * @return: -1: Error */ INTERN WUNUSED NONNULL((1)) int DCALL Dee_function_assembler_compilemorph(struct Dee_function_assembler *__restrict self) { - /* TODO */ + size_t i; + /* Initial morph after the prolog to match the start-state of the first block. */ + if unlikely(assemble_morph(self, &self->fa_prolog, self->fa_prolog_end, + self->fa_blockv[0], + self->fa_blockv[0]->bb_deemon_start)) + goto err; + for (i = 0; i < self->fa_blockc; ++i) { + size_t j; + struct Dee_basic_block *block = self->fa_blockv[i]; + ASSERT(block); + ASSERT(block->bb_mem_start); + ASSERT(block->bb_mem_end); + for (j = 0; j < block->bb_exits.jds_size; ++j) { + struct Dee_jump_descriptor *jd = block->bb_exits.jds_list[j]; + ASSERT(jd); + ASSERT(jd->jd_from >= block->bb_deemon_start); + ASSERT(jd->jd_from < block->bb_deemon_end); + ASSERT(jd->jd_stat != NULL); + ASSERT(jd->jd_to != NULL); + if unlikely(assemble_morph(self, &jd->jd_morph, jd->jd_stat, + jd->jd_to, jd->jd_from)) + goto err; + } + if (block->bb_next != NULL) { + if unlikely(assemble_morph(self, &block->bb_htext, block->bb_mem_end, + block->bb_next, block->bb_deemon_end)) + goto err; + } + } + + /* TODO: Generate cleanup/fallthru code for exit descriptors. */ (void)self; return DeeError_NOTIMPLEMENTED(); +err: + return -1; } /* Step #5: Generate missing unconditional jumps to jump from one block to the next diff --git a/src/dex/_hostasm/common.c b/src/dex/_hostasm/common.c index f4f769d5c..2708a1f77 100644 --- a/src/dex/_hostasm/common.c +++ b/src/dex/_hostasm/common.c @@ -407,6 +407,8 @@ Dee_function_assembler_fini(struct Dee_function_assembler *__restrict self) { 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]); + if (self->fa_prolog_end) + Dee_memstate_decref(self->fa_prolog_end); Dee_host_section_fini(&self->fa_prolog); Dee_Free(self->fa_blockv); Dee_Free(self->fa_except_exitv); diff --git a/src/dex/_hostasm/generator-arch.c b/src/dex/_hostasm/generator-arch.c index 95637807f..d0758c8cf 100644 --- a/src/dex/_hostasm/generator-arch.c +++ b/src/dex/_hostasm/generator-arch.c @@ -292,10 +292,12 @@ _Dee_memloc_debug_print(struct Dee_memloc *__restrict self, bool is_local) { Dee_DPRINT("r"); switch (self->ml_type) { case MEMLOC_TYPE_HSTACK: - Dee_DPRINTF("#%Iu", self->ml_value.v_hstack.s_cfa); + /* Signed, because we cheat when it comes to how caller-arguments */ + Dee_DPRINTF("#%Id", (ptrdiff_t)self->ml_value.v_hstack.s_cfa); break; case MEMLOC_TYPE_HSTACKIND: - Dee_DPRINTF("[#%Iu]", self->ml_value.v_hstack.s_cfa); + /* Signed, because we cheat when it comes to how caller-arguments */ + Dee_DPRINTF("[#%Id]", (ptrdiff_t)self->ml_value.v_hstack.s_cfa); if (self->ml_value.v_hstack.s_off != 0) Dee_DPRINTF("+%Id", self->ml_value.v_hstack.s_off); break; @@ -1119,87 +1121,6 @@ _Dee_host_section_gmov_reg2regind(struct Dee_host_section *__restrict self, return -1; } -PRIVATE WUNUSED NONNULL((1)) int DCALL -_Dee_function_generator_gmov_truearg2reg(struct Dee_function_generator *__restrict self, - size_t argno, Dee_host_register_t dst_regno) { - size_t sp_offset; - sp_offset = self->fg_state->ms_host_cfa_offset; /* Offset to return-pc */ -#if defined(HOSTASM_X86_64) && defined(HOSTASM_X86_64_SYSVABI) - /* This matches the way registers are saved in the prolog. */ - { - Dee_hostfunc_cc_t cc = self->fg_assembler->fa_cc; - size_t trueargc = cc & HOSTFUNC_CC_F_TUPLE ? 1 : 2; - if (cc & HOSTFUNC_CC_F_THIS) - ++trueargc; - if (cc & HOSTFUNC_CC_F_KW) - ++trueargc; - sp_offset -= trueargc * HOST_SIZEOF_POINTER; /* Base address of saved register arguments */ - sp_offset += argno * HOST_SIZEOF_POINTER; /* Load the relevant argument */ - } -#else /* HOSTASM_X86_64 */ - sp_offset += HOST_SIZEOF_POINTER; /* Skip over return-pc */ - sp_offset += argno * HOST_SIZEOF_POINTER; /* Load the relevant argument */ -#endif /* !HOSTASM_X86_64 */ - return _Dee_function_generator_gmov_hstackind2reg(self, sp_offset, dst_regno); -} - -/* Load special runtime values into `dst_regno' */ -INTERN WUNUSED NONNULL((1)) int DCALL -_Dee_function_generator_gmov_usage2reg(struct Dee_function_generator *__restrict self, - Dee_host_regusage_t usage, - Dee_host_register_t dst_regno) { - Dee_hostfunc_cc_t cc = self->fg_assembler->fa_cc; - switch (usage) { - case DEE_HOST_REGUSAGE_THIS: - if (!(cc & HOSTFUNC_CC_F_THIS)) - break; - return _Dee_function_generator_gmov_truearg2reg(self, 0, dst_regno); - - case DEE_HOST_REGUSAGE_ARGC: { - size_t offset = 0; - if (cc & HOSTFUNC_CC_F_TUPLE) - break; - if (cc & HOSTFUNC_CC_F_THIS) - ++offset; - return _Dee_function_generator_gmov_truearg2reg(self, offset, dst_regno); - } break; - - case DEE_HOST_REGUSAGE_ARGV: { - size_t offset = 1; - if (cc & HOSTFUNC_CC_F_TUPLE) - break; - if (cc & HOSTFUNC_CC_F_THIS) - ++offset; - return _Dee_function_generator_gmov_truearg2reg(self, offset, dst_regno); - } break; - - case DEE_HOST_REGUSAGE_ARGS: { - size_t offset = 0; - if (!(cc & HOSTFUNC_CC_F_TUPLE)) - break; - if (cc & HOSTFUNC_CC_F_THIS) - ++offset; - return _Dee_function_generator_gmov_truearg2reg(self, offset, dst_regno); - } break; - - case DEE_HOST_REGUSAGE_KW: { - size_t offset = 1; - if (!(cc & HOSTFUNC_CC_F_KW)) - break; - if (!(cc & HOSTFUNC_CC_F_TUPLE)) - ++offset; - if (cc & HOSTFUNC_CC_F_THIS) - ++offset; - return _Dee_function_generator_gmov_truearg2reg(self, offset, dst_regno); - } break; - - default: break; - } - return DeeError_Throwf(&DeeError_IllegalInstruction, - "Unsupported register usage %" PRFu8, - (uint8_t)usage); -} - INTERN WUNUSED NONNULL((1)) int DCALL _Dee_function_generator_gret(struct Dee_function_generator *__restrict self) { struct Dee_host_section *sect = self->fg_sect; diff --git a/src/dex/_hostasm/generator-common.c b/src/dex/_hostasm/generator-common.c index 0cd13ceb8..c32c39f1e 100644 --- a/src/dex/_hostasm/generator-common.c +++ b/src/dex/_hostasm/generator-common.c @@ -39,28 +39,6 @@ STATIC_ASSERT(offsetof(struct Dee_memloc, ml_value.v_hreg.r_voff) == /* COMMON CODE GENERATION FUNCTIONS */ /************************************************************************/ -INTERN WUNUSED NONNULL((1)) int DCALL -Dee_function_generator_gmov_arg2reg(struct Dee_function_generator *__restrict self, - uint16_t aid, Dee_host_register_t dst_regno) { - ptrdiff_t delta = (ptrdiff_t)aid * HOST_SIZEOF_POINTER; - struct Dee_memstate *state = self->fg_state; - Dee_host_register_t argv_regno; - argv_regno = Dee_memstate_hregs_find_usage(state, DEE_HOST_REGUSAGE_ARGV); - if (argv_regno >= HOST_REGISTER_COUNT) { - argv_regno = Dee_memstate_hregs_find_unused(state, true); - if (argv_regno >= HOST_REGISTER_COUNT) - argv_regno = dst_regno; - if unlikely(Dee_function_generator_gmov_usage2reg(self, DEE_HOST_REGUSAGE_ARGV, argv_regno)) - goto err; - } - if unlikely(Dee_function_generator_gmov_regind2reg(self, argv_regno, delta, dst_regno)) - goto err; - state->ms_rusage[dst_regno] = DEE_HOST_REGUSAGE_GENERIC; - return 0; -err: - return -1; -} - INTERN WUNUSED NONNULL((1, 4)) int DCALL Dee_function_generator_gmov_regx2loc(struct Dee_function_generator *__restrict self, Dee_host_register_t src_regno, ptrdiff_t src_delta, @@ -424,45 +402,6 @@ Dee_function_generator_gmov_loc2hstackindx(struct Dee_function_generator *__rest -/* Load special runtime values into `dst_regno' */ -INTERN WUNUSED NONNULL((1)) int DCALL -Dee_function_generator_gmov_usage2reg(struct Dee_function_generator *__restrict self, - Dee_host_regusage_t usage, - Dee_host_register_t dst_regno) { - struct Dee_memstate *state = self->fg_state; - if unlikely(state->ms_rusage[dst_regno] == usage) - return 0; - if ((usage == DEE_HOST_REGUSAGE_ARGC || usage == DEE_HOST_REGUSAGE_ARGV) && - (self->fg_assembler->fa_cc & HOSTFUNC_CC_F_TUPLE)) { - /* Special case: need to load via argc-tuple indirection */ - ptrdiff_t args_offset; - Dee_host_register_t args_regno; - args_regno = Dee_memstate_hregs_find_usage(state, DEE_HOST_REGUSAGE_ARGS); - if (args_regno >= HOST_REGISTER_COUNT) { - args_regno = Dee_memstate_hregs_find_unused(state, true); - if (args_regno >= HOST_REGISTER_COUNT) - args_regno = dst_regno; - if unlikely(_Dee_function_generator_gmov_usage2reg(self, DEE_HOST_REGUSAGE_ARGS, args_regno)) - goto err; - state->ms_rusage[args_regno] = DEE_HOST_REGUSAGE_ARGV; - } - - /* Now load the relevant tuple object field. */ - args_offset = offsetof(DeeTupleObject, t_elem); - if (usage == DEE_HOST_REGUSAGE_ARGC) - args_offset = offsetof(DeeTupleObject, t_size); - if unlikely(_Dee_function_generator_gmov_regind2reg(self, args_regno, args_offset, dst_regno)) - goto err; - } else { - if unlikely(_Dee_function_generator_gmov_usage2reg(self, usage, dst_regno)) - goto err; - } - state->ms_rusage[dst_regno] = usage; - return 0; -err: - return -1; -} - /* Generate code to return `loc'. No extra code to decref stack/locals is generated. If you * want that extra code to be generated, you need to use `Dee_function_generator_vret()'. */ INTERN WUNUSED NONNULL((1, 2)) int DCALL @@ -491,12 +430,71 @@ Dee_function_generator_gret(struct Dee_function_generator *__restrict self, return result; } +#if defined(HOSTASM_X86) && !defined(HOSTASM_X86_64) +#define HAVE_try_restore_xloc_arg_cfa_offset + +/* On i386, caller-argument locals don't have to be flushed to the stack. + * Instead, if you try to flush a register that's been populated with one + * of the function's caller-arguments, no code needs to be generated and + * the CFA offset can just be reset to point at the argument again. */ +PRIVATE WUNUSED NONNULL((1)) uintptr_t DCALL +try_restore_xloc_arg_cfa_offset(struct Dee_function_generator *__restrict self, + Dee_host_register_t regno) { +#define DEE_MEMSTATE_EXTRA_LOCAL_A_MIN DEE_MEMSTATE_EXTRA_LOCAL_A_THIS +#define DEE_MEMSTATE_EXTRA_LOCAL_A_MAX DEE_MEMSTATE_EXTRA_LOCAL_A_KW + size_t i, xloc_base = self->fg_assembler->fa_localc; + struct Dee_memstate *state = self->fg_state; + for (i = DEE_MEMSTATE_EXTRA_LOCAL_A_MIN; i <= DEE_MEMSTATE_EXTRA_LOCAL_A_MAX; ++i) { + struct Dee_memloc *xloc = &state->ms_localv[xloc_base + i]; + if (xloc->ml_type == MEMLOC_TYPE_HREG && + xloc->ml_value.v_hreg.r_regno == regno) { + uintptr_t cfa_offset; + Dee_hostfunc_cc_t cc = self->fg_assembler->fa_cc; + size_t true_argi = 0; + switch (i) { + case DEE_MEMSTATE_EXTRA_LOCAL_A_THIS: + ASSERT(cc & HOSTFUNC_CC_F_THIS); + break; + case DEE_MEMSTATE_EXTRA_LOCAL_A_ARGC: + ASSERT(!(cc & HOSTFUNC_CC_F_TUPLE)); + if (cc & HOSTFUNC_CC_F_THIS) + ++true_argi; + break; + case DEE_MEMSTATE_EXTRA_LOCAL_A_ARGV: /* or `DEE_MEMSTATE_EXTRA_LOCAL_A_ARGS' */ + if (cc & HOSTFUNC_CC_F_THIS) + ++true_argi; + if (!(cc & HOSTFUNC_CC_F_TUPLE)) + ++true_argi; + break; + case DEE_MEMSTATE_EXTRA_LOCAL_A_KW: + ASSERT(cc & HOSTFUNC_CC_F_KW); + if (cc & HOSTFUNC_CC_F_THIS) + ++true_argi; + if (cc & HOSTFUNC_CC_F_TUPLE) + ++true_argi; + ++true_argi; + break; + default: __builtin_unreachable(); + } + cfa_offset = (uintptr_t)(-(ptrdiff_t)((true_argi + 1) * HOST_SIZEOF_POINTER)); + return cfa_offset; + } + } + return (uintptr_t)-1; +} +#endif /* HOSTASM_X86 && !HOSTASM_X86_64 */ + /* Push/move `regno' onto the host stack, returning the CFA offset of the target location. */ PRIVATE WUNUSED NONNULL((1)) uintptr_t DCALL Dee_function_generator_gflushreg(struct Dee_function_generator *__restrict self, Dee_host_register_t regno) { uintptr_t cfa_offset; ASSERT(!Dee_memstate_isshared(self->fg_state)); +#ifdef HAVE_try_restore_xloc_arg_cfa_offset + cfa_offset = try_restore_xloc_arg_cfa_offset(self, regno); + if (cfa_offset != (uintptr_t)-1) + return cfa_offset; +#endif /* HAVE_try_restore_xloc_arg_cfa_offset */ cfa_offset = Dee_memstate_hstack_find(self->fg_state, HOST_SIZEOF_POINTER); if (cfa_offset != (uintptr_t)-1) { if unlikely(Dee_function_generator_gmov_reg2hstackind(self, regno, cfa_offset)) @@ -731,12 +729,10 @@ Dee_function_generator_gusagereg(struct Dee_function_generator *__restrict self, Dee_host_register_t regno; regno = Dee_memstate_hregs_find_usage(state, usage); if (regno >= HOST_REGISTER_COUNT) { - /* Need to allocate a register for `usage'. */ - regno = Dee_function_generator_gallocreg(self, dont_alloc_these); - if unlikely(regno >= HOST_REGISTER_COUNT) - goto err; - if unlikely(Dee_function_generator_gmov_usage2reg(self, usage, regno)) - goto err; + (void)dont_alloc_these; + /* TODO */ + DeeError_NOTIMPLEMENTED(); + goto err; } return regno; err: @@ -939,10 +935,61 @@ Dee_function_generator_gxdecref(struct Dee_function_generator *__restrict self, return -1; } +/* Change `loc' into the value of ` = *( + ind_delta)' + * Note that unlike the `Dee_function_generator_gmov*' functions, this + * one may use `MEMLOC_TYPE_*IND' to defer the indirection until later. */ +INTERN WUNUSED NONNULL((1, 2)) int DCALL +Dee_function_generator_gind(struct Dee_function_generator *__restrict self, + struct Dee_memloc *loc, ptrdiff_t ind_delta) { + ASSERTF(loc->ml_flags & MEMLOC_F_NOREF, "Dee_function_generator_gind() called on reference"); + switch (loc->ml_type) { + + case MEMLOC_TYPE_HSTACK: + loc->ml_type = MEMLOC_TYPE_HSTACKIND; +#ifdef HOSTASM_STACK_GROWS_DOWN + loc->ml_value.v_hstack.s_cfa -= ind_delta; +#else /* HOSTASM_STACK_GROWS_DOWN */ + loc->ml_value.v_hstack.s_cfa += ind_delta; +#endif /* !HOSTASM_STACK_GROWS_DOWN */ + loc->ml_value.v_hstack.s_off = 0; + return 0; + + case MEMLOC_TYPE_CONST: { + DeeObject **p_value; + Dee_host_register_t temp_regno; + temp_regno = Dee_function_generator_gallocreg(self, NULL); + if unlikely(temp_regno >= HOST_REGISTER_COUNT) + goto err; + p_value = (DeeObject **)((uintptr_t)loc->ml_value.v_const + ind_delta); + if unlikely(Dee_function_generator_gmov_constind2reg(self, p_value, temp_regno)) + goto err; + loc->ml_type = MEMLOC_TYPE_HREG; + loc->ml_value.v_hreg.r_regno = temp_regno; + loc->ml_value.v_hreg.r_off = 0; + loc->ml_value.v_hreg.r_voff = 0; + } break; + + default: + if unlikely(Dee_function_generator_vreg(self, NULL)) + goto err; + ASSERT(loc->ml_type == MEMLOC_TYPE_HREG); + ATTR_FALLTHROUGH + case MEMLOC_TYPE_HREG: + /* Turn the location from an HREG into HREGIND */ + loc->ml_type = MEMLOC_TYPE_HREGIND; + loc->ml_value.v_hreg.r_off += ind_delta; + loc->ml_value.v_hreg.r_voff = 0; + break; + } + return 0; +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, + struct Dee_memloc *loc, Dee_host_register_t const *not_these) { struct Dee_memstate *state; Dee_host_register_t regno; @@ -1023,7 +1070,7 @@ Dee_function_generator_greg(struct Dee_function_generator *__restrict self, /* Force `loc' to reside on the stack, giving it an address (`MEMLOC_TYPE_HSTACKIND, v_hstack.s_off = 0'). */ INTERN WUNUSED NONNULL((1, 2)) int DCALL Dee_function_generator_gflush(struct Dee_function_generator *__restrict self, - struct Dee_memloc *__restrict loc) { + struct Dee_memloc *loc) { uintptr_t cfa_offset; struct Dee_memstate *state = self->fg_state; ASSERT(!Dee_memstate_isshared(state)); @@ -1037,18 +1084,26 @@ Dee_function_generator_gflush(struct Dee_function_generator *__restrict self, } /* Figure out where we want to allocate the value. */ - cfa_offset = Dee_memstate_hstack_find(state, HOST_SIZEOF_POINTER); - if (cfa_offset != (uintptr_t)-1) { - if unlikely(Dee_function_generator_gmov_loc2hstackind(self, loc, cfa_offset)) - goto err; - } else { - if unlikely(Dee_function_generator_ghstack_pushloc(self, loc)) - goto err; +#ifdef HAVE_try_restore_xloc_arg_cfa_offset + if (loc->ml_type == MEMLOC_TYPE_HREG && + (cfa_offset = try_restore_xloc_arg_cfa_offset(self, loc->ml_value.v_hreg.r_regno)) != (uintptr_t)-1) { + /* CFA offset restored */ + } else +#endif /* HAVE_try_restore_xloc_arg_cfa_offset */ + { + cfa_offset = Dee_memstate_hstack_find(state, HOST_SIZEOF_POINTER); + if (cfa_offset != (uintptr_t)-1) { + if unlikely(Dee_function_generator_gmov_loc2hstackind(self, loc, cfa_offset)) + goto err; + } else { + if unlikely(Dee_function_generator_ghstack_pushloc(self, loc)) + goto err; #ifdef HOSTASM_STACK_GROWS_DOWN - cfa_offset = state->ms_host_cfa_offset; + cfa_offset = state->ms_host_cfa_offset; #else /* HOSTASM_STACK_GROWS_DOWN */ - cfa_offset = state->ms_host_cfa_offset - HOST_SIZEOF_POINTER; + cfa_offset = state->ms_host_cfa_offset - HOST_SIZEOF_POINTER; #endif /* !HOSTASM_STACK_GROWS_DOWN */ + } } /* If the location used to be a writable location, then we must diff --git a/src/dex/_hostasm/generator-deemon.c b/src/dex/_hostasm/generator-deemon.c index fdba48564..ebeef8ca8 100644 --- a/src/dex/_hostasm/generator-deemon.c +++ b/src/dex/_hostasm/generator-deemon.c @@ -399,7 +399,7 @@ gen_print_with_stdout_in_vtop(struct Dee_function_generator *__restrict self, /* For the non-file-print instructions, load the `stdout' file stream only the first * time a print-like instruction is reached. We then keep that object in a hidden - * local `DEE_MEMSTATE_EXTRA_LOCALS_STDOUT' until the end of the basic block, or until + * local `DEE_MEMSTATE_EXTRA_LOCAL_STDOUT' until the end of the basic block, or until * a chunk of at least `CONFIG_HOSTASM_STDOUT_CACHE_MAXINSTR_N' non-print-to-stdout * instructions are about to be compiled. * @@ -415,7 +415,7 @@ Dee_function_generator_gen_stdout_print(struct Dee_function_generator *__restric Dee_instruction_t const *iter, *print_block_end, *print_block_end_limit; if unlikely(Dee_function_generator_state_unshare(self)) goto err; - stdout_lid = (size_t)self->fg_assembler->fa_code->co_localc + DEE_MEMSTATE_EXTRA_LOCALS_STDOUT; + stdout_lid = (size_t)self->fg_assembler->fa_localc + DEE_MEMSTATE_EXTRA_LOCAL_STDOUT; /* Figure out where we want to end the print-block. */ print_block_end_limit = self->fg_block->bb_deemon_end; @@ -766,7 +766,7 @@ Dee_function_generator_geninstr(struct Dee_function_generator *__restrict self, reference = self->fg_assembler->fa_function->fo_refv[rid]; if unlikely(Dee_function_generator_vpush_const(self, reference)) goto err; - if unlikely(Dee_function_generator_vpush_usage(self, DEE_HOST_REGUSAGE_THIS)) + if unlikely(Dee_function_generator_vpush_this(self)) goto err; return Dee_function_generator_vcallapi(self, (void const *)&DeeSuper_New, VCALLOP_CC_OBJECT, 2); } break; @@ -785,18 +785,8 @@ Dee_function_generator_geninstr(struct Dee_function_generator *__restrict self, case ASM16_POP_GLOBAL: return Dee_function_generator_vpop_global(self, UNALIGNED_GETLE16(instr + 2)); - case ASM_PUSH_VARARGS: - if (!(self->fg_assembler->fa_cc & HOSTFUNC_CC_F_TUPLE)) { - if unlikely(Dee_function_generator_vpush_usage(self, DEE_HOST_REGUSAGE_ARGC)) - goto err; - if unlikely(Dee_function_generator_vpush_usage(self, DEE_HOST_REGUSAGE_ARGV)) - goto err; - return Dee_function_generator_vcallapi(self, (void const *)&DeeTuple_NewVector, VCALLOP_CC_OBJECT, 2); - } - return Dee_function_generator_vpush_usage(self, DEE_HOST_REGUSAGE_ARGS); - - case ASM_PUSH_VARKWDS: - return Dee_function_generator_vpush_usage(self, DEE_HOST_REGUSAGE_KW); + //TODO: case ASM_PUSH_VARARGS: + //TODO: case ASM_PUSH_VARKWDS: case ASM_PUSH_STATIC: return Dee_function_generator_vpush_static(self, instr[1]); @@ -973,7 +963,7 @@ Dee_function_generator_geninstr(struct Dee_function_generator *__restrict self, __IF0 { case ASM16_GETATTR_THIS_C: cid = UNALIGNED_GETLE16(instr + 2); } if unlikely(cid >= self->fg_assembler->fa_code->co_staticc) return err_illegal_cid(cid); - if unlikely(Dee_function_generator_vpush_usage(self, DEE_HOST_REGUSAGE_THIS)) + if unlikely(Dee_function_generator_vpush_this(self)) goto err; constant = self->fg_assembler->fa_code->co_staticv[cid]; if unlikely(Dee_function_generator_vpush_const(self, constant)) @@ -988,7 +978,7 @@ Dee_function_generator_geninstr(struct Dee_function_generator *__restrict self, __IF0 { case ASM16_DELATTR_THIS_C: cid = UNALIGNED_GETLE16(instr + 2); } if unlikely(cid >= self->fg_assembler->fa_code->co_staticc) return err_illegal_cid(cid); - if unlikely(Dee_function_generator_vpush_usage(self, DEE_HOST_REGUSAGE_THIS)) + if unlikely(Dee_function_generator_vpush_this(self)) goto err; constant = self->fg_assembler->fa_code->co_staticv[cid]; if unlikely(Dee_function_generator_vpush_const(self, constant)) @@ -1005,7 +995,7 @@ Dee_function_generator_geninstr(struct Dee_function_generator *__restrict self, __IF0 { case ASM16_SETATTR_THIS_C: cid = UNALIGNED_GETLE16(instr + 2); } if unlikely(cid >= self->fg_assembler->fa_code->co_staticc) return err_illegal_cid(cid); - if unlikely(Dee_function_generator_vpush_usage(self, DEE_HOST_REGUSAGE_THIS)) + if unlikely(Dee_function_generator_vpush_this(self)) goto err; constant = self->fg_assembler->fa_code->co_staticv[cid]; if unlikely(Dee_function_generator_vpush_const(self, constant)) @@ -1582,7 +1572,7 @@ Dee_function_generator_geninstr(struct Dee_function_generator *__restrict self, __IF0 { case ASM16_CALLATTR_THIS_C_TUPLE: cid = UNALIGNED_GETLE16(instr + 2); } if unlikely(cid >= self->fg_assembler->fa_code->co_staticc) return err_illegal_cid(cid); - if unlikely(Dee_function_generator_vpush_usage(self, DEE_HOST_REGUSAGE_THIS)) + if unlikely(Dee_function_generator_vpush_this(self)) goto err; constant = self->fg_assembler->fa_code->co_staticv[cid]; if unlikely(Dee_function_generator_vpush_const(self, constant)) @@ -1626,7 +1616,7 @@ Dee_function_generator_geninstr(struct Dee_function_generator *__restrict self, //TODO: case ASM_PUSH_EXCEPT: case ASM_PUSH_THIS: - return Dee_function_generator_vpush_usage(self, DEE_HOST_REGUSAGE_THIS); + return Dee_function_generator_vpush_this(self); case ASM_CAST_HASHSET: return Dee_function_generator_vcallapi(self, (void const *)&DeeHashSet_FromSequence, VCALLOP_CC_OBJECT, 1); case ASM_CAST_DICT: @@ -1741,7 +1731,7 @@ Dee_function_generator_geninstr(struct Dee_function_generator *__restrict self, return err_illegal_gid(mod, id1); } break; case ASM_LOCAL: - if unlikely(id1 >= self->fg_assembler->fa_code->co_localc) + if unlikely(id1 >= self->fg_assembler->fa_localc) return err_illegal_lid(id1); break; default: __builtin_unreachable(); diff --git a/src/dex/_hostasm/generator-vstack.c b/src/dex/_hostasm/generator-vstack.c index cc3f16d02..94d8ea577 100644 --- a/src/dex/_hostasm/generator-vstack.c +++ b/src/dex/_hostasm/generator-vstack.c @@ -132,31 +132,35 @@ INTERN WUNUSED NONNULL((1)) int DCALL Dee_function_generator_vpush_arg(struct Dee_function_generator *__restrict self, uint16_t aid) { DeeCodeObject *code = self->fg_assembler->fa_code; if (aid < code->co_argc_min) /* Simple case: mandatory argument */ - return _Dee_function_generator_vpush_arg(self, aid); + return Dee_function_generator_vpush_arg_present(self, aid); if (aid < code->co_argc_max) { /* Special case: optional argument */ - size_t xloc_id = DEE_MEMSTATE_EXTRA_LOCALS_DEFARG(aid - code->co_argc_min); - return Dee_function_generator_vpush_extra_local(self, xloc_id); + size_t xloc_id = DEE_MEMSTATE_EXTRA_LOCAL_DEFARG(aid - code->co_argc_min); + /* TODO: Lazily initialize the extra-local value if not unconditionally bound */ + return Dee_function_generator_vpush_xlocal(self, xloc_id); } return err_illegal_aid(aid); } INTERN WUNUSED NONNULL((1)) int DCALL -_Dee_function_generator_vpush_arg(struct Dee_function_generator *__restrict self, uint16_t aid) { +Dee_function_generator_vpush_arg_present(struct Dee_function_generator *__restrict self, uint16_t aid) { int result; ASSERT(aid < self->fg_assembler->fa_code->co_argc_min); result = Dee_function_generator_state_unshare(self); if likely(result == 0) { + STATIC_ASSERT(DEE_MEMSTATE_EXTRA_LOCAL_A_ARGS == DEE_MEMSTATE_EXTRA_LOCAL_A_ARGV); + struct Dee_memloc *args_or_argv_loc; ptrdiff_t ind_offset = (ptrdiff_t)aid * sizeof(DeeObject *); - Dee_host_register_t argv_regno; - if (self->fg_assembler->fa_cc & HOSTFUNC_CC_F_TUPLE) { + if (self->fg_assembler->fa_cc & HOSTFUNC_CC_F_TUPLE) ind_offset += offsetof(DeeTupleObject, t_elem); - argv_regno = Dee_function_generator_gusagereg(self, DEE_HOST_REGUSAGE_ARGS, NULL); - } else { - argv_regno = Dee_function_generator_gusagereg(self, DEE_HOST_REGUSAGE_ARGV, NULL); - } - if unlikely(argv_regno >= HOST_REGISTER_COUNT) + args_or_argv_loc = &self->fg_state->ms_localv[self->fg_assembler->fa_localc + + DEE_MEMSTATE_EXTRA_LOCAL_A_ARGV]; + if unlikely(Dee_function_generator_greg(self, args_or_argv_loc, NULL)) goto err; - result = Dee_memstate_vpush_regind(self->fg_state, argv_regno, ind_offset, 0); + ASSERT(args_or_argv_loc->ml_type == MEMLOC_TYPE_HREG); + result = Dee_memstate_vpush_regind(self->fg_state, + args_or_argv_loc->ml_value.v_hreg.r_regno, + args_or_argv_loc->ml_value.v_hreg.r_off + ind_offset, + 0); } return result; err: @@ -272,23 +276,6 @@ Dee_function_generator_vpop_n(struct Dee_function_generator *__restrict self, si return -1; } -INTERN WUNUSED NONNULL((1, 2)) int DCALL -Dee_function_generator_vpush_usage(struct Dee_function_generator *__restrict self, - Dee_host_regusage_t usage) { - Dee_host_register_t regno; - if unlikely(Dee_function_generator_state_unshare(self)) - goto err; -#ifdef HOSTASM_X86 - /* TODO: Some usage locations can be expressed using negative CFA offset */ -#endif /* HOSTASM_X86 */ - regno = Dee_function_generator_gusagereg(self, usage, NULL); - if unlikely(regno >= HOST_REGISTER_COUNT) - goto err; - return Dee_function_generator_vpush_reg(self, regno, 0); -err: - return -1; -} - INTERN WUNUSED NONNULL((1)) int DCALL Dee_function_generator_vpop_local(struct Dee_function_generator *__restrict self, size_t lid) { @@ -386,27 +373,6 @@ Dee_function_generator_vdel_local(struct Dee_function_generator *__restrict self return -1; } -INTERN WUNUSED NONNULL((1)) int DCALL -Dee_function_generator_vpush_ulocal(struct Dee_function_generator *__restrict self, uint16_t lid) { - if unlikely(lid >= self->fg_assembler->fa_code->co_localc) - return err_illegal_lid(lid); - return Dee_function_generator_vpush_local(self, (size_t)lid); -} - -INTERN WUNUSED NONNULL((1)) int DCALL -Dee_function_generator_vpop_ulocal(struct Dee_function_generator *__restrict self, uint16_t lid) { - if unlikely(lid >= self->fg_assembler->fa_code->co_localc) - return err_illegal_lid(lid); - return Dee_function_generator_vpop_local(self, (size_t)lid); -} - -INTERN WUNUSED NONNULL((1)) int DCALL -Dee_function_generator_vdel_ulocal(struct Dee_function_generator *__restrict self, uint16_t lid) { - if unlikely(lid >= self->fg_assembler->fa_code->co_localc) - return err_illegal_lid(lid); - return Dee_function_generator_vdel_local(self, (size_t)lid); -} - #ifdef CONFIG_HAVE_FPU /* API function called by `operator float()' */ @@ -570,36 +536,57 @@ Dee_function_generator_vopv(struct Dee_function_generator *__restrict self, } -/* Push (and lazily cache if not already done so) an "extra local" `id'. - * @param: id: Extra local variable ID (one of `DEE_MEMSTATE_EXTRA_LOCALS_*') */ INTERN WUNUSED NONNULL((1)) int DCALL -Dee_function_generator_vpush_extra_local(struct Dee_function_generator *__restrict self, size_t id) { - struct Dee_memloc *loc; - struct Dee_memstate *state; - DeeCodeObject *code; - size_t lid; - if unlikely(Dee_function_generator_state_unshare(self)) - goto err; - state = self->fg_state; - code = self->fg_assembler->fa_code; - lid = code->co_localc + id; - ASSERTF(id < state->ms_localc, "Bad extra-local id %" PRFuSIZ, id); - loc = &state->ms_localv[lid]; +Dee_function_generator_vpush_ulocal(struct Dee_function_generator *__restrict self, uint16_t lid) { + if unlikely(lid >= self->fg_assembler->fa_localc) + return err_illegal_lid(lid); + return Dee_function_generator_vpush_local(self, (size_t)lid); +} - /* Check if the local needs to be initialized. */ - if (!(loc->ml_flags & MEMLOC_F_LOCAL_BOUND)) { - /* TODO */ - return DeeError_NOTIMPLEMENTED(); - } - ASSERT(!(loc->ml_flags & MEMLOC_F_LOCAL_UNBOUND)); - ASSERT(loc->ml_flags & MEMLOC_F_LOCAL_BOUND); +INTERN WUNUSED NONNULL((1)) int DCALL +Dee_function_generator_vpop_ulocal(struct Dee_function_generator *__restrict self, uint16_t lid) { + if unlikely(lid >= self->fg_assembler->fa_localc) + return err_illegal_lid(lid); + return Dee_function_generator_vpop_local(self, (size_t)lid); +} - /* Push the location onto the V-stack. */ - return Dee_function_generator_vpush(self, loc); +INTERN WUNUSED NONNULL((1)) int DCALL +Dee_function_generator_vdel_ulocal(struct Dee_function_generator *__restrict self, uint16_t lid) { + if unlikely(lid >= self->fg_assembler->fa_localc) + return err_illegal_lid(lid); + return Dee_function_generator_vdel_local(self, (size_t)lid); +} + + + + +INTERN WUNUSED NONNULL((1)) int DCALL +Dee_function_generator_vpush_argc(struct Dee_function_generator *__restrict self) { + if (!(self->fg_assembler->fa_cc & HOSTFUNC_CC_F_TUPLE)) + return Dee_function_generator_vpush_xlocal(self, DEE_MEMSTATE_EXTRA_LOCAL_A_ARGC); + if unlikely(Dee_function_generator_vpush_xlocal(self, DEE_MEMSTATE_EXTRA_LOCAL_A_ARGS)) + goto err; + return Dee_function_generator_vind(self, offsetof(DeeTupleObject, t_size)); err: return -1; } +INTERN WUNUSED NONNULL((1)) int DCALL +Dee_function_generator_vpush_usage(struct Dee_function_generator *__restrict self, + Dee_host_regusage_t usage) { + Dee_host_register_t regno; + if unlikely(Dee_function_generator_state_unshare(self)) + goto err; + regno = Dee_function_generator_gusagereg(self, usage, NULL); + if unlikely(regno >= HOST_REGISTER_COUNT) + goto err; + return Dee_function_generator_vpush_reg(self, regno, 0); +err: + return -1; +} + + + /* Perform a conditional jump to `desc' based on `jump_if_true' * @param: instr: Pointer to start of deemon jmp-instruction (for bb-truncation, and error message) @@ -745,58 +732,64 @@ Dee_function_generator_vjcc(struct Dee_function_generator *__restrict self, -/* 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 **)' - * and dereference it, before storing the resulting value back into the to-most stack item. */ +/* >> TOP = *(TOP + ind_delta); */ INTERN WUNUSED NONNULL((1)) int DCALL -Dee_function_generator_vind(struct Dee_function_generator *__restrict self, ptrdiff_t offset) { - Dee_host_register_t dst_regno; - struct Dee_memstate *state = self->fg_state; +Dee_function_generator_vind(struct Dee_function_generator *__restrict self, + ptrdiff_t ind_delta) { struct Dee_memloc *loc; - if unlikely(state->ms_stackc < 1) + if unlikely(self->fg_state->ms_stackc < 1) return err_illegal_stack_effect(); - loc = Dee_memstate_vtop(state); - ASSERTF(loc->ml_flags & MEMLOC_F_NOREF, - "Dee_function_generator_vind() called on reference"); - /* TODO: Make use of dedicated location types! */ - - if (loc->ml_type == MEMLOC_TYPE_HREG) { - /* Special case: source operand is already a register. */ - dst_regno = loc->ml_value.v_hreg.r_regno; - return Dee_function_generator_gmov_locind2reg(self, loc, offset, dst_regno); - } - - /* Need a temporary register for the indirection result. */ - dst_regno = Dee_function_generator_gallocreg(self, NULL); - if unlikely(dst_regno >= HOST_REGISTER_COUNT) - goto err; - if unlikely(Dee_function_generator_gmov_locind2reg(self, loc, offset, dst_regno)) + if unlikely(Dee_function_generator_state_unshare(self)) goto err; - - /* Remember the register as stack value. */ - loc->ml_type = MEMLOC_TYPE_HREG; - loc->ml_value.v_hreg.r_regno = dst_regno; - loc->ml_value.v_hreg.r_off = 0; - return 0; + loc = Dee_function_generator_vtop(self); + return Dee_function_generator_gind(self, loc, ind_delta); err: return -1; } -/* >> *(SECOND + offset) = FIRST; POP(); POP(); */ +/* >> TOP = TOP + val_delta; */ INTERN WUNUSED NONNULL((1)) int DCALL -Dee_function_generator_vpop_ind(struct Dee_function_generator *__restrict self, ptrdiff_t offset) { - /* TODO */ - (void)self; - (void)offset; - return DeeError_NOTIMPLEMENTED(); +Dee_function_generator_vdelta(struct Dee_function_generator *__restrict self, + ptrdiff_t val_delta) { + struct Dee_memloc *loc; + if unlikely(self->fg_state->ms_stackc < 1) + return err_illegal_stack_effect(); + if unlikely(val_delta == 0) + return 0; + if unlikely(Dee_function_generator_state_unshare(self)) + goto err; + loc = Dee_function_generator_vtop(self); + switch (loc->ml_type) { + default: + if unlikely(Dee_function_generator_greg(self, loc, NULL)) + goto err; + ASSERT(loc->ml_type == MEMLOC_TYPE_HREG); + ATTR_FALLTHROUGH + case MEMLOC_TYPE_HREG: + case MEMLOC_TYPE_HSTACK: + loc->ml_value.v_hreg.r_off += val_delta; + break; + case MEMLOC_TYPE_HREGIND: + case MEMLOC_TYPE_HSTACKIND: + loc->ml_value.v_hreg.r_voff += val_delta; + break; + case MEMLOC_TYPE_CONST: + loc->ml_value.v_const = (DeeObject *)((uintptr_t)loc->ml_value.v_const + val_delta); + break; + } + return 0; +err: + return -1; } -/* >> temp = *(SECOND + offset); *(SECOND + offset) = FIRST; FIRST = temp; */ +/* >> temp = *(SECOND + ind_delta); + * >> *(SECOND + ind_delta) = FIRST; + * >> FIRST = temp; */ INTERN WUNUSED NONNULL((1)) int DCALL -Dee_function_generator_vxch_ind(struct Dee_function_generator *__restrict self, ptrdiff_t offset) { +Dee_function_generator_vxch_ind(struct Dee_function_generator *__restrict self, ptrdiff_t ind_delta) { /* TODO */ (void)self; - (void)offset; + (void)ind_delta; return DeeError_NOTIMPLEMENTED(); } @@ -898,6 +891,8 @@ vpush_global_or_extern(struct Dee_function_generator *__restrict self, goto err; if unlikely(Dee_function_generator_vind(self, 0)) goto err; + if unlikely(Dee_function_generator_vreg(self, NULL)) + goto err; 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)) @@ -1019,6 +1014,8 @@ Dee_function_generator_vpush_static(struct Dee_function_generator *__restrict se goto err; if unlikely(Dee_function_generator_vind(self, 0)) goto err; + if unlikely(Dee_function_generator_vreg(self, NULL)) + goto err; loc = Dee_function_generator_vtop(self); ASSERT(loc->ml_flags & MEMLOC_F_NOREF); loc->ml_flags &= ~MEMLOC_F_NOREF; diff --git a/src/dex/_hostasm/libhostasm.h b/src/dex/_hostasm/libhostasm.h index b31e1a0b9..ce9cb8796 100644 --- a/src/dex/_hostasm/libhostasm.h +++ b/src/dex/_hostasm/libhostasm.h @@ -189,27 +189,28 @@ Dee_memloc_sameloc(struct Dee_memloc const *a, struct Dee_memloc const *b); /* Possible values for `Dee_memstate::ms_rusage' */ #define DEE_HOST_REGUSAGE_GENERIC 0x00 /* Register usage is defined by `ms_stackv' and `ms_localv'. */ -#define DEE_HOST_REGUSAGE_THIS 0x01 /* Register contains: argc / DeeTuple_SIZE(args) */ -#define DEE_HOST_REGUSAGE_ARGC 0x02 /* Register contains: argc / DeeTuple_SIZE(args) */ -#define DEE_HOST_REGUSAGE_ARGV 0x03 /* Register contains: argv / DeeTuple_ELEM(args) */ -#define DEE_HOST_REGUSAGE_ARGS 0x04 /* Register contains: args */ -#define DEE_HOST_REGUSAGE_KW 0x05 /* Register contains: kw */ +#define DEE_HOST_REGUSAGE_THREAD 0x01 /* Register contains: DeeThread_Self() */ typedef uint8_t Dee_host_regusage_t; /* Extra local variable IDs always present in `ms_localv' * These indices appear after "normal" locals. */ -#define DEE_MEMSTATE_EXTRA_LOCALS_VARARGS 0 /* Varargs (s.a. `struct Dee_code_frame::cf_vargs') */ -#define DEE_MEMSTATE_EXTRA_LOCALS_VARKWDS 1 /* Varkwds (s.a. `struct Dee_code_frame_kwds::fk_varkwds') */ -#define DEE_MEMSTATE_EXTRA_LOCALS_STDOUT 2 /* Temporary slot for a cached version of `deemon.File.stdout' (to speed up `ASM_PRINT' & friends) */ -#define DEE_MEMSTATE_EXTRA_LOCALS_MINCOUNT 3 /* Min number of extra locals */ -#define DEE_MEMSTATE_EXTRA_LOCALS_DEFARG_MIN DEE_MEMSTATE_EXTRA_LOCALS_MINCOUNT -#define DEE_MEMSTATE_EXTRA_LOCALS_DEFARG(i) (DEE_MEMSTATE_EXTRA_LOCALS_DEFARG_MIN + (i)) /* Start of cached optional arguments. */ +#define DEE_MEMSTATE_EXTRA_LOCAL_A_THIS 0 /* Caller-argument: `DeeObject *this' (only for `HOSTFUNC_CC_F_THIS') */ +#define DEE_MEMSTATE_EXTRA_LOCAL_A_ARGC 1 /* Caller-argument: `size_t argc' (only for `!HOSTFUNC_CC_F_TUPLE') */ +#define DEE_MEMSTATE_EXTRA_LOCAL_A_ARGS 2 /* Caller-argument: `DeeTupleObject *args' (only for `HOSTFUNC_CC_F_TUPLE') */ +#define DEE_MEMSTATE_EXTRA_LOCAL_A_ARGV 2 /* Caller-argument: `DeeObject **argv' (only for `!HOSTFUNC_CC_F_TUPLE') */ +#define DEE_MEMSTATE_EXTRA_LOCAL_A_KW 3 /* Caller-argument: `DeeObject *kw' (only for `HOSTFUNC_CC_F_KW') */ +#define DEE_MEMSTATE_EXTRA_LOCAL_VARARGS 4 /* Varargs (s.a. `struct Dee_code_frame::cf_vargs') */ +#define DEE_MEMSTATE_EXTRA_LOCAL_VARKWDS 5 /* Varkwds (s.a. `struct Dee_code_frame_kwds::fk_varkwds') */ +#define DEE_MEMSTATE_EXTRA_LOCAL_STDOUT 6 /* Temporary slot for a cached version of `deemon.File.stdout' (to speed up `ASM_PRINT' & friends) */ +#define DEE_MEMSTATE_EXTRA_LOCAL_MINCOUNT 7 /* Min number of extra locals */ +#define DEE_MEMSTATE_EXTRA_LOCAL_DEFARG_MIN DEE_MEMSTATE_EXTRA_LOCAL_MINCOUNT +#define DEE_MEMSTATE_EXTRA_LOCAL_DEFARG(i) (DEE_MEMSTATE_EXTRA_LOCAL_DEFARG_MIN + (i)) /* Start of cached optional arguments. */ struct Dee_memstate { Dee_refcnt_t ms_refcnt; /* Reference counter for the mem-state (state becomes read-only when >1) */ uintptr_t ms_host_cfa_offset; /* Delta between SP to CFA (Canonical Frame Address) */ - size_t ms_localc; /* [== :co_localc+DEE_MEMSTATE_EXTRA_LOCALS_MINCOUNT+(:co_argc_max-:co_argc_min)] Number of local variables + extra slots. */ + size_t ms_localc; /* [== :co_localc+DEE_MEMSTATE_EXTRA_LOCAL_MINCOUNT+(:co_argc_max-:co_argc_min)] Number of local variables + extra slots. */ uint16_t ms_stackc; /* Number of (currently) used deemon stack slots in use. */ uint16_t ms_stacka; /* Allocated number of deemon stack slots in use. */ size_t ms_rinuse[HOST_REGISTER_COUNT]; /* Number of times each register is referenced by `ms_stackv' and `ms_localv' */ @@ -628,6 +629,7 @@ 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_host_section fa_prolog; /* Function prolog (output even before `fa_blockv[0]'; verify arguments & set-up initial memstate) */ + DREF struct Dee_memstate *fa_prolog_end; /* [0..1] Memory state at the end of the prolog (or `NULL' if `Dee_function_assembler_compileblocks()' wasn't called, yet) */ 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. */ @@ -638,6 +640,7 @@ struct Dee_function_assembler { #define DEE_FUNCTION_ASSEMBLER_F_NORMAL 0x0000 #define DEE_FUNCTION_ASSEMBLER_F_OSIZE 0x0001 /* Optimize for size (generally means: try not to use cold text) */ uint16_t fa_flags; /* [const] Code generation flags (set of `DEE_FUNCTION_ASSEMBLER_F_*'). */ + uint16_t fa_localc; /* [const][== fa_code->co_localc] */ Dee_hostfunc_cc_t fa_cc; /* [const] Calling convention. */ }; @@ -656,6 +659,7 @@ INTDEF size_t const _Dee_function_assembler_cfa_addend[HOSTFUNC_CC_COUNT]; (void)((self)->fa_function = (function), \ (self)->fa_code = (function)->fo_code, \ Dee_host_section_init(&(self)->fa_prolog), \ + (self)->fa_prolog_end = NULL, \ (self)->fa_blockv = NULL, \ (self)->fa_blockc = 0, \ (self)->fa_blocka = 0, \ @@ -664,6 +668,7 @@ INTDEF size_t const _Dee_function_assembler_cfa_addend[HOSTFUNC_CC_COUNT]; (self)->fa_except_exita = 0, \ (self)->fa_symbols = NULL, \ (self)->fa_flags = DEE_FUNCTION_ASSEMBLER_F_NORMAL, \ + (self)->fa_localc = (self)->fa_code->co_localc, \ (self)->fa_cc = (cc)) INTDEF NONNULL((1)) void DCALL Dee_function_assembler_fini(struct Dee_function_assembler *__restrict self); @@ -743,25 +748,31 @@ INTDEF WUNUSED NONNULL((1)) int DCALL Dee_function_generator_vpush_hstackind(str #define Dee_function_generator_vpush_imm32(self, imm32) Dee_function_generator_vpush_addr(self, (void *)(uintptr_t)(uint32_t)(imm32)) #define Dee_function_generator_vpush_Simm32(self, Simm32) Dee_function_generator_vpush_addr(self, (void *)(uintptr_t)(intptr_t)(int32_t)(Simm32)) 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_arg(struct Dee_function_generator *__restrict self, uint16_t aid); +INTDEF WUNUSED NONNULL((1)) int DCALL Dee_function_generator_vpush_arg_present(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, size_t lid); INTDEF WUNUSED NONNULL((1)) int DCALL Dee_function_generator_vdup_n(struct Dee_function_generator *__restrict self, size_t n); #define Dee_function_generator_vdup(self) Dee_function_generator_vdup_n(self, 1) INTDEF WUNUSED NONNULL((1)) int DCALL Dee_function_generator_vpop(struct Dee_function_generator *__restrict self); INTDEF WUNUSED NONNULL((1)) int DCALL Dee_function_generator_vpop_n(struct Dee_function_generator *__restrict self, size_t n); -INTDEF WUNUSED NONNULL((1, 2)) int DCALL Dee_function_generator_vpush_usage(struct Dee_function_generator *__restrict self, Dee_host_regusage_t usage); INTDEF WUNUSED NONNULL((1)) int DCALL Dee_function_generator_vpop_local(struct Dee_function_generator *__restrict self, size_t lid); INTDEF WUNUSED NONNULL((1)) int DCALL Dee_function_generator_vdel_local(struct Dee_function_generator *__restrict self, size_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 */ + +/* Helper wrappers to do checked operations on local variables as usercode sees them. */ INTDEF WUNUSED NONNULL((1)) int DCALL Dee_function_generator_vpush_ulocal(struct Dee_function_generator *__restrict self, uint16_t lid); INTDEF WUNUSED NONNULL((1)) int DCALL Dee_function_generator_vpop_ulocal(struct Dee_function_generator *__restrict self, uint16_t lid); INTDEF WUNUSED NONNULL((1)) int DCALL Dee_function_generator_vdel_ulocal(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 */ -/* Push (and lazily cache if not already done so) an "extra local" `id'. - * @param: id: Extra local variable ID (one of `DEE_MEMSTATE_EXTRA_LOCALS_*') */ -INTDEF WUNUSED NONNULL((1)) int DCALL -Dee_function_generator_vpush_extra_local(struct Dee_function_generator *__restrict self, size_t id); +/* Helper macros for operating on "extra" locals (s.a. `DEE_MEMSTATE_EXTRA_LOCAL_*') */ +#define Dee_function_generator_vpush_xlocal(self, xid) Dee_function_generator_vpush_local(self, (size_t)(self)->fg_assembler->fa_localc + (xid)) +#define Dee_function_generator_vpop_xlocal(self, xid) Dee_function_generator_vpop_local(self, (size_t)(self)->fg_assembler->fa_localc + (xid)) +#define Dee_function_generator_vdel_xlocal(self, xid) Dee_function_generator_vdel_local(self, (size_t)(self)->fg_assembler->fa_localc + (xid)) + +/* Push the "this" argument of thiscall functions. */ +#define Dee_function_generator_vpush_this(self) Dee_function_generator_vpush_xlocal(self, DEE_MEMSTATE_EXTRA_LOCAL_A_THIS) +INTDEF WUNUSED NONNULL((1)) int DCALL Dee_function_generator_vpush_argc(struct Dee_function_generator *__restrict self); +INTDEF WUNUSED NONNULL((1)) int DCALL Dee_function_generator_vpush_usage(struct Dee_function_generator *__restrict self, Dee_host_regusage_t usage); /* Perform a conditional jump to `desc' based on `jump_if_true' * @param: instr: Pointer to start of deemon jmp-instruction (for bb-truncation, and error message) @@ -772,15 +783,16 @@ Dee_function_generator_vjcc(struct Dee_function_generator *__restrict self, struct Dee_jump_descriptor *desc, Dee_instruction_t const *instr, bool jump_if_true); -/* 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 **)' - * and dereference it, before storing the resulting value back into the to-most stack item. */ -INTDEF WUNUSED NONNULL((1)) int DCALL Dee_function_generator_vind(struct Dee_function_generator *__restrict self, ptrdiff_t offset); +/* >> TOP = *(TOP + ind_delta); */ +INTDEF WUNUSED NONNULL((1)) int DCALL Dee_function_generator_vind(struct Dee_function_generator *__restrict self, ptrdiff_t ind_delta); -/* >> *(SECOND + offset) = FIRST; POP(); POP(); */ -INTDEF WUNUSED NONNULL((1)) int DCALL Dee_function_generator_vpop_ind(struct Dee_function_generator *__restrict self, ptrdiff_t offset); -/* >> temp = *(SECOND + offset); *(SECOND + offset) = FIRST; FIRST = temp; */ -INTDEF WUNUSED NONNULL((1)) int DCALL Dee_function_generator_vxch_ind(struct Dee_function_generator *__restrict self, ptrdiff_t offset); +/* >> TOP = TOP + val_delta; */ +INTDEF WUNUSED NONNULL((1)) int DCALL Dee_function_generator_vdelta(struct Dee_function_generator *__restrict self, ptrdiff_t val_delta); + +/* >> temp = *(SECOND + ind_delta); + * >> *(SECOND + ind_delta) = FIRST; + * >> FIRST = temp; */ +INTDEF WUNUSED NONNULL((1)) int DCALL Dee_function_generator_vxch_ind(struct Dee_function_generator *__restrict self, ptrdiff_t ind_delta); /* Ensure that the top-most `DeeObject' from the object-stack is a reference. */ INTDEF WUNUSED NONNULL((1)) int DCALL Dee_function_generator_vref(struct Dee_function_generator *__restrict self); @@ -857,16 +869,23 @@ INTDEF WUNUSED NONNULL((1)) int DCALL _Dee_host_section_gdecref_const(struct Dee INTDEF WUNUSED NONNULL((1)) int DCALL _Dee_host_section_gxincref_reg(struct Dee_host_section *__restrict self, Dee_host_register_t regno, ptrdiff_t reg_offset); INTDEF WUNUSED NONNULL((1)) int DCALL _Dee_function_generator_gxdecref_regx(struct Dee_function_generator *__restrict self, Dee_host_register_t regno, ptrdiff_t reg_offset); +/* Change `loc' into the value of ` = *( + ind_delta)' + * Note that unlike the `Dee_function_generator_gmov*' functions, this + * one may use `MEMLOC_TYPE_*IND' to defer the indirection until later. */ +INTDEF WUNUSED NONNULL((1, 2)) int DCALL +Dee_function_generator_gind(struct Dee_function_generator *__restrict self, + struct Dee_memloc *loc, ptrdiff_t ind_delta); + /* 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, + struct Dee_memloc *loc, Dee_host_register_t const *not_these); /* Force `loc' to reside on the stack, giving it an address (`MEMLOC_TYPE_HSTACKIND, v_hstack.s_off = 0'). */ INTDEF WUNUSED NONNULL((1, 2)) int DCALL Dee_function_generator_gflush(struct Dee_function_generator *__restrict self, - struct Dee_memloc *__restrict loc); + struct Dee_memloc *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) @@ -935,7 +954,6 @@ INTDEF WUNUSED NONNULL((1)) int DCALL _Dee_host_section_gmov_constind2reg(struct INTDEF WUNUSED NONNULL((1)) int DCALL _Dee_host_section_gmov_reg2constind(struct Dee_host_section *__restrict self, Dee_host_register_t src_regno, DeeObject **p_value); /* * = src_regno; */ #define _Dee_host_section_gmov_reg2reg(self, src_regno, dst_regno) _Dee_host_section_gmov_regx2reg(self, src_regno, 0, dst_regno) -INTDEF WUNUSED NONNULL((1)) int DCALL Dee_function_generator_gmov_arg2reg(struct Dee_function_generator *__restrict self, uint16_t aid, Dee_host_register_t dst_regno); /* dst_regno = ; */ INTDEF WUNUSED NONNULL((1, 4)) int DCALL Dee_function_generator_gmov_regx2loc(struct Dee_function_generator *__restrict self, Dee_host_register_t src_regno, ptrdiff_t src_delta, struct Dee_memloc const *__restrict dst_loc); /* = src_regno + src_delta; */ INTDEF WUNUSED NONNULL((1, 2)) int DCALL Dee_function_generator_gmov_loc2regx(struct Dee_function_generator *__restrict self, struct Dee_memloc const *__restrict src_loc, Dee_host_register_t dst_regno, ptrdiff_t dst_delta); /* dst_regno = - dst_delta; */ INTDEF WUNUSED NONNULL((1, 2, 4)) int DCALL Dee_function_generator_gmov_loc2regy(struct Dee_function_generator *__restrict self, struct Dee_memloc const *__restrict src_loc, Dee_host_register_t dst_regno, ptrdiff_t *__restrict p_dst_delta); /* dst_regno = - *p_dst_delta; */ @@ -948,10 +966,6 @@ INTDEF WUNUSED NONNULL((1, 2)) int DCALL Dee_function_generator_gmov_loc2hstacki #define Dee_function_generator_ghstack_pushloc(self, src_loc) Dee_function_generator_ghstack_pushlocx(self, src_loc, 0) #define Dee_function_generator_gmov_loc2hstackind(self, src_loc, dst_cfa_offset) Dee_function_generator_gmov_loc2hstackindx(self, src_loc, dst_cfa_offset, 0) -/* Load special runtime values into `dst_regno' */ -INTDEF WUNUSED NONNULL((1)) int DCALL Dee_function_generator_gmov_usage2reg(struct Dee_function_generator *__restrict self, Dee_host_regusage_t usage, Dee_host_register_t dst_regno); -INTDEF WUNUSED NONNULL((1)) int DCALL _Dee_function_generator_gmov_usage2reg(struct Dee_function_generator *__restrict self, Dee_host_regusage_t usage, Dee_host_register_t dst_regno); - /* Generate code to return `loc'. No extra code to decref stack/locals is generated. If you * want that extra code to be generated, you need to use `Dee_function_generator_vret()'. */ INTDEF WUNUSED NONNULL((1, 2)) int DCALL Dee_function_generator_gret(struct Dee_function_generator *__restrict self, /*inherit_ref*/ struct Dee_memloc *__restrict loc); @@ -1077,6 +1091,8 @@ INTDEF WUNUSED NONNULL((1)) int DCALL Dee_function_assembler_loadblocks(struct Dee_function_assembler *__restrict self); /* Step #2: Compile basic blocks and determine memory states. Fills in: + * - self->fa_prolog + * - self->fa_prolog_end * - self->fa_blockv[*]->bb_entries.jds_list[*]->jd_stat * - self->fa_blockv[*]->bb_mem_start * - self->fa_blockv[*]->bb_mem_end @@ -1110,7 +1126,7 @@ Dee_function_assembler_trimdead(struct Dee_function_assembler *__restrict self); /* Step #4: Generate morph instruction sequences to perform memory state transitions. * This also extends the host text of basic blocks that fall through to some * other basic block with an extra instructions needed for morphing: - * - self->fa_prolog + * - self->fa_prolog (to transition ) * - self->fa_blockv[*]->bb_exits.jds_list[*]->jd_morph * - self->fa_blockv[*]->bb_htext (extend with transition code so that `bb_mem_end == bb_next->bb_mem_start') * - self->fa_except_exitv[*]->bb_htext (generate morph-code to transition to an empty stack, or fall into another exit block) diff --git a/src/dex/_hostasm/memstate.c b/src/dex/_hostasm/memstate.c index 95439c828..c3708688f 100644 --- a/src/dex/_hostasm/memstate.c +++ b/src/dex/_hostasm/memstate.c @@ -224,6 +224,26 @@ Dee_memstate_hstack_unused(struct Dee_memstate const *__restrict self, INTERN ATTR_PURE WUNUSED NONNULL((1)) uintptr_t DCALL Dee_memstate_hstack_find(struct Dee_memstate const *__restrict self, size_t n_bytes) { ASSERT(IS_ALIGNED(n_bytes, HOST_SIZEOF_POINTER)); +#ifdef HOSTASM_X86_64_MSABI + /* MSABI provides an additional 32 bytes of GP memory at CFA offsets [-40, -8) */ + if (n_bytes <= 4 * HOST_SIZEOF_POINTER) { + size_t a_pointers = 4; + size_t n_pointers = n_bytes / HOST_SIZEOF_POINTER; + size_t i, check = (a_pointers - n_pointers) + 1; + for (i = 0; i < check; ++i) { + uintptr_t min_offset = (uintptr_t)(-(ptrdiff_t)((5 * HOST_SIZEOF_POINTER) - + (i * HOST_SIZEOF_POINTER))); + uintptr_t end_offset = min_offset + n_bytes; + if (Dee_memstate_hstack_unused(self, min_offset, end_offset)) { +#ifdef HOSTASM_STACK_GROWS_DOWN + return end_offset; +#else /* HOSTASM_STACK_GROWS_DOWN */ + return min_offset; +#endif /* !HOSTASM_STACK_GROWS_DOWN */ + } + } + } +#endif /* HOSTASM_X86_64_MSABI */ if (n_bytes <= self->ms_host_cfa_offset) { size_t a_pointers = self->ms_host_cfa_offset / HOST_SIZEOF_POINTER; size_t n_pointers = n_bytes / HOST_SIZEOF_POINTER;