mirror of
https://git.savannah.gnu.org/git/guile.git
synced 2025-04-30 11:50:28 +02:00
* check/alu_rsb.ok, check/alu_rsb.tst: New files implementing tests for jit_rsb*. * check/Makefile.am, check/lightning.c, include/lightning.h, lib/jit_aarch64-cpu.c, lib/jit_aarch64-fpu.c, lib/jit_aarch64-sz.c, lib/jit_aarch64.c, lib/jit_alpha-cpu.c, lib/jit_alpha-fpu.c, lib/jit_alpha-sz.c, lib/jit_alpha.c, lib/jit_arm-cpu.c, lib/jit_arm-swf.c, lib/jit_arm-sz.c, lib/jit_arm-vfp.c, lib/jit_arm.c, lib/jit_hppa-cpu.c, lib/jit_hppa-fpu.c, lib/jit_hppa-sz.c, lib/jit_hppa.c, lib/jit_ia64-cpu.c, lib/jit_ia64-fpu.c, lib/jit_ia64-sz.c, lib/jit_ia64.c, lib/jit_mips-cpu.c, lib/jit_mips-fpu.c, lib/jit_mips-sz.c, lib/jit_mips.c, lib/jit_names.c, lib/jit_ppc-cpu.c, lib/jit_ppc-fpu.c, lib/jit_ppc-sz.c, lib/jit_ppc.c, lib/jit_s390x-cpu.c, lib/jit_s390x-fpu.c, lib/jit_s390x-sz.c, lib/jit_s390x.c, lib/jit_sparc-cpu.c, lib/jit_sparc-fpu.c, lib/jit_sparc-sz.c, lib/jit_sparc.c, lib/jit_x86-cpu.c, lib/jit_x86-sse.c, lib/jit_x86-sz.c, lib/jit_x86-x87.c, lib/jit_x86.c, lib/lightning.c: Implement jit_rsb*. This was a missing lightning 1.x interface, that on most backends is synthesized, but on a few backends (hppa and ia64), it can generate better code as on those there is, or the only instruction with an immediate is in "rsb" format (left operand).
3181 lines
88 KiB
C
3181 lines
88 KiB
C
/*
|
|
* Copyright (C) 2012, 2013 Free Software Foundation, Inc.
|
|
*
|
|
* This file is part of GNU lightning.
|
|
*
|
|
* GNU lightning is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU Lesser General Public License as published
|
|
* by the Free Software Foundation; either version 3, or (at your option)
|
|
* any later version.
|
|
*
|
|
* GNU lightning is distributed in the hope that it will be useful, but
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
|
|
* License for more details.
|
|
*
|
|
* Authors:
|
|
* Paulo Cesar Pereira de Andrade
|
|
*/
|
|
|
|
#include <lightning.h>
|
|
#include <lightning/jit_private.h>
|
|
#include <sys/mman.h>
|
|
#if defined(__sgi)
|
|
# include <fcntl.h>
|
|
#endif
|
|
|
|
#ifndef MAP_ANON
|
|
# define MAP_ANON MAP_ANONYMOUS
|
|
# ifndef MAP_ANONYMOUS
|
|
# define MAP_ANONYMOUS 0
|
|
# endif
|
|
#endif
|
|
|
|
#define jit_regload_reload 0 /* convert to reload */
|
|
#define jit_regload_delete 1 /* just remove node */
|
|
#define jit_regload_isdead 2 /* delete and unset live bit */
|
|
|
|
#if __WORDSIZE == 32
|
|
# define bmp_shift 5
|
|
#else
|
|
# define bmp_shift 6
|
|
#endif
|
|
|
|
/*
|
|
* Prototypes
|
|
*/
|
|
static jit_word_t hash_data(jit_pointer_t, jit_word_t);
|
|
|
|
#define new_pool() _new_pool(_jit)
|
|
static void _new_pool(jit_state_t*);
|
|
|
|
#define new_node(u) _new_node(_jit, u)
|
|
static jit_node_t *_new_node(jit_state_t*, jit_code_t);
|
|
|
|
#define link_node(u) _link_node(_jit, u)
|
|
static inline jit_node_t *_link_node(jit_state_t*, jit_node_t*);
|
|
|
|
#define del_node(u, v) _del_node(_jit, u, v)
|
|
static inline void _del_node(jit_state_t*, jit_node_t*, jit_node_t*);
|
|
|
|
#define del_label(u, v) _del_label(_jit, u, v)
|
|
static void _del_label(jit_state_t*, jit_node_t*, jit_node_t*);
|
|
|
|
#define bmp_init() _bmp_init(_jit)
|
|
static void _bmp_init(jit_state_t*);
|
|
|
|
#define bmp_clear() _bmp_clear(_jit)
|
|
static void _bmp_clear(jit_state_t*);
|
|
|
|
#define bmp_zero() \
|
|
memset(_jitc->blockmask.ptr, 0, \
|
|
_jitc->blockmask.length * sizeof(jit_word_t))
|
|
|
|
#define bmp_set(bit) _bmp_set(_jit, bit)
|
|
static void _bmp_set(jit_state_t*, jit_word_t);
|
|
|
|
#define bmp_clr(bit) _bmp_clr(_jit, bit)
|
|
static void _bmp_clr(jit_state_t*, jit_word_t) maybe_unused;
|
|
|
|
#define bmp_tst(bit) _bmp_tst(_jit, bit)
|
|
static jit_bool_t _bmp_tst(jit_state_t*, jit_word_t);
|
|
|
|
#define jit_dataset() _jit_dataset(_jit)
|
|
static void
|
|
_jit_dataset(jit_state_t *_jit);
|
|
|
|
#define jit_setup(block) _jit_setup(_jit, block)
|
|
static void
|
|
_jit_setup(jit_state_t *_jit, jit_block_t *block);
|
|
|
|
#define jit_update(node, live, mask) _jit_update(_jit, node, live, mask)
|
|
static void
|
|
_jit_update(jit_state_t *_jit, jit_node_t *node,
|
|
jit_regset_t *live, jit_regset_t *mask);
|
|
|
|
#define thread_jumps() _thread_jumps(_jit)
|
|
static void
|
|
_thread_jumps(jit_state_t *_jit);
|
|
|
|
#define sequential_labels() _sequential_labels(_jit)
|
|
static void
|
|
_sequential_labels(jit_state_t *_jit);
|
|
|
|
#define shortcut_jump(prev, node) _shortcut_jump(_jit, prev, node)
|
|
static jit_bool_t
|
|
_shortcut_jump(jit_state_t *_jit, jit_node_t *prev, jit_node_t *node);
|
|
|
|
#define redundant_jump(prev, node) _redundant_jump(_jit, prev, node)
|
|
static jit_bool_t
|
|
_redundant_jump(jit_state_t *_jit, jit_node_t *prev, jit_node_t *node);
|
|
|
|
static jit_code_t
|
|
reverse_jump_code(jit_code_t code);
|
|
|
|
#define reverse_jump(prev, node) _reverse_jump(_jit, prev, node)
|
|
static jit_bool_t
|
|
_reverse_jump(jit_state_t *_jit, jit_node_t *prev, jit_node_t *node);
|
|
|
|
#define redundant_store(node, jump) _redundant_store(_jit, node, jump)
|
|
static void
|
|
_redundant_store(jit_state_t *_jit, jit_node_t *node, jit_bool_t jump);
|
|
|
|
#define simplify_movr(p, n, k, s) _simplify_movr(_jit, p, n, k, s)
|
|
static jit_bool_t
|
|
_simplify_movr(jit_state_t *_jit, jit_node_t *prev, jit_node_t *node,
|
|
jit_int32_t kind, jit_int32_t size);
|
|
|
|
#define simplify_movi(p, n, k, s) _simplify_movi(_jit, p, n, k, s)
|
|
static jit_bool_t
|
|
_simplify_movi(jit_state_t *_jit, jit_node_t *prev, jit_node_t *node,
|
|
jit_int32_t kind, jit_int32_t size);
|
|
|
|
#define simplify_ldxi(prev, node) _simplify_ldxi(_jit, prev, node)
|
|
static jit_bool_t
|
|
_simplify_ldxi(jit_state_t *_jit, jit_node_t *prev, jit_node_t *node);
|
|
|
|
#define simplify_stxi(prev, node) _simplify_stxi(_jit, prev, node)
|
|
static jit_bool_t
|
|
_simplify_stxi(jit_state_t *_jit, jit_node_t *prev, jit_node_t *node);
|
|
|
|
#define simplify_spill(node, regno) _simplify_spill(_jit, node, regno)
|
|
static void
|
|
_simplify_spill(jit_state_t *_jit, jit_node_t *node, jit_int32_t regno);
|
|
|
|
#define simplify() _simplify(_jit)
|
|
static void
|
|
_simplify(jit_state_t *_jit);
|
|
|
|
#define jit_reg_undef -1
|
|
#define jit_reg_static 0
|
|
#define jit_reg_change 1
|
|
#define register_change_p(n, l, r) _register_change_p(_jit, n, l, r)
|
|
static jit_int32_t
|
|
_register_change_p(jit_state_t *_jit, jit_node_t *node, jit_node_t *link,
|
|
jit_int32_t regno);
|
|
|
|
#define spill_reglive_p(node, regno) _spill_reglive_p(_jit, node, regno)
|
|
static jit_bool_t
|
|
_spill_reglive_p(jit_state_t *_jit, jit_node_t *node, jit_int32_t regno);
|
|
|
|
#define patch_registers() _patch_registers(_jit)
|
|
static void
|
|
_patch_registers(jit_state_t *_jit);
|
|
|
|
#define patch_register(n,l,r,p) _patch_register(_jit,n,l,r,p)
|
|
static void
|
|
_patch_register(jit_state_t *jit, jit_node_t *node, jit_node_t *link,
|
|
jit_int32_t regno, jit_int32_t patch);
|
|
|
|
/*
|
|
* Initialization
|
|
*/
|
|
#if !defined(__sgi)
|
|
#define mmap_fd -1
|
|
#endif
|
|
|
|
/*
|
|
* Implementation
|
|
*/
|
|
void
|
|
init_jit(char *progname)
|
|
{
|
|
jit_get_cpu();
|
|
jit_init_debug(progname);
|
|
jit_init_size();
|
|
}
|
|
|
|
void
|
|
finish_jit(void)
|
|
{
|
|
jit_finish_debug();
|
|
jit_finish_size();
|
|
}
|
|
|
|
jit_int32_t
|
|
_jit_get_reg(jit_state_t *_jit, jit_int32_t regspec)
|
|
{
|
|
jit_int32_t spec;
|
|
jit_int32_t regno;
|
|
|
|
spec = regspec & ~(jit_class_chk|jit_class_nospill);
|
|
if (spec & jit_class_named) {
|
|
regno = jit_regno(spec);
|
|
if (jit_regset_tstbit(&_jitc->regsav, regno))
|
|
/* fail if register is spilled */
|
|
goto fail;
|
|
if (jit_regset_tstbit(&_jitc->regarg, regno))
|
|
/* fail if register is an argument to current instruction */
|
|
goto fail;
|
|
if (jit_regset_tstbit(&_jitc->reglive, regno)) {
|
|
if (regspec & jit_class_nospill)
|
|
/* fail if register is live and should not spill/reload */
|
|
goto fail;
|
|
goto spill;
|
|
}
|
|
jit_regset_setbit(&_jitc->regarg, regno);
|
|
return (regno);
|
|
}
|
|
else
|
|
assert(jit_class(spec) != 0);
|
|
|
|
if (_jitc->emit) {
|
|
/* search for a free register matching spec */
|
|
for (regno = 0; regno < _jitc->reglen; regno++) {
|
|
if ((jit_class(_rvs[regno].spec) & spec) == spec &&
|
|
!jit_regset_tstbit(&_jitc->regarg, regno) &&
|
|
!jit_regset_tstbit(&_jitc->reglive, regno))
|
|
goto regarg;
|
|
}
|
|
|
|
/* search for a register matching spec that is not an argument
|
|
* for the current instruction */
|
|
for (regno = 0; regno < _jitc->reglen; regno++) {
|
|
if ((jit_class(_rvs[regno].spec) & spec) == spec &&
|
|
!jit_regset_tstbit(&_jitc->regsav, regno) &&
|
|
!jit_regset_tstbit(&_jitc->regarg, regno) &&
|
|
!(regspec & jit_class_nospill)) {
|
|
spill:
|
|
assert(_jitc->function != NULL);
|
|
if (spec & jit_class_gpr) {
|
|
if (!_jitc->function->regoff[regno]) {
|
|
_jitc->function->regoff[regno] =
|
|
jit_allocai(sizeof(jit_word_t));
|
|
_jitc->again = 1;
|
|
}
|
|
#if DEBUG
|
|
/* emit_stxi must not need temporary registers */
|
|
assert(!_jitc->getreg);
|
|
_jitc->getreg = 1;
|
|
#endif
|
|
emit_stxi(_jitc->function->regoff[regno], JIT_FP, regno);
|
|
#if DEBUG
|
|
_jitc->getreg = 0;
|
|
#endif
|
|
}
|
|
else {
|
|
if (!_jitc->function->regoff[regno]) {
|
|
_jitc->function->regoff[regno] =
|
|
jit_allocai(sizeof(jit_float64_t));
|
|
_jitc->again = 1;
|
|
}
|
|
#if DEBUG
|
|
/* emit_stxi must not need temporary registers */
|
|
assert(!_jitc->getreg);
|
|
_jitc->getreg = 1;
|
|
#endif
|
|
emit_stxi_d(_jitc->function->regoff[regno], JIT_FP, regno);
|
|
#if DEBUG
|
|
_jitc->getreg = 0;
|
|
#endif
|
|
}
|
|
jit_regset_setbit(&_jitc->regsav, regno);
|
|
regarg:
|
|
jit_regset_setbit(&_jitc->regarg, regno);
|
|
if (jit_class(_rvs[regno].spec) & jit_class_sav) {
|
|
/* if will modify callee save registers without a
|
|
* function prolog, better patch this assertion */
|
|
assert(_jitc->function != NULL);
|
|
if (!jit_regset_tstbit(&_jitc->function->regset, regno)) {
|
|
jit_regset_setbit(&_jitc->function->regset, regno);
|
|
_jitc->again = 1;
|
|
}
|
|
}
|
|
return (regno);
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
/* nospill hint only valid during emit" */
|
|
assert(!(regspec & jit_class_nospill));
|
|
for (regno = 0; regno < _jitc->reglen; regno++) {
|
|
if ((jit_class(_rvs[regno].spec) & spec) == spec &&
|
|
!jit_regset_tstbit(&_jitc->regsav, regno) &&
|
|
!jit_regset_tstbit(&_jitc->regarg, regno)) {
|
|
jit_regset_setbit(&_jitc->regarg, regno);
|
|
jit_regset_setbit(&_jitc->regsav, regno);
|
|
jit_save(regno);
|
|
return (jit_regno_patch|regno);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Out of hardware registers */
|
|
fail:
|
|
assert(regspec & jit_class_chk);
|
|
return (JIT_NOREG);
|
|
}
|
|
|
|
void
|
|
_jit_unget_reg(jit_state_t *_jit, jit_int32_t regno)
|
|
{
|
|
regno = jit_regno(regno);
|
|
if (jit_regset_tstbit(&_jitc->regsav, regno)) {
|
|
if (_jitc->emit) {
|
|
#if DEBUG
|
|
/* emit_ldxi must not need a temporary register */
|
|
assert(!_jitc->getreg);
|
|
_jitc->getreg = 1;
|
|
#endif
|
|
if (jit_class(_rvs[regno].spec) & jit_class_gpr)
|
|
emit_ldxi(regno, JIT_FP, _jitc->function->regoff[regno]);
|
|
else
|
|
emit_ldxi_d(regno, JIT_FP, _jitc->function->regoff[regno]);
|
|
#if DEBUG
|
|
/* emit_ldxi must not need a temporary register */
|
|
_jitc->getreg = 0;
|
|
#endif
|
|
}
|
|
else
|
|
jit_load(regno);
|
|
jit_regset_clrbit(&_jitc->regsav, regno);
|
|
}
|
|
assert(jit_regset_tstbit(&_jitc->regarg, regno) != 0);
|
|
jit_regset_clrbit(&_jitc->regarg, regno);
|
|
}
|
|
|
|
jit_bool_t
|
|
_jit_callee_save_p(jit_state_t *_jit, jit_int32_t regno)
|
|
{
|
|
assert(regno >= 0 && regno < JIT_NOREG);
|
|
return (!!(_rvs[regno].spec & jit_class_sav));
|
|
}
|
|
|
|
extern jit_bool_t
|
|
_jit_pointer_p(jit_state_t *_jit, jit_pointer_t address)
|
|
{
|
|
return ((jit_uint8_t *)address >= _jit->code.ptr &&
|
|
(jit_word_t)address < _jit->pc.w);
|
|
}
|
|
|
|
#if __ia64__
|
|
void
|
|
jit_regset_com(jit_regset_t *u, jit_regset_t *v)
|
|
{
|
|
u->rl = ~v->rl; u->rh = ~v->rh;
|
|
u->fl = ~v->fl; u->fh = ~v->fh;
|
|
}
|
|
|
|
void
|
|
jit_regset_and(jit_regset_t *u, jit_regset_t *v, jit_regset_t *w)
|
|
{
|
|
u->rl = v->rl & w->rl; u->rh = v->rh & w->rh;
|
|
u->fl = v->fl & w->fl; u->fh = v->fh & w->fh;
|
|
}
|
|
|
|
void
|
|
jit_regset_ior(jit_regset_t *u, jit_regset_t *v, jit_regset_t *w)
|
|
{
|
|
u->rl = v->rl | w->rl; u->rh = v->rh | w->rh;
|
|
u->fl = v->fl | w->fl; u->fh = v->fh | w->fh;
|
|
}
|
|
|
|
void
|
|
jit_regset_xor(jit_regset_t *u, jit_regset_t *v, jit_regset_t *w)
|
|
{
|
|
u->rl = v->rl ^ w->rl; u->rh = v->rh ^ w->rh;
|
|
u->fl = v->fl ^ w->fl; u->fh = v->fh ^ w->fh;
|
|
}
|
|
|
|
void
|
|
jit_regset_set(jit_regset_t *u, jit_regset_t *v)
|
|
{
|
|
u->rl = v->rl; u->rh = v->rh;
|
|
u->fl = v->fl; u->fh = v->fh;
|
|
}
|
|
|
|
void
|
|
jit_regset_set_mask(jit_regset_t *u, jit_int32_t v)
|
|
{
|
|
jit_bool_t w = !!(v & (v - 1));
|
|
|
|
assert(v >= 0 && v <= 256);
|
|
if (v == 0)
|
|
u->rl = u->rh = u->fl = u->fh = -1LL;
|
|
else if (v <= 64) {
|
|
u->rl = w ? (1LL << v) - 1 : -1LL;
|
|
u->rh = u->fl = u->fh = 0;
|
|
}
|
|
else if (v <= 128) {
|
|
u->rl = -1LL;
|
|
u->rh = w ? (1LL << (v - 64)) - 1 : -1LL;
|
|
u->fl = u->fh = 0;
|
|
}
|
|
else if (v <= 192) {
|
|
u->rl = u->rh = -1LL;
|
|
u->fl = w ? (1LL << (v - 128)) - 1 : -1LL;
|
|
u->fh = 0;
|
|
}
|
|
else {
|
|
u->rl = u->rh = u->fl = -1LL;
|
|
u->fh = w ? (1LL << (v - 128)) - 1 : -1LL;
|
|
}
|
|
}
|
|
|
|
jit_bool_t
|
|
jit_regset_cmp_ui(jit_regset_t *u, jit_word_t v)
|
|
{
|
|
return !((u->rl == v && u->rh == 0 && u->fl == 0 && u->fh == 0));
|
|
}
|
|
|
|
void
|
|
jit_regset_set_ui(jit_regset_t *u, jit_word_t v)
|
|
{
|
|
u->rl = v;
|
|
u->rh = u->fl = u->fh = 0;
|
|
}
|
|
|
|
jit_bool_t
|
|
jit_regset_set_p(jit_regset_t *u)
|
|
{
|
|
return (u->rl || u->rh || u->fl || u->fh);
|
|
}
|
|
|
|
void
|
|
jit_regset_clrbit(jit_regset_t *set, jit_int32_t bit)
|
|
{
|
|
assert(bit >= 0 && bit <= 255);
|
|
if (bit < 64)
|
|
set->rl &= ~(1LL << bit);
|
|
else if (bit < 128)
|
|
set->rh &= ~(1LL << (bit - 64));
|
|
else if (bit < 192)
|
|
set->fl &= ~(1LL << (bit - 128));
|
|
else
|
|
set->fh &= ~(1LL << (bit - 192));
|
|
}
|
|
|
|
void
|
|
jit_regset_setbit(jit_regset_t *set, jit_int32_t bit)
|
|
{
|
|
assert(bit >= 0 && bit <= 255);
|
|
if (bit < 64)
|
|
set->rl |= 1LL << bit;
|
|
else if (bit < 128)
|
|
set->rh |= 1LL << (bit - 64);
|
|
else if (bit < 192)
|
|
set->fl |= 1LL << (bit - 128);
|
|
else
|
|
set->fh |= 1LL << (bit - 192);
|
|
}
|
|
|
|
jit_bool_t
|
|
jit_regset_tstbit(jit_regset_t *set, jit_int32_t bit)
|
|
{
|
|
assert(bit >= 0 && bit <= 255);
|
|
if (bit < 64)
|
|
return (!!(set->rl & (1LL << bit)));
|
|
else if (bit < 128)
|
|
return (!!(set->rh & (1LL << (bit - 64))));
|
|
else if (bit < 192)
|
|
return (!!(set->fl & (1LL << (bit - 128))));
|
|
return (!!(set->fh & (1LL << (bit - 192))));
|
|
}
|
|
|
|
unsigned long
|
|
jit_regset_scan1(jit_regset_t *set, jit_int32_t offset)
|
|
{
|
|
assert(offset >= 0 && offset <= 255);
|
|
for (; offset < 64; offset++) {
|
|
if (set->rl & (1LL << offset))
|
|
return (offset);
|
|
}
|
|
for (; offset < 128; offset++) {
|
|
if (set->rh & (1LL << (offset - 64)))
|
|
return (offset);
|
|
}
|
|
for (; offset < 192; offset++) {
|
|
if (set->fl & (1LL << (offset - 128)))
|
|
return (offset);
|
|
}
|
|
for (; offset < 256; offset++) {
|
|
if (set->fh & (1LL << (offset - 192)))
|
|
return (offset);
|
|
}
|
|
return (ULONG_MAX);
|
|
}
|
|
#else
|
|
unsigned long
|
|
jit_regset_scan1(jit_regset_t *set, jit_int32_t offset)
|
|
{
|
|
jit_regset_t mask;
|
|
assert(offset >= 0 && offset <= 63);
|
|
if ((mask = *set >> offset)) {
|
|
for (;;) {
|
|
if (mask & 1)
|
|
return (offset);
|
|
mask >>= 1;
|
|
++offset;
|
|
}
|
|
}
|
|
return (ULONG_MAX);
|
|
}
|
|
#endif
|
|
|
|
void
|
|
_jit_save(jit_state_t *_jit, jit_int32_t reg)
|
|
{
|
|
reg = jit_regno(reg);
|
|
assert(!_jitc->realize);
|
|
_jitc->spill[reg] = jit_new_node_w(jit_code_save, reg);
|
|
}
|
|
|
|
void
|
|
_jit_load(jit_state_t *_jit, jit_int32_t reg)
|
|
{
|
|
jit_node_t *node;
|
|
|
|
reg = jit_regno(reg);
|
|
assert(!_jitc->realize);
|
|
assert(_jitc->spill[reg] != NULL);
|
|
node = jit_new_node_w(jit_code_load, reg);
|
|
/* create a path to flag the save/load is not required */
|
|
node->link = _jitc->spill[reg];
|
|
node->link->link = node;
|
|
_jitc->spill[reg] = NULL;
|
|
}
|
|
|
|
static jit_word_t
|
|
hash_data(jit_pointer_t data, jit_word_t length)
|
|
{
|
|
jit_uint8_t *ptr;
|
|
jit_word_t i, key;
|
|
for (i = key = 0, ptr = data; i < length; i++)
|
|
key = (key << (key & 1)) ^ ptr[i];
|
|
return (key);
|
|
}
|
|
|
|
jit_pointer_t
|
|
_jit_address(jit_state_t *_jit, jit_node_t *node)
|
|
{
|
|
assert(_jitc->done);
|
|
assert(node != NULL &&
|
|
/* If a node type that is documented to be a fixed marker */
|
|
(node->code == jit_code_note || node->code == jit_code_name ||
|
|
/* If another special fixed marker, returned by jit_indirect() */
|
|
(node->code == jit_code_label && (node->flag & jit_flag_use) != 0)));
|
|
return ((jit_pointer_t)node->u.w);
|
|
}
|
|
|
|
jit_node_t *
|
|
_jit_data(jit_state_t *_jit, jit_pointer_t data,
|
|
jit_word_t length, jit_int32_t align)
|
|
{
|
|
jit_word_t key;
|
|
jit_node_t *node;
|
|
|
|
assert(!_jitc->realize);
|
|
|
|
/* Ensure there is space even if asking for a duplicate */
|
|
if (((_jitc->data.offset + 7) & -8) + length > _jit->data.length) {
|
|
jit_word_t size;
|
|
|
|
size = (_jit->data.length + length + 4096) & - 4095;
|
|
assert(size >= _jit->data.length);
|
|
if (_jitc->data.ptr == NULL)
|
|
jit_alloc((jit_pointer_t *)&_jitc->data.ptr, size);
|
|
else
|
|
jit_realloc((jit_pointer_t *)&_jitc->data.ptr,
|
|
_jit->data.length, size);
|
|
_jit->data.length = size;
|
|
}
|
|
if (_jitc->data.table == NULL)
|
|
jit_alloc((jit_pointer_t *)&_jitc->data.table,
|
|
(_jitc->data.size = 16) * sizeof(jit_node_t*));
|
|
|
|
key = hash_data(data, length) & (_jitc->data.size - 1);
|
|
node = _jitc->data.table[key];
|
|
for (; node; node = node->next) {
|
|
if (node->v.w == length &&
|
|
memcmp(_jitc->data.ptr + node->u.w, data, length) == 0)
|
|
break;
|
|
}
|
|
|
|
if (!node) {
|
|
node = jit_new_node_no_link(jit_code_data);
|
|
if (!align)
|
|
align = length;
|
|
switch (align) {
|
|
case 0: case 1:
|
|
break;
|
|
case 2:
|
|
_jitc->data.offset = (_jitc->data.offset + 1) & -2;
|
|
break;
|
|
case 3: case 4:
|
|
_jitc->data.offset = (_jitc->data.offset + 3) & -4;
|
|
break;
|
|
default:
|
|
_jitc->data.offset = (_jitc->data.offset + 7) & -8;
|
|
break;
|
|
}
|
|
node->u.w = _jitc->data.offset;
|
|
node->v.w = length;
|
|
jit_memcpy(_jitc->data.ptr + _jitc->data.offset, data, length);
|
|
_jitc->data.offset += length;
|
|
|
|
node->next = _jitc->data.table[key];
|
|
_jitc->data.table[key] = node;
|
|
++_jitc->data.count;
|
|
|
|
/* Rehash if more than 75% used table */
|
|
if (_jitc->data.count >
|
|
(_jitc->data.size >> 1) + (_jitc->data.size >> 2) &&
|
|
(_jitc->data.size << 1) > _jitc->data.size) {
|
|
jit_word_t i;
|
|
jit_node_t **hash;
|
|
jit_node_t *next;
|
|
jit_node_t *temp;
|
|
|
|
jit_alloc((jit_pointer_t *)&hash,
|
|
(_jitc->data.size << 1) * sizeof(jit_node_t*));
|
|
for (i = 0; i < _jitc->data.size; i++) {
|
|
temp = _jitc->data.table[i];
|
|
for (; temp; temp = next) {
|
|
next = temp->next;
|
|
key = hash_data(_jitc->data.ptr + temp->u.w, temp->v.w) &
|
|
((_jitc->data.size << 1) - 1);
|
|
temp->next = hash[key];
|
|
hash[key] = temp;
|
|
}
|
|
}
|
|
jit_free((jit_pointer_t *)&_jitc->data.table);
|
|
_jitc->data.table = hash;
|
|
_jitc->data.size <<= 1;
|
|
}
|
|
}
|
|
|
|
return (node);
|
|
}
|
|
|
|
static void
|
|
_new_pool(jit_state_t *_jit)
|
|
{
|
|
jit_node_t *list;
|
|
jit_int32_t offset;
|
|
|
|
if (_jitc->pool.offset >= _jitc->pool.length) {
|
|
jit_int32_t length;
|
|
|
|
length = _jitc->pool.length + 16;
|
|
jit_realloc((jit_pointer_t *)&_jitc->pool.ptr,
|
|
_jitc->pool.length * sizeof(jit_node_t *),
|
|
length * sizeof(jit_node_t *));
|
|
_jitc->pool.length = length;
|
|
}
|
|
jit_alloc((jit_pointer_t *)(_jitc->pool.ptr + _jitc->pool.offset),
|
|
sizeof(jit_node_t) * 1024);
|
|
list = _jitc->pool.ptr[_jitc->pool.offset];
|
|
for (offset = 1; offset < 1024; offset++, list++)
|
|
list->next = list + 1;
|
|
list->next = _jitc->list;
|
|
_jitc->list = _jitc->pool.ptr[_jitc->pool.offset];
|
|
++_jitc->pool.offset;
|
|
}
|
|
|
|
static jit_node_t *
|
|
_new_node(jit_state_t *_jit, jit_code_t code)
|
|
{
|
|
jit_node_t *node;
|
|
|
|
if (_jitc->list == NULL)
|
|
new_pool();
|
|
node = _jitc->list;
|
|
_jitc->list = node->next;
|
|
node->next = NULL;
|
|
node->code = code;
|
|
|
|
return (node);
|
|
}
|
|
|
|
static inline jit_node_t *
|
|
_link_node(jit_state_t *_jit, jit_node_t *node)
|
|
{
|
|
if (_jitc->tail)
|
|
_jitc->tail->next = node;
|
|
else
|
|
_jitc->head = node;
|
|
return (_jitc->tail = node);
|
|
}
|
|
|
|
static inline void
|
|
_del_node(jit_state_t *_jit, jit_node_t *prev, jit_node_t *node)
|
|
{
|
|
if (prev == node) {
|
|
assert(prev == _jitc->head);
|
|
_jitc->head = node->next;
|
|
}
|
|
else
|
|
prev->next = node->next;
|
|
memset(node, 0, sizeof(jit_node_t));
|
|
node->next = _jitc->list;
|
|
_jitc->list = node;
|
|
}
|
|
|
|
static void
|
|
_del_label(jit_state_t *_jit, jit_node_t *prev, jit_node_t *node)
|
|
{
|
|
jit_block_t *block;
|
|
|
|
/* only allow call to del_label on linked labels */
|
|
block = _jitc->blocks.ptr + node->v.w;
|
|
assert(block->label == node);
|
|
|
|
/* del_label() should only be called when optimizing.
|
|
* This will leave an empty block index */
|
|
jit_regset_del(&block->reglive);
|
|
jit_regset_del(&block->regmask);
|
|
block->label = NULL;
|
|
|
|
/* redundant, should be already true */
|
|
assert(node->link == NULL);
|
|
del_node(prev, node);
|
|
}
|
|
|
|
static void
|
|
_bmp_init(jit_state_t *_jit)
|
|
{
|
|
_jitc->blockmask.length = 16;
|
|
jit_alloc((jit_pointer_t *)&_jitc->blockmask.ptr,
|
|
sizeof(jit_word_t) * _jitc->blockmask.length);
|
|
}
|
|
|
|
static void
|
|
_bmp_clear(jit_state_t *_jit)
|
|
{
|
|
_jitc->blockmask.length = 0;
|
|
jit_free((jit_pointer_t *)&_jitc->blockmask.ptr);
|
|
}
|
|
|
|
static void
|
|
_bmp_set(jit_state_t *_jit, jit_word_t bit)
|
|
{
|
|
jit_word_t woff, boff;
|
|
|
|
woff = bit >> bmp_shift;
|
|
boff = 1LL << (bit & (__WORDSIZE - 1));
|
|
if (woff >= _jitc->blockmask.length) {
|
|
jit_word_t length = (woff + 16) & -16;
|
|
jit_realloc((jit_pointer_t *)&_jitc->blockmask.ptr,
|
|
_jitc->blockmask.length * sizeof(jit_word_t),
|
|
length * sizeof(jit_word_t));
|
|
_jitc->blockmask.length = length;
|
|
}
|
|
_jitc->blockmask.ptr[woff] |= boff;
|
|
}
|
|
|
|
static void
|
|
_bmp_clr(jit_state_t *_jit, jit_word_t bit)
|
|
{
|
|
jit_word_t woff, boff;
|
|
|
|
woff = bit >> bmp_shift;
|
|
boff = 1LL << (bit & (__WORDSIZE - 1));
|
|
if (woff < _jitc->blockmask.length)
|
|
_jitc->blockmask.ptr[woff] &= ~boff;
|
|
}
|
|
|
|
static jit_bool_t
|
|
_bmp_tst(jit_state_t *_jit, jit_word_t bit)
|
|
{
|
|
jit_word_t woff, boff;
|
|
|
|
woff = bit >> bmp_shift;
|
|
boff = 1LL << (bit & (__WORDSIZE - 1));
|
|
if (woff < _jitc->blockmask.length)
|
|
return ((_jitc->blockmask.ptr[woff] & boff) != 0);
|
|
return (0);
|
|
}
|
|
|
|
jit_state_t *
|
|
jit_new_state(void)
|
|
{
|
|
jit_state_t *_jit;
|
|
|
|
jit_alloc((jit_pointer_t *)&_jit, sizeof(jit_state_t));
|
|
jit_alloc((jit_pointer_t *)&_jitc, sizeof(jit_compiler_t));
|
|
jit_regset_new(&_jitc->regarg);
|
|
jit_regset_new(&_jitc->regsav);
|
|
jit_regset_new(&_jitc->reglive);
|
|
jit_regset_new(&_jitc->regmask);
|
|
bmp_init();
|
|
|
|
jit_init();
|
|
|
|
jit_alloc((jit_pointer_t *)&_jitc->spill,
|
|
_jitc->reglen * sizeof(jit_node_t*));
|
|
jit_alloc((jit_pointer_t *)&_jitc->gen,
|
|
_jitc->reglen * sizeof(jit_int32_t));
|
|
jit_alloc((jit_pointer_t *)&_jitc->values,
|
|
_jitc->reglen * sizeof(jit_value_t));
|
|
|
|
jit_alloc((jit_pointer_t *)&_jitc->patches.ptr,
|
|
(_jitc->patches.length = 1024) * sizeof(jit_patch_t));
|
|
jit_alloc((jit_pointer_t *)&_jitc->functions.ptr,
|
|
(_jitc->functions.length = 16) * sizeof(jit_function_t));
|
|
jit_alloc((jit_pointer_t *)&_jitc->pool.ptr,
|
|
(_jitc->pool.length = 16) * sizeof(jit_node_t*));
|
|
jit_alloc((jit_pointer_t *)&_jitc->blocks.ptr,
|
|
(_jitc->blocks.length = 16) * sizeof(jit_block_t));
|
|
#if __arm__ && DISASSEMBLER
|
|
jit_alloc((jit_pointer_t *)&_jitc->data_info.ptr,
|
|
(_jitc->data_info.length = 1024) * sizeof(jit_data_info_t));
|
|
#endif
|
|
|
|
/* allocate at most one extra note in case jit_name() is
|
|
* never called, or called after adding at least one note */
|
|
_jit->note.length = 1;
|
|
_jitc->note.size = sizeof(jit_note_t);
|
|
|
|
return (_jit);
|
|
}
|
|
|
|
void
|
|
_jit_clear_state(jit_state_t *_jit)
|
|
{
|
|
jit_word_t offset;
|
|
jit_function_t *function;
|
|
|
|
/* release memory not required at jit execution time and set
|
|
* pointers to NULL to explicitly know they are released */
|
|
_jitc->head = _jitc->tail = NULL;
|
|
|
|
bmp_clear();
|
|
|
|
jit_free((jit_pointer_t *)&_jitc->data.table);
|
|
_jitc->data.size = _jitc->data.count = 0;
|
|
|
|
jit_free((jit_pointer_t *)&_jitc->spill);
|
|
jit_free((jit_pointer_t *)&_jitc->gen);
|
|
jit_free((jit_pointer_t *)&_jitc->values);
|
|
|
|
jit_free((jit_pointer_t *)&_jitc->blocks.ptr);
|
|
|
|
jit_free((jit_pointer_t *)&_jitc->patches.ptr);
|
|
_jitc->patches.offset = _jitc->patches.length = 0;
|
|
|
|
for (offset = 0; offset < _jitc->functions.offset; offset++) {
|
|
function = _jitc->functions.ptr + offset;
|
|
jit_free((jit_pointer_t *)&function->regoff);
|
|
}
|
|
jit_free((jit_pointer_t *)&_jitc->functions.ptr);
|
|
_jitc->functions.offset = _jitc->functions.length = 0;
|
|
_jitc->function = NULL;
|
|
|
|
for (offset = 0; offset < _jitc->pool.offset; offset++)
|
|
jit_free((jit_pointer_t *)(_jitc->pool.ptr + offset));
|
|
jit_free((jit_pointer_t *)&_jitc->pool.ptr);
|
|
_jitc->pool.offset = _jitc->pool.length = 0;
|
|
_jitc->list = NULL;
|
|
|
|
_jitc->note.head = _jitc->note.tail =
|
|
_jitc->note.name = _jitc->note.note = NULL;
|
|
_jitc->note.base = NULL;
|
|
|
|
#if __arm__ && DISASSEMBLER
|
|
jit_free((jit_pointer_t *)&_jitc->data_info.ptr);
|
|
#endif
|
|
|
|
#if __powerpc64__ || __ia64__
|
|
jit_free((jit_pointer_t *)&_jitc->prolog.ptr);
|
|
#endif
|
|
|
|
#if __ia64__
|
|
jit_regset_del(&_jitc->regs);
|
|
#endif
|
|
|
|
jit_free((jit_pointer_t *)&_jitc);
|
|
}
|
|
|
|
void
|
|
_jit_destroy_state(jit_state_t *_jit)
|
|
{
|
|
if (!_jit->user_code)
|
|
munmap(_jit->code.ptr, _jit->code.length);
|
|
if (!_jit->user_data)
|
|
munmap(_jit->data.ptr, _jit->data.length);
|
|
jit_free((jit_pointer_t *)&_jit);
|
|
}
|
|
|
|
jit_node_t *
|
|
_jit_new_node(jit_state_t *_jit, jit_code_t code)
|
|
{
|
|
assert(!_jitc->realize);
|
|
return (link_node(new_node(code)));
|
|
}
|
|
|
|
jit_node_t *
|
|
_jit_new_node_no_link(jit_state_t *_jit, jit_code_t code)
|
|
{
|
|
assert(!_jitc->realize);
|
|
return (new_node(code));
|
|
}
|
|
|
|
void
|
|
_jit_link_node(jit_state_t *_jit, jit_node_t *node)
|
|
{
|
|
assert(!_jitc->realize);
|
|
link_node(node);
|
|
}
|
|
|
|
jit_node_t *
|
|
_jit_new_node_w(jit_state_t *_jit, jit_code_t code,
|
|
jit_word_t u)
|
|
{
|
|
jit_node_t *node = new_node(code);
|
|
assert(!_jitc->realize);
|
|
node->u.w = u;
|
|
return (link_node(node));
|
|
}
|
|
|
|
jit_node_t *
|
|
_jit_new_node_p(jit_state_t *_jit, jit_code_t code,
|
|
jit_pointer_t u)
|
|
{
|
|
jit_node_t *node = new_node(code);
|
|
assert(!_jitc->realize);
|
|
node->u.p = u;
|
|
return (link_node(node));
|
|
}
|
|
|
|
jit_node_t *
|
|
_jit_new_node_ww(jit_state_t *_jit, jit_code_t code,
|
|
jit_word_t u, jit_word_t v)
|
|
{
|
|
jit_node_t *node = new_node(code);
|
|
assert(!_jitc->realize);
|
|
node->u.w = u;
|
|
node->v.w = v;
|
|
return (link_node(node));
|
|
}
|
|
|
|
jit_node_t *
|
|
_jit_new_node_wp(jit_state_t *_jit, jit_code_t code,
|
|
jit_word_t u, jit_pointer_t v)
|
|
{
|
|
return (jit_new_node_ww(code, u, (jit_word_t)v));
|
|
}
|
|
|
|
jit_node_t *
|
|
_jit_new_node_pw(jit_state_t *_jit, jit_code_t code,
|
|
jit_pointer_t u, jit_word_t v)
|
|
{
|
|
return (jit_new_node_ww(code, (jit_word_t)u, v));
|
|
}
|
|
|
|
jit_node_t *
|
|
_jit_new_node_wf(jit_state_t *_jit, jit_code_t code,
|
|
jit_word_t u, jit_float32_t v)
|
|
{
|
|
jit_node_t *node = new_node(code);
|
|
assert(!_jitc->realize);
|
|
node->u.w = u;
|
|
node->v.f = v;
|
|
return (link_node(node));
|
|
}
|
|
|
|
jit_node_t *
|
|
_jit_new_node_wd(jit_state_t *_jit, jit_code_t code,
|
|
jit_word_t u, jit_float64_t v)
|
|
{
|
|
jit_node_t *node = new_node(code);
|
|
assert(!_jitc->realize);
|
|
node->u.w = u;
|
|
node->v.d = v;
|
|
return (link_node(node));
|
|
}
|
|
|
|
jit_node_t *
|
|
_jit_new_node_www(jit_state_t *_jit, jit_code_t code,
|
|
jit_word_t u, jit_word_t v, jit_word_t w)
|
|
{
|
|
jit_node_t *node = new_node(code);
|
|
assert(!_jitc->realize);
|
|
node->u.w = u;
|
|
node->v.w = v;
|
|
node->w.w = w;
|
|
return (link_node(node));
|
|
}
|
|
|
|
jit_node_t *
|
|
_jit_new_node_qww(jit_state_t *_jit, jit_code_t code,
|
|
jit_int32_t l, jit_int32_t h,
|
|
jit_word_t v, jit_word_t w)
|
|
{
|
|
jit_node_t *node = new_node(code);
|
|
assert(!_jitc->realize);
|
|
assert(l != h);
|
|
node->u.q.l = l;
|
|
node->u.q.h = h;
|
|
node->v.w = v;
|
|
node->w.w = w;
|
|
return (link_node(node));
|
|
}
|
|
|
|
jit_node_t *
|
|
_jit_new_node_wwf(jit_state_t *_jit, jit_code_t code,
|
|
jit_word_t u, jit_word_t v, jit_float32_t w)
|
|
{
|
|
jit_node_t *node = new_node(code);
|
|
assert(!_jitc->realize);
|
|
node->u.w = u;
|
|
node->v.w = v;
|
|
node->w.f = w;
|
|
return (link_node(node));
|
|
}
|
|
|
|
jit_node_t *
|
|
_jit_new_node_wwd(jit_state_t *_jit, jit_code_t code,
|
|
jit_word_t u, jit_word_t v, jit_float64_t w)
|
|
{
|
|
jit_node_t *node = new_node(code);
|
|
assert(!_jitc->realize);
|
|
node->u.w = u;
|
|
node->v.w = v;
|
|
node->w.d = w;
|
|
return (link_node(node));
|
|
}
|
|
|
|
jit_node_t *
|
|
_jit_new_node_pww(jit_state_t *_jit, jit_code_t code,
|
|
jit_pointer_t u, jit_word_t v, jit_word_t w)
|
|
{
|
|
jit_node_t *node = new_node(code);
|
|
assert(!_jitc->realize);
|
|
node->u.p = u;
|
|
node->v.w = v;
|
|
node->w.w = w;
|
|
return (link_node(node));
|
|
}
|
|
|
|
jit_node_t *
|
|
_jit_new_node_pwf(jit_state_t *_jit, jit_code_t code,
|
|
jit_pointer_t u, jit_word_t v, jit_float32_t w)
|
|
{
|
|
jit_node_t *node = new_node(code);
|
|
assert(!_jitc->realize);
|
|
node->u.p = u;
|
|
node->v.w = v;
|
|
node->w.f = w;
|
|
return (link_node(node));
|
|
}
|
|
|
|
jit_node_t *
|
|
_jit_new_node_pwd(jit_state_t *_jit, jit_code_t code,
|
|
jit_pointer_t u, jit_word_t v, jit_float64_t w)
|
|
{
|
|
jit_node_t *node = new_node(code);
|
|
assert(!_jitc->realize);
|
|
node->u.p = u;
|
|
node->v.w = v;
|
|
node->w.d = w;
|
|
return (link_node(node));
|
|
}
|
|
|
|
jit_node_t *
|
|
_jit_label(jit_state_t *_jit)
|
|
{
|
|
jit_node_t *node;
|
|
|
|
if (!(node = _jitc->tail) || node->code != jit_code_label) {
|
|
node = jit_forward();
|
|
jit_link(node);
|
|
}
|
|
|
|
return (node);
|
|
}
|
|
|
|
jit_node_t *
|
|
_jit_forward(jit_state_t *_jit)
|
|
{
|
|
return (jit_new_node_no_link(jit_code_label));
|
|
}
|
|
|
|
jit_node_t *
|
|
_jit_indirect(jit_state_t *_jit)
|
|
{
|
|
jit_node_t *node;
|
|
|
|
node = jit_label();
|
|
node->flag |= jit_flag_use;
|
|
|
|
return (node);
|
|
}
|
|
|
|
void
|
|
_jit_link(jit_state_t *_jit, jit_node_t *node)
|
|
{
|
|
jit_block_t *block;
|
|
|
|
assert((node->code == jit_code_label ||
|
|
node->code == jit_code_prolog ||
|
|
node->code == jit_code_epilog) && !node->next);
|
|
jit_link_node(node);
|
|
if (_jitc->blocks.offset >= _jitc->blocks.length) {
|
|
jit_word_t length;
|
|
|
|
length = _jitc->blocks.length + 16;
|
|
jit_realloc((jit_pointer_t *)&_jitc->blocks.ptr,
|
|
_jitc->blocks.length * sizeof(jit_block_t),
|
|
length * sizeof(jit_block_t));
|
|
_jitc->blocks.length = length;
|
|
}
|
|
block = _jitc->blocks.ptr + _jitc->blocks.offset;
|
|
block->label = node;
|
|
node->v.w = _jitc->blocks.offset;
|
|
jit_regset_new(&block->reglive);
|
|
jit_regset_new(&block->regmask);
|
|
++_jitc->blocks.offset;
|
|
}
|
|
|
|
void
|
|
_jit_prepare(jit_state_t *_jit)
|
|
{
|
|
assert(_jitc->function != NULL);
|
|
_jitc->function->call.call = jit_call_default;
|
|
_jitc->function->call.argi =
|
|
_jitc->function->call.argf =
|
|
_jitc->function->call.size = 0;
|
|
_jitc->prepare = 1;
|
|
}
|
|
|
|
/* If declaring a jit function as varargs, in most backends it does
|
|
* not change anything. Currently only exception is arm backend, that
|
|
* if running in hardware float abi, switches to software float abi
|
|
* if "self" function is varargs. Otherwise, there is no logic to
|
|
* handle va_list like objects that need to parse runtime state, and
|
|
* that is mainly because jit_arg* and jit_getarg* work only with
|
|
* constants values, and one must not expect them to be handled at
|
|
* runtime, they are parsed only once (same applies to jit_allocai,
|
|
* that has no jit_allocar counterpart).
|
|
*/
|
|
void
|
|
_jit_ellipsis(jit_state_t *_jit)
|
|
{
|
|
if (_jitc->prepare) {
|
|
assert(!(_jitc->function->call.call & jit_call_varargs));
|
|
_jitc->function->call.call |= jit_call_varargs;
|
|
}
|
|
else {
|
|
assert(!(_jitc->function->self.call & jit_call_varargs));
|
|
_jitc->function->self.call |= jit_call_varargs;
|
|
}
|
|
}
|
|
|
|
void
|
|
_jit_patch(jit_state_t* _jit, jit_node_t *instr)
|
|
{
|
|
jit_node_t *label;
|
|
|
|
if (!(label = _jitc->tail) || label->code != jit_code_label)
|
|
label = jit_label();
|
|
jit_patch_at(instr, label);
|
|
}
|
|
|
|
jit_int32_t
|
|
_jit_classify(jit_state_t *_jit, jit_code_t code)
|
|
{
|
|
jit_int32_t mask;
|
|
|
|
switch (code) {
|
|
case jit_code_data: case jit_code_align: case jit_code_save:
|
|
case jit_code_load: case jit_code_name: case jit_code_label:
|
|
case jit_code_note: case jit_code_prolog: case jit_code_epilog:
|
|
mask = 0;
|
|
break;
|
|
case jit_code_live:
|
|
mask = jit_cc_a0_reg;
|
|
break;
|
|
case jit_code_arg: case jit_code_arg_f: case jit_code_arg_d:
|
|
mask = jit_cc_a0_int;
|
|
break;
|
|
case jit_code_calli: case jit_code_jmpi:
|
|
mask = jit_cc_a0_jmp;
|
|
break;
|
|
case jit_code_callr: case jit_code_jmpr:
|
|
mask = jit_cc_a0_reg|jit_cc_a0_jmp;
|
|
break;
|
|
case jit_code_x86_retval_f:
|
|
case jit_code_x86_retval_d:
|
|
mask = jit_cc_a0_reg|jit_cc_a0_chg;
|
|
break;
|
|
case jit_code_movi: case jit_code_ldi_c: case jit_code_ldi_uc:
|
|
case jit_code_ldi_s: case jit_code_ldi_us: case jit_code_ldi_i:
|
|
case jit_code_ldi_ui: case jit_code_ldi_l: case jit_code_ldi_f:
|
|
case jit_code_ldi_d:
|
|
mask = jit_cc_a0_reg|jit_cc_a0_chg|jit_cc_a1_int;
|
|
break;
|
|
case jit_code_movi_f: case jit_code_movi_f_w:
|
|
mask = jit_cc_a0_reg|jit_cc_a0_chg|jit_cc_a1_flt;
|
|
break;
|
|
case jit_code_movi_d: case jit_code_movi_d_w:
|
|
mask = jit_cc_a0_reg|jit_cc_a0_chg|jit_cc_a1_dbl;
|
|
break;
|
|
case jit_code_movi_d_ww:
|
|
mask = jit_cc_a0_reg|jit_cc_a0_chg|jit_cc_a1_reg|jit_cc_a1_chg|
|
|
jit_cc_a2_dbl;
|
|
break;
|
|
case jit_code_negr: case jit_code_comr: case jit_code_movr:
|
|
case jit_code_extr_c: case jit_code_extr_uc: case jit_code_extr_s:
|
|
case jit_code_extr_us: case jit_code_extr_i: case jit_code_extr_ui:
|
|
case jit_code_truncr_f_i: case jit_code_truncr_f_l:
|
|
case jit_code_truncr_d_i: case jit_code_truncr_d_l:
|
|
case jit_code_htonr: case jit_code_ldr_c: case jit_code_ldr_uc:
|
|
case jit_code_ldr_s: case jit_code_ldr_us: case jit_code_ldr_i:
|
|
case jit_code_ldr_ui: case jit_code_ldr_l: case jit_code_negr_f:
|
|
case jit_code_absr_f: case jit_code_sqrtr_f: case jit_code_movr_f:
|
|
case jit_code_extr_f: case jit_code_extr_d_f: case jit_code_ldr_f:
|
|
case jit_code_negr_d: case jit_code_absr_d: case jit_code_sqrtr_d:
|
|
case jit_code_movr_d: case jit_code_extr_d: case jit_code_extr_f_d:
|
|
case jit_code_ldr_d:
|
|
case jit_code_movr_w_f: case jit_code_movr_f_w:
|
|
case jit_code_movr_w_d: case jit_code_movr_d_w:
|
|
mask = jit_cc_a0_reg|jit_cc_a0_chg|jit_cc_a1_reg;
|
|
break;
|
|
case jit_code_movr_d_ww:
|
|
mask = jit_cc_a0_reg|jit_cc_a0_chg|jit_cc_a1_reg|jit_cc_a1_chg|
|
|
jit_cc_a2_reg;
|
|
break;
|
|
case jit_code_addi: case jit_code_addxi: case jit_code_addci:
|
|
case jit_code_subi: case jit_code_subxi: case jit_code_subci:
|
|
case jit_code_rsbi:
|
|
case jit_code_muli: case jit_code_divi: case jit_code_divi_u:
|
|
case jit_code_remi: case jit_code_remi_u: case jit_code_andi:
|
|
case jit_code_ori: case jit_code_xori: case jit_code_lshi:
|
|
case jit_code_rshi: case jit_code_rshi_u: case jit_code_lti:
|
|
case jit_code_lti_u: case jit_code_lei: case jit_code_lei_u:
|
|
case jit_code_eqi: case jit_code_gei: case jit_code_gei_u:
|
|
case jit_code_gti: case jit_code_gti_u: case jit_code_nei:
|
|
case jit_code_ldxi_c: case jit_code_ldxi_uc: case jit_code_ldxi_s:
|
|
case jit_code_ldxi_us: case jit_code_ldxi_i: case jit_code_ldxi_ui:
|
|
case jit_code_ldxi_l: case jit_code_ldxi_f: case jit_code_ldxi_d:
|
|
mask = jit_cc_a0_reg|jit_cc_a0_chg|jit_cc_a1_reg|jit_cc_a2_int;
|
|
break;
|
|
case jit_code_qmuli: case jit_code_qmuli_u:
|
|
case jit_code_qdivi: case jit_code_qdivi_u:
|
|
mask = jit_cc_a0_reg|jit_cc_a0_rlh|jit_cc_a0_chg|
|
|
jit_cc_a1_reg|jit_cc_a2_int;
|
|
break;
|
|
case jit_code_addi_f: case jit_code_subi_f: case jit_code_rsbi_f:
|
|
case jit_code_muli_f: case jit_code_divi_f: case jit_code_lti_f:
|
|
case jit_code_lei_f: case jit_code_eqi_f: case jit_code_gei_f:
|
|
case jit_code_gti_f: case jit_code_nei_f: case jit_code_unlti_f:
|
|
case jit_code_unlei_f: case jit_code_uneqi_f: case jit_code_ungei_f:
|
|
case jit_code_ungti_f: case jit_code_ltgti_f: case jit_code_ordi_f:
|
|
case jit_code_unordi_f:
|
|
mask = jit_cc_a0_reg|jit_cc_a0_chg|jit_cc_a1_reg|jit_cc_a2_flt;
|
|
break;
|
|
case jit_code_addi_d: case jit_code_subi_d: case jit_code_rsbi_d:
|
|
case jit_code_muli_d: case jit_code_divi_d: case jit_code_lti_d:
|
|
case jit_code_lei_d: case jit_code_eqi_d: case jit_code_gei_d:
|
|
case jit_code_gti_d: case jit_code_nei_d: case jit_code_unlti_d:
|
|
case jit_code_unlei_d: case jit_code_uneqi_d: case jit_code_ungei_d:
|
|
case jit_code_ungti_d: case jit_code_ltgti_d: case jit_code_ordi_d:
|
|
case jit_code_unordi_d:
|
|
mask = jit_cc_a0_reg|jit_cc_a0_chg|jit_cc_a1_reg|jit_cc_a2_dbl;
|
|
break;
|
|
case jit_code_addr: case jit_code_addxr: case jit_code_addcr:
|
|
case jit_code_subr: case jit_code_subxr: case jit_code_subcr:
|
|
case jit_code_mulr: case jit_code_divr: case jit_code_divr_u:
|
|
case jit_code_remr: case jit_code_remr_u: case jit_code_andr:
|
|
case jit_code_orr: case jit_code_xorr: case jit_code_lshr:
|
|
case jit_code_rshr: case jit_code_rshr_u: case jit_code_ltr:
|
|
case jit_code_ltr_u: case jit_code_ler: case jit_code_ler_u:
|
|
case jit_code_eqr: case jit_code_ger: case jit_code_ger_u:
|
|
case jit_code_gtr: case jit_code_gtr_u: case jit_code_ner:
|
|
case jit_code_ldxr_c: case jit_code_ldxr_uc: case jit_code_ldxr_s:
|
|
case jit_code_ldxr_us: case jit_code_ldxr_i: case jit_code_ldxr_ui:
|
|
case jit_code_ldxr_l: case jit_code_addr_f: case jit_code_subr_f:
|
|
case jit_code_mulr_f: case jit_code_divr_f: case jit_code_ltr_f:
|
|
case jit_code_ler_f: case jit_code_eqr_f: case jit_code_ger_f:
|
|
case jit_code_gtr_f: case jit_code_ner_f: case jit_code_unltr_f:
|
|
case jit_code_unler_f: case jit_code_uneqr_f: case jit_code_unger_f:
|
|
case jit_code_ungtr_f: case jit_code_ltgtr_f: case jit_code_ordr_f:
|
|
case jit_code_unordr_f: case jit_code_ldxr_f: case jit_code_addr_d:
|
|
case jit_code_subr_d: case jit_code_mulr_d: case jit_code_divr_d:
|
|
case jit_code_ltr_d: case jit_code_ler_d: case jit_code_eqr_d:
|
|
case jit_code_ger_d: case jit_code_gtr_d: case jit_code_ner_d:
|
|
case jit_code_unltr_d: case jit_code_unler_d: case jit_code_uneqr_d:
|
|
case jit_code_unger_d: case jit_code_ungtr_d: case jit_code_ltgtr_d:
|
|
case jit_code_ordr_d: case jit_code_unordr_d: case jit_code_ldxr_d:
|
|
case jit_code_movr_ww_d:
|
|
mask = jit_cc_a0_reg|jit_cc_a0_chg|jit_cc_a1_reg|jit_cc_a2_reg;
|
|
break;
|
|
case jit_code_qmulr: case jit_code_qmulr_u:
|
|
case jit_code_qdivr: case jit_code_qdivr_u:
|
|
mask = jit_cc_a0_reg|jit_cc_a0_rlh|jit_cc_a0_chg|
|
|
jit_cc_a1_reg|jit_cc_a2_reg;
|
|
break;
|
|
case jit_code_sti_c: case jit_code_sti_s: case jit_code_sti_i:
|
|
case jit_code_sti_l: case jit_code_sti_f: case jit_code_sti_d:
|
|
mask = jit_cc_a0_int|jit_cc_a1_reg;
|
|
break;
|
|
case jit_code_blti: case jit_code_blti_u: case jit_code_blei:
|
|
case jit_code_blei_u: case jit_code_beqi: case jit_code_bgei:
|
|
case jit_code_bgei_u: case jit_code_bgti: case jit_code_bgti_u:
|
|
case jit_code_bnei: case jit_code_bmsi: case jit_code_bmci:
|
|
mask = jit_cc_a0_jmp|jit_cc_a1_reg|jit_cc_a2_int;
|
|
break;
|
|
case jit_code_blti_f: case jit_code_blei_f: case jit_code_beqi_f:
|
|
case jit_code_bgei_f: case jit_code_bgti_f: case jit_code_bnei_f:
|
|
case jit_code_bunlti_f: case jit_code_bunlei_f: case jit_code_buneqi_f:
|
|
case jit_code_bungei_f: case jit_code_bungti_f: case jit_code_bltgti_f:
|
|
case jit_code_bordi_f: case jit_code_bunordi_f:
|
|
mask = jit_cc_a0_jmp|jit_cc_a1_reg|jit_cc_a2_flt;
|
|
break;
|
|
case jit_code_blti_d: case jit_code_blei_d: case jit_code_beqi_d:
|
|
case jit_code_bgei_d: case jit_code_bgti_d: case jit_code_bnei_d:
|
|
case jit_code_bunlti_d: case jit_code_bunlei_d: case jit_code_buneqi_d:
|
|
case jit_code_bungei_d: case jit_code_bungti_d: case jit_code_bltgti_d:
|
|
case jit_code_bordi_d: case jit_code_bunordi_d:
|
|
mask = jit_cc_a0_jmp|jit_cc_a1_reg|jit_cc_a2_dbl;
|
|
break;
|
|
case jit_code_str_c: case jit_code_str_s: case jit_code_str_i:
|
|
case jit_code_str_l: case jit_code_str_f: case jit_code_str_d:
|
|
mask = jit_cc_a0_reg|jit_cc_a1_reg;
|
|
break;
|
|
case jit_code_stxi_c: case jit_code_stxi_s: case jit_code_stxi_i:
|
|
case jit_code_stxi_l: case jit_code_stxi_f: case jit_code_stxi_d:
|
|
mask = jit_cc_a0_int|jit_cc_a1_reg|jit_cc_a2_reg;
|
|
break;
|
|
case jit_code_bltr: case jit_code_bltr_u: case jit_code_bler:
|
|
case jit_code_bler_u: case jit_code_beqr: case jit_code_bger:
|
|
case jit_code_bger_u: case jit_code_bgtr: case jit_code_bgtr_u:
|
|
case jit_code_bner: case jit_code_bmsr: case jit_code_bmcr:
|
|
case jit_code_bltr_f: case jit_code_bler_f: case jit_code_beqr_f:
|
|
case jit_code_bger_f: case jit_code_bgtr_f: case jit_code_bner_f:
|
|
case jit_code_bunltr_f: case jit_code_bunler_f: case jit_code_buneqr_f:
|
|
case jit_code_bunger_f: case jit_code_bungtr_f: case jit_code_bltgtr_f:
|
|
case jit_code_bordr_f: case jit_code_bunordr_f:case jit_code_bltr_d:
|
|
case jit_code_bler_d: case jit_code_beqr_d: case jit_code_bger_d:
|
|
case jit_code_bgtr_d: case jit_code_bner_d: case jit_code_bunltr_d:
|
|
case jit_code_bunler_d: case jit_code_buneqr_d: case jit_code_bunger_d:
|
|
case jit_code_bungtr_d: case jit_code_bltgtr_d: case jit_code_bordr_d:
|
|
case jit_code_bunordr_d:
|
|
mask = jit_cc_a0_jmp|jit_cc_a1_reg|jit_cc_a2_reg;
|
|
break;
|
|
case jit_code_boaddi: case jit_code_boaddi_u: case jit_code_bxaddi:
|
|
case jit_code_bxaddi_u: case jit_code_bosubi: case jit_code_bosubi_u:
|
|
case jit_code_bxsubi: case jit_code_bxsubi_u:
|
|
mask = jit_cc_a0_jmp|jit_cc_a1_reg|jit_cc_a1_chg|jit_cc_a2_int;
|
|
break;
|
|
case jit_code_stxr_c: case jit_code_stxr_s: case jit_code_stxr_i:
|
|
case jit_code_stxr_l: case jit_code_stxr_f: case jit_code_stxr_d:
|
|
mask = jit_cc_a0_reg|jit_cc_a1_reg|jit_cc_a2_reg;
|
|
break;
|
|
case jit_code_boaddr: case jit_code_boaddr_u: case jit_code_bxaddr:
|
|
case jit_code_bxaddr_u: case jit_code_bosubr: case jit_code_bosubr_u:
|
|
case jit_code_bxsubr: case jit_code_bxsubr_u:
|
|
mask = jit_cc_a0_jmp|jit_cc_a1_reg|jit_cc_a1_chg|jit_cc_a2_reg;
|
|
break;
|
|
default:
|
|
abort();
|
|
}
|
|
|
|
return (mask);
|
|
}
|
|
|
|
void
|
|
_jit_patch_abs(jit_state_t *_jit, jit_node_t *instr, jit_pointer_t address)
|
|
{
|
|
jit_int32_t mask;
|
|
|
|
if (instr->code == jit_code_movi)
|
|
instr->v.p = address;
|
|
else {
|
|
mask = jit_classify(instr->code);
|
|
assert((mask & (jit_cc_a0_reg|jit_cc_a0_jmp)) == jit_cc_a0_jmp);
|
|
instr->u.p = address;
|
|
}
|
|
}
|
|
|
|
void
|
|
_jit_patch_at(jit_state_t *_jit, jit_node_t *instr, jit_node_t *label)
|
|
{
|
|
jit_int32_t mask;
|
|
|
|
assert(!(instr->flag & jit_flag_node));
|
|
instr->flag |= jit_flag_node;
|
|
switch (instr->code) {
|
|
case jit_code_movi:
|
|
assert(label->code == jit_code_label ||
|
|
label->code == jit_code_data);
|
|
instr->v.n = label;
|
|
if (label->code == jit_code_data)
|
|
instr->flag |= jit_flag_data;
|
|
break;
|
|
case jit_code_jmpi:
|
|
assert(label->code == jit_code_label ||
|
|
label->code == jit_code_epilog);
|
|
instr->u.n = label;
|
|
break;
|
|
default:
|
|
mask = jit_classify(instr->code);
|
|
assert((mask & (jit_cc_a0_reg|jit_cc_a0_jmp)) == jit_cc_a0_jmp);
|
|
assert(label->code == jit_code_label);
|
|
instr->u.n = label;
|
|
break;
|
|
}
|
|
/* link field is used as list of nodes associated with a given label */
|
|
instr->link = label->link;
|
|
label->link = instr;
|
|
}
|
|
|
|
void
|
|
_jit_optimize(jit_state_t *_jit)
|
|
{
|
|
jit_bool_t jump;
|
|
jit_int32_t mask;
|
|
jit_node_t *node;
|
|
jit_block_t *block;
|
|
jit_word_t offset;
|
|
|
|
_jitc->function = NULL;
|
|
|
|
thread_jumps();
|
|
sequential_labels();
|
|
|
|
/* create initial mapping of live register values
|
|
* at the start of a basic block */
|
|
for (offset = 0; offset < _jitc->blocks.offset; offset++) {
|
|
block = _jitc->blocks.ptr + offset;
|
|
if (!block->label)
|
|
continue;
|
|
if (block->label->code != jit_code_epilog)
|
|
jit_setup(block);
|
|
}
|
|
/* call jit_update resolving undefined values in reverse
|
|
* order so that sequential code would find most data already
|
|
* resolved when reaching the start of a new basic block */
|
|
for (offset = _jitc->blocks.offset - 1; offset >= 0; offset--) {
|
|
block = _jitc->blocks.ptr + offset;
|
|
if (!block->label)
|
|
continue;
|
|
if (block->label->code != jit_code_epilog) {
|
|
jit_regset_set(&_jitc->regmask, &block->regmask);
|
|
jit_update(block->label->next, &block->reglive, &_jitc->regmask);
|
|
}
|
|
}
|
|
|
|
patch_registers();
|
|
simplify();
|
|
|
|
/* figure out labels that are only reached with a jump
|
|
* and is required to do a simple redundant_store removal
|
|
* on jit_beqi below */
|
|
jump = 1;
|
|
for (node = _jitc->head; node; node = node->next) {
|
|
switch (node->code) {
|
|
case jit_code_label:
|
|
if (!jump)
|
|
node->flag |= jit_flag_head;
|
|
break;
|
|
case jit_code_jmpi: case jit_code_jmpr:
|
|
case jit_code_epilog:
|
|
jump = 1;
|
|
break;
|
|
case jit_code_data: case jit_code_note:
|
|
break;
|
|
default:
|
|
jump = 0;
|
|
break;
|
|
}
|
|
}
|
|
|
|
for (node = _jitc->head; node; node = node->next) {
|
|
switch (node->code) {
|
|
case jit_code_prolog:
|
|
_jitc->function = _jitc->functions.ptr + node->w.w;
|
|
break;
|
|
case jit_code_epilog:
|
|
_jitc->function = NULL;
|
|
break;
|
|
case jit_code_beqi:
|
|
redundant_store(node, 1);
|
|
break;
|
|
case jit_code_bnei:
|
|
redundant_store(node, 0);
|
|
break;
|
|
default:
|
|
mask = jit_classify(node->code);
|
|
#if JIT_HASH_CONSTS
|
|
if (mask & jit_cc_a1_flt) {
|
|
node->v.p = jit_data(&node->v.f, sizeof(jit_float32_t), 4);
|
|
node->flag |= jit_flag_node | jit_flag_data;
|
|
}
|
|
else if (mask & jit_cc_a1_dbl) {
|
|
node->v.p = jit_data(&node->v.d, sizeof(jit_float64_t), 8);
|
|
node->flag |= jit_flag_node | jit_flag_data;
|
|
}
|
|
else if (mask & jit_cc_a2_flt) {
|
|
node->w.p = jit_data(&node->w.f, sizeof(jit_float32_t), 4);
|
|
node->flag |= jit_flag_node | jit_flag_data;
|
|
}
|
|
else if (mask & jit_cc_a2_dbl) {
|
|
node->w.p = jit_data(&node->w.d, sizeof(jit_float64_t), 8);
|
|
node->flag |= jit_flag_node | jit_flag_data;
|
|
}
|
|
#endif
|
|
if (_jitc->function) {
|
|
if ((mask & (jit_cc_a0_reg|jit_cc_a0_chg)) ==
|
|
(jit_cc_a0_reg|jit_cc_a0_chg)) {
|
|
if (mask & jit_cc_a0_rlh) {
|
|
jit_regset_setbit(&_jitc->function->regset,
|
|
jit_regno(node->u.q.l));
|
|
jit_regset_setbit(&_jitc->function->regset,
|
|
jit_regno(node->u.q.h));
|
|
}
|
|
else
|
|
jit_regset_setbit(&_jitc->function->regset,
|
|
jit_regno(node->u.w));
|
|
}
|
|
if ((mask & (jit_cc_a1_reg|jit_cc_a1_chg)) ==
|
|
(jit_cc_a1_reg|jit_cc_a1_chg))
|
|
jit_regset_setbit(&_jitc->function->regset,
|
|
jit_regno(node->v.w));
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
_jit_reglive(jit_state_t *_jit, jit_node_t *node)
|
|
{
|
|
jit_int32_t spec;
|
|
jit_int32_t value;
|
|
jit_block_t *block;
|
|
|
|
switch (node->code) {
|
|
case jit_code_label: case jit_code_prolog: case jit_code_epilog:
|
|
block = _jitc->blocks.ptr + node->v.w;
|
|
jit_regset_set(&_jitc->reglive, &block->reglive);
|
|
break;
|
|
case jit_code_callr:
|
|
value = jit_regno(node->u.w);
|
|
if (!(node->u.w & jit_regno_patch)) {
|
|
jit_regset_setbit(&_jitc->reglive, value);
|
|
}
|
|
case jit_code_calli:
|
|
for (value = 0; value < _jitc->reglen; value++) {
|
|
spec = jit_class(_rvs[value].spec);
|
|
if ((spec & jit_class_arg) && jit_regarg_p(node, value))
|
|
jit_regset_setbit(&_jitc->reglive, value);
|
|
else if (!(spec & jit_class_sav))
|
|
jit_regset_clrbit(&_jitc->reglive, value);
|
|
}
|
|
#if defined(JIT_RET)
|
|
/* Explicitly set return registers as live, because retval
|
|
* should be free to not create a note, and/or user not
|
|
* call jit_retval (but not a good idea to expect JIT_R0
|
|
* to match JIT_RET) */
|
|
jit_regset_setbit(&_jitc->reglive, JIT_RET);
|
|
# if __arm__
|
|
/* FIXME need a better logic (and r2-r3 may contain results) */
|
|
jit_regset_setbit(&_jitc->reglive, _R1);
|
|
# endif
|
|
#endif
|
|
#if defined(JIT_FRET)
|
|
jit_regset_setbit(&_jitc->reglive, JIT_FRET);
|
|
#endif
|
|
break;
|
|
default:
|
|
value = jit_classify(node->code);
|
|
if (value & jit_cc_a0_reg) {
|
|
if (value & jit_cc_a0_rlh) {
|
|
if (!(node->u.q.l & jit_regno_patch)) {
|
|
if (value & jit_cc_a0_chg) {
|
|
jit_regset_clrbit(&_jitc->reglive, node->u.q.l);
|
|
jit_regset_setbit(&_jitc->regmask, node->u.q.l);
|
|
}
|
|
else
|
|
jit_regset_setbit(&_jitc->reglive, node->u.q.l);
|
|
}
|
|
if (!(node->u.q.h & jit_regno_patch)) {
|
|
if (value & jit_cc_a0_chg) {
|
|
jit_regset_clrbit(&_jitc->reglive, node->u.q.h);
|
|
jit_regset_setbit(&_jitc->regmask, node->u.q.h);
|
|
}
|
|
else
|
|
jit_regset_setbit(&_jitc->reglive, node->u.q.h);
|
|
}
|
|
}
|
|
else {
|
|
if (!(node->u.w & jit_regno_patch)) {
|
|
if (value & jit_cc_a0_chg) {
|
|
jit_regset_clrbit(&_jitc->reglive, node->u.w);
|
|
jit_regset_setbit(&_jitc->regmask, node->u.w);
|
|
}
|
|
else
|
|
jit_regset_setbit(&_jitc->reglive, node->u.w);
|
|
}
|
|
}
|
|
}
|
|
if ((value & jit_cc_a1_reg) && !(node->v.w & jit_regno_patch)) {
|
|
if (value & jit_cc_a1_chg) {
|
|
jit_regset_clrbit(&_jitc->reglive, node->v.w);
|
|
jit_regset_setbit(&_jitc->regmask, node->v.w);
|
|
}
|
|
else
|
|
jit_regset_setbit(&_jitc->reglive, node->v.w);
|
|
}
|
|
if ((value & jit_cc_a2_reg) && !(node->w.w & jit_regno_patch))
|
|
jit_regset_setbit(&_jitc->reglive, node->w.w);
|
|
if (jit_regset_set_p(&_jitc->regmask)) {
|
|
bmp_zero();
|
|
jit_update(node->next, &_jitc->reglive, &_jitc->regmask);
|
|
if (jit_regset_set_p(&_jitc->regmask)) {
|
|
/* any unresolved live state is considered as live */
|
|
jit_regset_ior(&_jitc->reglive,
|
|
&_jitc->reglive, &_jitc->regmask);
|
|
jit_regset_set_ui(&_jitc->regmask, 0);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
void
|
|
_jit_regarg_set(jit_state_t *_jit, jit_node_t *node, jit_int32_t value)
|
|
{
|
|
#if GET_JIT_SIZE
|
|
jit_size_prepare();
|
|
#endif
|
|
if (value & jit_cc_a0_reg) {
|
|
if (value & jit_cc_a0_rlh) {
|
|
jit_regset_setbit(&_jitc->regarg, jit_regno(node->u.q.l));
|
|
jit_regset_setbit(&_jitc->regarg, jit_regno(node->u.q.h));
|
|
}
|
|
else
|
|
jit_regset_setbit(&_jitc->regarg, jit_regno(node->u.w));
|
|
}
|
|
if (value & jit_cc_a1_reg)
|
|
jit_regset_setbit(&_jitc->regarg, jit_regno(node->v.w));
|
|
if (value & jit_cc_a2_reg)
|
|
jit_regset_setbit(&_jitc->regarg, jit_regno(node->w.w));
|
|
}
|
|
|
|
void
|
|
_jit_regarg_clr(jit_state_t *_jit, jit_node_t *node, jit_int32_t value)
|
|
{
|
|
#if GET_JIT_SIZE
|
|
jit_size_collect(node);
|
|
#endif
|
|
if (value & jit_cc_a0_reg) {
|
|
if (value & jit_cc_a0_rlh) {
|
|
jit_regset_clrbit(&_jitc->regarg, jit_regno(node->u.q.l));
|
|
jit_regset_clrbit(&_jitc->regarg, jit_regno(node->u.q.h));
|
|
}
|
|
else
|
|
jit_regset_clrbit(&_jitc->regarg, jit_regno(node->u.w));
|
|
}
|
|
if (value & jit_cc_a1_reg)
|
|
jit_regset_clrbit(&_jitc->regarg, jit_regno(node->v.w));
|
|
if (value & jit_cc_a2_reg)
|
|
jit_regset_clrbit(&_jitc->regarg, jit_regno(node->w.w));
|
|
}
|
|
|
|
void
|
|
_jit_realize(jit_state_t *_jit)
|
|
{
|
|
assert(!_jitc->realize);
|
|
if (_jitc->function)
|
|
jit_epilog();
|
|
jit_optimize();
|
|
_jitc->realize = 1;
|
|
|
|
/* ensure it is aligned */
|
|
_jitc->data.offset = (_jitc->data.offset + 7) & -8;
|
|
|
|
#if GET_JIT_SIZE
|
|
/* Heuristic to guess code buffer size */
|
|
_jitc->mult = 4;
|
|
_jit->code.length = _jitc->pool.length * 1024 * _jitc->mult;
|
|
#else
|
|
_jit->code.length = jit_get_size();
|
|
#endif
|
|
}
|
|
|
|
void
|
|
_jit_dataset(jit_state_t *_jit)
|
|
{
|
|
jit_uint8_t *ptr;
|
|
jit_node_t *node;
|
|
jit_word_t offset;
|
|
#if defined(__sgi)
|
|
int mmap_fd;
|
|
#endif
|
|
|
|
assert(!_jitc->dataset);
|
|
if (!_jit->user_data) {
|
|
|
|
/* create read only data buffer */
|
|
_jit->data.length = (_jitc->data.offset +
|
|
/* reserve space for annotations */
|
|
_jitc->note.size + 4095) & -4096;
|
|
#if defined(__sgi)
|
|
mmap_fd = open("/dev/zero", O_RDWR);
|
|
#endif
|
|
_jit->data.ptr = mmap(NULL, _jit->data.length,
|
|
PROT_READ | PROT_WRITE,
|
|
MAP_PRIVATE | MAP_ANON, mmap_fd, 0);
|
|
assert(_jit->data.ptr != MAP_FAILED);
|
|
#if defined(__sgi)
|
|
close(mmap_fd);
|
|
#endif
|
|
}
|
|
|
|
if (!_jitc->no_data)
|
|
jit_memcpy(_jit->data.ptr, _jitc->data.ptr, _jitc->data.offset);
|
|
|
|
if (_jitc->no_note) {
|
|
/* Space for one note is always allocated, so revert it here
|
|
* if after jit_new_state was called, it is also requested to
|
|
* not generate annotation information */
|
|
_jit->note.length = 0;
|
|
_jitc->note.size = 0;
|
|
}
|
|
else {
|
|
_jitc->note.base = _jit->data.ptr;
|
|
if (!_jitc->no_data)
|
|
_jitc->note.base += _jitc->data.offset;
|
|
memset(_jitc->note.base, 0, _jitc->note.size);
|
|
}
|
|
|
|
if (_jit->user_data)
|
|
/* Need the temporary hashed data until jit_emit is finished */
|
|
ptr = _jitc->no_data ? _jitc->data.ptr : _jit->data.ptr;
|
|
else {
|
|
ptr = _jit->data.ptr;
|
|
/* Temporary hashed data no longer required */
|
|
jit_free((jit_pointer_t *)&_jitc->data.ptr);
|
|
}
|
|
|
|
for (offset = 0; offset < _jitc->data.size; offset++) {
|
|
for (node = _jitc->data.table[offset]; node; node = node->next) {
|
|
node->flag |= jit_flag_patch;
|
|
node->u.w = (jit_word_t)(ptr + node->u.w);
|
|
}
|
|
}
|
|
|
|
_jitc->dataset = 1;
|
|
}
|
|
|
|
jit_pointer_t
|
|
_jit_get_code(jit_state_t *_jit, jit_word_t *length)
|
|
{
|
|
assert(_jitc->realize);
|
|
if (length) {
|
|
if (_jitc->done)
|
|
/* If code already generated, return exact size of code */
|
|
*length = _jit->pc.uc - _jit->code.ptr;
|
|
else
|
|
/* Else return current size of the code buffer */
|
|
*length = _jit->code.length;
|
|
}
|
|
|
|
return (_jit->code.ptr);
|
|
}
|
|
|
|
void
|
|
_jit_set_code(jit_state_t *_jit, jit_pointer_t ptr, jit_word_t length)
|
|
{
|
|
assert(_jitc->realize);
|
|
_jit->code.ptr = ptr;
|
|
_jit->code.length = length;
|
|
_jit->user_code = 1;
|
|
}
|
|
|
|
jit_pointer_t
|
|
_jit_get_data(jit_state_t *_jit, jit_word_t *data_size, jit_word_t *note_size)
|
|
{
|
|
assert(_jitc->realize);
|
|
if (data_size)
|
|
*data_size = _jitc->data.offset;
|
|
if (note_size)
|
|
*note_size = _jitc->note.size;
|
|
return (_jit->data.ptr);
|
|
}
|
|
|
|
void
|
|
_jit_set_data(jit_state_t *_jit, jit_pointer_t ptr,
|
|
jit_word_t length, jit_word_t flags)
|
|
{
|
|
assert(_jitc->realize);
|
|
if (flags & JIT_DISABLE_DATA)
|
|
_jitc->no_data = 1;
|
|
else
|
|
assert(length >= _jitc->data.offset);
|
|
if (flags & JIT_DISABLE_NOTE)
|
|
_jitc->no_note = 1;
|
|
else {
|
|
if (flags & JIT_DISABLE_DATA)
|
|
assert(length >= _jitc->note.size);
|
|
else
|
|
assert(length >= _jitc->data.offset + _jitc->note.size);
|
|
}
|
|
_jit->data.ptr = ptr;
|
|
_jit->data.length = length;
|
|
_jit->user_data = 1;
|
|
}
|
|
|
|
jit_pointer_t
|
|
_jit_emit(jit_state_t *_jit)
|
|
{
|
|
jit_pointer_t code;
|
|
jit_node_t *node;
|
|
size_t length;
|
|
int result;
|
|
#if defined(__sgi)
|
|
int mmap_fd;
|
|
#endif
|
|
|
|
if (!_jitc->realize)
|
|
jit_realize();
|
|
|
|
if (!_jitc->dataset)
|
|
jit_dataset();
|
|
|
|
_jitc->emit = 1;
|
|
|
|
if (!_jit->user_code) {
|
|
#if defined(__sgi)
|
|
mmap_fd = open("/dev/zero", O_RDWR);
|
|
#endif
|
|
_jit->code.ptr = mmap(NULL, _jit->code.length,
|
|
PROT_EXEC | PROT_READ | PROT_WRITE,
|
|
MAP_PRIVATE | MAP_ANON, mmap_fd, 0);
|
|
assert(_jit->code.ptr != MAP_FAILED);
|
|
}
|
|
_jitc->code.end = _jit->code.ptr + _jit->code.length -
|
|
jit_get_max_instr();
|
|
_jit->pc.uc = _jit->code.ptr;
|
|
|
|
for (;;) {
|
|
if ((code = emit_code()) == NULL) {
|
|
_jitc->patches.offset = 0;
|
|
for (node = _jitc->head; node; node = node->next) {
|
|
if (node->link &&
|
|
(node->code == jit_code_label ||
|
|
node->code == jit_code_epilog))
|
|
node->flag &= ~jit_flag_patch;
|
|
}
|
|
if (_jit->user_code)
|
|
goto fail;
|
|
#if GET_JIT_SIZE
|
|
++_jitc->mult;
|
|
length = _jitc->pool.length * 1024 * _jitc->mult;
|
|
#else
|
|
/* Should only happen on very special cases */
|
|
length = _jit->code.length + 4096;
|
|
#endif
|
|
|
|
#if !HAVE_MREMAP
|
|
munmap(_jit->code.ptr, _jit->code.length);
|
|
#endif
|
|
|
|
#if HAVE_MREMAP
|
|
# if __NetBSD__
|
|
_jit->code.ptr = mremap(_jit->code.ptr, _jit->code.length,
|
|
_jit->code.ptr, length, 0);
|
|
# else
|
|
_jit->code.ptr = mremap(_jit->code.ptr, _jit->code.length,
|
|
length, MREMAP_MAYMOVE, NULL);
|
|
# endif
|
|
#else
|
|
_jit->code.ptr = mmap(NULL, length,
|
|
PROT_EXEC | PROT_READ | PROT_WRITE,
|
|
MAP_PRIVATE | MAP_ANON, mmap_fd, 0);
|
|
#endif
|
|
|
|
assert(_jit->code.ptr != MAP_FAILED);
|
|
_jit->code.length = length;
|
|
_jitc->code.end = _jit->code.ptr + _jit->code.length -
|
|
jit_get_max_instr();
|
|
_jit->pc.uc = _jit->code.ptr;
|
|
}
|
|
else
|
|
break;
|
|
}
|
|
|
|
#if defined(__sgi)
|
|
if (!_jit->user_code)
|
|
close(mmap_fd);
|
|
#endif
|
|
|
|
_jitc->done = 1;
|
|
if (!_jitc->no_note)
|
|
jit_annotate();
|
|
|
|
if (_jit->user_data)
|
|
jit_free((jit_pointer_t *)&_jitc->data.ptr);
|
|
else {
|
|
result = mprotect(_jit->data.ptr, _jit->data.length, PROT_READ);
|
|
assert(result == 0);
|
|
}
|
|
if (!_jit->user_code) {
|
|
result = mprotect(_jit->code.ptr, _jit->code.length,
|
|
PROT_READ | PROT_EXEC);
|
|
assert(result == 0);
|
|
}
|
|
|
|
return (_jit->code.ptr);
|
|
fail:
|
|
return (NULL);
|
|
}
|
|
|
|
void
|
|
_jit_frame(jit_state_t *_jit, jit_int32_t frame)
|
|
{
|
|
jit_trampoline(frame, 1);
|
|
}
|
|
|
|
void
|
|
_jit_tramp(jit_state_t *_jit, jit_int32_t frame)
|
|
{
|
|
jit_trampoline(frame, 0);
|
|
}
|
|
|
|
void
|
|
_jit_trampoline(jit_state_t *_jit, jit_int32_t frame, jit_bool_t prolog)
|
|
{
|
|
jit_int32_t regno;
|
|
|
|
/* Must be called after prolog, actually, just to simplify
|
|
* tests and know there is a current function and that
|
|
* _jitc->function->self.aoff is at the before any alloca value */
|
|
assert(_jitc->tail && _jitc->tail->code == jit_code_prolog);
|
|
|
|
/* + 24 for 3 possible spilled temporaries (that could be a double) */
|
|
frame += 24;
|
|
#if defined(__hppa__)
|
|
frame += _jitc->function->self.aoff;
|
|
#else
|
|
frame -= _jitc->function->self.aoff;
|
|
#endif
|
|
_jitc->function->frame = frame;
|
|
if (prolog)
|
|
_jitc->function->define_frame = 1;
|
|
else
|
|
_jitc->function->assume_frame = 1;
|
|
for (regno = 0; regno < _jitc->reglen; regno++)
|
|
if (jit_class(_rvs[regno].spec) & jit_class_sav)
|
|
jit_regset_setbit(&_jitc->function->regset, regno);
|
|
}
|
|
|
|
/* Compute initial reglive and regmask set values of a basic block.
|
|
* reglive is the set of known live registers
|
|
* regmask is the set of registers not referenced in the block
|
|
*/
|
|
static void
|
|
_jit_setup(jit_state_t *_jit, jit_block_t *block)
|
|
{
|
|
#define reglive block->reglive
|
|
#define regmask block->regmask
|
|
jit_node_t *node;
|
|
jit_bool_t live;
|
|
jit_bool_t jump;
|
|
unsigned long value;
|
|
|
|
jump = 0;
|
|
jit_regset_set_mask(®mask, _jitc->reglen);
|
|
for (node = block->label->next; node; node = node->next) {
|
|
switch (node->code) {
|
|
case jit_code_label: case jit_code_prolog:
|
|
case jit_code_epilog:
|
|
return;
|
|
default:
|
|
value = jit_classify(node->code);
|
|
if (value & jit_cc_a0_reg) {
|
|
live = !(value & jit_cc_a0_chg);
|
|
if (value & jit_cc_a0_rlh) {
|
|
if (!(node->u.q.l & jit_regno_patch) &&
|
|
jit_regset_tstbit(®mask, node->u.q.l)) {
|
|
if (live || !jump)
|
|
jit_regset_clrbit(®mask, node->u.q.l);
|
|
if (live)
|
|
jit_regset_setbit(®live, node->u.q.l);
|
|
}
|
|
if (!(node->u.q.h & jit_regno_patch) &&
|
|
jit_regset_tstbit(®mask, node->u.q.h)) {
|
|
if (live || !jump)
|
|
jit_regset_clrbit(®mask, node->u.q.h);
|
|
if (live)
|
|
jit_regset_setbit(®live, node->u.q.h);
|
|
}
|
|
}
|
|
else {
|
|
if (!(node->u.w & jit_regno_patch) &&
|
|
jit_regset_tstbit(®mask, node->u.w)) {
|
|
if (live || !jump)
|
|
jit_regset_clrbit(®mask, node->u.w);
|
|
if (live)
|
|
jit_regset_setbit(®live, node->u.w);
|
|
}
|
|
}
|
|
}
|
|
if ((value & jit_cc_a1_reg) &&
|
|
!(node->v.w & jit_regno_patch) &&
|
|
jit_regset_tstbit(®mask, node->v.w)) {
|
|
live = !(value & jit_cc_a1_chg);
|
|
if (live || !jump)
|
|
jit_regset_clrbit(®mask, node->v.w);
|
|
if (live)
|
|
jit_regset_setbit(®live, node->v.w);
|
|
}
|
|
if ((value & jit_cc_a2_reg) &&
|
|
!(node->w.w & jit_regno_patch) &&
|
|
jit_regset_tstbit(®mask, node->w.w)) {
|
|
jit_regset_clrbit(®mask, node->w.w);
|
|
jit_regset_setbit(®live, node->w.w);
|
|
}
|
|
if (value & jit_cc_a0_jmp)
|
|
jump = 1;
|
|
break;
|
|
}
|
|
}
|
|
#undef regmask
|
|
#undef reglive
|
|
}
|
|
|
|
/* Remove bit of mask argument based on instructions arguments up to end
|
|
* of code or finding a basic block boundary, if a value is used as argument,
|
|
* also set the live bit to know value cannot be clobbered; if value is
|
|
* modified, just remove it from the mask as if it not already in the live
|
|
* bitmask, then the value is dead.
|
|
*/
|
|
static void
|
|
_jit_update(jit_state_t *_jit, jit_node_t *node,
|
|
jit_regset_t *live, jit_regset_t *mask)
|
|
{
|
|
jit_int32_t spec;
|
|
jit_int32_t regno;
|
|
jit_regset_t ztmp;
|
|
jit_regset_t zmask;
|
|
unsigned long value;
|
|
jit_block_t *block;
|
|
jit_node_t *label;
|
|
|
|
for (; node; node = node->next) {
|
|
restart:
|
|
if (jit_regset_set_p(mask) == 0)
|
|
break;
|
|
switch (node->code) {
|
|
case jit_code_label:
|
|
block = _jitc->blocks.ptr + node->v.w;
|
|
if (bmp_tst(node->v.w))
|
|
return;
|
|
bmp_set(node->v.w);
|
|
jit_regset_and(&ztmp, mask, &block->reglive);
|
|
if (jit_regset_set_p(&ztmp)) {
|
|
jit_regset_ior(live, live, &ztmp);
|
|
jit_regset_com(&ztmp, &ztmp);
|
|
jit_regset_and(mask, mask, &ztmp);
|
|
}
|
|
break;
|
|
case jit_code_prolog:
|
|
jit_regset_set_ui(mask, 0);
|
|
return;
|
|
case jit_code_epilog:
|
|
jit_regset_set_ui(mask, 0);
|
|
#if defined(JIT_RET)
|
|
/* On some backends it may be required to allocate one
|
|
* or more registers to jump from a jit_ret* to the
|
|
* epilog label.
|
|
* Because currently there is no type information,
|
|
* assume JIT_RET and JIT_FRET are live in the epilog.
|
|
* Only JIT_RET really should be marked as live, to
|
|
* prevent it being allocated, usually in the jump to
|
|
* the epilog, but also mark JIT_FRET as live for the
|
|
* sake of correctness. */
|
|
jit_regset_setbit(live, JIT_RET);
|
|
#endif
|
|
#if defined(JIT_FRET)
|
|
jit_regset_setbit(live, JIT_FRET);
|
|
#endif
|
|
return;
|
|
case jit_code_callr:
|
|
value = jit_regno(node->u.w);
|
|
if (!(node->u.w & jit_regno_patch)) {
|
|
if (jit_regset_tstbit(mask, value)) {
|
|
jit_regset_clrbit(mask, value);
|
|
jit_regset_setbit(live, value);
|
|
}
|
|
}
|
|
case jit_code_calli:
|
|
#if defined(JIT_RET)
|
|
if (jit_regset_tstbit(mask, JIT_RET)) {
|
|
jit_regset_setbit(live, JIT_RET);
|
|
jit_regset_clrbit(mask, JIT_RET);
|
|
}
|
|
# if __arm__
|
|
if (jit_regset_tstbit(mask, _R1)) {
|
|
jit_regset_setbit(live, _R1);
|
|
jit_regset_clrbit(mask, _R1);
|
|
}
|
|
# endif
|
|
#endif
|
|
#if defined(JIT_FRET)
|
|
if (jit_regset_tstbit(mask, JIT_FRET)) {
|
|
jit_regset_setbit(live, JIT_FRET);
|
|
jit_regset_clrbit(mask, JIT_FRET);
|
|
}
|
|
#endif
|
|
for (value = 0; value < _jitc->reglen; ++value) {
|
|
value = jit_regset_scan1(mask, value);
|
|
if (value >= _jitc->reglen)
|
|
break;
|
|
spec = jit_class(_rvs[value].spec);
|
|
if (!(spec & jit_class_sav))
|
|
jit_regset_clrbit(mask, value);
|
|
if ((spec & jit_class_arg) && jit_regarg_p(node, value))
|
|
jit_regset_setbit(live, value);
|
|
}
|
|
break;
|
|
default:
|
|
value = jit_classify(node->code);
|
|
if (value & jit_cc_a2_reg) {
|
|
if (!(node->w.w & jit_regno_patch)) {
|
|
if (jit_regset_tstbit(mask, node->w.w)) {
|
|
jit_regset_clrbit(mask, node->w.w);
|
|
jit_regset_setbit(live, node->w.w);
|
|
}
|
|
}
|
|
}
|
|
if (value & jit_cc_a1_reg) {
|
|
if (!(node->v.w & jit_regno_patch)) {
|
|
if (jit_regset_tstbit(mask, node->v.w)) {
|
|
jit_regset_clrbit(mask, node->v.w);
|
|
if (!(value & jit_cc_a1_chg))
|
|
jit_regset_setbit(live, node->v.w);
|
|
}
|
|
}
|
|
}
|
|
if (value & jit_cc_a0_reg) {
|
|
if (value & jit_cc_a0_rlh) {
|
|
if (!(node->u.q.l & jit_regno_patch)) {
|
|
if (jit_regset_tstbit(mask, node->u.q.l)) {
|
|
jit_regset_clrbit(mask, node->u.q.l);
|
|
if (!(value & jit_cc_a0_chg))
|
|
jit_regset_setbit(live, node->u.q.l);
|
|
}
|
|
}
|
|
if (!(node->u.q.h & jit_regno_patch)) {
|
|
if (jit_regset_tstbit(mask, node->u.q.h)) {
|
|
jit_regset_clrbit(mask, node->u.q.h);
|
|
if (!(value & jit_cc_a0_chg))
|
|
jit_regset_setbit(live, node->u.q.h);
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
if (!(node->u.w & jit_regno_patch)) {
|
|
if (jit_regset_tstbit(mask, node->u.w)) {
|
|
jit_regset_clrbit(mask, node->u.w);
|
|
if (!(value & jit_cc_a0_chg))
|
|
jit_regset_setbit(live, node->u.w);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (value & jit_cc_a0_jmp) {
|
|
if (node->flag & jit_flag_node) {
|
|
label = node->u.n;
|
|
if (node->code == jit_code_jmpi) {
|
|
node = label;
|
|
goto restart;
|
|
}
|
|
block = _jitc->blocks.ptr + label->v.w;
|
|
jit_regset_and(&ztmp, mask, &block->reglive);
|
|
if (jit_regset_set_p(&ztmp)) {
|
|
jit_regset_ior(live, live, &ztmp);
|
|
jit_regset_com(&ztmp, &ztmp);
|
|
jit_regset_and(mask, mask, &ztmp);
|
|
}
|
|
if (bmp_tst(label->v.w))
|
|
continue;
|
|
bmp_set(label->v.w);
|
|
if (jit_regset_set_p(mask) == 0)
|
|
return;
|
|
/* restore mask if branch is conditional */
|
|
jit_regset_set(&zmask, mask);
|
|
jit_update(block->label->next, live, &zmask);
|
|
jit_regset_xor(&ztmp, &zmask, mask);
|
|
/* remove known live registers from mask */
|
|
if (jit_regset_set_p(&ztmp)) {
|
|
jit_regset_and(&ztmp, &ztmp, live);
|
|
jit_regset_com(&ztmp, &ztmp);
|
|
jit_regset_and(mask, mask, &ztmp);
|
|
}
|
|
continue;
|
|
}
|
|
/* Should not really mark as live all registers in unknown
|
|
* state if using jit_jmpr(), or jit_jmpi(absolute_address)
|
|
* because that would leave the register allocator with
|
|
* no options for "nospill" temporaries (other temporaries
|
|
* also benefit from not needing to spill/reload), so, the
|
|
* user must ensure to either spill/reload, or only leave
|
|
* live values on registers that are advertised as
|
|
* callee save (as per jit_callee_save_p); on most targets
|
|
* these are the JIT_Vn registers. */
|
|
for (regno = 0; regno < _jitc->reglen; regno++) {
|
|
spec = jit_class(_rvs[regno].spec);
|
|
if (jit_regset_tstbit(mask, regno) &&
|
|
(spec & (jit_class_gpr|jit_class_fpr)) &&
|
|
!(spec & jit_class_sav))
|
|
jit_regset_clrbit(mask, regno);
|
|
}
|
|
/* assume value is live due to jump to unknown location */
|
|
jit_regset_ior(live, live, mask);
|
|
jit_regset_set_ui(mask, 0);
|
|
return;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
_thread_jumps(jit_state_t *_jit)
|
|
{
|
|
jit_node_t *prev;
|
|
jit_node_t *node;
|
|
jit_node_t *next;
|
|
jit_int32_t mask;
|
|
|
|
for (prev = node = _jitc->head; node;) {
|
|
next = node->next;
|
|
switch (node->code) {
|
|
case jit_code_jmpi:
|
|
if (redundant_jump(prev, node)) {
|
|
node = prev;
|
|
continue;
|
|
}
|
|
if (shortcut_jump(prev, node))
|
|
continue;
|
|
break;
|
|
case jit_code_jmpr:
|
|
case jit_code_callr: case jit_code_calli:
|
|
/* non optimizable jump like code */
|
|
break;
|
|
default:
|
|
mask = jit_classify(node->code);
|
|
if (mask & jit_cc_a0_jmp) {
|
|
if (reverse_jump(prev, node) ||
|
|
shortcut_jump(prev, node))
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
prev = node;
|
|
node = next;
|
|
}
|
|
}
|
|
|
|
static void
|
|
_sequential_labels(jit_state_t *_jit)
|
|
{
|
|
jit_node_t *jump;
|
|
jit_node_t *link;
|
|
jit_node_t *prev;
|
|
jit_node_t *next;
|
|
jit_node_t *node;
|
|
|
|
for (prev = node = _jitc->head; node; node = next) {
|
|
next = node->next;
|
|
if (node->code == jit_code_label) {
|
|
if (!node->flag) {
|
|
if (!node->link) {
|
|
del_label(prev, node);
|
|
continue;
|
|
}
|
|
if (prev != node && prev->code == jit_code_label) {
|
|
if ((jump = node->link)) {
|
|
for (; jump; jump = link) {
|
|
link = jump->link;
|
|
jump->u.n = prev;
|
|
jump->link = prev->link;
|
|
prev->link = jump;
|
|
}
|
|
node->link = NULL;
|
|
}
|
|
del_label(prev, node);
|
|
continue;
|
|
}
|
|
}
|
|
if (next && next->code == jit_code_label && !next->flag) {
|
|
if ((jump = next->link)) {
|
|
for (; jump; jump = link) {
|
|
link = jump->link;
|
|
jump->u.n = node;
|
|
jump->link = node->link;
|
|
node->link = jump;
|
|
}
|
|
next->link = NULL;
|
|
}
|
|
del_label(node, next);
|
|
next = node->next;
|
|
continue;
|
|
}
|
|
}
|
|
prev = node;
|
|
}
|
|
}
|
|
|
|
static jit_bool_t
|
|
_shortcut_jump(jit_state_t *_jit, jit_node_t *prev, jit_node_t *node)
|
|
{
|
|
jit_bool_t cond;
|
|
jit_node_t *jump;
|
|
jit_node_t *next;
|
|
jit_node_t *temp;
|
|
|
|
if (!(node->flag & jit_flag_node))
|
|
return (0);
|
|
assert(node->code != jit_code_jmpr);
|
|
cond = node->code != jit_code_jmpi;
|
|
jump = node->u.n;
|
|
for (next = jump->next; next; next = next->next) {
|
|
switch (next->code) {
|
|
case jit_code_jmpi:
|
|
if (!(next->flag & jit_flag_node))
|
|
return (0);
|
|
if (jump->link == node)
|
|
jump->link = node->link;
|
|
else {
|
|
for (temp = jump->link;
|
|
temp->link != node;
|
|
temp = temp->link)
|
|
assert(temp != NULL);
|
|
temp->link = node->link;
|
|
}
|
|
jump = next->u.n;
|
|
node->u.n = jump;
|
|
node->link = jump->link;
|
|
jump->link = node;
|
|
return (1);
|
|
case jit_code_jmpr:
|
|
if (cond)
|
|
return (0);
|
|
node->code = jit_code_jmpr;
|
|
node->u.w = next->u.w;
|
|
node->link = NULL;
|
|
node->flag &= ~jit_flag_node;
|
|
return (1);
|
|
case jit_code_note: case jit_code_label:
|
|
break;
|
|
default:
|
|
return (0);
|
|
}
|
|
}
|
|
return (0);
|
|
}
|
|
|
|
static jit_bool_t
|
|
_redundant_jump(jit_state_t *_jit, jit_node_t *prev, jit_node_t *node)
|
|
{
|
|
jit_node_t *local_prev;
|
|
jit_node_t *local_next;
|
|
|
|
if (!(node->flag & jit_flag_node))
|
|
return (0);
|
|
for (local_prev = node, local_next = node->next;
|
|
local_next;
|
|
local_prev = local_next, local_next = local_next->next) {
|
|
|
|
switch (local_next->code) {
|
|
case jit_code_label: case jit_code_epilog:
|
|
if (node->u.n == local_next) {
|
|
if (local_next->link == node)
|
|
local_next->link = node->link;
|
|
else {
|
|
for (local_prev = local_next->link;
|
|
local_prev->link != node;
|
|
local_prev = local_prev->link)
|
|
assert(local_prev != NULL);
|
|
local_prev->link = node->link;
|
|
}
|
|
del_node(prev, node);
|
|
return (1);
|
|
}
|
|
break;
|
|
case jit_code_name: case jit_code_note:
|
|
case jit_code_align:
|
|
break;
|
|
default:
|
|
return (0);
|
|
}
|
|
}
|
|
return (0);
|
|
}
|
|
|
|
static jit_code_t
|
|
reverse_jump_code(jit_code_t code)
|
|
{
|
|
switch (code) {
|
|
case jit_code_bltr: return (jit_code_bger);
|
|
case jit_code_blti: return (jit_code_bgei);
|
|
case jit_code_bltr_u: return (jit_code_bger_u);
|
|
case jit_code_blti_u: return (jit_code_bgei_u);
|
|
case jit_code_bler: return (jit_code_bgtr);
|
|
case jit_code_blei: return (jit_code_bgti);
|
|
case jit_code_bler_u: return (jit_code_bgtr_u);
|
|
case jit_code_blei_u: return (jit_code_bgti_u);
|
|
case jit_code_beqr: return (jit_code_bner);
|
|
case jit_code_beqi: return (jit_code_bnei);
|
|
case jit_code_bger: return (jit_code_bltr);
|
|
case jit_code_bgei: return (jit_code_blti);
|
|
case jit_code_bger_u: return (jit_code_bltr_u);
|
|
case jit_code_bgei_u: return (jit_code_blti_u);
|
|
case jit_code_bgtr: return (jit_code_bler);
|
|
case jit_code_bgti: return (jit_code_blei);
|
|
case jit_code_bgtr_u: return (jit_code_bler_u);
|
|
case jit_code_bgti_u: return (jit_code_blei_u);
|
|
case jit_code_bner: return (jit_code_beqr);
|
|
case jit_code_bnei: return (jit_code_beqi);
|
|
case jit_code_bmsr: return (jit_code_bmcr);
|
|
case jit_code_bmsi: return (jit_code_bmci);
|
|
case jit_code_bmcr: return (jit_code_bmsr);
|
|
case jit_code_bmci: return (jit_code_bmsi);
|
|
case jit_code_bltr_f: return (jit_code_bunger_f);
|
|
case jit_code_blti_f: return (jit_code_bungei_f);
|
|
case jit_code_bler_f: return (jit_code_bungtr_f);
|
|
case jit_code_blei_f: return (jit_code_bungti_f);
|
|
|
|
case jit_code_beqr_f: return (jit_code_bner_f);
|
|
case jit_code_beqi_f: return (jit_code_bnei_f);
|
|
|
|
case jit_code_bger_f: return (jit_code_bunltr_f);
|
|
case jit_code_bgei_f: return (jit_code_bunlti_f);
|
|
case jit_code_bgtr_f: return (jit_code_bunler_f);
|
|
case jit_code_bgti_f: return (jit_code_bunlei_f);
|
|
|
|
case jit_code_bner_f: return (jit_code_beqr_f);
|
|
case jit_code_bnei_f: return (jit_code_beqr_f);
|
|
|
|
case jit_code_bunltr_f: return (jit_code_bger_f);
|
|
case jit_code_bunlti_f: return (jit_code_bgei_f);
|
|
case jit_code_bunler_f: return (jit_code_bgtr_f);
|
|
case jit_code_bunlei_f: return (jit_code_bgti_f);
|
|
|
|
case jit_code_buneqr_f: return (jit_code_bltgtr_f);
|
|
case jit_code_buneqi_f: return (jit_code_bltgti_f);
|
|
|
|
case jit_code_bunger_f: return (jit_code_bltr_f);
|
|
case jit_code_bungei_f: return (jit_code_blti_f);
|
|
case jit_code_bungtr_f: return (jit_code_bler_f);
|
|
case jit_code_bungti_f: return (jit_code_blei_f);
|
|
|
|
case jit_code_bltgtr_f: return (jit_code_buneqr_f);
|
|
case jit_code_bltgti_f: return (jit_code_buneqi_f);
|
|
|
|
case jit_code_bordr_f: return (jit_code_bunordr_f);
|
|
case jit_code_bordi_f: return (jit_code_bunordi_f);
|
|
case jit_code_bunordr_f:return (jit_code_bordr_f);
|
|
case jit_code_bunordi_f:return (jit_code_bordi_f);
|
|
case jit_code_bltr_d: return (jit_code_bunger_d);
|
|
case jit_code_blti_d: return (jit_code_bungei_d);
|
|
case jit_code_bler_d: return (jit_code_bungtr_d);
|
|
case jit_code_blei_d: return (jit_code_bungti_d);
|
|
|
|
case jit_code_beqr_d: return (jit_code_bner_d);
|
|
case jit_code_beqi_d: return (jit_code_bnei_d);
|
|
|
|
case jit_code_bger_d: return (jit_code_bunltr_d);
|
|
case jit_code_bgei_d: return (jit_code_bunlti_d);
|
|
case jit_code_bgtr_d: return (jit_code_bunler_d);
|
|
case jit_code_bgti_d: return (jit_code_bunlei_d);
|
|
|
|
case jit_code_bner_d: return (jit_code_beqr_d);
|
|
case jit_code_bnei_d: return (jit_code_beqi_d);
|
|
|
|
case jit_code_bunltr_d: return (jit_code_bger_d);
|
|
case jit_code_bunlti_d: return (jit_code_bgei_d);
|
|
case jit_code_bunler_d: return (jit_code_bgtr_d);
|
|
case jit_code_bunlei_d: return (jit_code_bgti_d);
|
|
|
|
case jit_code_buneqr_d: return (jit_code_bltgtr_d);
|
|
case jit_code_buneqi_d: return (jit_code_bltgti_d);
|
|
|
|
case jit_code_bunger_d: return (jit_code_bltr_d);
|
|
case jit_code_bungei_d: return (jit_code_blti_d);
|
|
case jit_code_bungtr_d: return (jit_code_bler_d);
|
|
case jit_code_bungti_d: return (jit_code_blei_d);
|
|
|
|
case jit_code_bltgtr_d: return (jit_code_buneqr_d);
|
|
case jit_code_bltgti_d: return (jit_code_buneqi_d);
|
|
|
|
case jit_code_bordr_d: return (jit_code_bunordr_d);
|
|
case jit_code_bordi_d: return (jit_code_bunordi_d);
|
|
case jit_code_bunordr_d:return (jit_code_bordr_d);
|
|
case jit_code_bunordi_d:return (jit_code_bordi_d);
|
|
case jit_code_boaddr: return (jit_code_bxaddr);
|
|
case jit_code_boaddi: return (jit_code_bxaddi);
|
|
case jit_code_boaddr_u: return (jit_code_bxaddr_u);
|
|
case jit_code_boaddi_u: return (jit_code_bxaddi_u);
|
|
case jit_code_bxaddr: return (jit_code_boaddr);
|
|
case jit_code_bxaddi: return (jit_code_boaddi);
|
|
case jit_code_bxaddr_u: return (jit_code_boaddr_u);
|
|
case jit_code_bxaddi_u: return (jit_code_boaddi_u);
|
|
case jit_code_bosubr: return (jit_code_bxsubr);
|
|
case jit_code_bosubi: return (jit_code_bxsubi);
|
|
case jit_code_bosubr_u: return (jit_code_bxsubr_u);
|
|
case jit_code_bosubi_u: return (jit_code_bxsubi_u);
|
|
case jit_code_bxsubr: return (jit_code_bosubr);
|
|
case jit_code_bxsubi: return (jit_code_bosubi);
|
|
case jit_code_bxsubr_u: return (jit_code_bosubr_u);
|
|
case jit_code_bxsubi_u: return (jit_code_bosubi_u);
|
|
default: abort(); /* invalid jump code */
|
|
}
|
|
}
|
|
|
|
/*
|
|
* change common pattern:
|
|
* <cond_jump L0> <jump L1> <label L0>
|
|
* into
|
|
* <reverse_cond_jump L1>
|
|
*/
|
|
static jit_bool_t
|
|
_reverse_jump(jit_state_t *_jit, jit_node_t *prev, jit_node_t *node)
|
|
{
|
|
jit_node_t *local_prev;
|
|
jit_node_t *local_next;
|
|
jit_node_t *local_jump;
|
|
|
|
if (!(node->flag & jit_flag_node))
|
|
return (0);
|
|
/* =><cond_jump L0> <jump L1> <label L0> */
|
|
local_next = node->next;
|
|
if (local_next->code != jit_code_jmpi ||
|
|
!(local_next->flag & jit_flag_node))
|
|
return (0);
|
|
/* <cond_jump L0> =><jump L1> <label L0> */
|
|
|
|
local_jump = local_next->u.n;
|
|
for (local_prev = local_next, local_next = local_next->next;
|
|
local_next;
|
|
local_prev = local_next, local_next = local_next->next) {
|
|
switch (local_next->code) {
|
|
case jit_code_label: case jit_code_epilog:
|
|
if (node->u.n == local_next) {
|
|
if (local_next->link == node)
|
|
local_next->link = node->link;
|
|
else {
|
|
for (local_prev = local_next->link;
|
|
local_prev->link != node;
|
|
local_prev = local_prev->link)
|
|
assert(local_prev != NULL);
|
|
local_prev->link = node->link;
|
|
}
|
|
del_node(node, node->next);
|
|
node->code = reverse_jump_code(node->code);
|
|
node->u.n = local_jump;
|
|
node->link = local_jump->link;
|
|
local_jump->link = node;
|
|
return (1);
|
|
}
|
|
break;
|
|
case jit_code_note:
|
|
break;
|
|
default:
|
|
return (0);
|
|
}
|
|
}
|
|
return (0);
|
|
}
|
|
|
|
static void
|
|
_redundant_store(jit_state_t *_jit, jit_node_t *node, jit_bool_t jump)
|
|
{
|
|
jit_node_t *iter;
|
|
jit_node_t *prev;
|
|
jit_word_t word;
|
|
jit_int32_t spec;
|
|
jit_int32_t regno;
|
|
|
|
if (jump) {
|
|
prev = node->u.n;
|
|
if (prev->code == jit_code_epilog)
|
|
return;
|
|
assert(prev->code == jit_code_label);
|
|
if ((prev->flag & jit_flag_head) || node->link || prev->link != node)
|
|
/* multiple sources */
|
|
return;
|
|
/* if there are sequential labels it will return below */
|
|
}
|
|
else
|
|
prev = node;
|
|
word = node->w.w;
|
|
regno = jit_regno(node->v.w);
|
|
for (iter = prev->next; iter; prev = iter, iter = iter->next) {
|
|
switch (iter->code) {
|
|
case jit_code_label: case jit_code_prolog:
|
|
case jit_code_epilog:
|
|
return;
|
|
case jit_code_movi:
|
|
if (regno == jit_regno(iter->u.w)) {
|
|
if (iter->flag || iter->v.w != word)
|
|
return;
|
|
del_node(prev, iter);
|
|
iter = prev;
|
|
}
|
|
break;
|
|
default:
|
|
spec = jit_classify(iter->code);
|
|
if (spec & jit_cc_a0_jmp)
|
|
return;
|
|
if ((spec & (jit_cc_a0_reg|jit_cc_a0_chg)) ==
|
|
(jit_cc_a0_reg|jit_cc_a0_chg)) {
|
|
if (spec & jit_cc_a0_rlh) {
|
|
if (regno == jit_regno(iter->u.q.l) ||
|
|
regno == jit_regno(iter->u.q.h))
|
|
return;
|
|
}
|
|
else {
|
|
if (regno == jit_regno(iter->u.w))
|
|
return;
|
|
}
|
|
}
|
|
if ((spec & (jit_cc_a1_reg|jit_cc_a1_chg)) ==
|
|
(jit_cc_a1_reg|jit_cc_a1_chg)) {
|
|
if (regno == jit_regno(iter->v.w))
|
|
return;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
static jit_bool_t
|
|
_simplify_movr(jit_state_t *_jit, jit_node_t *prev, jit_node_t *node,
|
|
jit_int32_t kind, jit_int32_t size)
|
|
{
|
|
jit_int32_t regno;
|
|
jit_int32_t right;
|
|
jit_value_t *value;
|
|
|
|
regno = jit_regno(node->u.w);
|
|
right = jit_regno(node->v.w);
|
|
value = _jitc->values + regno;
|
|
if ((value->kind == jit_kind_register &&
|
|
jit_regno(value->base.q.l) == right &&
|
|
value->base.q.h == _jitc->gen[right]) ||
|
|
(value->kind == kind && _jitc->values[right].kind == kind &&
|
|
memcmp(&value->base.w, &_jitc->values[right].base.w, size) == 0)) {
|
|
del_node(prev, node);
|
|
return (1);
|
|
}
|
|
if (_jitc->values[right].kind == jit_kind_word)
|
|
jit_memcpy(value, _jitc->values + right, sizeof(jit_value_t));
|
|
else {
|
|
value->kind = jit_kind_register;
|
|
value->base.q.l = right;
|
|
value->base.q.h = _jitc->gen[regno];
|
|
}
|
|
++_jitc->gen[regno];
|
|
|
|
return (0);
|
|
}
|
|
|
|
static jit_bool_t
|
|
_simplify_movi(jit_state_t *_jit, jit_node_t *prev, jit_node_t *node,
|
|
jit_int32_t kind, jit_int32_t size)
|
|
{
|
|
jit_value_t *value;
|
|
jit_int32_t spec;
|
|
jit_int32_t regno;
|
|
jit_int32_t offset;
|
|
|
|
regno = jit_regno(node->u.w);
|
|
value = _jitc->values + regno;
|
|
if (node->flag & jit_flag_node) {
|
|
/* set to undefined if value will be patched */
|
|
value->kind = 0;
|
|
++_jitc->gen[regno];
|
|
return (0);
|
|
}
|
|
if (value->kind == kind) {
|
|
if (memcmp(&node->v.w, &value->base.w, size) == 0) {
|
|
del_node(prev, node);
|
|
return (1);
|
|
}
|
|
spec = jit_class(_rvs[regno].spec);
|
|
if (kind == jit_kind_word)
|
|
spec &= jit_class_gpr;
|
|
else
|
|
spec &= (jit_class_xpr | jit_class_fpr);
|
|
for (offset = 0; offset < _jitc->reglen; offset++) {
|
|
if (_jitc->values[offset].kind == kind &&
|
|
memcmp(&node->v.w, &_jitc->values[offset].base.w, size) == 0 &&
|
|
(jit_class(_rvs[offset].spec) & spec) == spec) {
|
|
if (kind == jit_kind_word)
|
|
node->code = jit_code_movr;
|
|
else if (kind == jit_kind_float32)
|
|
node->code = jit_code_movr_f;
|
|
else
|
|
node->code = jit_code_movr_d;
|
|
node->v.w = offset;
|
|
jit_memcpy(value, _jitc->values + offset, sizeof(jit_value_t));
|
|
++_jitc->gen[regno];
|
|
return (0);
|
|
}
|
|
}
|
|
}
|
|
value->kind = kind;
|
|
jit_memcpy(&value->base.w, &node->v.w, size);
|
|
++_jitc->gen[regno];
|
|
|
|
return (0);
|
|
}
|
|
|
|
/* simple/safe redundandy test not checking if another register
|
|
* holds the same value
|
|
*/
|
|
static jit_bool_t
|
|
_simplify_ldxi(jit_state_t *_jit, jit_node_t *prev, jit_node_t *node)
|
|
{
|
|
jit_value_t *value;
|
|
jit_int32_t regno;
|
|
jit_int32_t right;
|
|
|
|
regno = jit_regno(node->u.w);
|
|
right = jit_regno(node->v.w);
|
|
value = _jitc->values + regno;
|
|
if (value->kind == jit_kind_code && value->code == node->code &&
|
|
value->base.q.l == right && value->base.q.h == _jitc->gen[right] &&
|
|
node->w.w == value->disp.w) {
|
|
del_node(prev, node);
|
|
return (1);
|
|
}
|
|
value->kind = jit_kind_code;
|
|
value->code = node->code;
|
|
value->base.q.l = right;
|
|
value->base.q.h = _jitc->gen[right];
|
|
value->disp.w = node->w.w;
|
|
++_jitc->gen[regno];
|
|
|
|
return (0);
|
|
}
|
|
|
|
static jit_bool_t
|
|
_simplify_stxi(jit_state_t *_jit, jit_node_t *prev, jit_node_t *node)
|
|
{
|
|
jit_value_t *value;
|
|
jit_int32_t regno;
|
|
jit_int32_t right;
|
|
jit_int32_t offset;
|
|
|
|
regno = jit_regno(node->w.w);
|
|
right = jit_regno(node->v.w);
|
|
value = _jitc->values + regno;
|
|
|
|
/* check for redundant store after load */
|
|
if (value->kind == jit_kind_code && value->code == node->code &&
|
|
value->base.q.l == right && value->base.q.h == _jitc->gen[right] &&
|
|
node->w.w == value->disp.w) {
|
|
del_node(prev, node);
|
|
return (1);
|
|
}
|
|
|
|
/* assume anything can alias, and invalidate tracked values */
|
|
for (offset = 0; offset < _jitc->reglen; offset++) {
|
|
if (_jitc->values[offset].kind == jit_kind_code) {
|
|
_jitc->values[offset].kind = 0;
|
|
++_jitc->gen[offset];
|
|
}
|
|
}
|
|
|
|
/* no multiple information, so, if set to a constant,
|
|
* prefer to keep that information */
|
|
if (value->kind == 0) {
|
|
value->kind = jit_kind_code;
|
|
switch (node->code) {
|
|
/* no information about signed/unsigned either */
|
|
case jit_code_stxi_c: value->code = jit_code_ldxi_c; break;
|
|
case jit_code_stxi_s: value->code = jit_code_ldxi_s; break;
|
|
case jit_code_stxi_i: value->code = jit_code_ldxi_i; break;
|
|
case jit_code_stxi_l: value->code = jit_code_ldxi_l; break;
|
|
case jit_code_stxi_f: value->code = jit_code_ldxi_f; break;
|
|
case jit_code_stxi_d: value->code = jit_code_ldxi_d; break;
|
|
default: abort();
|
|
}
|
|
value->kind = jit_kind_code;
|
|
value->code = node->code;
|
|
value->base.q.l = right;
|
|
value->base.q.h = _jitc->gen[right];
|
|
value->disp.w = node->u.w;
|
|
}
|
|
|
|
return (0);
|
|
}
|
|
|
|
/* usually there should be only one store in the
|
|
* jit_get_reg/jit_unget_reg, but properly handle
|
|
* multiple ones by moving the save node */
|
|
static void
|
|
_simplify_spill(jit_state_t *_jit, jit_node_t *node, jit_int32_t regno)
|
|
{
|
|
jit_node_t *save;
|
|
jit_node_t *temp;
|
|
|
|
if ((temp = _jitc->spill[regno]) && (save = temp->next) != node) {
|
|
temp->next = save->next;
|
|
save->next = node->next;
|
|
node->next = save;
|
|
_jitc->spill[regno] = node;
|
|
}
|
|
}
|
|
|
|
/* checks for simple cases where a register is set more than
|
|
* once to the same value, and is a common pattern of calls
|
|
* to jit_pushargi and jit_pushargr
|
|
*/
|
|
static void
|
|
_simplify(jit_state_t *_jit)
|
|
{
|
|
jit_node_t *prev;
|
|
jit_node_t *node;
|
|
jit_node_t *next;
|
|
jit_int32_t info;
|
|
jit_int32_t regno;
|
|
jit_int32_t offset;
|
|
|
|
for (prev = NULL, node = _jitc->head; node; prev = node, node = next) {
|
|
next = node->next;
|
|
switch (node->code) {
|
|
case jit_code_label: case jit_code_prolog:
|
|
reset:
|
|
memset(_jitc->gen, 0, sizeof(jit_int32_t) * _jitc->reglen);
|
|
memset(_jitc->values, 0, sizeof(jit_value_t) * _jitc->reglen);
|
|
break;
|
|
case jit_code_save:
|
|
_jitc->spill[jit_regno(node->u.w)] = prev;
|
|
break;
|
|
case jit_code_load:
|
|
regno = jit_regno(node->u.w);
|
|
if (register_change_p(node->link->next, node, regno) !=
|
|
jit_reg_change) {
|
|
/* spill not required due to optimizing common
|
|
* redundancy case of calling jit_get_reg/jit_unget_reg
|
|
* and then setting the register to the value it is
|
|
* already holding */
|
|
patch_register(node->link->next, node,
|
|
jit_regno_patch|regno, regno);
|
|
del_node(_jitc->spill[regno], node->link);
|
|
del_node(prev, node);
|
|
node = prev;
|
|
}
|
|
_jitc->spill[regno] = NULL;
|
|
break;
|
|
case jit_code_movr:
|
|
regno = jit_regno(node->u.w);
|
|
if (simplify_movr(prev, node,
|
|
jit_kind_word, sizeof(jit_word_t)))
|
|
simplify_spill(node = prev, regno);
|
|
break;
|
|
case jit_code_movi:
|
|
regno = jit_regno(node->u.w);
|
|
if (simplify_movi(prev, node,
|
|
jit_kind_word, sizeof(jit_word_t)))
|
|
simplify_spill(node = prev, regno);
|
|
break;
|
|
case jit_code_movr_f:
|
|
regno = jit_regno(node->u.w);
|
|
if (simplify_movr(prev, node,
|
|
jit_kind_float32, sizeof(jit_float32_t)))
|
|
simplify_spill(node = prev, regno);
|
|
break;
|
|
case jit_code_movi_f:
|
|
regno = jit_regno(node->u.w);
|
|
if (simplify_movi(prev, node,
|
|
jit_kind_float32, sizeof(jit_float32_t)))
|
|
simplify_spill(node = prev, regno);
|
|
break;
|
|
case jit_code_movr_d:
|
|
regno = jit_regno(node->u.w);
|
|
if (simplify_movr(prev, node,
|
|
jit_kind_float64, sizeof(jit_float64_t)))
|
|
simplify_spill(node = prev, regno);
|
|
break;
|
|
case jit_code_movi_d:
|
|
regno = jit_regno(node->u.w);
|
|
if (simplify_movi(prev, node,
|
|
jit_kind_float64, sizeof(jit_float64_t)))
|
|
simplify_spill(node = prev, regno);
|
|
break;
|
|
case jit_code_ldxi_c: case jit_code_ldxi_uc:
|
|
case jit_code_ldxi_s: case jit_code_ldxi_us:
|
|
case jit_code_ldxi_i: case jit_code_ldxi_ui:
|
|
case jit_code_ldxi_l:
|
|
case jit_code_ldxi_f: case jit_code_ldxi_d:
|
|
regno = jit_regno(node->u.w);
|
|
if (simplify_ldxi(prev, node))
|
|
simplify_spill(node = prev, regno);
|
|
break;
|
|
case jit_code_stxi_c: case jit_code_stxi_s:
|
|
case jit_code_stxi_i: case jit_code_stxi_l:
|
|
case jit_code_stxi_f: case jit_code_stxi_d:
|
|
regno = jit_regno(node->u.w);
|
|
if (simplify_stxi(prev, node))
|
|
simplify_spill(node = prev, regno);
|
|
break;
|
|
case jit_code_callr: case jit_code_calli:
|
|
for (offset = 0; offset < _jitc->reglen; offset++) {
|
|
if (!(jit_class(_rvs[offset].spec) & jit_class_sav)) {
|
|
_jitc->values[offset].kind = 0;
|
|
++_jitc->gen[offset];
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
info = jit_classify(node->code);
|
|
if (info & jit_cc_a0_jmp)
|
|
/* labels are not implicitly added when not taking
|
|
* a conditional branch */
|
|
goto reset;
|
|
if (info & jit_cc_a0_chg) {
|
|
if (info & jit_cc_a0_rlh) {
|
|
regno = jit_regno(node->u.q.l);
|
|
_jitc->values[regno].kind = 0;
|
|
++_jitc->gen[regno];
|
|
regno = jit_regno(node->u.q.h);
|
|
_jitc->values[regno].kind = 0;
|
|
++_jitc->gen[regno];
|
|
}
|
|
else {
|
|
regno = jit_regno(node->u.w);
|
|
_jitc->values[regno].kind = 0;
|
|
++_jitc->gen[regno];
|
|
}
|
|
}
|
|
if (info & jit_cc_a1_chg) {
|
|
regno = jit_regno(node->v.w);
|
|
_jitc->values[regno].kind = 0;
|
|
++_jitc->gen[regno];
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
static jit_int32_t
|
|
_register_change_p(jit_state_t *_jit, jit_node_t *node, jit_node_t *link,
|
|
jit_int32_t regno)
|
|
{
|
|
jit_int32_t value;
|
|
|
|
for (; node != link; node = node->next) {
|
|
switch (node->code) {
|
|
case jit_code_label: case jit_code_prolog:
|
|
/* lack of extra information so cannot say it is undefined */
|
|
return (jit_reg_change);
|
|
case jit_code_callr: case jit_code_calli:
|
|
if (!(jit_class(_rvs[regno].spec) & jit_class_sav))
|
|
return (jit_reg_undef);
|
|
break;
|
|
default:
|
|
value = jit_classify(node->code);
|
|
/* lack of extra information */
|
|
if (value & jit_cc_a0_jmp)
|
|
return (jit_reg_change);
|
|
else if ((value & (jit_cc_a0_reg|jit_cc_a0_chg)) ==
|
|
(jit_cc_a0_reg|jit_cc_a0_chg) &&
|
|
(((value & jit_cc_a0_rlh) &&
|
|
(node->u.q.l == regno || node->u.q.h == regno)) ||
|
|
(!(value & jit_cc_a0_rlh) &&
|
|
node->u.w == regno)))
|
|
return (jit_reg_change);
|
|
else if ((value & jit_cc_a1_reg) && node->v.w == regno &&
|
|
(value & jit_cc_a1_chg))
|
|
return (jit_reg_change);
|
|
}
|
|
}
|
|
|
|
return (jit_reg_static);
|
|
}
|
|
|
|
/* most of this could be done at the same time as generating jit, but
|
|
* avoid complications on different cpu backends and patch spill/loads
|
|
* here, by simulating jit generation */
|
|
static jit_bool_t
|
|
_spill_reglive_p(jit_state_t *_jit, jit_node_t *node, jit_int32_t regno)
|
|
{
|
|
if (!jit_regset_tstbit(&_jitc->reglive, regno)) {
|
|
bmp_zero();
|
|
jit_regset_setbit(&_jitc->regmask, regno);
|
|
jit_update(node->next, &_jitc->reglive, &_jitc->regmask);
|
|
if (!jit_regset_tstbit(&_jitc->reglive, regno) &&
|
|
register_change_p(node->next, node->link, regno) != jit_reg_change)
|
|
return (0);
|
|
}
|
|
|
|
return (1);
|
|
}
|
|
|
|
static void
|
|
_patch_registers(jit_state_t *_jit)
|
|
{
|
|
jit_node_t *prev;
|
|
jit_node_t *node;
|
|
jit_node_t *next;
|
|
jit_int32_t info;
|
|
jit_int32_t spec;
|
|
jit_int32_t regno;
|
|
jit_int32_t value;
|
|
|
|
_jitc->function = NULL;
|
|
|
|
jit_reglive_setup();
|
|
for (prev = NULL, node = _jitc->head; node; node = next) {
|
|
next = node->next;
|
|
|
|
info = jit_classify(node->code);
|
|
jit_regarg_set(node, info);
|
|
|
|
switch (node->code) {
|
|
case jit_code_save:
|
|
regno = jit_regno(node->u.w);
|
|
if (!spill_reglive_p(node, regno)) {
|
|
/* register is not live, just remove spill/reload */
|
|
jit_regarg_clr(node, info);
|
|
node->link->v.w = jit_regload_delete;
|
|
del_node(prev, node);
|
|
continue;
|
|
}
|
|
else {
|
|
/* try to find a free register of the same class */
|
|
spec = jit_class(_rvs[regno].spec) & ~jit_class_arg;
|
|
for (value = 0; value < _jitc->reglen; value++) {
|
|
if (value != regno &&
|
|
((jit_class(_rvs[value].spec) & spec) &
|
|
~jit_class_arg) == spec &&
|
|
!jit_regset_tstbit(&_jitc->regarg, value) &&
|
|
!spill_reglive_p(node, value))
|
|
break;
|
|
}
|
|
if (value < _jitc->reglen) {
|
|
jit_regarg_clr(node, info);
|
|
patch_register(node->next, node->link,
|
|
jit_regno_patch|node->u.w,
|
|
jit_regno_patch|value);
|
|
/* mark as live just in case there are nested
|
|
* register patches, so that next patch will
|
|
* not want to use the same register */
|
|
jit_regset_setbit(&_jitc->reglive, value);
|
|
/* register is not live, just remove spill/reload */
|
|
node->link->v.w = jit_regload_isdead;
|
|
del_node(prev, node);
|
|
continue;
|
|
}
|
|
else {
|
|
/* failed to find a free register */
|
|
if (spec & jit_class_gpr) {
|
|
if (!_jitc->function->regoff[regno])
|
|
_jitc->function->regoff[regno] =
|
|
jit_allocai(sizeof(jit_word_t));
|
|
#if __WORDSIZE == 32
|
|
node->code = jit_code_stxi_i;
|
|
#else
|
|
node->code = jit_code_stxi_l;
|
|
#endif
|
|
}
|
|
else {
|
|
node->code = jit_code_stxi_d;
|
|
if (!_jitc->function->regoff[regno])
|
|
_jitc->function->regoff[regno] =
|
|
jit_allocai(sizeof(jit_float64_t));
|
|
}
|
|
node->u.w = _jitc->function->regoff[regno];
|
|
node->v.w = JIT_FP;
|
|
node->w.w = regno;
|
|
node->link = NULL;
|
|
}
|
|
}
|
|
break;
|
|
case jit_code_load:
|
|
regno = jit_regno(node->u.w);
|
|
if (node->v.w) {
|
|
if (node->v.w == jit_regload_isdead)
|
|
jit_regset_clrbit(&_jitc->reglive, regno);
|
|
del_node(prev, node);
|
|
continue;
|
|
}
|
|
spec = jit_class(_rvs[regno].spec);
|
|
if (spec & jit_class_gpr) {
|
|
#if __WORDSIZE == 32
|
|
node->code = jit_code_ldxi_i;
|
|
#else
|
|
node->code = jit_code_ldxi_l;
|
|
#endif
|
|
}
|
|
else
|
|
node->code = jit_code_ldxi_d;
|
|
node->v.w = regno;
|
|
node->v.w = JIT_FP;
|
|
node->w.w = _jitc->function->regoff[regno];
|
|
node->link = NULL;
|
|
break;
|
|
case jit_code_prolog:
|
|
_jitc->function = _jitc->functions.ptr + node->w.w;
|
|
break;
|
|
case jit_code_epilog:
|
|
_jitc->function = NULL;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
jit_regarg_clr(node, info);
|
|
/* update register live state */
|
|
jit_reglive(node);
|
|
prev = node;
|
|
}
|
|
}
|
|
|
|
static void
|
|
_patch_register(jit_state_t *_jit, jit_node_t *node, jit_node_t *link,
|
|
jit_int32_t regno, jit_int32_t patch)
|
|
{
|
|
jit_int32_t value;
|
|
|
|
for (; node != link; node = node->next) {
|
|
value = jit_classify(node->code);
|
|
if (value & jit_cc_a0_reg) {
|
|
if (value & jit_cc_a0_rlh) {
|
|
if (node->u.q.l == regno)
|
|
node->u.q.l = patch;
|
|
if (node->u.q.h == regno)
|
|
node->u.q.h = patch;
|
|
}
|
|
else {
|
|
if (node->u.w == regno)
|
|
node->u.w = patch;
|
|
}
|
|
}
|
|
if ((value & jit_cc_a1_reg) && node->v.w == regno)
|
|
node->v.w = patch;
|
|
if ((value & jit_cc_a2_reg) && node->w.w == regno)
|
|
node->w.w = patch;
|
|
}
|
|
}
|
|
|
|
#if defined(__i386__) || defined(__x86_64__)
|
|
# include "jit_x86.c"
|
|
#elif defined(__mips__)
|
|
# include "jit_mips.c"
|
|
#elif defined(__arm__)
|
|
# include "jit_arm.c"
|
|
#elif defined(__ppc__) || defined(__powerpc__)
|
|
# include "jit_ppc.c"
|
|
#elif defined(__sparc__)
|
|
# include "jit_sparc.c"
|
|
#elif defined(__ia64__)
|
|
# include "jit_ia64.c"
|
|
#elif defined(__hppa__)
|
|
# include "jit_hppa.c"
|
|
#elif defined(__aarch64__)
|
|
# include "jit_aarch64.c"
|
|
#elif defined(__s390x__)
|
|
# include "jit_s390x.c"
|
|
#elif defined(__alpha__)
|
|
# include "jit_alpha.c"
|
|
#endif
|