Implement JIT IR for integer load/store opcodes (#1087)

This commit is contained in:
liang.he 2022-04-18 17:22:55 +08:00 committed by GitHub
parent 5f0fab03a5
commit 166f12fef1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 624 additions and 37 deletions

View File

@ -426,7 +426,13 @@ static bool
extend_r8_to_r32(x86::Assembler &a, int32 reg_no_dst, int32 reg_no_src,
bool is_signed)
{
return false;
if (is_signed) {
a.movsx(regs_i32[reg_no_dst], regs_i8[reg_no_src]);
}
else {
a.movzx(regs_i32[reg_no_dst], regs_i8[reg_no_src]);
}
return true;
}
/**
* Encode extending register of word to register of dword
@ -441,7 +447,13 @@ static bool
extend_r16_to_r32(x86::Assembler &a, int32 reg_no_dst, int32 reg_no_src,
bool is_signed)
{
return false;
if (is_signed) {
a.movsx(regs_i32[reg_no_dst], regs_i16[reg_no_src]);
}
else {
a.movzx(regs_i32[reg_no_dst], regs_i16[reg_no_src]);
}
return true;
}
/**
@ -457,7 +469,13 @@ static bool
extend_r8_to_r64(x86::Assembler &a, int32 reg_no_dst, int32 reg_no_src,
bool is_signed)
{
return false;
if (is_signed) {
a.movsx(regs_i64[reg_no_dst], regs_i8[reg_no_src]);
}
else {
a.movzx(regs_i64[reg_no_dst], regs_i8[reg_no_src]);
}
return true;
}
/**
@ -473,7 +491,13 @@ static bool
extend_r16_to_r64(x86::Assembler &a, int32 reg_no_dst, int32 reg_no_src,
bool is_signed)
{
return false;
if (is_signed) {
a.movsx(regs_i64[reg_no_dst], regs_i16[reg_no_src]);
}
else {
a.movzx(regs_i64[reg_no_dst], regs_i16[reg_no_src]);
}
return true;
}
/**
@ -489,29 +513,40 @@ static bool
extend_r32_to_r64(x86::Assembler &a, int32 reg_no_dst, int32 reg_no_src,
bool is_signed)
{
return false;
if (is_signed) {
a.movsxd(regs_i64[reg_no_dst], regs_i32[reg_no_src]);
}
else {
a.xor_(regs_i64[reg_no_dst], regs_i64[reg_no_dst]);
a.mov(regs_i32[reg_no_dst], regs_i32[reg_no_src]);
}
return true;
}
static bool
mov_r_to_r_i32(x86::Assembler &a, int32 reg_no_dst, int32 reg_no_src);
static bool
mov_r_to_r_i64(x86::Assembler &a, int32 reg_no_dst, int32 reg_no_src);
static void
mov_r_to_r(x86::Assembler &a, uint32 kind_dst, int32 reg_no_dst,
int32 reg_no_src)
{
if (reg_no_dst != reg_no_src) {
if (kind_dst == JIT_REG_KIND_I32)
a.mov(regs_i32[reg_no_dst], regs_i32[reg_no_src]);
else if (kind_dst == JIT_REG_KIND_I64)
a.mov(regs_i64[reg_no_dst], regs_i64[reg_no_src]);
else if (kind_dst == JIT_REG_KIND_F32) {
/* TODO */
bh_assert(0);
}
else if (kind_dst == JIT_REG_KIND_F64) {
/* TODO */
bh_assert(0);
}
else {
bh_assert(0);
}
if (kind_dst == JIT_REG_KIND_I32)
mov_r_to_r_i32(a, reg_no_dst, reg_no_src);
else if (kind_dst == JIT_REG_KIND_I64)
mov_r_to_r_i64(a, reg_no_dst, reg_no_src);
else if (kind_dst == JIT_REG_KIND_F32) {
/* TODO */
bh_assert(0);
}
else if (kind_dst == JIT_REG_KIND_F64) {
/* TODO */
bh_assert(0);
}
else {
bh_assert(0);
}
}
@ -1109,7 +1144,9 @@ mov_r_to_r_f64(x86::Assembler &a, int32 reg_no_dst, int32 reg_no_src)
static bool
convert_imm_i32_to_r_i8(x86::Assembler &a, int32 reg_no, int32 data)
{
return false;
Imm imm((int8)data);
a.mov(regs_i32[reg_no], imm);
return true;
}
/**
@ -1124,6 +1161,8 @@ convert_imm_i32_to_r_i8(x86::Assembler &a, int32 reg_no, int32 data)
static bool
convert_r_i32_to_r_i8(x86::Assembler &a, int32 reg_no_dst, int32 reg_no_src)
{
/* TODO */
bh_assert(0);
return false;
}
@ -1139,7 +1178,9 @@ convert_r_i32_to_r_i8(x86::Assembler &a, int32 reg_no_dst, int32 reg_no_src)
static bool
convert_imm_i32_to_r_u8(x86::Assembler &a, int32 reg_no, int32 data)
{
return false;
Imm imm((uint8)data);
a.mov(regs_i32[reg_no], imm);
return true;
}
/**
@ -1154,6 +1195,8 @@ convert_imm_i32_to_r_u8(x86::Assembler &a, int32 reg_no, int32 data)
static bool
convert_r_i32_to_r_u8(x86::Assembler &a, int32 reg_no_dst, int32 reg_no_src)
{
/* TODO */
bh_assert(0);
return false;
}
@ -1169,7 +1212,9 @@ convert_r_i32_to_r_u8(x86::Assembler &a, int32 reg_no_dst, int32 reg_no_src)
static bool
convert_imm_i32_to_r_i16(x86::Assembler &a, int32 reg_no, int32 data)
{
return false;
Imm imm((int16)data);
a.mov(regs_i32[reg_no], imm);
return true;
}
/**
@ -1184,6 +1229,8 @@ convert_imm_i32_to_r_i16(x86::Assembler &a, int32 reg_no, int32 data)
static bool
convert_r_i32_to_r_i16(x86::Assembler &a, int32 reg_no_dst, int32 reg_no_src)
{
/* TODO */
bh_assert(0);
return false;
}
@ -1199,7 +1246,9 @@ convert_r_i32_to_r_i16(x86::Assembler &a, int32 reg_no_dst, int32 reg_no_src)
static bool
convert_imm_i32_to_r_u16(x86::Assembler &a, int32 reg_no, int32 data)
{
return false;
Imm imm((uint16)data);
a.mov(regs_i32[reg_no], imm);
return true;
}
/**
@ -1214,11 +1263,13 @@ convert_imm_i32_to_r_u16(x86::Assembler &a, int32 reg_no, int32 data)
static bool
convert_r_i32_to_r_u16(x86::Assembler &a, int32 reg_no_dst, int32 reg_no_src)
{
/* TODO */
bh_assert(0);
return false;
}
/**
* Encoding convert int32 immediate data to uint64 register
* Encoding convert int32 immediate data to int64 register
*
* @param a the assembler to emit the code
* @param reg_no the dst register, need to be converted to uint64
@ -1227,13 +1278,16 @@ convert_r_i32_to_r_u16(x86::Assembler &a, int32 reg_no_dst, int32 reg_no_src)
* @return true if success, false otherwise
*/
static bool
convert_imm_i32_to_r_u64(x86::Assembler &a, int32 reg_no, int32 data)
convert_imm_i32_to_r_i64(x86::Assembler &a, int32 reg_no, int32 data)
{
return false;
/* let compiler do sign-extending */
Imm imm((int64)data);
a.mov(regs_i64[reg_no], imm);
return true;
}
/**
* Encoding convert int32 immediate data to uint64 register
* Encoding convert int32 register data to int64 register with signed extension
*
* @param a the assembler to emit the code
* @param reg_no_dst the dst register, need to be converted to uint64
@ -1242,9 +1296,9 @@ convert_imm_i32_to_r_u64(x86::Assembler &a, int32 reg_no, int32 data)
* @return true if success, false otherwise
*/
static bool
convert_r_i32_to_r_u64(x86::Assembler &a, int32 reg_no_dst, int32 reg_no_src)
convert_r_i32_to_r_i64(x86::Assembler &a, int32 reg_no_dst, int32 reg_no_src)
{
return false;
return extend_r32_to_r64(a, reg_no_dst, reg_no_src, true);
}
/**
@ -1307,6 +1361,132 @@ convert_r_i32_to_r_f64(x86::Assembler &a, int32 reg_no_dst, int32 reg_no_src)
return false;
}
/**
* Encode converting uint32 immediate data to int64 register data
*
* @param a the assembler to emit the code
* @param reg_no the no of dst int64 register
* @param data the src immediate uint32 data
*
* @return true if success, false otherwise
*/
static bool
convert_imm_u32_to_r_i64(x86::Assembler &a, int32 reg_no, uint32 data)
{
Imm imm((uint64)data);
a.mov(regs_i64[reg_no], imm);
return true;
}
/**
* Encode converting uint32 register data to int64 register data
*
* @param a the assembler to emit the code
* @param reg_no_dst the no of dst uint32 register
* @param reg_no_src the no of src int64 register
*
* @return true if success, false otherwise
*/
static bool
convert_r_u32_to_r_i64(x86::Assembler &a, int32 reg_no_dst, int32 reg_no_src)
{
return extend_r32_to_r64(a, reg_no_dst, reg_no_src, false);
}
/**
* Encode converting int64 immediate data to int32 register data
*
* @param a the assembler to emit the code
* @param reg_no the no of dst int32 register
* @param data the src immediate int64 data
*
* @return true if success, false otherwise
*/
static bool
convert_imm_i64_to_r_i32(x86::Assembler &a, int32 reg_no, int64 data)
{
Imm imm((int32)data);
a.mov(regs_i32[reg_no], imm);
return true;
}
/**
* Encode converting int64 register data to int32 register data
*
* @param a the assembler to emit the code
* @param reg_no_dst the no of dst int32 register
* @param reg_no_src the no of src int64 register
*
* @return true if success, false otherwise
*/
static bool
convert_r_i64_to_r_i32(x86::Assembler &a, int32 reg_no_dst, int32 reg_no_src)
{
/* TODO */
bh_assert(0);
return false;
}
/**
* Encode converting int64 immediate data to float register data
*
* @param a the assembler to emit the code
* @param reg_no the no of dst float register
* @param data the src immediate int64 data
*
* @return true if success, false otherwise
*/
static bool
convert_imm_i64_to_r_f32(x86::Assembler &a, int32 reg_no_dst, int64 data)
{
return false;
}
/**
* Encode converting int64 register data to float register data
*
* @param a the assembler to emit the code
* @param reg_no_dst the no of dst float register
* @param reg_no_src the no of src int64 register
*
* @return true if success, false otherwise
*/
static bool
convert_r_i64_to_r_f32(x86::Assembler &a, int32 reg_no_dst, int32 reg_no_src)
{
return false;
}
/**
* Encode converting int64 immediate data to double register data
*
* @param a the assembler to emit the code
* @param reg_no the no of dst double register
* @param data the src immediate int64 data
*
* @return true if success, false otherwise
*/
static bool
convert_imm_i64_to_r_f64(x86::Assembler &a, int32 reg_no_dst, int64 data)
{
return false;
}
/**
* Encode converting int64 register data to double register data
*
* @param a the assembler to emit the code
* @param reg_no_dst the no of dst double register
* @param reg_no_src the no of src int64 register
*
* @return true if success, false otherwise
*/
static bool
convert_r_i64_to_r_f64(x86::Assembler &a, int32 reg_no_dst, int32 reg_no_src)
{
return false;
}
/**
* Encode converting float immediate data to int32 register data
*
@ -1397,6 +1577,36 @@ convert_r_f64_to_r_i32(x86::Assembler &a, int32 reg_no_dst, int32 reg_no_src)
return false;
}
/**
* Encode converting double immediate data to int64 register data
*
* @param a the assembler to emit the code
* @param reg_no the no of dst int64 register
* @param data the src immediate double data
*
* @return true if success, false otherwise
*/
static bool
convert_imm_f64_to_r_i64(x86::Assembler &a, int32 reg_no, double data)
{
return false;
}
/**
* Encode converting double register data to int64 register data
*
* @param a the assembler to emit the code
* @param reg_no_dst the no of dst int64 register
* @param reg_no_src the no of src double register
*
* @return true if success, false otherwise
*/
static bool
convert_r_f64_to_r_i64(x86::Assembler &a, int32 reg_no_dst, int32 reg_no_src)
{
return false;
}
/**
* Encode converting double immediate data to float register data
*
@ -3370,10 +3580,12 @@ fail:
/**
* Encode insn convert: I32TOI8 r0, r1, or I32TOI16, I32TOF32, F32TOF64, etc.
* @param kind0 the dst data kind, such as I32, I64, F32 and F64
* @param kind1 the src data kind, such as I32, I64, F32 and F64
* @param type0 the dst data type, such as int32, float and double
* @param type1 the src data type, such as int32, float and double
* @param kind0 the dst JIT_REG_KIND, such as I32, I64, F32 and F64
* @param kind1 the src JIT_REG_KIND, such as I32, I64, F32 and F64
* @param type0 the dst data type, such as i8, u8, i16, u16, i32, f32, i64, f32,
* f64
* @param type1 the src data type, such as i8, u8, i16, u16, i32, f32, i64, f32,
* f64
*/
#define CONVERT_R_R(kind0, kind1, type0, type1, Type1) \
do { \
@ -3670,8 +3882,9 @@ fail:
* @param cc the compiler context
* @param a the assembler to emit the code
* @param r0 dst jit register that contains the dst operand info
* @param r1 src jit register that contains the first src operand info
* @param r2 src jit register that contains the second src operand info
* @param r1 condition jit register
* @param r2 src jit register that contains the first src operand info
* @param r3 src jit register that contains the second src operand info
*
* @return true if success, false if failed
*/
@ -4373,6 +4586,16 @@ jit_codegen_gen_native(JitCompContext *cc)
CONVERT_R_R(I32, I32, u16, i32, int32);
break;
case JIT_OP_I32TOI64:
LOAD_2ARGS();
CONVERT_R_R(I64, I32, i64, i32, int32);
break;
case JIT_OP_U32TOI64:
LOAD_2ARGS();
CONVERT_R_R(I64, I32, i64, u32, int32);
break;
case JIT_OP_I32TOF32:
case JIT_OP_U32TOF32:
LOAD_2ARGS();
@ -4385,6 +4608,21 @@ jit_codegen_gen_native(JitCompContext *cc)
CONVERT_R_R(F64, I32, f64, i32, int32);
break;
case JIT_OP_I64TOI32:
LOAD_2ARGS();
CONVERT_R_R(I32, I64, i32, i64, int64);
break;
case JIT_OP_I64TOF32:
LOAD_2ARGS();
CONVERT_R_R(F32, I64, f32, i64, int64);
break;
case JIT_OP_I64TOF64:
LOAD_2ARGS();
CONVERT_R_R(F64, I64, f64, i64, int64);
break;
case JIT_OP_F32TOI32:
LOAD_2ARGS();
CONVERT_R_R(I32, F32, i32, f32, int32);
@ -4400,6 +4638,11 @@ jit_codegen_gen_native(JitCompContext *cc)
CONVERT_R_R(I32, F64, i32, f64, float64);
break;
case JIT_OP_F64TOI64:
LOAD_2ARGS();
CONVERT_R_R(I64, F64, i64, f64, float64);
break;
case JIT_OP_F64TOF32:
LOAD_2ARGS();
CONVERT_R_R(F32, F64, f32, f64, float64);

View File

@ -5,11 +5,219 @@
#include "jit_emit_memory.h"
#include "../jit_frontend.h"
#include "fe/jit_emit_exception.h"
static JitReg
get_memory_boundary(JitCompContext *cc, uint32 mem_idx, uint32 bytes)
{
JitReg memory_boundary;
switch (bytes) {
case 1:
{
memory_boundary =
get_mem_bound_check_1byte_reg(cc->jit_frame, mem_idx);
break;
}
case 2:
{
memory_boundary =
get_mem_bound_check_2bytes_reg(cc->jit_frame, mem_idx);
break;
}
case 4:
{
memory_boundary =
get_mem_bound_check_4bytes_reg(cc->jit_frame, mem_idx);
break;
}
case 8:
{
memory_boundary =
get_mem_bound_check_8bytes_reg(cc->jit_frame, mem_idx);
break;
}
case 16:
{
memory_boundary =
get_mem_bound_check_16bytes_reg(cc->jit_frame, mem_idx);
break;
}
default:
{
bh_assert(0);
goto fail;
}
}
return memory_boundary;
fail:
return 0;
}
#if UINTPTR_MAX == UINT64_MAX
static JitReg
check_and_seek_on_64bit_platform(JitCompContext *cc, JitReg addr, JitReg offset,
JitReg memory_boundary)
{
JitReg long_addr, offset1;
/* long_addr = (int64_t)addr */
long_addr = jit_cc_new_reg_I64(cc);
GEN_INSN(U32TOI64, long_addr, addr);
/* offset1 = offset + long_addr */
offset1 = jit_cc_new_reg_I64(cc);
GEN_INSN(ADD, offset1, offset, long_addr);
/* if (offset1 > memory_boundary) goto EXCEPTION */
GEN_INSN(CMP, cc->cmp_reg, offset1, memory_boundary);
if (!jit_emit_exception(cc, EXCE_OUT_OF_BOUNDS_MEMORY_ACCESS, JIT_OP_BGTU,
cc->cmp_reg, NULL)) {
goto fail;
}
return offset1;
fail:
return 0;
}
#else
static JitReg
check_and_seek_on_32bit_platform(JitCompContext *cc, JitReg addr, JitReg offset,
JitReg memory_boundary)
{
JitReg offset1;
/* offset1 = offset + addr */
offset1 = jit_cc_new_reg_I32(cc);
GEN_INSN(ADD, offset1, offset, addr);
/* if (offset1 < addr) goto EXCEPTION */
GEN_INSN(CMP, cc->cmp_reg, offset1, addr);
if (!jit_emit_exception(cc, EXCE_OUT_OF_BOUNDS_MEMORY_ACCESS, JIT_OP_BLTU,
cc->cmp_reg, NULL)) {
goto fail;
}
/* if (offset1 > memory_boundary) goto EXCEPTION */
GEN_INSN(CMP, cc->cmp_reg, offset1, memory_boundary);
if (!jit_emit_exception(cc, EXCE_OUT_OF_BOUNDS_MEMORY_ACCESS, JIT_OP_BGTU,
cc->cmp_reg, NULL)) {
goto fail;
}
return offset1;
fail:
return 0;
}
#endif
static JitReg
check_and_seek(JitCompContext *cc, JitReg addr, uint32 offset, uint32 bytes)
{
JitReg memory_boundary, offset1, memory_data, maddr;
/* the default memory */
uint32 mem_idx = 0;
/* ---------- check ---------- */
/* 1. shortcut if the memory size is 0*/
if (0 == cc->cur_wasm_module->memories[mem_idx].init_page_count) {
JitReg memory_inst, cur_mem_page_count;
/* if (cur_mem_page_count == 0) goto EXCEPTION */
memory_inst = get_memory_inst_reg(cc->jit_frame, mem_idx);
cur_mem_page_count = jit_cc_new_reg_I32(cc);
GEN_INSN(LDI32, cur_mem_page_count, memory_inst,
NEW_CONST(I32, offsetof(WASMMemoryInstance, cur_page_count)));
GEN_INSN(CMP, cc->cmp_reg, cur_mem_page_count, NEW_CONST(I32, 0));
if (!jit_emit_exception(cc, EXCE_OUT_OF_BOUNDS_MEMORY_ACCESS,
JIT_OP_BEQ, cc->cmp_reg, NULL)) {
goto fail;
}
}
/* 2. a complete boundary check */
memory_boundary = get_memory_boundary(cc, mem_idx, bytes);
if (!memory_boundary)
goto fail;
#if UINTPTR_MAX == UINT64_MAX
offset1 = check_and_seek_on_64bit_platform(cc, addr, NEW_CONST(I64, offset),
memory_boundary);
if (!offset1)
goto fail;
#else
offset1 = check_and_seek_on_32bit_platform(cc, addr, NEW_CONST(I32, offset),
memory_boundary);
if (!offset1)
goto fail;
#endif
/* ---------- seek ---------- */
memory_data = get_memory_data_reg(cc->jit_frame, mem_idx);
maddr = jit_cc_new_reg_ptr(cc);
GEN_INSN(ADD, maddr, memory_data, offset1);
return maddr;
fail:
return 0;
}
bool
jit_compile_op_i32_load(JitCompContext *cc, uint32 align, uint32 offset,
uint32 bytes, bool sign, bool atomic)
{
JitReg addr, maddr, value;
POP_I32(addr);
maddr = check_and_seek(cc, addr, offset, bytes);
if (!maddr) {
goto fail;
}
value = jit_cc_new_reg_I32(cc);
switch (bytes) {
case 1:
{
if (sign) {
GEN_INSN(LDI8, value, maddr, NEW_CONST(I32, 0));
}
else {
GEN_INSN(LDU8, value, maddr, NEW_CONST(I32, 0));
}
break;
}
case 2:
{
if (sign) {
GEN_INSN(LDI16, value, maddr, NEW_CONST(I32, 0));
}
else {
GEN_INSN(LDU16, value, maddr, NEW_CONST(I32, 0));
}
break;
}
case 4:
{
if (sign) {
GEN_INSN(LDI32, value, maddr, NEW_CONST(I32, 0));
}
else {
GEN_INSN(LDU32, value, maddr, NEW_CONST(I32, 0));
}
break;
}
default:
{
bh_assert(0);
goto fail;
}
}
PUSH_I32(value);
return true;
fail:
return false;
}
@ -17,6 +225,67 @@ bool
jit_compile_op_i64_load(JitCompContext *cc, uint32 align, uint32 offset,
uint32 bytes, bool sign, bool atomic)
{
JitReg addr, maddr, value;
POP_I32(addr);
maddr = check_and_seek(cc, addr, offset, bytes);
if (!maddr) {
goto fail;
}
value = jit_cc_new_reg_I64(cc);
switch (bytes) {
case 1:
{
if (sign) {
GEN_INSN(LDI8, value, maddr, NEW_CONST(I32, 0));
}
else {
GEN_INSN(LDU8, value, maddr, NEW_CONST(I32, 0));
}
break;
}
case 2:
{
if (sign) {
GEN_INSN(LDI16, value, maddr, NEW_CONST(I32, 0));
}
else {
GEN_INSN(LDU16, value, maddr, NEW_CONST(I32, 0));
}
break;
}
case 4:
{
if (sign) {
GEN_INSN(LDI16, value, maddr, NEW_CONST(I32, 0));
}
else {
GEN_INSN(LDU16, value, maddr, NEW_CONST(I32, 0));
}
break;
}
case 8:
{
if (sign) {
GEN_INSN(LDI64, value, maddr, NEW_CONST(I32, 0));
}
else {
GEN_INSN(LDU64, value, maddr, NEW_CONST(I32, 0));
}
break;
}
default:
{
bh_assert(0);
goto fail;
}
}
PUSH_I64(value);
return true;
fail:
return false;
}
@ -36,6 +305,41 @@ bool
jit_compile_op_i32_store(JitCompContext *cc, uint32 align, uint32 offset,
uint32 bytes, bool atomic)
{
JitReg value, addr, maddr;
POP_I32(value);
POP_I32(addr);
maddr = check_and_seek(cc, addr, offset, bytes);
if (!maddr) {
goto fail;
}
switch (bytes) {
case 1:
{
GEN_INSN(STI8, value, maddr, NEW_CONST(I32, 0));
break;
}
case 2:
{
GEN_INSN(STI16, value, maddr, NEW_CONST(I32, 0));
break;
}
case 4:
{
GEN_INSN(STI32, value, maddr, NEW_CONST(I32, 0));
break;
}
default:
{
bh_assert(0);
goto fail;
}
}
return true;
fail:
return false;
}
@ -43,6 +347,46 @@ bool
jit_compile_op_i64_store(JitCompContext *cc, uint32 align, uint32 offset,
uint32 bytes, bool atomic)
{
JitReg value, addr, maddr;
POP_I64(value);
POP_I32(addr);
maddr = check_and_seek(cc, addr, offset, bytes);
if (!maddr) {
goto fail;
}
switch (bytes) {
case 1:
{
GEN_INSN(STI8, value, maddr, NEW_CONST(I32, 0));
break;
}
case 2:
{
GEN_INSN(STI16, value, maddr, NEW_CONST(I32, 0));
break;
}
case 4:
{
GEN_INSN(STI32, value, maddr, NEW_CONST(I32, 0));
break;
}
case 8:
{
GEN_INSN(STI64, value, maddr, NEW_CONST(I32, 0));
break;
}
default:
{
bh_assert(0);
goto fail;
}
}
return true;
fail:
return false;
}