summaryrefslogtreecommitdiffstats
path: root/fluent-bit/lib/wasm-micro-runtime-WAMR-1.2.2/core/iwasm/fast-jit/jit_ir.c
diff options
context:
space:
mode:
Diffstat (limited to 'fluent-bit/lib/wasm-micro-runtime-WAMR-1.2.2/core/iwasm/fast-jit/jit_ir.c')
-rw-r--r--fluent-bit/lib/wasm-micro-runtime-WAMR-1.2.2/core/iwasm/fast-jit/jit_ir.c1427
1 files changed, 1427 insertions, 0 deletions
diff --git a/fluent-bit/lib/wasm-micro-runtime-WAMR-1.2.2/core/iwasm/fast-jit/jit_ir.c b/fluent-bit/lib/wasm-micro-runtime-WAMR-1.2.2/core/iwasm/fast-jit/jit_ir.c
new file mode 100644
index 000000000..68503e3f5
--- /dev/null
+++ b/fluent-bit/lib/wasm-micro-runtime-WAMR-1.2.2/core/iwasm/fast-jit/jit_ir.c
@@ -0,0 +1,1427 @@
+/*
+ * 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;
+}