mirror of
https://github.com/bytecodealliance/wasm-micro-runtime.git
synced 2025-02-06 15:05:19 +00:00
1254 lines
44 KiB
C
1254 lines
44 KiB
C
/*
|
|
* Copyright (C) 2020 Intel Corporation. All rights reserved.
|
|
* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
*/
|
|
|
|
#include "aot_emit_numberic.h"
|
|
#include "aot_emit_exception.h"
|
|
#include "aot_emit_control.h"
|
|
#include "../aot/aot_runtime.h"
|
|
#include "../aot/aot_intrinsic.h"
|
|
|
|
#include <stdarg.h>
|
|
|
|
#define LLVM_BUILD_ICMP(op, left, right, res, name) \
|
|
do { \
|
|
if (!(res = \
|
|
LLVMBuildICmp(comp_ctx->builder, op, left, right, name))) { \
|
|
aot_set_last_error("llvm build " name " fail."); \
|
|
return false; \
|
|
} \
|
|
} while (0)
|
|
|
|
#define LLVM_BUILD_OP(Op, left, right, res, name, err_ret) \
|
|
do { \
|
|
if (!(res = LLVMBuild##Op(comp_ctx->builder, left, right, name))) { \
|
|
aot_set_last_error("llvm build " #name " fail."); \
|
|
return err_ret; \
|
|
} \
|
|
} while (0)
|
|
|
|
#define LLVM_BUILD_OP_OR_INTRINSIC(Op, left, right, res, intrinsic, name, \
|
|
err_ret) \
|
|
do { \
|
|
if (comp_ctx->disable_llvm_intrinsics \
|
|
&& aot_intrinsic_check_capability(comp_ctx, intrinsic)) { \
|
|
res = aot_call_llvm_intrinsic(comp_ctx, func_ctx, intrinsic, \
|
|
param_types[0], param_types, 2, \
|
|
left, right); \
|
|
} \
|
|
else { \
|
|
LLVM_BUILD_OP(Op, left, right, res, name, false); \
|
|
} \
|
|
} while (0)
|
|
|
|
#define ADD_BASIC_BLOCK(block, name) \
|
|
do { \
|
|
if (!(block = LLVMAppendBasicBlockInContext(comp_ctx->context, \
|
|
func_ctx->func, name))) { \
|
|
aot_set_last_error("llvm add basic block failed."); \
|
|
goto fail; \
|
|
} \
|
|
\
|
|
LLVMMoveBasicBlockAfter(block, LLVMGetInsertBlock(comp_ctx->builder)); \
|
|
} while (0)
|
|
|
|
#if LLVM_VERSION_NUMBER >= 12
|
|
#define IS_CONST_ZERO(val) \
|
|
(LLVMIsEfficientConstInt(val) \
|
|
&& ((is_i32 && (int32)LLVMConstIntGetZExtValue(val) == 0) \
|
|
|| (!is_i32 && (int64)LLVMConstIntGetSExtValue(val) == 0)))
|
|
#else
|
|
#define IS_CONST_ZERO(val) \
|
|
(LLVMIsEfficientConstInt(val) \
|
|
&& ((is_i32 && (int32)LLVMConstIntGetZExtValue(val) == 0) \
|
|
|| (!is_i32 && (int64)LLVMConstIntGetSExtValue(val) == 0)))
|
|
#endif
|
|
|
|
#define CHECK_INT_OVERFLOW(type) \
|
|
do { \
|
|
LLVMValueRef cmp_min_int, cmp_neg_one; \
|
|
LLVM_BUILD_ICMP(LLVMIntEQ, left, type##_MIN, cmp_min_int, \
|
|
"cmp_min_int"); \
|
|
LLVM_BUILD_ICMP(LLVMIntEQ, right, type##_NEG_ONE, cmp_neg_one, \
|
|
"cmp_neg_one"); \
|
|
LLVM_BUILD_OP(And, cmp_min_int, cmp_neg_one, overflow, "overflow", \
|
|
false); \
|
|
} while (0)
|
|
|
|
#define PUSH_INT(v) \
|
|
do { \
|
|
if (is_i32) \
|
|
PUSH_I32(v); \
|
|
else \
|
|
PUSH_I64(v); \
|
|
} while (0)
|
|
|
|
#define POP_INT(v) \
|
|
do { \
|
|
if (is_i32) \
|
|
POP_I32(v); \
|
|
else \
|
|
POP_I64(v); \
|
|
} while (0)
|
|
|
|
#define PUSH_FLOAT(v) \
|
|
do { \
|
|
if (is_f32) \
|
|
PUSH_F32(v); \
|
|
else \
|
|
PUSH_F64(v); \
|
|
} while (0)
|
|
|
|
#define POP_FLOAT(v) \
|
|
do { \
|
|
if (is_f32) \
|
|
POP_F32(v); \
|
|
else \
|
|
POP_F64(v); \
|
|
} while (0)
|
|
|
|
#define DEF_INT_UNARY_OP(op, err) \
|
|
do { \
|
|
LLVMValueRef res, operand; \
|
|
POP_INT(operand); \
|
|
if (!(res = op)) { \
|
|
if (err) \
|
|
aot_set_last_error(err); \
|
|
return false; \
|
|
} \
|
|
PUSH_INT(res); \
|
|
} while (0)
|
|
|
|
#define DEF_INT_BINARY_OP(op, err) \
|
|
do { \
|
|
LLVMValueRef res, left, right; \
|
|
POP_INT(right); \
|
|
POP_INT(left); \
|
|
if (!(res = op)) { \
|
|
if (err) \
|
|
aot_set_last_error(err); \
|
|
return false; \
|
|
} \
|
|
PUSH_INT(res); \
|
|
} while (0)
|
|
|
|
#define DEF_FP_UNARY_OP(op, err) \
|
|
do { \
|
|
LLVMValueRef res, operand; \
|
|
POP_FLOAT(operand); \
|
|
if (!(res = op)) { \
|
|
if (err) \
|
|
aot_set_last_error(err); \
|
|
return false; \
|
|
} \
|
|
PUSH_FLOAT(res); \
|
|
} while (0)
|
|
|
|
#define DEF_FP_BINARY_OP(op, err) \
|
|
do { \
|
|
LLVMValueRef res, left, right; \
|
|
POP_FLOAT(right); \
|
|
POP_FLOAT(left); \
|
|
if (!(res = op)) { \
|
|
if (err) \
|
|
aot_set_last_error(err); \
|
|
return false; \
|
|
} \
|
|
PUSH_FLOAT(res); \
|
|
} while (0)
|
|
|
|
#define SHIFT_COUNT_MASK \
|
|
do { \
|
|
/* LLVM has undefined behavior if shift count is greater than \
|
|
* bits count while Webassembly spec requires the shift count \
|
|
* be wrapped. \
|
|
*/ \
|
|
LLVMValueRef shift_count_mask, bits_minus_one; \
|
|
bits_minus_one = is_i32 ? I32_31 : I64_63; \
|
|
LLVM_BUILD_OP(And, right, bits_minus_one, shift_count_mask, \
|
|
"shift_count_mask", NULL); \
|
|
right = shift_count_mask; \
|
|
} while (0)
|
|
|
|
/* Call llvm constrained floating-point intrinsic */
|
|
static LLVMValueRef
|
|
call_llvm_float_experimental_constrained_intrinsic(AOTCompContext *comp_ctx,
|
|
AOTFuncContext *func_ctx,
|
|
bool is_f32,
|
|
const char *intrinsic, ...)
|
|
{
|
|
va_list param_value_list;
|
|
LLVMValueRef ret;
|
|
LLVMTypeRef param_types[4], ret_type = is_f32 ? F32_TYPE : F64_TYPE;
|
|
int param_count = (comp_ctx->disable_llvm_intrinsics
|
|
&& aot_intrinsic_check_capability(comp_ctx, intrinsic))
|
|
? 2
|
|
: 4;
|
|
|
|
param_types[0] = param_types[1] = ret_type;
|
|
param_types[2] = param_types[3] = MD_TYPE;
|
|
|
|
va_start(param_value_list, intrinsic);
|
|
|
|
ret = aot_call_llvm_intrinsic_v(comp_ctx, func_ctx, intrinsic, ret_type,
|
|
param_types, param_count, param_value_list);
|
|
|
|
va_end(param_value_list);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/* Call llvm constrained libm-equivalent intrinsic */
|
|
static LLVMValueRef
|
|
call_llvm_libm_experimental_constrained_intrinsic(AOTCompContext *comp_ctx,
|
|
AOTFuncContext *func_ctx,
|
|
bool is_f32,
|
|
const char *intrinsic, ...)
|
|
{
|
|
va_list param_value_list;
|
|
LLVMValueRef ret;
|
|
LLVMTypeRef param_types[3], ret_type = is_f32 ? F32_TYPE : F64_TYPE;
|
|
|
|
param_types[0] = ret_type;
|
|
param_types[1] = param_types[2] = MD_TYPE;
|
|
|
|
va_start(param_value_list, intrinsic);
|
|
|
|
ret = aot_call_llvm_intrinsic_v(comp_ctx, func_ctx, intrinsic, ret_type,
|
|
param_types, 3, param_value_list);
|
|
|
|
va_end(param_value_list);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static LLVMValueRef
|
|
compile_op_float_min_max(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
|
|
bool is_f32, LLVMValueRef left, LLVMValueRef right,
|
|
bool is_min)
|
|
{
|
|
LLVMTypeRef float_param_types[2];
|
|
LLVMTypeRef param_types[2], ret_type = is_f32 ? F32_TYPE : F64_TYPE,
|
|
int_type = is_f32 ? I32_TYPE : I64_TYPE;
|
|
LLVMValueRef cmp, is_eq, is_nan, ret, left_int, right_int, tmp,
|
|
nan = LLVMConstRealOfString(ret_type, "NaN");
|
|
char *intrinsic = is_min ? (is_f32 ? "llvm.minnum.f32" : "llvm.minnum.f64")
|
|
: (is_f32 ? "llvm.maxnum.f32" : "llvm.maxnum.f64");
|
|
CHECK_LLVM_CONST(nan);
|
|
|
|
/* Note: param_types is used by LLVM_BUILD_OP_OR_INTRINSIC */
|
|
param_types[0] = param_types[1] = int_type;
|
|
float_param_types[0] = float_param_types[1] = ret_type;
|
|
|
|
if (comp_ctx->disable_llvm_intrinsics
|
|
&& aot_intrinsic_check_capability(comp_ctx,
|
|
is_f32 ? "f32_cmp" : "f64_cmp")) {
|
|
LLVMTypeRef param_types_intrinsic[3];
|
|
LLVMValueRef opcond = LLVMConstInt(I32_TYPE, FLOAT_UNO, true);
|
|
param_types_intrinsic[0] = I32_TYPE;
|
|
param_types_intrinsic[1] = is_f32 ? F32_TYPE : F64_TYPE;
|
|
param_types_intrinsic[2] = param_types_intrinsic[1];
|
|
is_nan = aot_call_llvm_intrinsic(
|
|
comp_ctx, func_ctx, is_f32 ? "f32_cmp" : "f64_cmp", I32_TYPE,
|
|
param_types_intrinsic, 3, opcond, left, right);
|
|
|
|
opcond = LLVMConstInt(I32_TYPE, FLOAT_EQ, true);
|
|
is_eq = aot_call_llvm_intrinsic(
|
|
comp_ctx, func_ctx, is_f32 ? "f32_cmp" : "f64_cmp", I32_TYPE,
|
|
param_types_intrinsic, 3, opcond, left, right);
|
|
|
|
if (!is_nan || !is_eq) {
|
|
return NULL;
|
|
}
|
|
|
|
if (!(is_nan = LLVMBuildIntCast(comp_ctx->builder, is_nan, INT1_TYPE,
|
|
"bit_cast_is_nan"))) {
|
|
aot_set_last_error("llvm build is_nan bit cast fail.");
|
|
return NULL;
|
|
}
|
|
|
|
if (!(is_eq = LLVMBuildIntCast(comp_ctx->builder, is_eq, INT1_TYPE,
|
|
"bit_cast_is_eq"))) {
|
|
aot_set_last_error("llvm build is_eq bit cast fail.");
|
|
return NULL;
|
|
}
|
|
}
|
|
else if (!(is_nan = LLVMBuildFCmp(comp_ctx->builder, LLVMRealUNO, left,
|
|
right, "is_nan"))
|
|
|| !(is_eq = LLVMBuildFCmp(comp_ctx->builder, LLVMRealOEQ, left,
|
|
right, "is_eq"))) {
|
|
aot_set_last_error("llvm build fcmp fail.");
|
|
return NULL;
|
|
}
|
|
|
|
/* If left and right are equal, they may be zero with different sign.
|
|
Webassembly spec assert -0 < +0. So do a bitwise here. */
|
|
if (!(left_int =
|
|
LLVMBuildBitCast(comp_ctx->builder, left, int_type, "left_int"))
|
|
|| !(right_int = LLVMBuildBitCast(comp_ctx->builder, right, int_type,
|
|
"right_int"))) {
|
|
aot_set_last_error("llvm build bitcast fail.");
|
|
return NULL;
|
|
}
|
|
|
|
if (is_min)
|
|
LLVM_BUILD_OP_OR_INTRINSIC(Or, left_int, right_int, tmp,
|
|
is_f32 ? "i32.or" : "i64.or", "tmp_int",
|
|
false);
|
|
else
|
|
LLVM_BUILD_OP_OR_INTRINSIC(And, left_int, right_int, tmp,
|
|
is_f32 ? "i32.and" : "i64.and", "tmp_int",
|
|
false);
|
|
|
|
if (!(tmp = LLVMBuildBitCast(comp_ctx->builder, tmp, ret_type, "tmp"))) {
|
|
aot_set_last_error("llvm build bitcast fail.");
|
|
return NULL;
|
|
}
|
|
|
|
if (!(cmp = aot_call_llvm_intrinsic(comp_ctx, func_ctx, intrinsic, ret_type,
|
|
float_param_types, 2, left, right)))
|
|
return NULL;
|
|
|
|
/* The result of XIP intrinsic is 0 or 1, should return it directly */
|
|
|
|
if (comp_ctx->disable_llvm_intrinsics
|
|
&& aot_intrinsic_check_capability(comp_ctx,
|
|
is_f32 ? "f32_cmp" : "f64_cmp")) {
|
|
return cmp;
|
|
}
|
|
|
|
if (!(cmp = LLVMBuildSelect(comp_ctx->builder, is_eq, tmp, cmp, "cmp"))) {
|
|
aot_set_last_error("llvm build select fail.");
|
|
return NULL;
|
|
}
|
|
|
|
if (!(ret = LLVMBuildSelect(comp_ctx->builder, is_nan, nan, cmp,
|
|
is_min ? "min" : "max"))) {
|
|
aot_set_last_error("llvm build select fail.");
|
|
return NULL;
|
|
}
|
|
|
|
return ret;
|
|
fail:
|
|
return NULL;
|
|
}
|
|
|
|
typedef enum BitCountType {
|
|
CLZ32 = 0,
|
|
CLZ64,
|
|
CTZ32,
|
|
CTZ64,
|
|
POP_CNT32,
|
|
POP_CNT64
|
|
} BitCountType;
|
|
|
|
/* clang-format off */
|
|
static char *bit_cnt_llvm_intrinsic[] = {
|
|
"llvm.ctlz.i32",
|
|
"llvm.ctlz.i64",
|
|
"llvm.cttz.i32",
|
|
"llvm.cttz.i64",
|
|
"llvm.ctpop.i32",
|
|
"llvm.ctpop.i64",
|
|
};
|
|
/* clang-format on */
|
|
|
|
static bool
|
|
aot_compile_int_bit_count(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
|
|
BitCountType type, bool is_i32)
|
|
{
|
|
LLVMValueRef zero_undef;
|
|
LLVMTypeRef ret_type, param_types[2];
|
|
|
|
param_types[0] = ret_type = is_i32 ? I32_TYPE : I64_TYPE;
|
|
param_types[1] = LLVMInt1TypeInContext(comp_ctx->context);
|
|
|
|
zero_undef = LLVMConstInt(param_types[1], false, true);
|
|
CHECK_LLVM_CONST(zero_undef);
|
|
|
|
/* Call the LLVM intrinsic function */
|
|
if (type < POP_CNT32)
|
|
DEF_INT_UNARY_OP(aot_call_llvm_intrinsic(
|
|
comp_ctx, func_ctx, bit_cnt_llvm_intrinsic[type],
|
|
ret_type, param_types, 2, operand, zero_undef),
|
|
NULL);
|
|
else
|
|
DEF_INT_UNARY_OP(aot_call_llvm_intrinsic(
|
|
comp_ctx, func_ctx, bit_cnt_llvm_intrinsic[type],
|
|
ret_type, param_types, 1, operand),
|
|
NULL);
|
|
|
|
return true;
|
|
|
|
fail:
|
|
return false;
|
|
}
|
|
|
|
static bool
|
|
compile_rems(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
|
|
LLVMValueRef left, LLVMValueRef right, LLVMValueRef overflow_cond,
|
|
bool is_i32)
|
|
{
|
|
LLVMValueRef phi, no_overflow_value, zero = is_i32 ? I32_ZERO : I64_ZERO;
|
|
LLVMBasicBlockRef block_curr, no_overflow_block, rems_end_block;
|
|
LLVMTypeRef param_types[2];
|
|
|
|
param_types[1] = param_types[0] = is_i32 ? I32_TYPE : I64_TYPE;
|
|
|
|
block_curr = LLVMGetInsertBlock(comp_ctx->builder);
|
|
|
|
/* Add 2 blocks: no_overflow_block and rems_end block */
|
|
ADD_BASIC_BLOCK(rems_end_block, "rems_end");
|
|
ADD_BASIC_BLOCK(no_overflow_block, "rems_no_overflow");
|
|
|
|
/* Create condition br */
|
|
if (!LLVMBuildCondBr(comp_ctx->builder, overflow_cond, rems_end_block,
|
|
no_overflow_block)) {
|
|
aot_set_last_error("llvm build cond br failed.");
|
|
return false;
|
|
}
|
|
|
|
/* Translate no_overflow_block */
|
|
LLVMPositionBuilderAtEnd(comp_ctx->builder, no_overflow_block);
|
|
|
|
LLVM_BUILD_OP_OR_INTRINSIC(SRem, left, right, no_overflow_value,
|
|
is_i32 ? "i32.rem_s" : "i64.rem_s", "rem_s",
|
|
false);
|
|
|
|
/* Jump to rems_end block */
|
|
if (!LLVMBuildBr(comp_ctx->builder, rems_end_block)) {
|
|
aot_set_last_error("llvm build br failed.");
|
|
return false;
|
|
}
|
|
|
|
/* Translate rems_end_block */
|
|
LLVMPositionBuilderAtEnd(comp_ctx->builder, rems_end_block);
|
|
|
|
/* Create result phi */
|
|
if (!(phi = LLVMBuildPhi(comp_ctx->builder, is_i32 ? I32_TYPE : I64_TYPE,
|
|
"rems_result_phi"))) {
|
|
aot_set_last_error("llvm build phi failed.");
|
|
return false;
|
|
}
|
|
|
|
/* Add phi incoming values */
|
|
LLVMAddIncoming(phi, &no_overflow_value, &no_overflow_block, 1);
|
|
LLVMAddIncoming(phi, &zero, &block_curr, 1);
|
|
|
|
if (is_i32)
|
|
PUSH_I32(phi);
|
|
else
|
|
PUSH_I64(phi);
|
|
|
|
return true;
|
|
|
|
fail:
|
|
return false;
|
|
}
|
|
|
|
static bool
|
|
compile_int_div(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
|
|
IntArithmetic arith_op, bool is_i32, uint8 **p_frame_ip)
|
|
{
|
|
LLVMValueRef left, right, cmp_div_zero, overflow, res;
|
|
LLVMBasicBlockRef check_div_zero_succ, check_overflow_succ;
|
|
LLVMTypeRef param_types[2];
|
|
const char *intrinsic = NULL;
|
|
|
|
param_types[1] = param_types[0] = is_i32 ? I32_TYPE : I64_TYPE;
|
|
|
|
bh_assert(arith_op == INT_DIV_S || arith_op == INT_DIV_U
|
|
|| arith_op == INT_REM_S || arith_op == INT_REM_U);
|
|
|
|
POP_INT(right);
|
|
POP_INT(left);
|
|
|
|
if (LLVMIsUndef(right) || LLVMIsUndef(left)
|
|
#if LLVM_VERSION_NUMBER >= 12
|
|
|| LLVMIsPoison(right) || LLVMIsPoison(left)
|
|
#endif
|
|
) {
|
|
if (!(aot_emit_exception(comp_ctx, func_ctx, EXCE_INTEGER_OVERFLOW,
|
|
false, NULL, NULL))) {
|
|
goto fail;
|
|
}
|
|
return aot_handle_next_reachable_block(comp_ctx, func_ctx, p_frame_ip);
|
|
}
|
|
|
|
if (LLVMIsEfficientConstInt(right)) {
|
|
int64 right_val = (int64)LLVMConstIntGetSExtValue(right);
|
|
switch (right_val) {
|
|
case 0:
|
|
/* Directly throw exception if divided by zero */
|
|
if (!(aot_emit_exception(comp_ctx, func_ctx,
|
|
EXCE_INTEGER_DIVIDE_BY_ZERO, false,
|
|
NULL, NULL)))
|
|
goto fail;
|
|
|
|
return aot_handle_next_reachable_block(comp_ctx, func_ctx,
|
|
p_frame_ip);
|
|
case 1:
|
|
if (arith_op == INT_DIV_S || arith_op == INT_DIV_U)
|
|
PUSH_INT(left);
|
|
else
|
|
PUSH_INT(is_i32 ? I32_ZERO : I64_ZERO);
|
|
return true;
|
|
case -1:
|
|
if (arith_op == INT_DIV_S) {
|
|
LLVM_BUILD_ICMP(LLVMIntEQ, left, is_i32 ? I32_MIN : I64_MIN,
|
|
overflow, "overflow");
|
|
ADD_BASIC_BLOCK(check_overflow_succ,
|
|
"check_overflow_success");
|
|
|
|
/* Throw conditional exception if overflow */
|
|
if (!(aot_emit_exception(comp_ctx, func_ctx,
|
|
EXCE_INTEGER_OVERFLOW, true,
|
|
overflow, check_overflow_succ)))
|
|
goto fail;
|
|
|
|
/* Push -(left) to stack */
|
|
if (!(res = LLVMBuildNeg(comp_ctx->builder, left, "neg"))) {
|
|
aot_set_last_error("llvm build neg fail.");
|
|
return false;
|
|
}
|
|
PUSH_INT(res);
|
|
return true;
|
|
}
|
|
else if (arith_op == INT_REM_S) {
|
|
PUSH_INT(is_i32 ? I32_ZERO : I64_ZERO);
|
|
return true;
|
|
}
|
|
else {
|
|
/* fall to default */
|
|
goto handle_default;
|
|
}
|
|
handle_default:
|
|
default:
|
|
/* Build div */
|
|
switch (arith_op) {
|
|
case INT_DIV_S:
|
|
LLVM_BUILD_OP_OR_INTRINSIC(
|
|
SDiv, left, right, res,
|
|
is_i32 ? "i32.div_s" : "i64.div_s", "div_s", false);
|
|
break;
|
|
case INT_DIV_U:
|
|
LLVM_BUILD_OP_OR_INTRINSIC(
|
|
UDiv, left, right, res,
|
|
is_i32 ? "i32.div_u" : "i64.div_u", "div_u", false);
|
|
break;
|
|
case INT_REM_S:
|
|
LLVM_BUILD_OP_OR_INTRINSIC(
|
|
SRem, left, right, res,
|
|
is_i32 ? "i32.rem_s" : "i64.rem_s", "rem_s", false);
|
|
break;
|
|
case INT_REM_U:
|
|
LLVM_BUILD_OP_OR_INTRINSIC(
|
|
URem, left, right, res,
|
|
is_i32 ? "i32.rem_u" : "i64.rem_u", "rem_u", false);
|
|
break;
|
|
default:
|
|
bh_assert(0);
|
|
return false;
|
|
}
|
|
|
|
PUSH_INT(res);
|
|
return true;
|
|
}
|
|
}
|
|
else {
|
|
/* Check divided by zero */
|
|
LLVM_BUILD_ICMP(LLVMIntEQ, right, is_i32 ? I32_ZERO : I64_ZERO,
|
|
cmp_div_zero, "cmp_div_zero");
|
|
ADD_BASIC_BLOCK(check_div_zero_succ, "check_div_zero_success");
|
|
|
|
/* Throw conditional exception if divided by zero */
|
|
if (!(aot_emit_exception(comp_ctx, func_ctx,
|
|
EXCE_INTEGER_DIVIDE_BY_ZERO, true,
|
|
cmp_div_zero, check_div_zero_succ)))
|
|
goto fail;
|
|
|
|
switch (arith_op) {
|
|
case INT_DIV_S:
|
|
/* Check integer overflow */
|
|
if (is_i32)
|
|
CHECK_INT_OVERFLOW(I32);
|
|
else
|
|
CHECK_INT_OVERFLOW(I64);
|
|
|
|
ADD_BASIC_BLOCK(check_overflow_succ, "check_overflow_success");
|
|
|
|
/* Throw conditional exception if integer overflow */
|
|
if (!(aot_emit_exception(comp_ctx, func_ctx,
|
|
EXCE_INTEGER_OVERFLOW, true, overflow,
|
|
check_overflow_succ)))
|
|
goto fail;
|
|
|
|
LLVM_BUILD_OP_OR_INTRINSIC(SDiv, left, right, res,
|
|
is_i32 ? "i32.div_s" : "i64.div_s",
|
|
"div_s", false);
|
|
PUSH_INT(res);
|
|
return true;
|
|
case INT_DIV_U:
|
|
intrinsic = is_i32 ? "i32.div_u" : "i64.div_u";
|
|
if (comp_ctx->disable_llvm_intrinsics
|
|
&& aot_intrinsic_check_capability(comp_ctx, intrinsic)) {
|
|
res = aot_call_llvm_intrinsic(comp_ctx, func_ctx, intrinsic,
|
|
param_types[0], param_types,
|
|
2, left, right);
|
|
}
|
|
else {
|
|
LLVM_BUILD_OP(UDiv, left, right, res, "div_u", false);
|
|
}
|
|
PUSH_INT(res);
|
|
return true;
|
|
case INT_REM_S:
|
|
/* Webassembly spec requires it return 0 */
|
|
if (is_i32)
|
|
CHECK_INT_OVERFLOW(I32);
|
|
else
|
|
CHECK_INT_OVERFLOW(I64);
|
|
return compile_rems(comp_ctx, func_ctx, left, right, overflow,
|
|
is_i32);
|
|
case INT_REM_U:
|
|
LLVM_BUILD_OP_OR_INTRINSIC(URem, left, right, res,
|
|
is_i32 ? "i32.rem_u" : "i64.rem_u",
|
|
"rem_u", false);
|
|
PUSH_INT(res);
|
|
return true;
|
|
default:
|
|
bh_assert(0);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
fail:
|
|
return false;
|
|
}
|
|
|
|
static LLVMValueRef
|
|
compile_int_add(AOTCompContext *comp_ctx, LLVMValueRef left, LLVMValueRef right,
|
|
bool is_i32)
|
|
{
|
|
/* If one of the operands is 0, just return the other */
|
|
if (IS_CONST_ZERO(left))
|
|
return right;
|
|
if (IS_CONST_ZERO(right))
|
|
return left;
|
|
|
|
/* Build add */
|
|
return LLVMBuildAdd(comp_ctx->builder, left, right, "add");
|
|
}
|
|
|
|
static LLVMValueRef
|
|
compile_int_sub(AOTCompContext *comp_ctx, LLVMValueRef left, LLVMValueRef right,
|
|
bool is_i32)
|
|
{
|
|
/* If the right operand is 0, just return the left */
|
|
if (IS_CONST_ZERO(right))
|
|
return left;
|
|
|
|
/* Build sub */
|
|
return LLVMBuildSub(comp_ctx->builder, left, right, "sub");
|
|
}
|
|
|
|
static LLVMValueRef
|
|
compile_int_mul(AOTCompContext *comp_ctx, LLVMValueRef left, LLVMValueRef right,
|
|
bool is_i32)
|
|
{
|
|
/* If one of the operands is 0, just return constant 0 */
|
|
if (IS_CONST_ZERO(left) || IS_CONST_ZERO(right))
|
|
return is_i32 ? I32_ZERO : I64_ZERO;
|
|
|
|
/* Build mul */
|
|
return LLVMBuildMul(comp_ctx->builder, left, right, "mul");
|
|
}
|
|
|
|
static bool
|
|
compile_op_int_arithmetic(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
|
|
IntArithmetic arith_op, bool is_i32,
|
|
uint8 **p_frame_ip)
|
|
{
|
|
switch (arith_op) {
|
|
case INT_ADD:
|
|
DEF_INT_BINARY_OP(compile_int_add(comp_ctx, left, right, is_i32),
|
|
"compile int add fail.");
|
|
return true;
|
|
case INT_SUB:
|
|
DEF_INT_BINARY_OP(compile_int_sub(comp_ctx, left, right, is_i32),
|
|
"compile int sub fail.");
|
|
return true;
|
|
case INT_MUL:
|
|
DEF_INT_BINARY_OP(compile_int_mul(comp_ctx, left, right, is_i32),
|
|
"compile int mul fail.");
|
|
return true;
|
|
case INT_DIV_S:
|
|
case INT_DIV_U:
|
|
case INT_REM_S:
|
|
case INT_REM_U:
|
|
return compile_int_div(comp_ctx, func_ctx, arith_op, is_i32,
|
|
p_frame_ip);
|
|
default:
|
|
bh_assert(0);
|
|
return false;
|
|
}
|
|
|
|
fail:
|
|
return false;
|
|
}
|
|
|
|
static bool
|
|
compile_op_int_bitwise(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
|
|
IntBitwise bitwise_op, bool is_i32)
|
|
{
|
|
switch (bitwise_op) {
|
|
case INT_AND:
|
|
DEF_INT_BINARY_OP(
|
|
LLVMBuildAnd(comp_ctx->builder, left, right, "and"),
|
|
"llvm build and fail.");
|
|
return true;
|
|
case INT_OR:
|
|
DEF_INT_BINARY_OP(LLVMBuildOr(comp_ctx->builder, left, right, "or"),
|
|
"llvm build or fail.");
|
|
return true;
|
|
case INT_XOR:
|
|
DEF_INT_BINARY_OP(
|
|
LLVMBuildXor(comp_ctx->builder, left, right, "xor"),
|
|
"llvm build xor fail.");
|
|
return true;
|
|
default:
|
|
bh_assert(0);
|
|
return false;
|
|
}
|
|
|
|
fail:
|
|
return false;
|
|
}
|
|
|
|
static LLVMValueRef
|
|
compile_int_shl(AOTCompContext *comp_ctx, LLVMValueRef left, LLVMValueRef right,
|
|
bool is_i32)
|
|
{
|
|
LLVMValueRef res;
|
|
|
|
SHIFT_COUNT_MASK;
|
|
|
|
/* Build shl */
|
|
LLVM_BUILD_OP(Shl, left, right, res, "shl", NULL);
|
|
|
|
return res;
|
|
}
|
|
|
|
static LLVMValueRef
|
|
compile_int_shr_s(AOTCompContext *comp_ctx, LLVMValueRef left,
|
|
LLVMValueRef right, bool is_i32)
|
|
{
|
|
LLVMValueRef res;
|
|
|
|
SHIFT_COUNT_MASK;
|
|
|
|
/* Build shl */
|
|
LLVM_BUILD_OP(AShr, left, right, res, "shr_s", NULL);
|
|
|
|
return res;
|
|
}
|
|
|
|
static LLVMValueRef
|
|
compile_int_shr_u(AOTCompContext *comp_ctx, LLVMValueRef left,
|
|
LLVMValueRef right, bool is_i32)
|
|
{
|
|
LLVMValueRef res;
|
|
|
|
SHIFT_COUNT_MASK;
|
|
|
|
/* Build shl */
|
|
LLVM_BUILD_OP(LShr, left, right, res, "shr_u", NULL);
|
|
|
|
return res;
|
|
}
|
|
|
|
static LLVMValueRef
|
|
compile_int_rot(AOTCompContext *comp_ctx, LLVMValueRef left, LLVMValueRef right,
|
|
bool is_rotl, bool is_i32)
|
|
{
|
|
LLVMValueRef bits_minus_shift_count, res, tmp_l, tmp_r;
|
|
char *name = is_rotl ? "rotl" : "rotr";
|
|
|
|
SHIFT_COUNT_MASK;
|
|
|
|
/* rotl/rotr with 0 */
|
|
if (IS_CONST_ZERO(right))
|
|
return left;
|
|
|
|
/* Calculate (bits - shift_count) */
|
|
LLVM_BUILD_OP(Sub, is_i32 ? I32_32 : I64_64, right, bits_minus_shift_count,
|
|
"bits_minus_shift_count", NULL);
|
|
/* Calculate (bits - shift_count) & mask */
|
|
bits_minus_shift_count =
|
|
LLVMBuildAnd(comp_ctx->builder, bits_minus_shift_count,
|
|
is_i32 ? I32_31 : I64_63, "bits_minus_shift_count_and");
|
|
if (!bits_minus_shift_count) {
|
|
aot_set_last_error("llvm build and failed.");
|
|
return NULL;
|
|
}
|
|
|
|
if (is_rotl) {
|
|
/* (left << count) | (left >> ((BITS - count) & mask)) */
|
|
LLVM_BUILD_OP(Shl, left, right, tmp_l, "tmp_l", NULL);
|
|
LLVM_BUILD_OP(LShr, left, bits_minus_shift_count, tmp_r, "tmp_r", NULL);
|
|
}
|
|
else {
|
|
/* (left >> count) | (left << ((BITS - count) & mask)) */
|
|
LLVM_BUILD_OP(LShr, left, right, tmp_l, "tmp_l", NULL);
|
|
LLVM_BUILD_OP(Shl, left, bits_minus_shift_count, tmp_r, "tmp_r", NULL);
|
|
}
|
|
|
|
LLVM_BUILD_OP(Or, tmp_l, tmp_r, res, name, NULL);
|
|
|
|
return res;
|
|
}
|
|
|
|
static bool
|
|
compile_op_int_shift(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
|
|
IntShift shift_op, bool is_i32)
|
|
{
|
|
switch (shift_op) {
|
|
case INT_SHL:
|
|
DEF_INT_BINARY_OP(compile_int_shl(comp_ctx, left, right, is_i32),
|
|
NULL);
|
|
return true;
|
|
case INT_SHR_S:
|
|
DEF_INT_BINARY_OP(compile_int_shr_s(comp_ctx, left, right, is_i32),
|
|
NULL);
|
|
return true;
|
|
case INT_SHR_U:
|
|
DEF_INT_BINARY_OP(compile_int_shr_u(comp_ctx, left, right, is_i32),
|
|
NULL);
|
|
return true;
|
|
case INT_ROTL:
|
|
DEF_INT_BINARY_OP(
|
|
compile_int_rot(comp_ctx, left, right, true, is_i32), NULL);
|
|
return true;
|
|
case INT_ROTR:
|
|
DEF_INT_BINARY_OP(
|
|
compile_int_rot(comp_ctx, left, right, false, is_i32), NULL);
|
|
return true;
|
|
default:
|
|
bh_assert(0);
|
|
return false;
|
|
}
|
|
|
|
fail:
|
|
return false;
|
|
}
|
|
|
|
static bool
|
|
is_target_arm(AOTCompContext *comp_ctx)
|
|
{
|
|
return !strncmp(comp_ctx->target_arch, "arm", 3)
|
|
|| !strncmp(comp_ctx->target_arch, "aarch64", 7)
|
|
|| !strncmp(comp_ctx->target_arch, "thumb", 5);
|
|
}
|
|
|
|
static bool
|
|
is_target_x86(AOTCompContext *comp_ctx)
|
|
{
|
|
return !strncmp(comp_ctx->target_arch, "x86_64", 6)
|
|
|| !strncmp(comp_ctx->target_arch, "i386", 4);
|
|
}
|
|
|
|
static bool
|
|
is_target_xtensa(AOTCompContext *comp_ctx)
|
|
{
|
|
return !strncmp(comp_ctx->target_arch, "xtensa", 6);
|
|
}
|
|
|
|
static bool
|
|
is_target_mips(AOTCompContext *comp_ctx)
|
|
{
|
|
return !strncmp(comp_ctx->target_arch, "mips", 4);
|
|
}
|
|
|
|
static bool
|
|
is_target_riscv(AOTCompContext *comp_ctx)
|
|
{
|
|
return !strncmp(comp_ctx->target_arch, "riscv", 5);
|
|
}
|
|
|
|
static bool
|
|
is_targeting_soft_float(AOTCompContext *comp_ctx, bool is_f32)
|
|
{
|
|
bool ret = false;
|
|
char *feature_string;
|
|
|
|
if (!(feature_string =
|
|
LLVMGetTargetMachineFeatureString(comp_ctx->target_machine))) {
|
|
aot_set_last_error("llvm get target machine feature string fail.");
|
|
return false;
|
|
}
|
|
|
|
/* Note:
|
|
* LLVM CodeGen uses FPU Coprocessor registers by default,
|
|
* so user must specify '--cpu-features=+soft-float' to wamrc if the target
|
|
* doesn't have or enable FPU on arm, x86 or mips. */
|
|
if (is_target_arm(comp_ctx) || is_target_x86(comp_ctx)
|
|
|| is_target_mips(comp_ctx)) {
|
|
ret = strstr(feature_string, "+soft-float") ? true : false;
|
|
}
|
|
else if (is_target_xtensa(comp_ctx)) {
|
|
/* Note:
|
|
* 1. The Floating-Point Coprocessor Option of xtensa only support
|
|
* single-precision floating-point operations, so must use soft-float
|
|
* for f64(i.e. double).
|
|
* 2. LLVM CodeGen uses Floating-Point Coprocessor registers by default,
|
|
* so user must specify '--cpu-features=-fp' to wamrc if the target
|
|
* doesn't have or enable Floating-Point Coprocessor Option on xtensa.
|
|
*/
|
|
if (comp_ctx->disable_llvm_intrinsics)
|
|
ret = false;
|
|
else
|
|
ret = (!is_f32 || strstr(feature_string, "-fp")) ? true : false;
|
|
}
|
|
else if (is_target_riscv(comp_ctx)) {
|
|
/*
|
|
* Note: Use builtin intrinsics since hardware float operation
|
|
* will cause rodata relocation, this will try to use hardware
|
|
* float unit (by return false) but handled by software finally
|
|
*/
|
|
if (comp_ctx->disable_llvm_intrinsics)
|
|
ret = false;
|
|
else
|
|
ret = !strstr(feature_string, "+d") ? true : false;
|
|
}
|
|
else {
|
|
ret = true;
|
|
}
|
|
|
|
LLVMDisposeMessage(feature_string);
|
|
return ret;
|
|
}
|
|
|
|
static bool
|
|
compile_op_float_arithmetic(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
|
|
FloatArithmetic arith_op, bool is_f32)
|
|
{
|
|
switch (arith_op) {
|
|
case FLOAT_ADD:
|
|
if (is_targeting_soft_float(comp_ctx, is_f32))
|
|
DEF_FP_BINARY_OP(
|
|
LLVMBuildFAdd(comp_ctx->builder, left, right, "fadd"),
|
|
"llvm build fadd fail.");
|
|
else
|
|
DEF_FP_BINARY_OP(
|
|
call_llvm_float_experimental_constrained_intrinsic(
|
|
comp_ctx, func_ctx, is_f32,
|
|
(is_f32 ? "llvm.experimental.constrained.fadd.f32"
|
|
: "llvm.experimental.constrained.fadd.f64"),
|
|
left, right, comp_ctx->fp_rounding_mode,
|
|
comp_ctx->fp_exception_behavior),
|
|
NULL);
|
|
return true;
|
|
case FLOAT_SUB:
|
|
if (is_targeting_soft_float(comp_ctx, is_f32))
|
|
DEF_FP_BINARY_OP(
|
|
LLVMBuildFSub(comp_ctx->builder, left, right, "fsub"),
|
|
"llvm build fsub fail.");
|
|
else
|
|
DEF_FP_BINARY_OP(
|
|
call_llvm_float_experimental_constrained_intrinsic(
|
|
comp_ctx, func_ctx, is_f32,
|
|
(is_f32 ? "llvm.experimental.constrained.fsub.f32"
|
|
: "llvm.experimental.constrained.fsub.f64"),
|
|
left, right, comp_ctx->fp_rounding_mode,
|
|
comp_ctx->fp_exception_behavior),
|
|
NULL);
|
|
return true;
|
|
case FLOAT_MUL:
|
|
if (is_targeting_soft_float(comp_ctx, is_f32))
|
|
DEF_FP_BINARY_OP(
|
|
LLVMBuildFMul(comp_ctx->builder, left, right, "fmul"),
|
|
"llvm build fmul fail.");
|
|
else
|
|
DEF_FP_BINARY_OP(
|
|
call_llvm_float_experimental_constrained_intrinsic(
|
|
comp_ctx, func_ctx, is_f32,
|
|
(is_f32 ? "llvm.experimental.constrained.fmul.f32"
|
|
: "llvm.experimental.constrained.fmul.f64"),
|
|
left, right, comp_ctx->fp_rounding_mode,
|
|
comp_ctx->fp_exception_behavior),
|
|
NULL);
|
|
return true;
|
|
case FLOAT_DIV:
|
|
if (is_targeting_soft_float(comp_ctx, is_f32))
|
|
DEF_FP_BINARY_OP(
|
|
LLVMBuildFDiv(comp_ctx->builder, left, right, "fdiv"),
|
|
"llvm build fdiv fail.");
|
|
else
|
|
DEF_FP_BINARY_OP(
|
|
call_llvm_float_experimental_constrained_intrinsic(
|
|
comp_ctx, func_ctx, is_f32,
|
|
(is_f32 ? "llvm.experimental.constrained.fdiv.f32"
|
|
: "llvm.experimental.constrained.fdiv.f64"),
|
|
left, right, comp_ctx->fp_rounding_mode,
|
|
comp_ctx->fp_exception_behavior),
|
|
NULL);
|
|
return true;
|
|
case FLOAT_MIN:
|
|
DEF_FP_BINARY_OP(compile_op_float_min_max(
|
|
comp_ctx, func_ctx, is_f32, left, right, true),
|
|
NULL);
|
|
return true;
|
|
case FLOAT_MAX:
|
|
DEF_FP_BINARY_OP(compile_op_float_min_max(comp_ctx, func_ctx,
|
|
is_f32, left, right,
|
|
false),
|
|
NULL);
|
|
|
|
return true;
|
|
default:
|
|
bh_assert(0);
|
|
return false;
|
|
}
|
|
|
|
fail:
|
|
return false;
|
|
}
|
|
|
|
static LLVMValueRef
|
|
call_llvm_float_math_intrinsic(AOTCompContext *comp_ctx,
|
|
AOTFuncContext *func_ctx, bool is_f32,
|
|
const char *intrinsic, ...)
|
|
{
|
|
va_list param_value_list;
|
|
LLVMValueRef ret;
|
|
LLVMTypeRef param_type, ret_type = is_f32 ? F32_TYPE : F64_TYPE;
|
|
|
|
param_type = ret_type;
|
|
|
|
va_start(param_value_list, intrinsic);
|
|
|
|
ret = aot_call_llvm_intrinsic_v(comp_ctx, func_ctx, intrinsic, ret_type,
|
|
¶m_type, 1, param_value_list);
|
|
|
|
va_end(param_value_list);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static bool
|
|
compile_op_float_math(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
|
|
FloatMath math_op, bool is_f32)
|
|
{
|
|
switch (math_op) {
|
|
case FLOAT_ABS:
|
|
DEF_FP_UNARY_OP(call_llvm_float_math_intrinsic(
|
|
comp_ctx, func_ctx, is_f32,
|
|
is_f32 ? "llvm.fabs.f32" : "llvm.fabs.f64",
|
|
operand),
|
|
NULL);
|
|
return true;
|
|
case FLOAT_NEG:
|
|
DEF_FP_UNARY_OP(LLVMBuildFNeg(comp_ctx->builder, operand, "fneg"),
|
|
"llvm build fneg fail.");
|
|
return true;
|
|
|
|
case FLOAT_CEIL:
|
|
DEF_FP_UNARY_OP(call_llvm_float_math_intrinsic(
|
|
comp_ctx, func_ctx, is_f32,
|
|
is_f32 ? "llvm.ceil.f32" : "llvm.ceil.f64",
|
|
operand),
|
|
NULL);
|
|
return true;
|
|
case FLOAT_FLOOR:
|
|
DEF_FP_UNARY_OP(call_llvm_float_math_intrinsic(
|
|
comp_ctx, func_ctx, is_f32,
|
|
is_f32 ? "llvm.floor.f32" : "llvm.floor.f64",
|
|
operand),
|
|
NULL);
|
|
return true;
|
|
case FLOAT_TRUNC:
|
|
DEF_FP_UNARY_OP(call_llvm_float_math_intrinsic(
|
|
comp_ctx, func_ctx, is_f32,
|
|
is_f32 ? "llvm.trunc.f32" : "llvm.trunc.f64",
|
|
operand),
|
|
NULL);
|
|
return true;
|
|
case FLOAT_NEAREST:
|
|
DEF_FP_UNARY_OP(call_llvm_float_math_intrinsic(
|
|
comp_ctx, func_ctx, is_f32,
|
|
is_f32 ? "llvm.rint.f32" : "llvm.rint.f64",
|
|
operand),
|
|
NULL);
|
|
return true;
|
|
case FLOAT_SQRT:
|
|
if (is_targeting_soft_float(comp_ctx, is_f32)
|
|
|| comp_ctx->disable_llvm_intrinsics)
|
|
DEF_FP_UNARY_OP(call_llvm_float_math_intrinsic(
|
|
comp_ctx, func_ctx, is_f32,
|
|
is_f32 ? "llvm.sqrt.f32" : "llvm.sqrt.f64",
|
|
operand),
|
|
NULL);
|
|
else
|
|
DEF_FP_UNARY_OP(
|
|
call_llvm_libm_experimental_constrained_intrinsic(
|
|
comp_ctx, func_ctx, is_f32,
|
|
(is_f32 ? "llvm.experimental.constrained.sqrt.f32"
|
|
: "llvm.experimental.constrained.sqrt.f64"),
|
|
operand, comp_ctx->fp_rounding_mode,
|
|
comp_ctx->fp_exception_behavior),
|
|
NULL);
|
|
return true;
|
|
default:
|
|
bh_assert(0);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
|
|
fail:
|
|
return false;
|
|
}
|
|
|
|
static bool
|
|
compile_float_copysign(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
|
|
bool is_f32)
|
|
{
|
|
LLVMTypeRef ret_type, param_types[2];
|
|
|
|
param_types[0] = param_types[1] = ret_type = is_f32 ? F32_TYPE : F64_TYPE;
|
|
|
|
DEF_FP_BINARY_OP(aot_call_llvm_intrinsic(
|
|
comp_ctx, func_ctx,
|
|
is_f32 ? "llvm.copysign.f32" : "llvm.copysign.f64",
|
|
ret_type, param_types, 2, left, right),
|
|
NULL);
|
|
return true;
|
|
|
|
fail:
|
|
return false;
|
|
}
|
|
|
|
bool
|
|
aot_compile_op_i32_clz(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx)
|
|
{
|
|
return aot_compile_int_bit_count(comp_ctx, func_ctx, CLZ32, true);
|
|
}
|
|
|
|
bool
|
|
aot_compile_op_i32_ctz(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx)
|
|
{
|
|
return aot_compile_int_bit_count(comp_ctx, func_ctx, CTZ32, true);
|
|
}
|
|
|
|
bool
|
|
aot_compile_op_i32_popcnt(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx)
|
|
{
|
|
return aot_compile_int_bit_count(comp_ctx, func_ctx, POP_CNT32, true);
|
|
}
|
|
|
|
bool
|
|
aot_compile_op_i64_clz(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx)
|
|
{
|
|
return aot_compile_int_bit_count(comp_ctx, func_ctx, CLZ64, false);
|
|
}
|
|
|
|
bool
|
|
aot_compile_op_i64_ctz(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx)
|
|
{
|
|
return aot_compile_int_bit_count(comp_ctx, func_ctx, CTZ64, false);
|
|
}
|
|
|
|
bool
|
|
aot_compile_op_i64_popcnt(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx)
|
|
{
|
|
return aot_compile_int_bit_count(comp_ctx, func_ctx, POP_CNT64, false);
|
|
}
|
|
|
|
bool
|
|
aot_compile_op_i32_arithmetic(AOTCompContext *comp_ctx,
|
|
AOTFuncContext *func_ctx, IntArithmetic arith_op,
|
|
uint8 **p_frame_ip)
|
|
{
|
|
return compile_op_int_arithmetic(comp_ctx, func_ctx, arith_op, true,
|
|
p_frame_ip);
|
|
}
|
|
|
|
bool
|
|
aot_compile_op_i64_arithmetic(AOTCompContext *comp_ctx,
|
|
AOTFuncContext *func_ctx, IntArithmetic arith_op,
|
|
uint8 **p_frame_ip)
|
|
{
|
|
return compile_op_int_arithmetic(comp_ctx, func_ctx, arith_op, false,
|
|
p_frame_ip);
|
|
}
|
|
|
|
bool
|
|
aot_compile_op_i32_bitwise(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
|
|
IntBitwise bitwise_op)
|
|
{
|
|
return compile_op_int_bitwise(comp_ctx, func_ctx, bitwise_op, true);
|
|
}
|
|
|
|
bool
|
|
aot_compile_op_i64_bitwise(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
|
|
IntBitwise bitwise_op)
|
|
{
|
|
return compile_op_int_bitwise(comp_ctx, func_ctx, bitwise_op, false);
|
|
}
|
|
|
|
bool
|
|
aot_compile_op_i32_shift(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
|
|
IntShift shift_op)
|
|
{
|
|
return compile_op_int_shift(comp_ctx, func_ctx, shift_op, true);
|
|
}
|
|
|
|
bool
|
|
aot_compile_op_i64_shift(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
|
|
IntShift shift_op)
|
|
{
|
|
return compile_op_int_shift(comp_ctx, func_ctx, shift_op, false);
|
|
}
|
|
|
|
bool
|
|
aot_compile_op_f32_math(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
|
|
FloatMath math_op)
|
|
{
|
|
return compile_op_float_math(comp_ctx, func_ctx, math_op, true);
|
|
}
|
|
|
|
bool
|
|
aot_compile_op_f64_math(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
|
|
FloatMath math_op)
|
|
{
|
|
return compile_op_float_math(comp_ctx, func_ctx, math_op, false);
|
|
}
|
|
|
|
bool
|
|
aot_compile_op_f32_arithmetic(AOTCompContext *comp_ctx,
|
|
AOTFuncContext *func_ctx,
|
|
FloatArithmetic arith_op)
|
|
{
|
|
return compile_op_float_arithmetic(comp_ctx, func_ctx, arith_op, true);
|
|
}
|
|
|
|
bool
|
|
aot_compile_op_f64_arithmetic(AOTCompContext *comp_ctx,
|
|
AOTFuncContext *func_ctx,
|
|
FloatArithmetic arith_op)
|
|
{
|
|
return compile_op_float_arithmetic(comp_ctx, func_ctx, arith_op, false);
|
|
}
|
|
|
|
bool
|
|
aot_compile_op_f32_copysign(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx)
|
|
{
|
|
return compile_float_copysign(comp_ctx, func_ctx, true);
|
|
}
|
|
|
|
bool
|
|
aot_compile_op_f64_copysign(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx)
|
|
{
|
|
return compile_float_copysign(comp_ctx, func_ctx, false);
|
|
}
|