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 */