/* * Copyright (C) 2019 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ #include "jit_emit_compare.h" #include "jit_emit_function.h" #include "../jit_frontend.h" #include "../jit_codegen.h" static bool jit_compile_op_compare_integer(JitCompContext *cc, IntCond cond, bool is64Bit) { JitReg lhs, rhs, res, const_zero, const_one; if (cond < INT_EQZ || cond > INT_GE_U) { jit_set_last_error(cc, "unsupported comparation operation"); goto fail; } res = jit_cc_new_reg_I32(cc); const_zero = NEW_CONST(I32, 0); const_one = NEW_CONST(I32, 1); if (is64Bit) { if (INT_EQZ == cond) { rhs = NEW_CONST(I64, 0); } else { POP_I64(rhs); } POP_I64(lhs); } else { if (INT_EQZ == cond) { rhs = NEW_CONST(I32, 0); } else { POP_I32(rhs); } POP_I32(lhs); } GEN_INSN(CMP, cc->cmp_reg, lhs, rhs); switch (cond) { case INT_EQ: case INT_EQZ: { GEN_INSN(SELECTEQ, res, cc->cmp_reg, const_one, const_zero); break; } case INT_NE: { GEN_INSN(SELECTNE, res, cc->cmp_reg, const_one, const_zero); break; } case INT_LT_S: { GEN_INSN(SELECTLTS, res, cc->cmp_reg, const_one, const_zero); break; } case INT_LT_U: { GEN_INSN(SELECTLTU, res, cc->cmp_reg, const_one, const_zero); break; } case INT_GT_S: { GEN_INSN(SELECTGTS, res, cc->cmp_reg, const_one, const_zero); break; } case INT_GT_U: { GEN_INSN(SELECTGTU, res, cc->cmp_reg, const_one, const_zero); break; } case INT_LE_S: { GEN_INSN(SELECTLES, res, cc->cmp_reg, const_one, const_zero); break; } case INT_LE_U: { GEN_INSN(SELECTLEU, res, cc->cmp_reg, const_one, const_zero); break; } case INT_GE_S: { GEN_INSN(SELECTGES, res, cc->cmp_reg, const_one, const_zero); break; } default: /* INT_GE_U */ { GEN_INSN(SELECTGEU, res, cc->cmp_reg, const_one, const_zero); break; } } PUSH_I32(res); return true; fail: return false; } bool jit_compile_op_i32_compare(JitCompContext *cc, IntCond cond) { return jit_compile_op_compare_integer(cc, cond, false); } bool jit_compile_op_i64_compare(JitCompContext *cc, IntCond cond) { return jit_compile_op_compare_integer(cc, cond, true); } static int32 float_cmp_eq(float f1, float f2) { if (isnan(f1) || isnan(f2)) return 0; return f1 == f2; } static int32 float_cmp_ne(float f1, float f2) { if (isnan(f1) || isnan(f2)) return 1; return f1 != f2; } static int32 double_cmp_eq(double d1, double d2) { if (isnan(d1) || isnan(d2)) return 0; return d1 == d2; } static int32 double_cmp_ne(double d1, double d2) { if (isnan(d1) || isnan(d2)) return 1; return d1 != d2; } static bool jit_compile_op_compare_float_point(JitCompContext *cc, FloatCond cond, JitReg lhs, JitReg rhs) { JitReg res, args[2], const_zero, const_one; JitRegKind kind; void *func; if (cond == FLOAT_EQ || cond == FLOAT_NE) { kind = jit_reg_kind(lhs); if (cond == FLOAT_EQ) func = (kind == JIT_REG_KIND_F32) ? (void *)float_cmp_eq : (void *)double_cmp_eq; else func = (kind == JIT_REG_KIND_F32) ? (void *)float_cmp_ne : (void *)double_cmp_ne; res = jit_cc_new_reg_I32(cc); args[0] = lhs; args[1] = rhs; if (!jit_emit_callnative(cc, func, res, args, 2)) { goto fail; } } else { res = jit_cc_new_reg_I32(cc); const_zero = NEW_CONST(I32, 0); const_one = NEW_CONST(I32, 1); switch (cond) { case FLOAT_LT: { GEN_INSN(CMP, cc->cmp_reg, rhs, lhs); GEN_INSN(SELECTGTS, res, cc->cmp_reg, const_one, const_zero); break; } case FLOAT_GT: { GEN_INSN(CMP, cc->cmp_reg, lhs, rhs); GEN_INSN(SELECTGTS, res, cc->cmp_reg, const_one, const_zero); break; } case FLOAT_LE: { GEN_INSN(CMP, cc->cmp_reg, rhs, lhs); GEN_INSN(SELECTGES, res, cc->cmp_reg, const_one, const_zero); break; } case FLOAT_GE: { GEN_INSN(CMP, cc->cmp_reg, lhs, rhs); 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 jit_compile_op_f32_compare(JitCompContext *cc, FloatCond cond) { JitReg res, const_zero, const_one; JitReg lhs, rhs; POP_F32(rhs); POP_F32(lhs); if (jit_reg_is_const_val(lhs) && jit_reg_is_const_val(rhs)) { float32 lvalue = jit_cc_get_const_F32(cc, lhs); float32 rvalue = jit_cc_get_const_F32(cc, rhs); const_zero = NEW_CONST(I32, 0); const_one = NEW_CONST(I32, 1); switch (cond) { case FLOAT_EQ: { res = (lvalue == rvalue) ? const_one : const_zero; break; } case FLOAT_NE: { res = (lvalue != rvalue) ? const_one : const_zero; break; } case FLOAT_LT: { res = (lvalue < rvalue) ? const_one : const_zero; break; } case FLOAT_GT: { res = (lvalue > rvalue) ? const_one : const_zero; break; } case FLOAT_LE: { res = (lvalue <= rvalue) ? const_one : const_zero; break; } case FLOAT_GE: { res = (lvalue >= rvalue) ? const_one : const_zero; break; } default: { bh_assert(!"unknown FloatCond"); goto fail; } } PUSH_I32(res); return true; } return jit_compile_op_compare_float_point(cc, cond, lhs, rhs); fail: return false; } bool jit_compile_op_f64_compare(JitCompContext *cc, FloatCond cond) { JitReg res, const_zero, const_one; JitReg lhs, rhs; POP_F64(rhs); POP_F64(lhs); if (jit_reg_is_const_val(lhs) && jit_reg_is_const_val(rhs)) { float64 lvalue = jit_cc_get_const_F64(cc, lhs); float64 rvalue = jit_cc_get_const_F64(cc, rhs); const_zero = NEW_CONST(I32, 0); const_one = NEW_CONST(I32, 1); switch (cond) { case FLOAT_EQ: { res = (lvalue == rvalue) ? const_one : const_zero; break; } case FLOAT_NE: { res = (lvalue != rvalue) ? const_one : const_zero; break; } case FLOAT_LT: { res = (lvalue < rvalue) ? const_one : const_zero; break; } case FLOAT_GT: { res = (lvalue > rvalue) ? const_one : const_zero; break; } case FLOAT_LE: { res = (lvalue <= rvalue) ? const_one : const_zero; break; } case FLOAT_GE: { res = (lvalue >= rvalue) ? const_one : const_zero; break; } default: { bh_assert(!"unknown FloatCond"); goto fail; } } PUSH_I32(res); return true; } return jit_compile_op_compare_float_point(cc, cond, lhs, rhs); fail: return false; }