mirror of
https://github.com/bytecodealliance/wasm-micro-runtime.git
synced 2025-02-06 23:15:16 +00:00
7e9bf9cdf5
- Translate all the opcodes of threads spec proposal for Fast JIT - Add the atomic flag for Fast JIT load/store IRs to support atomic load/store - Add new atomic related Fast JIT IRs and translate them in the codegen - Add suspend_flags check in branch opcodes and before/after call function - Modify CI to enable Fast JIT multi-threading test Co-authored-by: TianlongLiang <tianlong.liang@intel.com>
1428 lines
39 KiB
C
1428 lines
39 KiB
C
/*
|
|
* Copyright (C) 2021 Intel Corporation. All rights reserved.
|
|
* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
*/
|
|
|
|
#include "jit_ir.h"
|
|
#include "jit_codegen.h"
|
|
#include "jit_frontend.h"
|
|
|
|
/**
|
|
* Operand kinds of instructions.
|
|
*/
|
|
enum {
|
|
JIT_OPND_KIND_Reg,
|
|
JIT_OPND_KIND_VReg,
|
|
JIT_OPND_KIND_LookupSwitch,
|
|
};
|
|
|
|
/**
|
|
* Operand kind of each instruction.
|
|
*/
|
|
static const uint8 insn_opnd_kind[] = {
|
|
#define INSN(NAME, OPND_KIND, OPND_NUM, FIRST_USE) JIT_OPND_KIND_##OPND_KIND,
|
|
#include "jit_ir.def"
|
|
#undef INSN
|
|
};
|
|
|
|
/**
|
|
* Operand number of each instruction.
|
|
*/
|
|
static const uint8 insn_opnd_num[] = {
|
|
#define INSN(NAME, OPND_KIND, OPND_NUM, FIRST_USE) OPND_NUM,
|
|
#include "jit_ir.def"
|
|
#undef INSN
|
|
};
|
|
|
|
/**
|
|
* Operand number of each instruction.
|
|
*/
|
|
static const uint8 insn_opnd_first_use[] = {
|
|
#define INSN(NAME, OPND_KIND, OPND_NUM, FIRST_USE) FIRST_USE,
|
|
#include "jit_ir.def"
|
|
#undef INSN
|
|
};
|
|
|
|
#define JIT_INSN_NEW_Reg(OPND_NUM) \
|
|
jit_calloc(offsetof(JitInsn, _opnd) + sizeof(JitReg) * (OPND_NUM))
|
|
#define JIT_INSN_NEW_VReg(OPND_NUM) \
|
|
jit_calloc(offsetof(JitInsn, _opnd._opnd_VReg._reg) \
|
|
+ sizeof(JitReg) * (OPND_NUM))
|
|
|
|
JitInsn *
|
|
_jit_insn_new_Reg_0(JitOpcode opc)
|
|
{
|
|
JitInsn *insn = JIT_INSN_NEW_Reg(0);
|
|
|
|
if (insn) {
|
|
insn->opcode = opc;
|
|
}
|
|
|
|
return insn;
|
|
}
|
|
|
|
JitInsn *
|
|
_jit_insn_new_Reg_1(JitOpcode opc, JitReg r0)
|
|
{
|
|
JitInsn *insn = JIT_INSN_NEW_Reg(1);
|
|
|
|
if (insn) {
|
|
insn->opcode = opc;
|
|
*jit_insn_opnd(insn, 0) = r0;
|
|
}
|
|
|
|
return insn;
|
|
}
|
|
|
|
JitInsn *
|
|
_jit_insn_new_Reg_2(JitOpcode opc, JitReg r0, JitReg r1)
|
|
{
|
|
JitInsn *insn = JIT_INSN_NEW_Reg(2);
|
|
|
|
if (insn) {
|
|
insn->opcode = opc;
|
|
*jit_insn_opnd(insn, 0) = r0;
|
|
*jit_insn_opnd(insn, 1) = r1;
|
|
}
|
|
|
|
return insn;
|
|
}
|
|
|
|
JitInsn *
|
|
_jit_insn_new_Reg_3(JitOpcode opc, JitReg r0, JitReg r1, JitReg r2)
|
|
{
|
|
JitInsn *insn = JIT_INSN_NEW_Reg(3);
|
|
|
|
if (insn) {
|
|
insn->opcode = opc;
|
|
*jit_insn_opnd(insn, 0) = r0;
|
|
*jit_insn_opnd(insn, 1) = r1;
|
|
*jit_insn_opnd(insn, 2) = r2;
|
|
}
|
|
|
|
return insn;
|
|
}
|
|
|
|
JitInsn *
|
|
_jit_insn_new_Reg_4(JitOpcode opc, JitReg r0, JitReg r1, JitReg r2, JitReg r3)
|
|
{
|
|
JitInsn *insn = JIT_INSN_NEW_Reg(4);
|
|
|
|
if (insn) {
|
|
insn->opcode = opc;
|
|
*jit_insn_opnd(insn, 0) = r0;
|
|
*jit_insn_opnd(insn, 1) = r1;
|
|
*jit_insn_opnd(insn, 2) = r2;
|
|
*jit_insn_opnd(insn, 3) = r3;
|
|
}
|
|
|
|
return insn;
|
|
}
|
|
|
|
JitInsn *
|
|
_jit_insn_new_Reg_5(JitOpcode opc, JitReg r0, JitReg r1, JitReg r2, JitReg r3,
|
|
JitReg r4)
|
|
{
|
|
JitInsn *insn = JIT_INSN_NEW_Reg(5);
|
|
|
|
if (insn) {
|
|
insn->opcode = opc;
|
|
*jit_insn_opnd(insn, 0) = r0;
|
|
*jit_insn_opnd(insn, 1) = r1;
|
|
*jit_insn_opnd(insn, 2) = r2;
|
|
*jit_insn_opnd(insn, 3) = r3;
|
|
*jit_insn_opnd(insn, 4) = r4;
|
|
}
|
|
|
|
return insn;
|
|
}
|
|
|
|
JitInsn *
|
|
_jit_insn_new_VReg_1(JitOpcode opc, JitReg r0, int n)
|
|
{
|
|
JitInsn *insn = JIT_INSN_NEW_VReg(1 + n);
|
|
|
|
if (insn) {
|
|
insn->opcode = opc;
|
|
insn->_opnd._opnd_VReg._reg_num = 1 + n;
|
|
*(jit_insn_opndv(insn, 0)) = r0;
|
|
}
|
|
|
|
return insn;
|
|
}
|
|
|
|
JitInsn *
|
|
_jit_insn_new_VReg_2(JitOpcode opc, JitReg r0, JitReg r1, int n)
|
|
{
|
|
JitInsn *insn = JIT_INSN_NEW_VReg(2 + n);
|
|
|
|
if (insn) {
|
|
insn->opcode = opc;
|
|
insn->_opnd._opnd_VReg._reg_num = 2 + n;
|
|
*(jit_insn_opndv(insn, 0)) = r0;
|
|
*(jit_insn_opndv(insn, 1)) = r1;
|
|
}
|
|
|
|
return insn;
|
|
}
|
|
|
|
JitInsn *
|
|
_jit_insn_new_LookupSwitch_1(JitOpcode opc, JitReg value, uint32 num)
|
|
{
|
|
JitOpndLookupSwitch *opnd = NULL;
|
|
JitInsn *insn =
|
|
jit_calloc(offsetof(JitInsn, _opnd._opnd_LookupSwitch.match_pairs)
|
|
+ sizeof(opnd->match_pairs[0]) * num);
|
|
|
|
if (insn) {
|
|
insn->opcode = opc;
|
|
opnd = jit_insn_opndls(insn);
|
|
opnd->value = value;
|
|
opnd->match_pairs_num = num;
|
|
}
|
|
|
|
return insn;
|
|
}
|
|
|
|
#undef JIT_INSN_NEW_Reg
|
|
#undef JIT_INSN_NEW_VReg
|
|
|
|
void
|
|
jit_insn_insert_before(JitInsn *insn1, JitInsn *insn2)
|
|
{
|
|
bh_assert(insn1->prev);
|
|
insn1->prev->next = insn2;
|
|
insn2->prev = insn1->prev;
|
|
insn2->next = insn1;
|
|
insn1->prev = insn2;
|
|
}
|
|
|
|
void
|
|
jit_insn_insert_after(JitInsn *insn1, JitInsn *insn2)
|
|
{
|
|
bh_assert(insn1->next);
|
|
insn1->next->prev = insn2;
|
|
insn2->next = insn1->next;
|
|
insn2->prev = insn1;
|
|
insn1->next = insn2;
|
|
}
|
|
|
|
void
|
|
jit_insn_unlink(JitInsn *insn)
|
|
{
|
|
bh_assert(insn->prev);
|
|
insn->prev->next = insn->next;
|
|
bh_assert(insn->next);
|
|
insn->next->prev = insn->prev;
|
|
insn->prev = insn->next = NULL;
|
|
}
|
|
|
|
unsigned
|
|
jit_insn_hash(JitInsn *insn)
|
|
{
|
|
const uint8 opcode = insn->opcode;
|
|
unsigned hash = opcode, i;
|
|
|
|
/* Currently, only instructions with Reg kind operand require
|
|
hashing. For others, simply use opcode as the hash value. */
|
|
if (insn_opnd_kind[opcode] != JIT_OPND_KIND_Reg
|
|
|| insn_opnd_num[opcode] < 1)
|
|
return hash;
|
|
|
|
/* All the instructions with hashing support must be in the
|
|
assignment format, i.e. the first operand is the result (hence
|
|
being ignored) and all the others are operands. This is also
|
|
true for CHK instructions, whose first operand is the instruction
|
|
pointer. */
|
|
for (i = 1; i < insn_opnd_num[opcode]; i++)
|
|
hash = ((hash << 5) - hash) + *(jit_insn_opnd(insn, i));
|
|
|
|
return hash;
|
|
}
|
|
|
|
bool
|
|
jit_insn_equal(JitInsn *insn1, JitInsn *insn2)
|
|
{
|
|
const uint8 opcode = insn1->opcode;
|
|
unsigned i;
|
|
|
|
if (insn2->opcode != opcode)
|
|
return false;
|
|
|
|
if (insn_opnd_kind[opcode] != JIT_OPND_KIND_Reg
|
|
|| insn_opnd_num[opcode] < 1)
|
|
return false;
|
|
|
|
for (i = 1; i < insn_opnd_num[opcode]; i++)
|
|
if (*(jit_insn_opnd(insn1, i)) != *(jit_insn_opnd(insn2, i)))
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
JitRegVec
|
|
jit_insn_opnd_regs(JitInsn *insn)
|
|
{
|
|
JitRegVec vec = { 0 };
|
|
JitOpndLookupSwitch *ls;
|
|
|
|
vec._stride = 1;
|
|
|
|
switch (insn_opnd_kind[insn->opcode]) {
|
|
case JIT_OPND_KIND_Reg:
|
|
vec.num = insn_opnd_num[insn->opcode];
|
|
vec._base = jit_insn_opnd(insn, 0);
|
|
break;
|
|
|
|
case JIT_OPND_KIND_VReg:
|
|
vec.num = jit_insn_opndv_num(insn);
|
|
vec._base = jit_insn_opndv(insn, 0);
|
|
break;
|
|
|
|
case JIT_OPND_KIND_LookupSwitch:
|
|
ls = jit_insn_opndls(insn);
|
|
vec.num = ls->match_pairs_num + 2;
|
|
vec._base = &ls->value;
|
|
vec._stride = sizeof(ls->match_pairs[0]) / sizeof(*vec._base);
|
|
break;
|
|
}
|
|
|
|
return vec;
|
|
}
|
|
|
|
unsigned
|
|
jit_insn_opnd_first_use(JitInsn *insn)
|
|
{
|
|
return insn_opnd_first_use[insn->opcode];
|
|
}
|
|
|
|
JitBasicBlock *
|
|
jit_basic_block_new(JitReg label, int n)
|
|
{
|
|
JitBasicBlock *block = jit_insn_new_PHI(label, n);
|
|
if (!block)
|
|
return NULL;
|
|
|
|
block->prev = block->next = block;
|
|
return block;
|
|
}
|
|
|
|
void
|
|
jit_basic_block_delete(JitBasicBlock *block)
|
|
{
|
|
JitInsn *insn, *next_insn, *end;
|
|
|
|
if (!block)
|
|
return;
|
|
|
|
insn = jit_basic_block_first_insn(block);
|
|
end = jit_basic_block_end_insn(block);
|
|
|
|
for (; insn != end; insn = next_insn) {
|
|
next_insn = insn->next;
|
|
jit_insn_delete(insn);
|
|
}
|
|
|
|
jit_insn_delete(block);
|
|
}
|
|
|
|
JitRegVec
|
|
jit_basic_block_preds(JitBasicBlock *block)
|
|
{
|
|
JitRegVec vec;
|
|
|
|
vec.num = jit_insn_opndv_num(block) - 1;
|
|
vec._base = vec.num > 0 ? jit_insn_opndv(block, 1) : NULL;
|
|
vec._stride = 1;
|
|
|
|
return vec;
|
|
}
|
|
|
|
JitRegVec
|
|
jit_basic_block_succs(JitBasicBlock *block)
|
|
{
|
|
JitInsn *last_insn = jit_basic_block_last_insn(block);
|
|
JitRegVec vec;
|
|
|
|
vec.num = 0;
|
|
vec._base = NULL;
|
|
vec._stride = 1;
|
|
|
|
switch (last_insn->opcode) {
|
|
case JIT_OP_JMP:
|
|
vec.num = 1;
|
|
vec._base = jit_insn_opnd(last_insn, 0);
|
|
break;
|
|
|
|
case JIT_OP_BEQ:
|
|
case JIT_OP_BNE:
|
|
case JIT_OP_BGTS:
|
|
case JIT_OP_BGES:
|
|
case JIT_OP_BLTS:
|
|
case JIT_OP_BLES:
|
|
case JIT_OP_BGTU:
|
|
case JIT_OP_BGEU:
|
|
case JIT_OP_BLTU:
|
|
case JIT_OP_BLEU:
|
|
vec.num = 2;
|
|
vec._base = jit_insn_opnd(last_insn, 1);
|
|
break;
|
|
|
|
case JIT_OP_LOOKUPSWITCH:
|
|
{
|
|
JitOpndLookupSwitch *opnd = jit_insn_opndls(last_insn);
|
|
vec.num = opnd->match_pairs_num + 1;
|
|
vec._base = &opnd->default_target;
|
|
vec._stride = sizeof(opnd->match_pairs[0]) / sizeof(*vec._base);
|
|
break;
|
|
}
|
|
|
|
default:
|
|
vec._stride = 0;
|
|
}
|
|
|
|
return vec;
|
|
}
|
|
|
|
JitCompContext *
|
|
jit_cc_init(JitCompContext *cc, unsigned htab_size)
|
|
{
|
|
JitBasicBlock *entry_block, *exit_block;
|
|
unsigned i, num;
|
|
|
|
memset(cc, 0, sizeof(*cc));
|
|
cc->_reference_count = 1;
|
|
jit_annl_enable_basic_block(cc);
|
|
|
|
/* Create entry and exit blocks. They must be the first two
|
|
blocks respectively. */
|
|
if (!(entry_block = jit_cc_new_basic_block(cc, 0)))
|
|
goto fail;
|
|
|
|
if (!(exit_block = jit_cc_new_basic_block(cc, 0))) {
|
|
jit_basic_block_delete(entry_block);
|
|
goto fail;
|
|
}
|
|
|
|
/* Record the entry and exit labels, whose indexes must be 0 and 1
|
|
respectively. */
|
|
cc->entry_label = jit_basic_block_label(entry_block);
|
|
cc->exit_label = jit_basic_block_label(exit_block);
|
|
bh_assert(jit_reg_no(cc->entry_label) == 0
|
|
&& jit_reg_no(cc->exit_label) == 1);
|
|
|
|
if (!(cc->exce_basic_blocks =
|
|
jit_calloc(sizeof(JitBasicBlock *) * EXCE_NUM)))
|
|
goto fail;
|
|
|
|
if (!(cc->incoming_insns_for_exec_bbs =
|
|
jit_calloc(sizeof(JitIncomingInsnList) * EXCE_NUM)))
|
|
goto fail;
|
|
|
|
cc->hreg_info = jit_codegen_get_hreg_info();
|
|
bh_assert(cc->hreg_info->info[JIT_REG_KIND_I32].num > 3);
|
|
|
|
/* Initialize virtual registers for hard registers. */
|
|
for (i = JIT_REG_KIND_VOID; i < JIT_REG_KIND_L32; i++) {
|
|
if ((num = cc->hreg_info->info[i].num)) {
|
|
/* Initialize the capacity to be large enough. */
|
|
jit_cc_new_reg(cc, i);
|
|
bh_assert(cc->_ann._reg_capacity[i] > num);
|
|
cc->_ann._reg_num[i] = num;
|
|
}
|
|
}
|
|
|
|
/* Create registers for frame pointer, exec_env and cmp. */
|
|
cc->fp_reg = jit_reg_new(JIT_REG_KIND_PTR, cc->hreg_info->fp_hreg_index);
|
|
cc->exec_env_reg =
|
|
jit_reg_new(JIT_REG_KIND_PTR, cc->hreg_info->exec_env_hreg_index);
|
|
cc->cmp_reg = jit_reg_new(JIT_REG_KIND_I32, cc->hreg_info->cmp_hreg_index);
|
|
|
|
cc->_const_val._hash_table_size = htab_size;
|
|
|
|
if (!(cc->_const_val._hash_table =
|
|
jit_calloc(htab_size * sizeof(*cc->_const_val._hash_table))))
|
|
goto fail;
|
|
|
|
return cc;
|
|
|
|
fail:
|
|
jit_cc_destroy(cc);
|
|
return NULL;
|
|
}
|
|
|
|
void
|
|
jit_cc_destroy(JitCompContext *cc)
|
|
{
|
|
unsigned i, end;
|
|
JitBasicBlock *block;
|
|
JitIncomingInsn *incoming_insn, *incoming_insn_next;
|
|
|
|
jit_block_stack_destroy(&cc->block_stack);
|
|
|
|
if (cc->jit_frame) {
|
|
if (cc->jit_frame->memory_regs)
|
|
jit_free(cc->jit_frame->memory_regs);
|
|
if (cc->jit_frame->table_regs)
|
|
jit_free(cc->jit_frame->table_regs);
|
|
jit_free(cc->jit_frame);
|
|
}
|
|
|
|
if (cc->memory_regs)
|
|
jit_free(cc->memory_regs);
|
|
|
|
if (cc->table_regs)
|
|
jit_free(cc->table_regs);
|
|
|
|
jit_free(cc->_const_val._hash_table);
|
|
|
|
/* Release the instruction hash table. */
|
|
jit_cc_disable_insn_hash(cc);
|
|
|
|
jit_free(cc->exce_basic_blocks);
|
|
|
|
if (cc->incoming_insns_for_exec_bbs) {
|
|
for (i = 0; i < EXCE_NUM; i++) {
|
|
incoming_insn = cc->incoming_insns_for_exec_bbs[i];
|
|
while (incoming_insn) {
|
|
incoming_insn_next = incoming_insn->next;
|
|
jit_free(incoming_insn);
|
|
incoming_insn = incoming_insn_next;
|
|
}
|
|
}
|
|
jit_free(cc->incoming_insns_for_exec_bbs);
|
|
}
|
|
|
|
/* Release entry and exit blocks. */
|
|
if (0 != cc->entry_label)
|
|
jit_basic_block_delete(jit_cc_entry_basic_block(cc));
|
|
if (0 != cc->exit_label)
|
|
jit_basic_block_delete(jit_cc_exit_basic_block(cc));
|
|
|
|
/* clang-format off */
|
|
/* Release blocks and instructions. */
|
|
JIT_FOREACH_BLOCK(cc, i, end, block)
|
|
{
|
|
jit_basic_block_delete(block);
|
|
}
|
|
/* clang-format on */
|
|
|
|
/* Release constant values. */
|
|
for (i = JIT_REG_KIND_VOID; i < JIT_REG_KIND_L32; i++) {
|
|
jit_free(cc->_const_val._value[i]);
|
|
jit_free(cc->_const_val._next[i]);
|
|
}
|
|
|
|
/* Release storage of annotations. */
|
|
#define ANN_LABEL(TYPE, NAME) jit_annl_disable_##NAME(cc);
|
|
#define ANN_INSN(TYPE, NAME) jit_anni_disable_##NAME(cc);
|
|
#define ANN_REG(TYPE, NAME) jit_annr_disable_##NAME(cc);
|
|
#include "jit_ir.def"
|
|
#undef ANN_LABEL
|
|
#undef ANN_INSN
|
|
#undef ANN_REG
|
|
}
|
|
|
|
void
|
|
jit_cc_delete(JitCompContext *cc)
|
|
{
|
|
if (cc && --cc->_reference_count == 0) {
|
|
jit_cc_destroy(cc);
|
|
jit_free(cc);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Reallocate a memory block with the new_size.
|
|
* TODO: replace this with imported jit_realloc when it's available.
|
|
*/
|
|
static void *
|
|
_jit_realloc(void *ptr, unsigned new_size, unsigned old_size)
|
|
{
|
|
void *new_ptr = jit_malloc(new_size);
|
|
|
|
if (new_ptr) {
|
|
bh_assert(new_size > old_size);
|
|
|
|
if (ptr) {
|
|
memcpy(new_ptr, ptr, old_size);
|
|
memset((uint8 *)new_ptr + old_size, 0, new_size - old_size);
|
|
jit_free(ptr);
|
|
}
|
|
else
|
|
memset(new_ptr, 0, new_size);
|
|
}
|
|
|
|
return new_ptr;
|
|
}
|
|
|
|
static unsigned
|
|
hash_of_const(unsigned kind, unsigned size, void *val)
|
|
{
|
|
uint8 *p = (uint8 *)val, *end = p + size;
|
|
unsigned hash = kind;
|
|
|
|
do
|
|
hash = ((hash << 5) - hash) + *p++;
|
|
while (p != end);
|
|
|
|
return hash;
|
|
}
|
|
|
|
static inline void *
|
|
address_of_const(JitCompContext *cc, JitReg reg, unsigned size)
|
|
{
|
|
int kind = jit_reg_kind(reg);
|
|
unsigned no = jit_reg_no(reg);
|
|
unsigned idx = no & ~_JIT_REG_CONST_IDX_FLAG;
|
|
|
|
bh_assert(kind < JIT_REG_KIND_L32);
|
|
bh_assert(jit_reg_is_const_idx(reg) && idx < cc->_const_val._num[kind]);
|
|
|
|
return cc->_const_val._value[kind] + size * idx;
|
|
}
|
|
|
|
static inline JitReg
|
|
next_of_const(JitCompContext *cc, JitReg reg)
|
|
{
|
|
int kind = jit_reg_kind(reg);
|
|
unsigned no = jit_reg_no(reg);
|
|
unsigned idx = no & ~_JIT_REG_CONST_IDX_FLAG;
|
|
|
|
bh_assert(kind < JIT_REG_KIND_L32);
|
|
bh_assert(jit_reg_is_const_idx(reg) && idx < cc->_const_val._num[kind]);
|
|
|
|
return cc->_const_val._next[kind][idx];
|
|
}
|
|
|
|
/**
|
|
* Put a constant value into the compilation context.
|
|
*
|
|
* @param cc compilation context
|
|
* @param kind register kind
|
|
* @param size size of the value
|
|
* @param val pointer to value which must be aligned
|
|
*
|
|
* @return a constant register containing the value
|
|
*/
|
|
static JitReg
|
|
_jit_cc_new_const(JitCompContext *cc, int kind, unsigned size, void *val)
|
|
{
|
|
unsigned num = cc->_const_val._num[kind], slot;
|
|
unsigned capacity = cc->_const_val._capacity[kind];
|
|
uint8 *new_value;
|
|
JitReg r, *new_next;
|
|
|
|
bh_assert(num <= capacity);
|
|
|
|
/* Find the existing value first. */
|
|
slot = hash_of_const(kind, size, val) % cc->_const_val._hash_table_size;
|
|
r = cc->_const_val._hash_table[slot];
|
|
|
|
for (; r; r = next_of_const(cc, r))
|
|
if (jit_reg_kind(r) == kind
|
|
&& !memcmp(val, address_of_const(cc, r, size), size))
|
|
return r;
|
|
|
|
if (num == capacity) {
|
|
/* Increase the space of value and next. */
|
|
capacity = capacity > 0 ? (capacity + capacity / 2) : 16;
|
|
new_value = _jit_realloc(cc->_const_val._value[kind], size * capacity,
|
|
size * num);
|
|
new_next =
|
|
_jit_realloc(cc->_const_val._next[kind],
|
|
sizeof(*new_next) * capacity, sizeof(*new_next) * num);
|
|
|
|
if (new_value && new_next) {
|
|
cc->_const_val._value[kind] = new_value;
|
|
cc->_const_val._next[kind] = new_next;
|
|
}
|
|
else {
|
|
jit_set_last_error(cc, "create const register failed");
|
|
jit_free(new_value);
|
|
jit_free(new_next);
|
|
return 0;
|
|
}
|
|
|
|
cc->_const_val._capacity[kind] = capacity;
|
|
}
|
|
|
|
bh_assert(num + 1 < (uint32)_JIT_REG_CONST_IDX_FLAG);
|
|
r = jit_reg_new(kind, _JIT_REG_CONST_IDX_FLAG | num);
|
|
memcpy(cc->_const_val._value[kind] + size * num, val, size);
|
|
cc->_const_val._next[kind][num] = cc->_const_val._hash_table[slot];
|
|
cc->_const_val._hash_table[slot] = r;
|
|
cc->_const_val._num[kind] = num + 1;
|
|
|
|
return r;
|
|
}
|
|
|
|
static inline int32
|
|
get_const_val_in_reg(JitReg reg)
|
|
{
|
|
int shift = 8 * sizeof(reg) - _JIT_REG_KIND_SHIFT + 1;
|
|
return ((int32)(reg << shift)) >> shift;
|
|
}
|
|
|
|
#define _JIT_CC_NEW_CONST_HELPER(KIND, TYPE, val) \
|
|
do { \
|
|
JitReg reg = jit_reg_new( \
|
|
JIT_REG_KIND_##KIND, \
|
|
(_JIT_REG_CONST_VAL_FLAG | ((JitReg)val & ~_JIT_REG_KIND_MASK))); \
|
|
\
|
|
if ((TYPE)get_const_val_in_reg(reg) == val) \
|
|
return reg; \
|
|
return _jit_cc_new_const(cc, JIT_REG_KIND_##KIND, sizeof(val), &val); \
|
|
} while (0)
|
|
|
|
JitReg
|
|
jit_cc_new_const_I32_rel(JitCompContext *cc, int32 val, uint32 rel)
|
|
{
|
|
uint64 val64 = (uint64)(uint32)val | ((uint64)rel << 32);
|
|
_JIT_CC_NEW_CONST_HELPER(I32, uint64, val64);
|
|
}
|
|
|
|
JitReg
|
|
jit_cc_new_const_I64(JitCompContext *cc, int64 val)
|
|
{
|
|
_JIT_CC_NEW_CONST_HELPER(I64, int64, val);
|
|
}
|
|
|
|
JitReg
|
|
jit_cc_new_const_F32(JitCompContext *cc, float val)
|
|
{
|
|
int32 float_neg_zero = 0x80000000;
|
|
|
|
if (!memcmp(&val, &float_neg_zero, sizeof(float)))
|
|
/* Create const -0.0f */
|
|
return _jit_cc_new_const(cc, JIT_REG_KIND_F32, sizeof(float), &val);
|
|
|
|
_JIT_CC_NEW_CONST_HELPER(F32, float, val);
|
|
}
|
|
|
|
JitReg
|
|
jit_cc_new_const_F64(JitCompContext *cc, double val)
|
|
{
|
|
int64 double_neg_zero = 0x8000000000000000ll;
|
|
|
|
if (!memcmp(&val, &double_neg_zero, sizeof(double)))
|
|
/* Create const -0.0d */
|
|
return _jit_cc_new_const(cc, JIT_REG_KIND_F64, sizeof(double), &val);
|
|
|
|
_JIT_CC_NEW_CONST_HELPER(F64, double, val);
|
|
}
|
|
|
|
#undef _JIT_CC_NEW_CONST_HELPER
|
|
|
|
#define _JIT_CC_GET_CONST_HELPER(KIND, TYPE) \
|
|
do { \
|
|
bh_assert(jit_reg_kind(reg) == JIT_REG_KIND_##KIND); \
|
|
bh_assert(jit_reg_is_const(reg)); \
|
|
\
|
|
return (jit_reg_is_const_val(reg) \
|
|
? (TYPE)get_const_val_in_reg(reg) \
|
|
: *(TYPE *)(address_of_const(cc, reg, sizeof(TYPE)))); \
|
|
} while (0)
|
|
|
|
static uint64
|
|
jit_cc_get_const_I32_helper(JitCompContext *cc, JitReg reg)
|
|
{
|
|
_JIT_CC_GET_CONST_HELPER(I32, uint64);
|
|
}
|
|
|
|
uint32
|
|
jit_cc_get_const_I32_rel(JitCompContext *cc, JitReg reg)
|
|
{
|
|
return (uint32)(jit_cc_get_const_I32_helper(cc, reg) >> 32);
|
|
}
|
|
|
|
int32
|
|
jit_cc_get_const_I32(JitCompContext *cc, JitReg reg)
|
|
{
|
|
return (int32)(jit_cc_get_const_I32_helper(cc, reg));
|
|
}
|
|
|
|
int64
|
|
jit_cc_get_const_I64(JitCompContext *cc, JitReg reg)
|
|
{
|
|
_JIT_CC_GET_CONST_HELPER(I64, int64);
|
|
}
|
|
|
|
float
|
|
jit_cc_get_const_F32(JitCompContext *cc, JitReg reg)
|
|
{
|
|
_JIT_CC_GET_CONST_HELPER(F32, float);
|
|
}
|
|
|
|
double
|
|
jit_cc_get_const_F64(JitCompContext *cc, JitReg reg)
|
|
{
|
|
_JIT_CC_GET_CONST_HELPER(F64, double);
|
|
}
|
|
|
|
#undef _JIT_CC_GET_CONST_HELPER
|
|
|
|
#define _JIT_REALLOC_ANN(TYPE, NAME, ANN, POSTFIX) \
|
|
if (successful && cc->_ann._##ANN##_##NAME##_enabled) { \
|
|
TYPE *ptr = _jit_realloc(cc->_ann._##ANN##_##NAME POSTFIX, \
|
|
sizeof(TYPE) * capacity, sizeof(TYPE) * num); \
|
|
if (ptr) \
|
|
cc->_ann._##ANN##_##NAME POSTFIX = ptr; \
|
|
else \
|
|
successful = false; \
|
|
}
|
|
|
|
JitReg
|
|
jit_cc_new_label(JitCompContext *cc)
|
|
{
|
|
unsigned num = cc->_ann._label_num;
|
|
unsigned capacity = cc->_ann._label_capacity;
|
|
bool successful = true;
|
|
|
|
bh_assert(num <= capacity);
|
|
|
|
if (num == capacity) {
|
|
capacity = capacity > 0 ? (capacity + capacity / 2) : 16;
|
|
|
|
#define EMPTY_POSTFIX
|
|
#define ANN_LABEL(TYPE, NAME) _JIT_REALLOC_ANN(TYPE, NAME, label, EMPTY_POSTFIX)
|
|
#include "jit_ir.def"
|
|
#undef ANN_LABEL
|
|
#undef EMPTY_POSTFIX
|
|
|
|
if (!successful) {
|
|
jit_set_last_error(cc, "create label register failed");
|
|
return 0;
|
|
}
|
|
|
|
cc->_ann._label_capacity = capacity;
|
|
}
|
|
|
|
cc->_ann._label_num = num + 1;
|
|
|
|
return jit_reg_new(JIT_REG_KIND_L32, num);
|
|
}
|
|
|
|
JitBasicBlock *
|
|
jit_cc_new_basic_block(JitCompContext *cc, int n)
|
|
{
|
|
JitReg label = jit_cc_new_label(cc);
|
|
JitBasicBlock *block = NULL;
|
|
|
|
if (label && (block = jit_basic_block_new(label, n)))
|
|
/* Void 0 register indicates error in creation. */
|
|
*(jit_annl_basic_block(cc, label)) = block;
|
|
else
|
|
jit_set_last_error(cc, "create basic block failed");
|
|
|
|
return block;
|
|
}
|
|
|
|
JitBasicBlock *
|
|
jit_cc_resize_basic_block(JitCompContext *cc, JitBasicBlock *block, int n)
|
|
{
|
|
JitReg label = jit_basic_block_label(block);
|
|
JitInsn *insn = jit_basic_block_first_insn(block);
|
|
JitBasicBlock *new_block = jit_basic_block_new(label, n);
|
|
|
|
if (!new_block) {
|
|
jit_set_last_error(cc, "resize basic block failed");
|
|
return NULL;
|
|
}
|
|
|
|
jit_insn_unlink(block);
|
|
|
|
if (insn != block)
|
|
jit_insn_insert_before(insn, new_block);
|
|
|
|
bh_assert(*(jit_annl_basic_block(cc, label)) == block);
|
|
*(jit_annl_basic_block(cc, label)) = new_block;
|
|
jit_insn_delete(block);
|
|
|
|
return new_block;
|
|
}
|
|
|
|
bool
|
|
jit_cc_enable_insn_hash(JitCompContext *cc, unsigned n)
|
|
{
|
|
if (jit_anni_is_enabled__hash_link(cc))
|
|
return true;
|
|
|
|
if (!jit_anni_enable__hash_link(cc))
|
|
return false;
|
|
|
|
/* The table must not exist. */
|
|
bh_assert(!cc->_insn_hash_table._table);
|
|
|
|
/* Integer overflow cannot happen because n << 4G (at most several
|
|
times of 64K in the most extreme case). */
|
|
if (!(cc->_insn_hash_table._table =
|
|
jit_calloc(n * sizeof(*cc->_insn_hash_table._table)))) {
|
|
jit_anni_disable__hash_link(cc);
|
|
return false;
|
|
}
|
|
|
|
cc->_insn_hash_table._size = n;
|
|
return true;
|
|
}
|
|
|
|
void
|
|
jit_cc_disable_insn_hash(JitCompContext *cc)
|
|
{
|
|
jit_anni_disable__hash_link(cc);
|
|
jit_free(cc->_insn_hash_table._table);
|
|
cc->_insn_hash_table._table = NULL;
|
|
cc->_insn_hash_table._size = 0;
|
|
}
|
|
|
|
void
|
|
jit_cc_reset_insn_hash(JitCompContext *cc)
|
|
{
|
|
if (jit_anni_is_enabled__hash_link(cc))
|
|
memset(cc->_insn_hash_table._table, 0,
|
|
cc->_insn_hash_table._size
|
|
* sizeof(*cc->_insn_hash_table._table));
|
|
}
|
|
|
|
JitInsn *
|
|
jit_cc_set_insn_uid(JitCompContext *cc, JitInsn *insn)
|
|
{
|
|
if (insn) {
|
|
unsigned num = cc->_ann._insn_num;
|
|
unsigned capacity = cc->_ann._insn_capacity;
|
|
bool successful = true;
|
|
|
|
bh_assert(num <= capacity);
|
|
|
|
if (num == capacity) {
|
|
capacity = capacity > 0 ? (capacity + capacity / 2) : 64;
|
|
|
|
#define EMPTY_POSTFIX
|
|
#define ANN_INSN(TYPE, NAME) _JIT_REALLOC_ANN(TYPE, NAME, insn, EMPTY_POSTFIX)
|
|
#include "jit_ir.def"
|
|
#undef ANN_INSN
|
|
#undef EMPTY_POSTFIX
|
|
|
|
if (!successful) {
|
|
jit_set_last_error(cc, "set insn uid failed");
|
|
return NULL;
|
|
}
|
|
|
|
cc->_ann._insn_capacity = capacity;
|
|
}
|
|
|
|
cc->_ann._insn_num = num + 1;
|
|
insn->uid = num;
|
|
}
|
|
|
|
return insn;
|
|
}
|
|
|
|
JitInsn *
|
|
_jit_cc_set_insn_uid_for_new_insn(JitCompContext *cc, JitInsn *insn)
|
|
{
|
|
if (jit_cc_set_insn_uid(cc, insn))
|
|
return insn;
|
|
|
|
jit_insn_delete(insn);
|
|
return NULL;
|
|
}
|
|
|
|
JitReg
|
|
jit_cc_new_reg(JitCompContext *cc, unsigned kind)
|
|
{
|
|
unsigned num = jit_cc_reg_num(cc, kind);
|
|
unsigned capacity = cc->_ann._reg_capacity[kind];
|
|
bool successful = true;
|
|
|
|
bh_assert(num <= capacity);
|
|
|
|
if (num == capacity) {
|
|
capacity = (capacity == 0
|
|
/* Initialize the capacity to be larger than hard
|
|
register number. */
|
|
? cc->hreg_info->info[kind].num + 16
|
|
: capacity + capacity / 2);
|
|
|
|
#define ANN_REG(TYPE, NAME) _JIT_REALLOC_ANN(TYPE, NAME, reg, [kind])
|
|
#include "jit_ir.def"
|
|
#undef ANN_REG
|
|
|
|
if (!successful) {
|
|
jit_set_last_error(cc, "create register failed");
|
|
return 0;
|
|
}
|
|
|
|
cc->_ann._reg_capacity[kind] = capacity;
|
|
}
|
|
|
|
cc->_ann._reg_num[kind] = num + 1;
|
|
|
|
return jit_reg_new(kind, num);
|
|
}
|
|
|
|
#undef _JIT_REALLOC_ANN
|
|
|
|
#define ANN_LABEL(TYPE, NAME) \
|
|
bool jit_annl_enable_##NAME(JitCompContext *cc) \
|
|
{ \
|
|
if (cc->_ann._label_##NAME##_enabled) \
|
|
return true; \
|
|
\
|
|
if (cc->_ann._label_capacity > 0 \
|
|
&& !(cc->_ann._label_##NAME = \
|
|
jit_calloc(cc->_ann._label_capacity * sizeof(TYPE)))) { \
|
|
jit_set_last_error(cc, "annl enable " #NAME "failed"); \
|
|
return false; \
|
|
} \
|
|
\
|
|
cc->_ann._label_##NAME##_enabled = 1; \
|
|
return true; \
|
|
}
|
|
#define ANN_INSN(TYPE, NAME) \
|
|
bool jit_anni_enable_##NAME(JitCompContext *cc) \
|
|
{ \
|
|
if (cc->_ann._insn_##NAME##_enabled) \
|
|
return true; \
|
|
\
|
|
if (cc->_ann._insn_capacity > 0 \
|
|
&& !(cc->_ann._insn_##NAME = \
|
|
jit_calloc(cc->_ann._insn_capacity * sizeof(TYPE)))) { \
|
|
jit_set_last_error(cc, "anni enable " #NAME "failed"); \
|
|
return false; \
|
|
} \
|
|
\
|
|
cc->_ann._insn_##NAME##_enabled = 1; \
|
|
return true; \
|
|
}
|
|
#define ANN_REG(TYPE, NAME) \
|
|
bool jit_annr_enable_##NAME(JitCompContext *cc) \
|
|
{ \
|
|
unsigned k; \
|
|
\
|
|
if (cc->_ann._reg_##NAME##_enabled) \
|
|
return true; \
|
|
\
|
|
for (k = JIT_REG_KIND_VOID; k < JIT_REG_KIND_L32; k++) \
|
|
if (cc->_ann._reg_capacity[k] > 0 \
|
|
&& !(cc->_ann._reg_##NAME[k] = jit_calloc( \
|
|
cc->_ann._reg_capacity[k] * sizeof(TYPE)))) { \
|
|
jit_set_last_error(cc, "annr enable " #NAME "failed"); \
|
|
jit_annr_disable_##NAME(cc); \
|
|
return false; \
|
|
} \
|
|
\
|
|
cc->_ann._reg_##NAME##_enabled = 1; \
|
|
return true; \
|
|
}
|
|
#include "jit_ir.def"
|
|
#undef ANN_LABEL
|
|
#undef ANN_INSN
|
|
#undef ANN_REG
|
|
|
|
#define ANN_LABEL(TYPE, NAME) \
|
|
void jit_annl_disable_##NAME(JitCompContext *cc) \
|
|
{ \
|
|
jit_free(cc->_ann._label_##NAME); \
|
|
cc->_ann._label_##NAME = NULL; \
|
|
cc->_ann._label_##NAME##_enabled = 0; \
|
|
}
|
|
#define ANN_INSN(TYPE, NAME) \
|
|
void jit_anni_disable_##NAME(JitCompContext *cc) \
|
|
{ \
|
|
jit_free(cc->_ann._insn_##NAME); \
|
|
cc->_ann._insn_##NAME = NULL; \
|
|
cc->_ann._insn_##NAME##_enabled = 0; \
|
|
}
|
|
#define ANN_REG(TYPE, NAME) \
|
|
void jit_annr_disable_##NAME(JitCompContext *cc) \
|
|
{ \
|
|
unsigned k; \
|
|
\
|
|
for (k = JIT_REG_KIND_VOID; k < JIT_REG_KIND_L32; k++) { \
|
|
jit_free(cc->_ann._reg_##NAME[k]); \
|
|
cc->_ann._reg_##NAME[k] = NULL; \
|
|
} \
|
|
\
|
|
cc->_ann._reg_##NAME##_enabled = 0; \
|
|
}
|
|
#include "jit_ir.def"
|
|
#undef ANN_LABEL
|
|
#undef ANN_INSN
|
|
#undef ANN_REG
|
|
|
|
char *
|
|
jit_get_last_error(JitCompContext *cc)
|
|
{
|
|
return cc->last_error[0] == '\0' ? NULL : cc->last_error;
|
|
}
|
|
|
|
void
|
|
jit_set_last_error_v(JitCompContext *cc, const char *format, ...)
|
|
{
|
|
va_list args;
|
|
va_start(args, format);
|
|
vsnprintf(cc->last_error, sizeof(cc->last_error), format, args);
|
|
va_end(args);
|
|
}
|
|
|
|
void
|
|
jit_set_last_error(JitCompContext *cc, const char *error)
|
|
{
|
|
if (error)
|
|
snprintf(cc->last_error, sizeof(cc->last_error), "Error: %s", error);
|
|
else
|
|
cc->last_error[0] = '\0';
|
|
}
|
|
|
|
bool
|
|
jit_cc_update_cfg(JitCompContext *cc)
|
|
{
|
|
JitBasicBlock *block;
|
|
unsigned block_index, end, succ_index, idx;
|
|
JitReg *target;
|
|
bool retval = false;
|
|
|
|
if (!jit_annl_enable_pred_num(cc))
|
|
return false;
|
|
|
|
/* Update pred_num of all blocks. */
|
|
JIT_FOREACH_BLOCK_ENTRY_EXIT(cc, block_index, end, block)
|
|
{
|
|
JitRegVec succs = jit_basic_block_succs(block);
|
|
|
|
JIT_REG_VEC_FOREACH(succs, succ_index, target)
|
|
if (jit_reg_is_kind(L32, *target))
|
|
*(jit_annl_pred_num(cc, *target)) += 1;
|
|
}
|
|
|
|
/* Resize predecessor vectors of body blocks. */
|
|
JIT_FOREACH_BLOCK(cc, block_index, end, block)
|
|
{
|
|
if (!jit_cc_resize_basic_block(
|
|
cc, block,
|
|
*(jit_annl_pred_num(cc, jit_basic_block_label(block)))))
|
|
goto cleanup_and_return;
|
|
}
|
|
|
|
/* Fill in predecessor vectors all blocks. */
|
|
JIT_FOREACH_BLOCK_REVERSE_ENTRY_EXIT(cc, block_index, block)
|
|
{
|
|
JitRegVec succs = jit_basic_block_succs(block), preds;
|
|
|
|
JIT_REG_VEC_FOREACH(succs, succ_index, target)
|
|
if (jit_reg_is_kind(L32, *target)) {
|
|
preds = jit_basic_block_preds(*(jit_annl_basic_block(cc, *target)));
|
|
bh_assert(*(jit_annl_pred_num(cc, *target)) > 0);
|
|
idx = *(jit_annl_pred_num(cc, *target)) - 1;
|
|
*(jit_annl_pred_num(cc, *target)) = idx;
|
|
*(jit_reg_vec_at(&preds, idx)) = jit_basic_block_label(block);
|
|
}
|
|
}
|
|
|
|
retval = true;
|
|
|
|
cleanup_and_return:
|
|
jit_annl_disable_pred_num(cc);
|
|
return retval;
|
|
}
|
|
|
|
void
|
|
jit_value_stack_push(JitValueStack *stack, JitValue *value)
|
|
{
|
|
if (!stack->value_list_head)
|
|
stack->value_list_head = stack->value_list_end = value;
|
|
else {
|
|
stack->value_list_end->next = value;
|
|
value->prev = stack->value_list_end;
|
|
stack->value_list_end = value;
|
|
}
|
|
}
|
|
|
|
JitValue *
|
|
jit_value_stack_pop(JitValueStack *stack)
|
|
{
|
|
JitValue *value = stack->value_list_end;
|
|
|
|
bh_assert(stack->value_list_end);
|
|
|
|
if (stack->value_list_head == stack->value_list_end)
|
|
stack->value_list_head = stack->value_list_end = NULL;
|
|
else {
|
|
stack->value_list_end = stack->value_list_end->prev;
|
|
stack->value_list_end->next = NULL;
|
|
value->prev = NULL;
|
|
}
|
|
|
|
return value;
|
|
}
|
|
|
|
void
|
|
jit_value_stack_destroy(JitValueStack *stack)
|
|
{
|
|
JitValue *value = stack->value_list_head, *p;
|
|
|
|
while (value) {
|
|
p = value->next;
|
|
jit_free(value);
|
|
value = p;
|
|
}
|
|
|
|
stack->value_list_head = NULL;
|
|
stack->value_list_end = NULL;
|
|
}
|
|
|
|
void
|
|
jit_block_stack_push(JitBlockStack *stack, JitBlock *block)
|
|
{
|
|
if (!stack->block_list_head)
|
|
stack->block_list_head = stack->block_list_end = block;
|
|
else {
|
|
stack->block_list_end->next = block;
|
|
block->prev = stack->block_list_end;
|
|
stack->block_list_end = block;
|
|
}
|
|
}
|
|
|
|
JitBlock *
|
|
jit_block_stack_top(JitBlockStack *stack)
|
|
{
|
|
return stack->block_list_end;
|
|
}
|
|
|
|
JitBlock *
|
|
jit_block_stack_pop(JitBlockStack *stack)
|
|
{
|
|
JitBlock *block = stack->block_list_end;
|
|
|
|
bh_assert(stack->block_list_end);
|
|
|
|
if (stack->block_list_head == stack->block_list_end)
|
|
stack->block_list_head = stack->block_list_end = NULL;
|
|
else {
|
|
stack->block_list_end = stack->block_list_end->prev;
|
|
stack->block_list_end->next = NULL;
|
|
block->prev = NULL;
|
|
}
|
|
|
|
return block;
|
|
}
|
|
|
|
void
|
|
jit_block_stack_destroy(JitBlockStack *stack)
|
|
{
|
|
JitBlock *block = stack->block_list_head, *p;
|
|
|
|
while (block) {
|
|
p = block->next;
|
|
jit_value_stack_destroy(&block->value_stack);
|
|
jit_block_destroy(block);
|
|
block = p;
|
|
}
|
|
|
|
stack->block_list_head = NULL;
|
|
stack->block_list_end = NULL;
|
|
}
|
|
|
|
bool
|
|
jit_block_add_incoming_insn(JitBlock *block, JitInsn *insn, uint32 opnd_idx)
|
|
{
|
|
JitIncomingInsn *incoming_insn;
|
|
|
|
if (!(incoming_insn = jit_calloc((uint32)sizeof(JitIncomingInsn))))
|
|
return false;
|
|
|
|
incoming_insn->insn = insn;
|
|
incoming_insn->opnd_idx = opnd_idx;
|
|
incoming_insn->next = block->incoming_insns_for_end_bb;
|
|
block->incoming_insns_for_end_bb = incoming_insn;
|
|
return true;
|
|
}
|
|
|
|
void
|
|
jit_block_destroy(JitBlock *block)
|
|
{
|
|
JitIncomingInsn *incoming_insn, *incoming_insn_next;
|
|
|
|
jit_value_stack_destroy(&block->value_stack);
|
|
if (block->param_types)
|
|
jit_free(block->param_types);
|
|
if (block->result_types)
|
|
jit_free(block->result_types);
|
|
|
|
incoming_insn = block->incoming_insns_for_end_bb;
|
|
while (incoming_insn) {
|
|
incoming_insn_next = incoming_insn->next;
|
|
jit_free(incoming_insn);
|
|
incoming_insn = incoming_insn_next;
|
|
}
|
|
|
|
jit_free(block);
|
|
}
|
|
|
|
static inline uint8
|
|
to_stack_value_type(uint8 type)
|
|
{
|
|
#if WASM_ENABLE_REF_TYPES != 0
|
|
if (type == VALUE_TYPE_EXTERNREF || type == VALUE_TYPE_FUNCREF)
|
|
return VALUE_TYPE_I32;
|
|
#endif
|
|
return type;
|
|
}
|
|
|
|
bool
|
|
jit_cc_pop_value(JitCompContext *cc, uint8 type, JitReg *p_value)
|
|
{
|
|
JitValue *jit_value = NULL;
|
|
JitReg value = 0;
|
|
|
|
if (!jit_block_stack_top(&cc->block_stack)) {
|
|
jit_set_last_error(cc, "WASM block stack underflow");
|
|
return false;
|
|
}
|
|
if (!jit_block_stack_top(&cc->block_stack)->value_stack.value_list_end) {
|
|
jit_set_last_error(cc, "WASM data stack underflow");
|
|
return false;
|
|
}
|
|
|
|
jit_value = jit_value_stack_pop(
|
|
&jit_block_stack_top(&cc->block_stack)->value_stack);
|
|
bh_assert(jit_value);
|
|
|
|
if (jit_value->type != to_stack_value_type(type)) {
|
|
jit_set_last_error(cc, "invalid WASM stack data type");
|
|
jit_free(jit_value);
|
|
return false;
|
|
}
|
|
|
|
switch (jit_value->type) {
|
|
case VALUE_TYPE_I32:
|
|
value = pop_i32(cc->jit_frame);
|
|
break;
|
|
case VALUE_TYPE_I64:
|
|
value = pop_i64(cc->jit_frame);
|
|
break;
|
|
case VALUE_TYPE_F32:
|
|
value = pop_f32(cc->jit_frame);
|
|
break;
|
|
case VALUE_TYPE_F64:
|
|
value = pop_f64(cc->jit_frame);
|
|
break;
|
|
default:
|
|
bh_assert(0);
|
|
break;
|
|
}
|
|
|
|
bh_assert(cc->jit_frame->sp == jit_value->value);
|
|
bh_assert(value == jit_value->value->reg);
|
|
*p_value = value;
|
|
jit_free(jit_value);
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
jit_cc_push_value(JitCompContext *cc, uint8 type, JitReg value)
|
|
{
|
|
JitValue *jit_value;
|
|
|
|
if (!jit_block_stack_top(&cc->block_stack)) {
|
|
jit_set_last_error(cc, "WASM block stack underflow");
|
|
return false;
|
|
}
|
|
|
|
if (!(jit_value = jit_calloc(sizeof(JitValue)))) {
|
|
jit_set_last_error(cc, "allocate memory failed");
|
|
return false;
|
|
}
|
|
|
|
bh_assert(value);
|
|
|
|
jit_value->type = to_stack_value_type(type);
|
|
jit_value->value = cc->jit_frame->sp;
|
|
jit_value_stack_push(&jit_block_stack_top(&cc->block_stack)->value_stack,
|
|
jit_value);
|
|
|
|
switch (jit_value->type) {
|
|
case VALUE_TYPE_I32:
|
|
push_i32(cc->jit_frame, value);
|
|
break;
|
|
case VALUE_TYPE_I64:
|
|
push_i64(cc->jit_frame, value);
|
|
break;
|
|
case VALUE_TYPE_F32:
|
|
push_f32(cc->jit_frame, value);
|
|
break;
|
|
case VALUE_TYPE_F64:
|
|
push_f64(cc->jit_frame, value);
|
|
break;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
_jit_insn_check_opnd_access_Reg(const JitInsn *insn, unsigned n)
|
|
{
|
|
unsigned opcode = insn->opcode;
|
|
return (insn_opnd_kind[opcode] == JIT_OPND_KIND_Reg
|
|
&& n < insn_opnd_num[opcode]);
|
|
}
|
|
|
|
bool
|
|
_jit_insn_check_opnd_access_VReg(const JitInsn *insn, unsigned n)
|
|
{
|
|
unsigned opcode = insn->opcode;
|
|
return (insn_opnd_kind[opcode] == JIT_OPND_KIND_VReg
|
|
&& n < insn->_opnd._opnd_VReg._reg_num);
|
|
}
|
|
|
|
bool
|
|
_jit_insn_check_opnd_access_LookupSwitch(const JitInsn *insn)
|
|
{
|
|
unsigned opcode = insn->opcode;
|
|
return (insn_opnd_kind[opcode] == JIT_OPND_KIND_LookupSwitch);
|
|
}
|
|
|
|
bool
|
|
jit_lock_reg_in_insn(JitCompContext *cc, JitInsn *the_insn, JitReg reg_to_lock)
|
|
{
|
|
bool ret = false;
|
|
JitInsn *prevent_spill = NULL;
|
|
JitInsn *indicate_using = NULL;
|
|
|
|
if (!the_insn)
|
|
goto just_return;
|
|
|
|
if (jit_cc_is_hreg_fixed(cc, reg_to_lock)) {
|
|
ret = true;
|
|
goto just_return;
|
|
}
|
|
|
|
/**
|
|
* give the virtual register of the locked hard register a minimum, non-zero
|
|
* distance, * so as to prevent it from being spilled out
|
|
*/
|
|
prevent_spill = jit_insn_new_MOV(reg_to_lock, reg_to_lock);
|
|
if (!prevent_spill)
|
|
goto just_return;
|
|
|
|
jit_insn_insert_before(the_insn, prevent_spill);
|
|
|
|
/**
|
|
* announce the locked hard register is being used, and do necessary spill
|
|
* ASAP
|
|
*/
|
|
indicate_using = jit_insn_new_MOV(reg_to_lock, reg_to_lock);
|
|
if (!indicate_using)
|
|
goto just_return;
|
|
|
|
jit_insn_insert_after(the_insn, indicate_using);
|
|
|
|
ret = true;
|
|
|
|
just_return:
|
|
if (!ret)
|
|
jit_set_last_error(cc, "generate insn failed");
|
|
return ret;
|
|
}
|