Implement float comparison, conversion and numeric opcodes (#1170)

This commit is contained in:
liang.he 2022-05-24 19:03:46 +08:00 committed by GitHub
parent 4746eaa029
commit b01ae11217
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 1315 additions and 160 deletions

View File

@ -73,6 +73,7 @@ wasm_exec_env_create_internal(struct WASMModuleInstanceCommon *module_inst,
#if WASM_ENABLE_MEMORY_TRACING != 0 #if WASM_ENABLE_MEMORY_TRACING != 0
wasm_runtime_dump_exec_env_mem_consumption(exec_env); wasm_runtime_dump_exec_env_mem_consumption(exec_env);
#endif #endif
return exec_env; return exec_env;
#if WASM_ENABLE_THREAD_MGR != 0 #if WASM_ENABLE_THREAD_MGR != 0

View File

@ -85,13 +85,15 @@ typedef struct WASMExecEnv {
#endif #endif
#if WASM_ENABLE_FAST_JIT != 0 #if WASM_ENABLE_FAST_JIT != 0
#if defined(BUILD_TARGET_X86_32) /**
/* Cache for jit native operations in 32-bit target which hasn't 64-bit * Cache for
int/float registers, mainly for the operations of double and int64, * - jit native operations in 32-bit target which hasn't 64-bit
such as F64TOI64, F32TOI64, I64 MUL/REM, and so on. */ * int/float registers, mainly for the operations of double and int64,
* such as F64TOI64, F32TOI64, I64 MUL/REM, and so on.
* - SSE instructions.
**/
uint64 jit_cache[2]; uint64 jit_cache[2];
#endif #endif
#endif
#if WASM_ENABLE_THREAD_MGR != 0 #if WASM_ENABLE_THREAD_MGR != 0
/* thread return value */ /* thread return value */

File diff suppressed because it is too large Load Diff

View File

@ -7,7 +7,7 @@
#include "../jit_frontend.h" #include "../jit_frontend.h"
static bool static bool
jit_compile_op_compare(JitCompContext *cc, IntCond cond, bool is64Bit) jit_compile_op_compare_integer(JitCompContext *cc, IntCond cond, bool is64Bit)
{ {
JitReg lhs, rhs, res, const_zero, const_one; JitReg lhs, rhs, res, const_zero, const_one;
@ -107,25 +107,92 @@ fail:
bool bool
jit_compile_op_i32_compare(JitCompContext *cc, IntCond cond) jit_compile_op_i32_compare(JitCompContext *cc, IntCond cond)
{ {
return jit_compile_op_compare(cc, cond, false); return jit_compile_op_compare_integer(cc, cond, false);
} }
bool bool
jit_compile_op_i64_compare(JitCompContext *cc, IntCond cond) jit_compile_op_i64_compare(JitCompContext *cc, IntCond cond)
{ {
return jit_compile_op_compare(cc, cond, true); return jit_compile_op_compare_integer(cc, cond, true);
}
static bool
jit_compile_op_compare_float_point(JitCompContext *cc, FloatCond cond,
JitReg lhs, JitReg rhs)
{
JitReg res, const_zero, const_one;
GEN_INSN(CMP, cc->cmp_reg, lhs, rhs);
res = jit_cc_new_reg_I32(cc);
const_zero = NEW_CONST(I32, 0);
const_one = NEW_CONST(I32, 1);
switch (cond) {
case FLOAT_EQ:
{
GEN_INSN(SELECTEQ, res, cc->cmp_reg, const_one, const_zero);
break;
}
case FLOAT_NE:
{
GEN_INSN(SELECTNE, res, cc->cmp_reg, const_one, const_zero);
break;
}
case FLOAT_LT:
{
GEN_INSN(SELECTLTS, res, cc->cmp_reg, const_one, const_zero);
break;
}
case FLOAT_GT:
{
GEN_INSN(SELECTGTS, res, cc->cmp_reg, const_one, const_zero);
break;
}
case FLOAT_LE:
{
GEN_INSN(SELECTLES, res, cc->cmp_reg, const_one, const_zero);
break;
}
case FLOAT_GE:
{
GEN_INSN(SELECTGES, res, cc->cmp_reg, const_one, const_zero);
break;
}
default:
{
bh_assert(!"unknown FloatCond");
goto fail;
}
}
PUSH_I32(res);
return true;
fail:
return false;
} }
bool bool
jit_compile_op_f32_compare(JitCompContext *cc, FloatCond cond) jit_compile_op_f32_compare(JitCompContext *cc, FloatCond cond)
{ {
jit_set_last_error(cc, "unimplement f32 comparison"); JitReg lhs, rhs;
POP_F32(rhs);
POP_F32(lhs);
return jit_compile_op_compare_float_point(cc, cond, lhs, rhs);
fail:
return false; return false;
} }
bool bool
jit_compile_op_f64_compare(JitCompContext *cc, FloatCond cond) jit_compile_op_f64_compare(JitCompContext *cc, FloatCond cond)
{ {
jit_set_last_error(cc, "unimplement f64 comparison"); JitReg lhs, rhs;
POP_F64(rhs);
POP_F64(lhs);
return jit_compile_op_compare_float_point(cc, cond, lhs, rhs);
fail:
return false; return false;
} }

View File

@ -3,8 +3,22 @@
* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
*/ */
#include "../jit_codegen.h"
#include "jit_emit_conversion.h" #include "jit_emit_conversion.h"
#include "../jit_frontend.h" #include "../jit_frontend.h"
#include "jit_emit_exception.h"
static double
uint64_to_double(uint64 u64)
{
return (double)u64;
}
static float
uint64_to_float(uint64 u64)
{
return (float)u64;
}
bool bool
jit_compile_op_i32_wrap_i64(JitCompContext *cc) jit_compile_op_i32_wrap_i64(JitCompContext *cc)
@ -25,12 +39,138 @@ fail:
bool bool
jit_compile_op_i32_trunc_f32(JitCompContext *cc, bool sign, bool saturating) jit_compile_op_i32_trunc_f32(JitCompContext *cc, bool sign, bool saturating)
{ {
JitReg value, nan_ret, max_valid_float, min_valid_float, res;
JitInsn *insn = NULL;
POP_F32(value);
min_valid_float =
sign ? NEW_CONST(F32, -2147483904.0f) : NEW_CONST(F32, -1.0f);
max_valid_float =
sign ? NEW_CONST(F32, 2147483648.0f) : NEW_CONST(F32, 4294967296.0f);
if (!saturating) {
/*if (value is Nan) goto exception*/
#if defined(BUILD_TARGET_X86_64) || defined(BUILD_TARGET_AMD_64)
/* Set nan_ret to x86::eax */
nan_ret = jit_codegen_get_hreg_by_name("eax");
#else
nan_ret = jit_cc_new_reg_I32(cc);
#endif
insn =
GEN_INSN(CALLNATIVE, nan_ret, NEW_CONST(PTR, (uintptr_t)isnanf), 1);
if (!insn) {
goto fail;
}
*(jit_insn_opndv(insn, 2)) = value;
GEN_INSN(CMP, cc->cmp_reg, nan_ret, NEW_CONST(I32, 1));
if (!jit_emit_exception(cc, EXCE_INVALID_CONVERSION_TO_INTEGER,
JIT_OP_BEQ, cc->cmp_reg, NULL)) {
goto fail;
}
/*if (value is out of integer range) goto exception*/
GEN_INSN(CMP, cc->cmp_reg, value, min_valid_float);
if (!jit_emit_exception(cc, EXCE_INTEGER_OVERFLOW, JIT_OP_BLES,
cc->cmp_reg, NULL)) {
goto fail;
}
GEN_INSN(CMP, cc->cmp_reg, value, max_valid_float);
if (!jit_emit_exception(cc, EXCE_INTEGER_OVERFLOW, JIT_OP_BGES,
cc->cmp_reg, NULL)) {
goto fail;
}
}
if (saturating) {
JitReg tmp = jit_cc_new_reg_F32(cc);
GEN_INSN(MAX, tmp, value, min_valid_float);
GEN_INSN(MIN, value, tmp, max_valid_float);
}
res = jit_cc_new_reg_I32(cc);
if (sign) {
GEN_INSN(F32TOI32, res, value);
}
else {
GEN_INSN(F32TOU32, res, value);
}
PUSH_I32(res);
return true;
fail:
return false; return false;
} }
bool bool
jit_compile_op_i32_trunc_f64(JitCompContext *cc, bool sign, bool saturating) jit_compile_op_i32_trunc_f64(JitCompContext *cc, bool sign, bool saturating)
{ {
JitReg value, nan_ret, max_valid_double, min_valid_double, res;
JitInsn *insn = NULL;
POP_F64(value);
min_valid_double =
sign ? NEW_CONST(F64, -2147483649.0) : NEW_CONST(F64, -1.0);
max_valid_double =
sign ? NEW_CONST(F64, 2147483648.0) : NEW_CONST(F64, 4294967296.0);
if (!saturating) {
/*if (value is Nan) goto exception*/
#if defined(BUILD_TARGET_X86_64) || defined(BUILD_TARGET_AMD_64)
/* Set nan_ret to x86::eax */
nan_ret = jit_codegen_get_hreg_by_name("eax");
#else
nan_ret = jit_cc_new_reg_I32(cc);
#endif
insn =
GEN_INSN(CALLNATIVE, nan_ret, NEW_CONST(PTR, (uintptr_t)isnan), 1);
if (!insn) {
goto fail;
}
*(jit_insn_opndv(insn, 2)) = value;
GEN_INSN(CMP, cc->cmp_reg, nan_ret, NEW_CONST(I32, 1));
if (!jit_emit_exception(cc, EXCE_INVALID_CONVERSION_TO_INTEGER,
JIT_OP_BEQ, cc->cmp_reg, NULL)) {
goto fail;
}
/*if (value is out of integer range) goto exception*/
GEN_INSN(CMP, cc->cmp_reg, value, min_valid_double);
if (!jit_emit_exception(cc, EXCE_INTEGER_OVERFLOW, JIT_OP_BLES,
cc->cmp_reg, NULL)) {
goto fail;
}
GEN_INSN(CMP, cc->cmp_reg, value, max_valid_double);
if (!jit_emit_exception(cc, EXCE_INTEGER_OVERFLOW, JIT_OP_BGES,
cc->cmp_reg, NULL)) {
goto fail;
}
}
if (saturating) {
JitReg tmp = jit_cc_new_reg_F64(cc);
GEN_INSN(MAX, tmp, value, min_valid_double);
GEN_INSN(MIN, value, tmp, max_valid_double);
}
res = jit_cc_new_reg_I32(cc);
if (sign) {
GEN_INSN(F64TOI32, res, value);
}
else {
GEN_INSN(F64TOU32, res, value);
}
PUSH_I32(res);
return true;
fail:
return false; return false;
} }
@ -56,85 +196,234 @@ fail:
} }
bool bool
jit_compile_op_i64_extend_i64(JitCompContext *comp_ctx, int8 bitwidth) jit_compile_op_i64_extend_i64(JitCompContext *cc, int8 bitwidth)
{ {
bh_assert(0);
return false; return false;
} }
bool bool
jit_compile_op_i32_extend_i32(JitCompContext *comp_ctx, int8 bitwidth) jit_compile_op_i32_extend_i32(JitCompContext *cc, int8 bitwidth)
{ {
bh_assert(0);
return false; return false;
} }
bool bool
jit_compile_op_i64_trunc_f32(JitCompContext *cc, bool sign, bool saturating) jit_compile_op_i64_trunc_f32(JitCompContext *cc, bool sign, bool saturating)
{ {
/* TODO: f32 -> u64 algorithm */
bh_assert(0);
return false; return false;
} }
bool bool
jit_compile_op_i64_trunc_f64(JitCompContext *cc, bool sign, bool saturating) jit_compile_op_i64_trunc_f64(JitCompContext *cc, bool sign, bool saturating)
{ {
/* TODO: f64 -> u64 algorithm */
bh_assert(0);
return false; return false;
} }
bool bool
jit_compile_op_f32_convert_i32(JitCompContext *comp_ctx, bool sign) jit_compile_op_f32_convert_i32(JitCompContext *cc, bool sign)
{ {
JitReg value, res;
POP_I32(value);
res = jit_cc_new_reg_F32(cc);
if (sign) {
GEN_INSN(I32TOF32, res, value);
}
else {
GEN_INSN(U32TOF32, res, value);
}
PUSH_F32(res);
return true;
fail:
return false; return false;
} }
bool bool
jit_compile_op_f32_convert_i64(JitCompContext *comp_ctx, bool sign) jit_compile_op_f32_convert_i64(JitCompContext *cc, bool sign)
{ {
JitReg value, res;
POP_I64(value);
if (sign) {
res = jit_cc_new_reg_F32(cc);
GEN_INSN(I64TOF32, res, value);
}
else {
JitInsn *insn;
#if defined(BUILD_TARGET_X86_64) || defined(BUILD_TARGET_AMD_64)
res = jit_codegen_get_hreg_by_name("xmm0");
#else
res = jit_cc_new_reg_F32(cc);
#endif
insn = GEN_INSN(CALLNATIVE, res,
NEW_CONST(PTR, (uintptr_t)uint64_to_float), 1);
if (!insn) {
goto fail;
}
*(jit_insn_opndv(insn, 2)) = value;
}
PUSH_F32(res);
return true;
fail:
return false; return false;
} }
bool bool
jit_compile_op_f32_demote_f64(JitCompContext *comp_ctx) jit_compile_op_f32_demote_f64(JitCompContext *cc)
{ {
JitReg value, res;
POP_F64(value);
res = jit_cc_new_reg_F32(cc);
GEN_INSN(F64TOF32, res, value);
PUSH_F32(res);
return true;
fail:
return false; return false;
} }
bool bool
jit_compile_op_f64_convert_i32(JitCompContext *comp_ctx, bool sign) jit_compile_op_f64_convert_i32(JitCompContext *cc, bool sign)
{ {
JitReg value, res;
POP_I32(value);
res = jit_cc_new_reg_F64(cc);
if (sign) {
GEN_INSN(I32TOF64, res, value);
}
else {
GEN_INSN(U32TOF64, res, value);
}
PUSH_F64(res);
return true;
fail:
return false; return false;
} }
bool bool
jit_compile_op_f64_convert_i64(JitCompContext *comp_ctx, bool sign) jit_compile_op_f64_convert_i64(JitCompContext *cc, bool sign)
{ {
JitReg value, res;
POP_I64(value);
if (sign) {
res = jit_cc_new_reg_F64(cc);
GEN_INSN(I64TOF64, res, value);
}
else {
JitInsn *insn;
#if defined(BUILD_TARGET_X86_64) || defined(BUILD_TARGET_AMD_64)
res = jit_codegen_get_hreg_by_name("xmm0_f64");
#else
res = jit_cc_new_reg_F64(cc);
#endif
insn = GEN_INSN(CALLNATIVE, res,
NEW_CONST(PTR, (uintptr_t)uint64_to_double), 1);
if (!insn) {
goto fail;
}
*(jit_insn_opndv(insn, 2)) = value;
}
PUSH_F64(res);
return true;
fail:
return false; return false;
} }
bool bool
jit_compile_op_f64_promote_f32(JitCompContext *comp_ctx) jit_compile_op_f64_promote_f32(JitCompContext *cc)
{ {
JitReg value, res;
POP_F32(value);
res = jit_cc_new_reg_F64(cc);
GEN_INSN(F32TOF64, res, value);
PUSH_F64(res);
return true;
fail:
return false; return false;
} }
bool bool
jit_compile_op_i64_reinterpret_f64(JitCompContext *comp_ctx) jit_compile_op_i64_reinterpret_f64(JitCompContext *cc)
{ {
JitReg value, res;
POP_F64(value);
res = jit_cc_new_reg_I64(cc);
GEN_INSN(F64CASTI64, res, value);
PUSH_I64(res);
return true;
fail:
return false; return false;
} }
bool bool
jit_compile_op_i32_reinterpret_f32(JitCompContext *comp_ctx) jit_compile_op_i32_reinterpret_f32(JitCompContext *cc)
{ {
JitReg value, res;
POP_F32(value);
res = jit_cc_new_reg_I32(cc);
GEN_INSN(F32CASTI32, res, value);
PUSH_I32(res);
return true;
fail:
return false; return false;
} }
bool bool
jit_compile_op_f64_reinterpret_i64(JitCompContext *comp_ctx) jit_compile_op_f64_reinterpret_i64(JitCompContext *cc)
{ {
JitReg value, res;
POP_I64(value);
res = jit_cc_new_reg_F64(cc);
GEN_INSN(I64CASTF64, res, value);
PUSH_F64(res);
return true;
fail:
return false; return false;
} }
bool bool
jit_compile_op_f32_reinterpret_i32(JitCompContext *comp_ctx) jit_compile_op_f32_reinterpret_i32(JitCompContext *cc)
{ {
JitReg value, res;
POP_I32(value);
res = jit_cc_new_reg_F32(cc);
GEN_INSN(I32CASTF32, res, value);
PUSH_F32(res);
return true;
fail:
return false; return false;
} }

View File

@ -1159,24 +1159,76 @@ jit_compile_op_i64_shift(JitCompContext *cc, IntShift shift_op)
bool bool
jit_compile_op_f32_math(JitCompContext *cc, FloatMath math_op) jit_compile_op_f32_math(JitCompContext *cc, FloatMath math_op)
{ {
bh_assert(0);
return false; return false;
} }
bool bool
jit_compile_op_f64_math(JitCompContext *cc, FloatMath math_op) jit_compile_op_f64_math(JitCompContext *cc, FloatMath math_op)
{ {
bh_assert(0);
return false; return false;
} }
bool bool
jit_compile_op_f32_arithmetic(JitCompContext *cc, FloatArithmetic arith_op) jit_compile_op_f32_arithmetic(JitCompContext *cc, FloatArithmetic arith_op)
{ {
bh_assert(0);
return false; return false;
} }
bool bool
jit_compile_op_f64_arithmetic(JitCompContext *cc, FloatArithmetic arith_op) jit_compile_op_f64_arithmetic(JitCompContext *cc, FloatArithmetic arith_op)
{ {
JitReg lhs, rhs, res;
POP_F64(rhs);
POP_F64(lhs);
res = jit_cc_new_reg_F64(cc);
switch (arith_op) {
case FLOAT_ADD:
{
GEN_INSN(ADD, res, lhs, rhs);
break;
}
case FLOAT_SUB:
{
GEN_INSN(SUB, res, lhs, rhs);
break;
}
case FLOAT_MUL:
{
GEN_INSN(MUL, res, lhs, rhs);
break;
}
case FLOAT_DIV:
{
/*TODO: add divided by zero interception */
GEN_INSN(DIV_S, res, lhs, rhs);
break;
}
case FLOAT_MIN:
{
GEN_INSN(MIN, res, lhs, rhs);
break;
}
case FLOAT_MAX:
{
GEN_INSN(MAX, res, lhs, rhs);
break;
}
default:
{
bh_assert(0);
goto fail;
}
}
PUSH_F64(res);
return true;
fail:
return false; return false;
} }

View File

@ -817,7 +817,7 @@ init_func_translation(JitCompContext *cc)
local_off = (uint32)offsetof(WASMInterpFrame, lp) local_off = (uint32)offsetof(WASMInterpFrame, lp)
+ cur_wasm_func->param_cell_num * 4; + cur_wasm_func->param_cell_num * 4;
for (i = 0; i < cur_wasm_func->local_cell_num / 2; i++, local_off += 8) { for (i = 0; i < cur_wasm_func->local_cell_num / 2; i++, local_off += 8) {
GEN_INSN(STI64, NEW_CONST(I32, 0), cc->fp_reg, GEN_INSN(STI64, NEW_CONST(I64, 0), cc->fp_reg,
NEW_CONST(I32, local_off)); NEW_CONST(I32, local_off));
} }
if (cur_wasm_func->local_cell_num & 1) { if (cur_wasm_func->local_cell_num & 1) {

View File

@ -76,6 +76,8 @@
registers of the same kind (move) or different kinds (convert) */ registers of the same kind (move) or different kinds (convert) */
INSN(MOV, Reg, 2, 1) INSN(MOV, Reg, 2, 1)
INSN(PHI, VReg, 1, 1) INSN(PHI, VReg, 1, 1)
/* conversion. will extend or truncate */
INSN(I32TOI8, Reg, 2, 1) INSN(I32TOI8, Reg, 2, 1)
INSN(I32TOU8, Reg, 2, 1) INSN(I32TOU8, Reg, 2, 1)
INSN(I32TOI16, Reg, 2, 1) INSN(I32TOI16, Reg, 2, 1)
@ -92,9 +94,17 @@ INSN(I64TOF64, Reg, 2, 1)
INSN(F32TOI32, Reg, 2, 1) INSN(F32TOI32, Reg, 2, 1)
INSN(F32TOI64, Reg, 2, 1) INSN(F32TOI64, Reg, 2, 1)
INSN(F32TOF64, Reg, 2, 1) INSN(F32TOF64, Reg, 2, 1)
INSN(F32TOU32, Reg, 2, 1)
INSN(F64TOI32, Reg, 2, 1) INSN(F64TOI32, Reg, 2, 1)
INSN(F64TOI64, Reg, 2, 1) INSN(F64TOI64, Reg, 2, 1)
INSN(F64TOF32, Reg, 2, 1) INSN(F64TOF32, Reg, 2, 1)
INSN(F64TOU32, Reg, 2, 1)
/* re-interpreter binary presentation. like *(integer*)&floating_point, and *(floating_point*)&integer */
INSN(I32CASTF32, Reg, 2, 1)
INSN(I64CASTF64, Reg, 2, 1)
INSN(F32CASTI32, Reg, 2, 1)
INSN(F64CASTI64, Reg, 2, 1)
/* Arithmetic and bitwise instructions: */ /* Arithmetic and bitwise instructions: */
INSN(NEG, Reg, 2, 1) INSN(NEG, Reg, 2, 1)
@ -115,6 +125,8 @@ INSN(OR, Reg, 3, 1)
INSN(XOR, Reg, 3, 1) INSN(XOR, Reg, 3, 1)
INSN(AND, Reg, 3, 1) INSN(AND, Reg, 3, 1)
INSN(CMP, Reg, 3, 1) INSN(CMP, Reg, 3, 1)
INSN(MAX, Reg, 3, 1)
INSN(MIN, Reg, 3, 1)
/* Select instruction: */ /* Select instruction: */
INSN(SELECTEQ, Reg, 4, 1) INSN(SELECTEQ, Reg, 4, 1)

View File

@ -1181,6 +1181,10 @@ typedef struct JitCompContext {
/* The hash table. */ /* The hash table. */
JitInsn **_table; JitInsn **_table;
} _insn_hash_table; } _insn_hash_table;
/* indicate if the last comparision is about floating-point numbers or not
*/
bool last_cmp_on_fp;
} JitCompContext; } JitCompContext;
/* /*