void _jit_gen_spill_reg(jit_gencode_t gen, int reg, int other_reg, jit_value_t value) { int offset; /* Make sure that we have sufficient space */ jit_cache_setup_output(32); if(flush_if_too_far(gen)) { jit_gen_load_inst_ptr(gen, inst); } /* Output an appropriate instruction to spill the value */ if(value->has_global_register) { arm_mov_reg_reg(inst, _jit_reg_info[value->global_reg].cpu_reg, _jit_reg_info[reg].cpu_reg); } else { _jit_gen_fix_value(value); offset = (int)(value->frame_offset); if(reg < 16) { arm_store_membase(inst, reg, ARM_FP, offset); if(other_reg != -1) { /* Spill the other word register in a pair */ offset += sizeof(void *); arm_store_membase(inst, other_reg, ARM_FP, offset); } } else if(jit_type_normalize(value->type)->kind == JIT_TYPE_FLOAT32) { arm_store_membase_float32(inst, reg - 16, ARM_FP, offset); } else { arm_store_membase_float64(inst, reg - 16, ARM_FP, offset); } } /* End the code output process */ jit_cache_end_output(); }
void _jit_gen_spill_top(jit_gencode_t gen, int reg, jit_value_t value, int pop) { int offset; if(IS_FLOAT_REG(reg)) { /* Make sure that we have sufficient space */ jit_cache_setup_output(16); /* Fix the value in place within the local variable frame */ _jit_gen_fix_value(value); /* Output an appropriate instruction to spill the value */ offset = (int)(value->frame_offset); /* Spill the top of the floating-point register stack */ switch(jit_type_normalize(value->type)->kind) { case JIT_TYPE_FLOAT32: { x86_fst_membase(inst, X86_EBP, offset, 0, pop); } break; case JIT_TYPE_FLOAT64: { x86_fst_membase(inst, X86_EBP, offset, 1, pop); } break; case JIT_TYPE_NFLOAT: { x86_fst80_membase(inst, X86_EBP, offset); if(!pop) { x86_fld80_membase(inst, X86_EBP, offset); } } break; } /* End the code output process */ jit_cache_end_output(); } }
static void store_value(jit_gencode_t gen, jit_value_t value) { int opcode; jit_nint offset; /* Fix the value in place within the local variable frame */ _jit_gen_fix_value(value); /* Output an appropriate instruction to store the value */ offset = value->frame_offset; if(offset >= 0) { opcode = _jit_store_opcode(JIT_INTERP_OP_STL_0_BYTE, 0, value->type); } else { opcode = _jit_store_opcode(JIT_INTERP_OP_STA_0_BYTE, 0, value->type); offset = -(offset + 1); } jit_cache_opcode(gen, opcode); jit_cache_native(gen, offset); }
static void load_value(jit_gencode_t gen, jit_value_t value, int index) { int opcode; jit_nint offset; if(value->is_constant) { /* Determine the type of constant to be loaded */ switch(jit_type_normalize(value->type)->kind) { case JIT_TYPE_SBYTE: case JIT_TYPE_UBYTE: case JIT_TYPE_SHORT: case JIT_TYPE_USHORT: case JIT_TYPE_INT: case JIT_TYPE_UINT: jit_cache_opcode(gen, JIT_INTERP_OP_LDC_0_INT + index); jit_cache_native(gen, (jit_nint)(value->address)); break; case JIT_TYPE_LONG: case JIT_TYPE_ULONG: { jit_long long_value; long_value = jit_value_get_long_constant(value); jit_cache_opcode(gen, JIT_INTERP_OP_LDC_0_LONG + index); #ifdef JIT_NATIVE_INT64 jit_cache_native(gen, long_value); #else jit_cache_add_n(gen, &long_value, sizeof(long_value)); #endif break; } case JIT_TYPE_FLOAT32: { jit_float32 float32_value; float32_value = jit_value_get_float32_constant(value); jit_cache_opcode(gen, JIT_INTERP_OP_LDC_0_FLOAT32 + index); jit_cache_add_n(gen, &float32_value, sizeof(float32_value)); break; } case JIT_TYPE_FLOAT64: { jit_float64 float64_value; float64_value = jit_value_get_float64_constant(value); jit_cache_opcode(gen, JIT_INTERP_OP_LDC_0_FLOAT64 + index); jit_cache_add_n (gen, &float64_value, sizeof(float64_value)); break; } case JIT_TYPE_NFLOAT: { jit_nfloat nfloat_value; nfloat_value = jit_value_get_nfloat_constant(value); jit_cache_opcode(gen, JIT_INTERP_OP_LDC_0_NFLOAT + index); jit_cache_add_n (gen, &nfloat_value, sizeof(nfloat_value)); break; } } } else { /* Fix the position of the value in the stack frame */ _jit_gen_fix_value(value); /* Generate a local or argument access opcode, as appropriate */ if(value->frame_offset >= 0) { /* Load a local variable value onto the stack */ switch(index) { case 0: opcode = JIT_INTERP_OP_LDL_0_SBYTE; break; case 1: opcode = JIT_INTERP_OP_LDL_1_SBYTE; break; case 2: opcode = JIT_INTERP_OP_LDL_2_SBYTE; break; default: return; } opcode = _jit_load_opcode(opcode, value->type); offset = value->frame_offset; } else { /* Load an argument value onto the stack */ switch(index) { case 0: opcode = JIT_INTERP_OP_LDA_0_SBYTE; break; case 1: opcode = JIT_INTERP_OP_LDA_1_SBYTE; break; case 2: opcode = JIT_INTERP_OP_LDA_2_SBYTE; break; default: return; } opcode = _jit_load_opcode(opcode, value->type); offset = -(value->frame_offset + 1); } jit_cache_opcode(gen, opcode); jit_cache_native(gen, offset); } }
/*@ * @deftypefun void _jit_gen_insn (jit_gencode_t @var{gen}, jit_function_t @var{func}, jit_block_t @var{block}, jit_insn_t @var{insn}) * Generate native code for the specified @var{insn}. This function should * call the appropriate register allocation routines, output the instruction, * and then arrange for the result to be placed in an appropriate register * or memory destination. * @end deftypefun @*/ void _jit_gen_insn(jit_gencode_t gen, jit_function_t func, jit_block_t block, jit_insn_t insn) { jit_label_t label; void **pc; jit_nint offset; jit_nint size; switch(insn->opcode) { case JIT_OP_BR_IEQ: case JIT_OP_BR_INE: case JIT_OP_BR_ILT: case JIT_OP_BR_ILT_UN: case JIT_OP_BR_ILE: case JIT_OP_BR_ILE_UN: case JIT_OP_BR_IGT: case JIT_OP_BR_IGT_UN: case JIT_OP_BR_IGE: case JIT_OP_BR_IGE_UN: case JIT_OP_BR_LEQ: case JIT_OP_BR_LNE: case JIT_OP_BR_LLT: case JIT_OP_BR_LLT_UN: case JIT_OP_BR_LLE: case JIT_OP_BR_LLE_UN: case JIT_OP_BR_LGT: case JIT_OP_BR_LGT_UN: case JIT_OP_BR_LGE: case JIT_OP_BR_LGE_UN: case JIT_OP_BR_FEQ: case JIT_OP_BR_FNE: case JIT_OP_BR_FLT: case JIT_OP_BR_FLE: case JIT_OP_BR_FGT: case JIT_OP_BR_FGE: case JIT_OP_BR_FLT_INV: case JIT_OP_BR_FLE_INV: case JIT_OP_BR_FGT_INV: case JIT_OP_BR_FGE_INV: case JIT_OP_BR_DEQ: case JIT_OP_BR_DNE: case JIT_OP_BR_DLT: case JIT_OP_BR_DLE: case JIT_OP_BR_DGT: case JIT_OP_BR_DGE: case JIT_OP_BR_DLT_INV: case JIT_OP_BR_DLE_INV: case JIT_OP_BR_DGT_INV: case JIT_OP_BR_DGE_INV: case JIT_OP_BR_NFEQ: case JIT_OP_BR_NFNE: case JIT_OP_BR_NFLT: case JIT_OP_BR_NFLE: case JIT_OP_BR_NFGT: case JIT_OP_BR_NFGE: case JIT_OP_BR_NFLT_INV: case JIT_OP_BR_NFLE_INV: case JIT_OP_BR_NFGT_INV: case JIT_OP_BR_NFGE_INV: /* Binary branch */ load_value(gen, insn->value2, 2); /* Fall through */ case JIT_OP_BR_IFALSE: case JIT_OP_BR_ITRUE: case JIT_OP_BR_LFALSE: case JIT_OP_BR_LTRUE: /* Unary branch */ load_value(gen, insn->value1, 1); /* Fall through */ case JIT_OP_BR: case JIT_OP_CALL_FINALLY: /* Unconditional branch */ branch: label = (jit_label_t)(insn->dest); pc = (void **)(gen->ptr); jit_cache_opcode(gen, insn->opcode); block = jit_block_from_label(func, label); if(!block) { break; } if(block->address) { /* We already know the address of the block */ jit_cache_native(gen, ((void **)(block->address)) - pc); } else { /* Record this position on the block's fixup list */ jit_cache_native(gen, block->fixup_list); block->fixup_list = (void *)pc; } break; case JIT_OP_CALL_FILTER: /* Branch to a filter subroutine, load the filter parameter to the r0 register */ load_value(gen, insn->value1, 0); goto branch; case JIT_OP_JUMP_TABLE: { jit_label_t *labels; jit_nint num_labels; jit_nint index; load_value(gen, insn->dest, 0); labels = (jit_label_t *) insn->value1->address; num_labels = insn->value2->address; jit_cache_opcode(gen, insn->opcode); jit_cache_native(gen, num_labels); for(index = 0; index < num_labels; index++) { block = jit_block_from_label(func, labels[index]); if(!block) { return; } if(block->address) { /* We already know the address of the block */ jit_cache_native(gen, block->address); } else { /* Record this position on the block's fixup list */ pc = (void **)(gen->ptr); jit_cache_native(gen, block->fixup_absolute_list); block->fixup_absolute_list = pc; } } break; } case JIT_OP_ADDRESS_OF_LABEL: /* Get the address of a particular label */ label = (jit_label_t)(insn->value1); block = jit_block_from_label(func, label); if(!block) { break; } pc = (void **)(gen->ptr); jit_cache_opcode(gen, insn->opcode); if(block->address) { /* We already know the address of the block */ jit_cache_native(gen, ((void **)(block->address)) - pc); } else { /* Record this position on the block's fixup list */ jit_cache_native(gen, block->fixup_list); block->fixup_list = (void *)pc; } store_value(gen, insn->dest); break; case JIT_OP_CALL: case JIT_OP_CALL_TAIL: /* Call a function, whose pointer is supplied explicitly */ jit_cache_opcode(gen, insn->opcode); jit_cache_native(gen, (jit_nint)(insn->dest)); break; case JIT_OP_CALL_INDIRECT: case JIT_OP_CALL_INDIRECT_TAIL: /* Call a function, whose pointer is supplied in the register */ load_value(gen, insn->value1, 1); jit_cache_opcode(gen, insn->opcode); jit_cache_native(gen, (jit_nint)(insn->value2)); jit_cache_native(gen, (jit_nint) (jit_type_num_params((jit_type_t)(insn->value2)))); break; case JIT_OP_CALL_VTABLE_PTR: case JIT_OP_CALL_VTABLE_PTR_TAIL: /* Call a function, whose vtable pointer is supplied in the register */ load_value(gen, insn->value1, 1); jit_cache_opcode(gen, insn->opcode); break; case JIT_OP_CALL_EXTERNAL: case JIT_OP_CALL_EXTERNAL_TAIL: /* Call a native function, whose pointer is supplied explicitly */ jit_cache_opcode(gen, insn->opcode); jit_cache_native(gen, (jit_nint)(insn->value2)); jit_cache_native(gen, (jit_nint)(insn->dest)); jit_cache_native(gen, (jit_nint) (jit_type_num_params((jit_type_t)(insn->value2)))); break; case JIT_OP_RETURN: /* Return from the current function with no result */ jit_cache_opcode(gen, JIT_OP_RETURN); break; case JIT_OP_RETURN_INT: case JIT_OP_RETURN_LONG: case JIT_OP_RETURN_FLOAT32: case JIT_OP_RETURN_FLOAT64: case JIT_OP_RETURN_NFLOAT: /* Return from the current function with a specific result */ load_value(gen, insn->value1, 1); jit_cache_opcode(gen, insn->opcode); break; case JIT_OP_RETURN_SMALL_STRUCT: /* Return from current function with a small structure result */ load_value(gen, insn->value1, 1); jit_cache_opcode(gen, insn->opcode); jit_cache_native(gen, jit_value_get_nint_constant(insn->value2)); break; case JIT_OP_SETUP_FOR_NESTED: /* TODO!!! */ /* Set up to call a nested child */ jit_cache_opcode(gen, insn->opcode); adjust_working(gen, 2); break; case JIT_OP_SETUP_FOR_SIBLING: /* TODO!!! */ /* Set up to call a nested sibling */ jit_cache_opcode(gen, insn->opcode); jit_cache_native(gen, jit_value_get_nint_constant(insn->value1)); adjust_working(gen, 2); break; case JIT_OP_IMPORT: /* Import a local variable from an outer nested scope */ _jit_gen_fix_value(insn->value1); if(insn->value1->frame_offset >= 0) { jit_cache_opcode(gen, JIT_INTERP_OP_IMPORT_LOCAL); jit_cache_native(gen, insn->value1->frame_offset); jit_cache_native(gen, jit_value_get_nint_constant(insn->value2)); } else { jit_cache_opcode(gen, JIT_INTERP_OP_IMPORT_ARG); jit_cache_native(gen, -(insn->value1->frame_offset + 1)); jit_cache_native(gen, jit_value_get_nint_constant(insn->value2)); } store_value(gen, insn->dest); break; case JIT_OP_THROW: /* Throw an exception */ load_value(gen, insn->value1, 1); jit_cache_opcode(gen, insn->opcode); break; case JIT_OP_LOAD_PC: case JIT_OP_LOAD_EXCEPTION_PC: /* Load the current program counter onto the stack */ jit_cache_opcode(gen, insn->opcode); store_value(gen, insn->dest); break; case JIT_OP_CALL_FILTER_RETURN: /* The r0 register currently contains "dest" */ store_value(gen, insn->dest); break; case JIT_OP_ENTER_FINALLY: /* Record that the finally return address is on the stack */ ++(gen->extra_working_space); break; case JIT_OP_LEAVE_FINALLY: /* Leave a finally clause */ jit_cache_opcode(gen, insn->opcode); break; case JIT_OP_ENTER_FILTER: /* The top of the stack contains the return address, the r0 register contains the "dest" (filter parameter). */ ++(gen->extra_working_space); store_value(gen, insn->dest); break; case JIT_OP_LEAVE_FILTER: /* Leave a filter clause, returning a particular value */ load_value(gen, insn->value1, 0); jit_cache_opcode(gen, insn->opcode); break; case JIT_OP_INCOMING_REG: /* Store incoming value (in interpreter this is used to pass an exception object to the catcher) */ store_value(gen, insn->value1); break; case JIT_OP_RETURN_REG: /* Push a function return value back onto the stack */ switch(jit_type_normalize(insn->value1->type)->kind) { case JIT_TYPE_SBYTE: case JIT_TYPE_UBYTE: case JIT_TYPE_SHORT: case JIT_TYPE_USHORT: case JIT_TYPE_INT: case JIT_TYPE_UINT: jit_cache_opcode(gen, JIT_INTERP_OP_LDR_0_INT); store_value(gen, insn->value1); break; case JIT_TYPE_LONG: case JIT_TYPE_ULONG: jit_cache_opcode(gen, JIT_INTERP_OP_LDR_0_LONG); store_value(gen, insn->value1); break; case JIT_TYPE_FLOAT32: jit_cache_opcode(gen, JIT_INTERP_OP_LDR_0_FLOAT32); store_value(gen, insn->value1); break; case JIT_TYPE_FLOAT64: jit_cache_opcode(gen, JIT_INTERP_OP_LDR_0_FLOAT64); store_value(gen, insn->value1); break; case JIT_TYPE_NFLOAT: jit_cache_opcode(gen, JIT_INTERP_OP_LDR_0_NFLOAT); store_value(gen, insn->value1); break; } break; case JIT_OP_COPY_LOAD_SBYTE: case JIT_OP_COPY_LOAD_UBYTE: case JIT_OP_COPY_LOAD_SHORT: case JIT_OP_COPY_LOAD_USHORT: case JIT_OP_COPY_INT: case JIT_OP_COPY_LONG: case JIT_OP_COPY_FLOAT32: case JIT_OP_COPY_FLOAT64: case JIT_OP_COPY_NFLOAT: case JIT_OP_COPY_STORE_BYTE: case JIT_OP_COPY_STORE_SHORT: /* Copy a value from one temporary variable to another */ load_value(gen, insn->value1, 0); store_value(gen, insn->dest); break; case JIT_OP_COPY_STRUCT: /* Copy a struct from one address to another */ load_value(gen, insn->dest, 0); load_value(gen, insn->value1, 1); size = (jit_nint)jit_type_get_size(jit_value_get_type(insn->dest)); jit_cache_opcode(gen, insn->opcode); jit_cache_native(gen, size); break; case JIT_OP_ADDRESS_OF: /* Get the address of a local variable */ _jit_gen_fix_value(insn->value1); if(insn->value1->frame_offset >= 0) { jit_cache_opcode(gen, JIT_INTERP_OP_LDLA_0); jit_cache_native(gen, insn->value1->frame_offset); } else { jit_cache_opcode(gen, JIT_INTERP_OP_LDAA_0); jit_cache_native(gen, -(insn->value1->frame_offset + 1)); } store_value(gen, insn->dest); break; case JIT_OP_PUSH_INT: case JIT_OP_PUSH_LONG: case JIT_OP_PUSH_FLOAT32: case JIT_OP_PUSH_FLOAT64: case JIT_OP_PUSH_NFLOAT: /* Push an item onto the stack, ready for a function call */ load_value(gen, insn->value1, 1); jit_cache_opcode(gen, insn->opcode); adjust_working(gen, 1); break; case JIT_OP_PUSH_STRUCT: /* Load the pointer value */ load_value(gen, insn->value1, 1); /* Push the structure at the designated pointer */ size = jit_value_get_nint_constant(insn->value2); jit_cache_opcode(gen, insn->opcode); jit_cache_native(gen, size); adjust_working(gen, JIT_NUM_ITEMS_IN_STRUCT(size)); break; case JIT_OP_PUSH_RETURN_AREA_PTR: /* Push the address of the interpreter's return area */ jit_cache_opcode(gen, insn->opcode); adjust_working(gen, 1); break; case JIT_OP_POP_STACK: /* Pop parameter values from the stack after a function returns */ size = jit_value_get_nint_constant(insn->value1); if(size == 1) { jit_cache_opcode(gen, JIT_INTERP_OP_POP); } else if(size == 2) { jit_cache_opcode(gen, JIT_INTERP_OP_POP_2); } else if(size == 3) { jit_cache_opcode(gen, JIT_INTERP_OP_POP_3); } else if(size != 0) { jit_cache_opcode(gen, JIT_OP_POP_STACK); jit_cache_native(gen, size); } break; case JIT_OP_FLUSH_SMALL_STRUCT: /* Flush a small structure return value back into the frame */ load_value(gen, insn->value1, 0); size = (jit_nint)jit_type_get_size(jit_value_get_type(insn->value1)); jit_cache_opcode(gen, insn->opcode); jit_cache_native(gen, size); break; case JIT_OP_LOAD_RELATIVE_SBYTE: case JIT_OP_LOAD_RELATIVE_UBYTE: case JIT_OP_LOAD_RELATIVE_SHORT: case JIT_OP_LOAD_RELATIVE_USHORT: case JIT_OP_LOAD_RELATIVE_INT: case JIT_OP_LOAD_RELATIVE_LONG: case JIT_OP_LOAD_RELATIVE_FLOAT32: case JIT_OP_LOAD_RELATIVE_FLOAT64: case JIT_OP_LOAD_RELATIVE_NFLOAT: /* Load a value from a relative pointer */ load_value(gen, insn->value1, 1); offset = jit_value_get_nint_constant(insn->value2); jit_cache_opcode(gen, insn->opcode); jit_cache_native(gen, offset); store_value(gen, insn->dest); break; case JIT_OP_LOAD_RELATIVE_STRUCT: /* Load a structured value from a relative pointer */ load_value(gen, insn->dest, 0); load_value(gen, insn->value1, 1); offset = jit_value_get_nint_constant(insn->value2); size = (jit_nint)jit_type_get_size(jit_value_get_type(insn->dest)); jit_cache_opcode(gen, insn->opcode); jit_cache_native(gen, offset); jit_cache_native(gen, size); break; case JIT_OP_STORE_RELATIVE_BYTE: case JIT_OP_STORE_RELATIVE_SHORT: case JIT_OP_STORE_RELATIVE_INT: case JIT_OP_STORE_RELATIVE_LONG: case JIT_OP_STORE_RELATIVE_FLOAT32: case JIT_OP_STORE_RELATIVE_FLOAT64: case JIT_OP_STORE_RELATIVE_NFLOAT: /* Store a value to a relative pointer */ load_value(gen, insn->dest, 0); load_value(gen, insn->value1, 1); offset = jit_value_get_nint_constant(insn->value2); jit_cache_opcode(gen, insn->opcode); jit_cache_native(gen, offset); break; case JIT_OP_STORE_RELATIVE_STRUCT: /* Store a structured value to a relative pointer */ load_value(gen, insn->dest, 0); load_value(gen, insn->value1, 1); offset = jit_value_get_nint_constant(insn->value2); size = (jit_nint)jit_type_get_size(jit_value_get_type(insn->value1)); jit_cache_opcode(gen, insn->opcode); jit_cache_native(gen, offset); jit_cache_native(gen, size); break; case JIT_OP_ADD_RELATIVE: /* Add a relative offset to a pointer */ offset = jit_value_get_nint_constant(insn->value2); if(offset != 0) { load_value(gen, insn->value1, 1); jit_cache_opcode(gen, insn->opcode); jit_cache_native(gen, offset); store_value(gen, insn->dest); } else { load_value(gen, insn->value1, 0); store_value(gen, insn->dest); } break; case JIT_OP_MARK_BREAKPOINT: /* Mark the current location as a potential breakpoint */ jit_cache_opcode(gen, insn->opcode); jit_cache_native(gen, insn->value1->address); jit_cache_native(gen, insn->value2->address); break; default: if(insn->dest && (insn->flags & JIT_INSN_DEST_IS_VALUE) != 0) { load_value(gen, insn->dest, 0); } if(insn->value1) { load_value(gen, insn->value1, 1); } if(insn->value2) { load_value(gen, insn->value2, 2); } jit_cache_opcode(gen, insn->opcode); if(insn->dest && (insn->flags & JIT_INSN_DEST_IS_VALUE) == 0) { store_value(gen, insn->dest); } break; } }
void _jit_gen_spill_reg(jit_gencode_t gen, int reg, int other_reg, jit_value_t value) { int offset; /* Make sure that we have sufficient space */ jit_cache_setup_output(16); /* If the value is associated with a global register, then copy to that */ if(value->has_global_register) { reg = _jit_reg_info[reg].cpu_reg; other_reg = _jit_reg_info[value->global_reg].cpu_reg; x86_mov_reg_reg(inst, other_reg, reg, sizeof(void *)); jit_cache_end_output(); return; } /* Fix the value in place within the local variable frame */ _jit_gen_fix_value(value); /* Output an appropriate instruction to spill the value */ offset = (int)(value->frame_offset); if(IS_WORD_REG(reg)) { /* Spill a word register. If the value is smaller than a word, then we write the entire word. The local stack frame is allocated such that the extra bytes will be simply ignored */ reg = _jit_reg_info[reg].cpu_reg; x86_mov_membase_reg(inst, X86_EBP, offset, reg, 4); if(other_reg != -1) { /* Spill the other word register in a pair */ reg = _jit_reg_info[other_reg].cpu_reg; offset += sizeof(void *); x86_mov_membase_reg(inst, X86_EBP, offset, reg, 4); } } else { /* Spill the top of the floating-point register stack */ switch(jit_type_normalize(value->type)->kind) { case JIT_TYPE_FLOAT32: { x86_fst_membase(inst, X86_EBP, offset, 0, 1); } break; case JIT_TYPE_FLOAT64: { x86_fst_membase(inst, X86_EBP, offset, 1, 1); } break; case JIT_TYPE_NFLOAT: { x86_fst80_membase(inst, X86_EBP, offset); } break; } } /* End the code output process */ jit_cache_end_output(); }