mirror of
https://github.com/rizinorg/rizin
synced 2026-08-22 20:26:16 -04:00
Improve RzIL floating-point support (#6626)
The `RzFloat` changes fix or improve: - binary80 explicit-integer-bit, pseudo-value, infinity, and NaN handling; - binary16 conversions; - gradual underflow and directed rounding; - overflow, underflow, invalid-operation, and inexact exception reporting; - exception propagation through nested conversions and arithmetic operations; - binary80 fused multiply-add rounding, including reduced-precision and double-rounding edge cases; - thread-local SoftFloat state, preventing rounding state from leaking between threads. The `RzIL` changes add scoped binary80 precision support through `RzFloatRPrecision` and `FWITH_RPREC`. The supported precisions are 32, 64, and 80. Precision scopes restore the previous thread-local SoftFloat state after successful evaluation and evaluation failures. Runtime rounding modes are represented explicitly by dedicated pure opcodes: - `FCONVERT_WITH_RMODE` - `FROUND_WITH_RMODE` - `FSQRT_WITH_RMODE` - `FADD_WITH_RMODE` - `FSUB_WITH_RMODE` - `FMUL_WITH_RMODE` - `FDIV_WITH_RMODE` - `FMOD_WITH_RMODE` Their rounding-mode operand is a 32-bit IL bitvector whose values correspond to `RzFloatRMode`: RNE, RNA, RTP, RTN, and RTZ. Invalid operand widths are rejected by validation, while invalid runtime values cause evaluation to fail with an error. Dedicated opcodes keep runtime-controlled floating-point expressions compact. This is useful for architectures whose rounding mode is selected from register state and avoids the expression duplication caused by expanding every operation into nested `ITE` branches. The new operations are supported by: - construction, duplication, and destruction; - type and operand validation; - VM evaluation; - plain, Unicode, and JSON exporters; - graph output and opcode stringification. `FEXCEPT` now emits a VM event only when the queried exception is present, while preserving exceptions raised by nested conversions and arithmetic operations.
This commit is contained in:
parent
e33674578c
commit
19b1783c88
27 changed files with 2714 additions and 492 deletions
|
|
@ -544,7 +544,7 @@ static RzAnalysisLiftedILOp madd_f(RzAsmTriCoreContext *ctx) {
|
|||
set_PSW_FX(BOOL_TO_BV32(AND(VARG("set_FX"), INV(VARG("set_FI"))))),
|
||||
set_FS_or_FI_FV_FU_FX);
|
||||
|
||||
return SEQN(12,
|
||||
return SEQN(13,
|
||||
SETL("_fa", _32F64(VARG(R(1)))),
|
||||
SETL("_fb", _32F64(VARG(R(2)))),
|
||||
SETL("_fc", _32F64(VARG(R(3)))),
|
||||
|
|
@ -577,7 +577,7 @@ static RzAnalysisLiftedILOp msub_f(RzAsmTriCoreContext *ctx) {
|
|||
set_PSW_FX(BOOL_TO_BV32(AND(VARG("set_FX"), INV(VARG("set_FI"))))),
|
||||
set_FS_or_FI_FV_FU_FX);
|
||||
|
||||
return SEQN(12,
|
||||
return SEQN(13,
|
||||
SETL("_fa", _32F64(VARG(R(1)))),
|
||||
SETL("_fb", _32F64(VARG(R(2)))),
|
||||
SETL("_fc", _32F64(VARG(R(3)))),
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
* Some of them should be moved to rz_util/float in the future and resolve conflict to merge
|
||||
*/
|
||||
#include <rz_il/definitions/float.h>
|
||||
#include <rz_il/rz_il_opcodes.h>
|
||||
|
||||
/**
|
||||
* create a float by specifying `format` and `bitv`
|
||||
|
|
|
|||
|
|
@ -48,6 +48,21 @@
|
|||
static void il_op_pure_json_resolve(RzILOpPure *op, PJ *pj);
|
||||
static void il_op_effect_json_resolve(RzILOpEffect *op, PJ *pj);
|
||||
|
||||
static void il_op_float_rmode_json_resolve(const RzILOpArgFloatRMode *rmode, PJ *pj) {
|
||||
switch (rmode->kind) {
|
||||
case RZ_IL_OP_ARG_FLOAT_RMODE_STATIC:
|
||||
pj_s(pj, rz_il_float_stringify_rmode(rmode->value.static_mode));
|
||||
break;
|
||||
case RZ_IL_OP_ARG_FLOAT_RMODE_DYNAMIC:
|
||||
il_op_pure_json_resolve(rmode->value.dynamic_mode, pj);
|
||||
break;
|
||||
default:
|
||||
rz_warn_if_reached();
|
||||
pj_s(pj, "invalid_rmode");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#define il_op_param_0(name) \
|
||||
do { \
|
||||
pj_o(pj); \
|
||||
|
|
@ -88,42 +103,42 @@ static void il_op_effect_json_resolve(RzILOpEffect *op, PJ *pj);
|
|||
pj_end(pj); \
|
||||
} while (0)
|
||||
|
||||
#define il_op_param_1_with_rmode(name, opx, v0, vr) \
|
||||
#define il_op_param_1_with_rmode_arg(name, opx, v0, vr) \
|
||||
do { \
|
||||
const char *rmode_str = rz_il_float_stringify_rmode(opx.vr); \
|
||||
pj_o(pj); \
|
||||
pj_ks(pj, "opcode", name); \
|
||||
pj_ks(pj, "rmode", rmode_str); \
|
||||
pj_k(pj, "rmode"); \
|
||||
il_op_float_rmode_json_resolve(&(opx).vr, pj); \
|
||||
pj_k(pj, #v0); \
|
||||
il_op_pure_json_resolve(opx.v0, pj); \
|
||||
il_op_pure_json_resolve((opx).v0, pj); \
|
||||
pj_end(pj); \
|
||||
} while (0)
|
||||
|
||||
#define il_op_param_2_with_rmode(name, opx, sort0, v0, sort1, v1, vr) \
|
||||
#define il_op_param_2_with_rmode_arg(name, opx, sort0, v0, sort1, v1, vr) \
|
||||
do { \
|
||||
const char *rmode_str = rz_il_float_stringify_rmode(opx.vr); \
|
||||
pj_o(pj); \
|
||||
pj_ks(pj, "opcode", name); \
|
||||
pj_ks(pj, "rmode", rmode_str); \
|
||||
pj_k(pj, "rmode"); \
|
||||
il_op_float_rmode_json_resolve(&(opx).vr, pj); \
|
||||
pj_k(pj, #v0); \
|
||||
il_op_##sort0##_json_resolve(opx.v0, pj); \
|
||||
il_op_##sort0##_json_resolve((opx).v0, pj); \
|
||||
pj_k(pj, #v1); \
|
||||
il_op_##sort1##_json_resolve(opx.v1, pj); \
|
||||
il_op_##sort1##_json_resolve((opx).v1, pj); \
|
||||
pj_end(pj); \
|
||||
} while (0)
|
||||
|
||||
#define il_op_param_3_with_rmode(name, opx, sort0, v0, sort1, v1, sort2, v2, vr) \
|
||||
#define il_op_param_3_with_rmode_arg(name, opx, sort0, v0, sort1, v1, sort2, v2, vr) \
|
||||
do { \
|
||||
const char *rmode_str = rz_il_float_stringify_rmode(opx.vr); \
|
||||
pj_o(pj); \
|
||||
pj_ks(pj, "opcode", name); \
|
||||
pj_ks(pj, "rmode", rmode_str); \
|
||||
pj_k(pj, "rmode"); \
|
||||
il_op_float_rmode_json_resolve(&(opx).vr, pj); \
|
||||
pj_k(pj, #v0); \
|
||||
il_op_##sort0##_json_resolve(opx.v0, pj); \
|
||||
il_op_##sort0##_json_resolve((opx).v0, pj); \
|
||||
pj_k(pj, #v1); \
|
||||
il_op_##sort1##_json_resolve(opx.v1, pj); \
|
||||
il_op_##sort1##_json_resolve((opx).v1, pj); \
|
||||
pj_k(pj, #v2); \
|
||||
il_op_##sort2##_json_resolve(opx.v2, pj); \
|
||||
il_op_##sort2##_json_resolve((opx).v2, pj); \
|
||||
pj_end(pj); \
|
||||
} while (0)
|
||||
|
||||
|
|
@ -339,7 +354,8 @@ static void il_opdmp_fcast_int(RzILOpPure *op, PJ *pj) {
|
|||
pj_o(pj);
|
||||
pj_ks(pj, "opcode", "fcast_int");
|
||||
pj_kn(pj, "length", opx->length);
|
||||
pj_ks(pj, "rmode", rz_il_float_stringify_rmode(opx->mode));
|
||||
pj_k(pj, "rmode");
|
||||
il_op_float_rmode_json_resolve(&opx->rmode, pj);
|
||||
pj_k(pj, "value");
|
||||
il_op_pure_json_resolve(opx->f, pj);
|
||||
pj_end(pj);
|
||||
|
|
@ -350,7 +366,8 @@ static void il_opdmp_fcast_sint(RzILOpPure *op, PJ *pj) {
|
|||
pj_o(pj);
|
||||
pj_ks(pj, "opcode", "fcast_sint");
|
||||
pj_kn(pj, "length", opx->length);
|
||||
pj_ks(pj, "rmode", rz_il_float_stringify_rmode(opx->mode));
|
||||
pj_k(pj, "rmode");
|
||||
il_op_float_rmode_json_resolve(&opx->rmode, pj);
|
||||
pj_k(pj, "value");
|
||||
il_op_pure_json_resolve(opx->f, pj);
|
||||
pj_end(pj);
|
||||
|
|
@ -361,7 +378,8 @@ static void il_opdmp_fcast_float(RzILOpPure *op, PJ *pj) {
|
|||
pj_o(pj);
|
||||
pj_ks(pj, "opcode", "fcast_float");
|
||||
pj_ks(pj, "format", rz_il_float_stringify_format(opx->format));
|
||||
pj_ks(pj, "rmode", rz_il_float_stringify_rmode(opx->mode));
|
||||
pj_k(pj, "rmode");
|
||||
il_op_float_rmode_json_resolve(&opx->rmode, pj);
|
||||
pj_k(pj, "value");
|
||||
il_op_pure_json_resolve(opx->bv, pj);
|
||||
pj_end(pj);
|
||||
|
|
@ -372,7 +390,8 @@ static void il_opdmp_fcast_sfloat(RzILOpPure *op, PJ *pj) {
|
|||
pj_o(pj);
|
||||
pj_ks(pj, "opcode", "fcast_sfloat");
|
||||
pj_ks(pj, "format", rz_il_float_stringify_format(opx->format));
|
||||
pj_ks(pj, "rmode", rz_il_float_stringify_rmode(opx->mode));
|
||||
pj_k(pj, "rmode");
|
||||
il_op_float_rmode_json_resolve(&opx->rmode, pj);
|
||||
pj_k(pj, "value");
|
||||
il_op_pure_json_resolve(opx->bv, pj);
|
||||
pj_end(pj);
|
||||
|
|
@ -383,7 +402,8 @@ static void il_opdmp_fconvert(RzILOpPure *op, PJ *pj) {
|
|||
pj_o(pj);
|
||||
pj_ks(pj, "opcode", "fconvert");
|
||||
pj_ks(pj, "format", rz_il_float_stringify_format(opx->format));
|
||||
pj_ks(pj, "rmode", rz_il_float_stringify_rmode(opx->mode));
|
||||
pj_k(pj, "rmode");
|
||||
il_op_float_rmode_json_resolve(&opx->rmode, pj);
|
||||
pj_k(pj, "value");
|
||||
il_op_pure_json_resolve(opx->f, pj);
|
||||
pj_end(pj);
|
||||
|
|
@ -393,7 +413,8 @@ static void il_opdmp_fround(RzILOpPure *op, PJ *pj) {
|
|||
RzILOpArgsFround *opx = &op->op.fround;
|
||||
pj_o(pj);
|
||||
pj_ks(pj, "opcode", "fround");
|
||||
pj_ks(pj, "rmode", rz_il_float_stringify_rmode(opx->rmode));
|
||||
pj_k(pj, "rmode");
|
||||
il_op_float_rmode_json_resolve(&opx->rmode, pj);
|
||||
pj_k(pj, "value");
|
||||
il_op_pure_json_resolve(opx->f, pj);
|
||||
pj_end(pj);
|
||||
|
|
@ -420,55 +441,55 @@ static void il_opdmp_forder(RzILOpPure *op, PJ *pj) {
|
|||
}
|
||||
|
||||
static void il_opdmp_fsqrt(RzILOpPure *op, PJ *pj) {
|
||||
il_op_param_1_with_rmode("fsqrt", op->op.fsqrt, f, rmode);
|
||||
il_op_param_1_with_rmode_arg("fsqrt", op->op.fsqrt, f, rmode);
|
||||
}
|
||||
|
||||
static void il_opdmp_frsqrt(RzILOpPure *op, PJ *pj) {
|
||||
il_op_param_1_with_rmode("frsqrt", op->op.frsqrt, f, rmode);
|
||||
il_op_param_1_with_rmode_arg("frsqrt", op->op.frsqrt, f, rmode);
|
||||
}
|
||||
|
||||
static void il_opdmp_fadd(RzILOpPure *op, PJ *pj) {
|
||||
il_op_param_2_with_rmode("+.", op->op.fadd, pure, x, pure, y, rmode);
|
||||
il_op_param_2_with_rmode_arg("+.", op->op.fadd, pure, x, pure, y, rmode);
|
||||
}
|
||||
|
||||
static void il_opdmp_fsub(RzILOpPure *op, PJ *pj) {
|
||||
il_op_param_2_with_rmode("-.", op->op.fsub, pure, x, pure, y, rmode);
|
||||
il_op_param_2_with_rmode_arg("-.", op->op.fsub, pure, x, pure, y, rmode);
|
||||
}
|
||||
|
||||
static void il_opdmp_fmul(RzILOpPure *op, PJ *pj) {
|
||||
il_op_param_2_with_rmode("*.", op->op.fmul, pure, x, pure, y, rmode);
|
||||
il_op_param_2_with_rmode_arg("*.", op->op.fmul, pure, x, pure, y, rmode);
|
||||
}
|
||||
|
||||
static void il_opdmp_fdiv(RzILOpPure *op, PJ *pj) {
|
||||
il_op_param_2_with_rmode("/.", op->op.fdiv, pure, x, pure, y, rmode);
|
||||
il_op_param_2_with_rmode_arg("/.", op->op.fdiv, pure, x, pure, y, rmode);
|
||||
}
|
||||
|
||||
static void il_opdmp_fmod(RzILOpPure *op, PJ *pj) {
|
||||
il_op_param_2_with_rmode("%.", op->op.fmod, pure, x, pure, y, rmode);
|
||||
il_op_param_2_with_rmode_arg("%.", op->op.fmod, pure, x, pure, y, rmode);
|
||||
}
|
||||
|
||||
static void il_opdmp_fhypot(RzILOpPure *op, PJ *pj) {
|
||||
il_op_param_2_with_rmode("hypot", op->op.fhypot, pure, x, pure, y, rmode);
|
||||
il_op_param_2_with_rmode_arg("hypot", op->op.fhypot, pure, x, pure, y, rmode);
|
||||
}
|
||||
|
||||
static void il_opdmp_fpow(RzILOpPure *op, PJ *pj) {
|
||||
il_op_param_2_with_rmode("pow", op->op.fpow, pure, x, pure, y, rmode);
|
||||
il_op_param_2_with_rmode_arg("pow", op->op.fpow, pure, x, pure, y, rmode);
|
||||
}
|
||||
|
||||
static void il_opdmp_fmad(RzILOpPure *op, PJ *pj) {
|
||||
il_op_param_3_with_rmode("fmad", op->op.fmad, pure, x, pure, y, pure, z, rmode);
|
||||
il_op_param_3_with_rmode_arg("fmad", op->op.fmad, pure, x, pure, y, pure, z, rmode);
|
||||
}
|
||||
|
||||
static void il_opdmp_fpown(RzILOpPure *op, PJ *pj) {
|
||||
il_op_param_2_with_rmode("fpown", op->op.fpown, pure, f, pure, n, rmode);
|
||||
il_op_param_2_with_rmode_arg("fpown", op->op.fpown, pure, f, pure, n, rmode);
|
||||
}
|
||||
|
||||
static void il_opdmp_frootn(RzILOpPure *op, PJ *pj) {
|
||||
il_op_param_2_with_rmode("frootn", op->op.frootn, pure, f, pure, n, rmode);
|
||||
il_op_param_2_with_rmode_arg("frootn", op->op.frootn, pure, f, pure, n, rmode);
|
||||
}
|
||||
|
||||
static void il_opdmp_fcompound(RzILOpPure *op, PJ *pj) {
|
||||
il_op_param_2_with_rmode("fcompound", op->op.fcompound, pure, f, pure, n, rmode);
|
||||
il_op_param_2_with_rmode_arg("fcompound", op->op.fcompound, pure, f, pure, n, rmode);
|
||||
}
|
||||
|
||||
static void il_opdmp_load(RzILOpPure *op, PJ *pj) {
|
||||
|
|
|
|||
|
|
@ -30,6 +30,21 @@
|
|||
static void il_op_pure_string_resolve(RzILOpPure *op, RzStrBuf *sb, int pad);
|
||||
static void il_op_effect_string_resolve(RzILOpEffect *op, RzStrBuf *sb, int pad);
|
||||
|
||||
static void il_op_float_rmode_string_resolve(const RzILOpArgFloatRMode *rmode, RzStrBuf *sb, int pad) {
|
||||
switch (rmode->kind) {
|
||||
case RZ_IL_OP_ARG_FLOAT_RMODE_STATIC:
|
||||
rz_strbuf_append(sb, rz_il_float_stringify_rmode(rmode->value.static_mode));
|
||||
break;
|
||||
case RZ_IL_OP_ARG_FLOAT_RMODE_DYNAMIC:
|
||||
il_op_pure_string_resolve(rmode->value.dynamic_mode, sb, pad);
|
||||
break;
|
||||
default:
|
||||
rz_warn_if_reached();
|
||||
rz_strbuf_append(sb, "invalid_rmode");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#define il_op_param_0(name) \
|
||||
do { \
|
||||
if (pad < 0) { \
|
||||
|
|
@ -90,64 +105,91 @@ static void il_op_effect_string_resolve(RzILOpEffect *op, RzStrBuf *sb, int pad)
|
|||
} \
|
||||
} while (0)
|
||||
|
||||
#define il_op_param_1_with_rmode(name, opx, v0, vr) \
|
||||
#define il_op_param_1_with_rmode_arg(name, opx, v0, vr) \
|
||||
do { \
|
||||
const char *rmode_str = rz_il_float_stringify_rmode(opx.vr); \
|
||||
if (pad < 0) { \
|
||||
rz_strbuf_append(sb, "(" name " "); \
|
||||
rz_strbuf_append(sb, rmode_str); \
|
||||
il_op_float_rmode_string_resolve(&(opx).vr, sb, pad); \
|
||||
rz_strbuf_append(sb, " "); \
|
||||
il_op_pure_string_resolve(opx.v0, sb, pad); \
|
||||
il_op_pure_string_resolve((opx).v0, sb, pad); \
|
||||
rz_strbuf_append(sb, ")"); \
|
||||
} else if ((opx).vr.kind == RZ_IL_OP_ARG_FLOAT_RMODE_STATIC) { \
|
||||
rz_strbuf_appendf(sb, "%*.s(%s ", pad, "", name); \
|
||||
il_op_float_rmode_string_resolve(&(opx).vr, sb, pad); \
|
||||
rz_strbuf_append(sb, "\n"); \
|
||||
il_op_pure_string_resolve((opx).v0, sb, pad + PRETTY_PAD); \
|
||||
rz_strbuf_append(sb, ")"); \
|
||||
} else { \
|
||||
rz_strbuf_appendf(sb, "%*.s(" name " %s\n", pad, "", rmode_str); \
|
||||
il_op_pure_string_resolve(opx.v0, sb, pad + PRETTY_PAD); \
|
||||
rz_strbuf_appendf(sb, "%*.s(%s\n", pad, "", name); \
|
||||
il_op_float_rmode_string_resolve(&(opx).vr, sb, pad + PRETTY_PAD); \
|
||||
rz_strbuf_append(sb, "\n"); \
|
||||
il_op_pure_string_resolve((opx).v0, sb, pad + PRETTY_PAD); \
|
||||
rz_strbuf_append(sb, ")"); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define il_op_param_2_with_rmode(name, opx, sort0, v0, sort1, v1, vr) \
|
||||
#define il_op_param_2_with_rmode_arg(name, opx, sort0, v0, sort1, v1, vr) \
|
||||
do { \
|
||||
const char *rmode_str = rz_il_float_stringify_rmode(opx.vr); \
|
||||
if (pad < 0) { \
|
||||
rz_strbuf_append(sb, "(" name " "); \
|
||||
rz_strbuf_append(sb, rmode_str); \
|
||||
il_op_float_rmode_string_resolve(&(opx).vr, sb, pad); \
|
||||
rz_strbuf_append(sb, " "); \
|
||||
il_op_##sort0##_string_resolve(opx.v0, sb, pad); \
|
||||
il_op_##sort0##_string_resolve((opx).v0, sb, pad); \
|
||||
rz_strbuf_append(sb, " "); \
|
||||
il_op_##sort1##_string_resolve(opx.v1, sb, pad); \
|
||||
il_op_##sort1##_string_resolve((opx).v1, sb, pad); \
|
||||
rz_strbuf_append(sb, ")"); \
|
||||
} else if ((opx).vr.kind == RZ_IL_OP_ARG_FLOAT_RMODE_STATIC) { \
|
||||
rz_strbuf_appendf(sb, "%*.s(%s ", pad, "", name); \
|
||||
il_op_float_rmode_string_resolve(&(opx).vr, sb, pad); \
|
||||
rz_strbuf_append(sb, "\n"); \
|
||||
rz_strbuf_append(sb, "\n"); \
|
||||
il_op_##sort0##_string_resolve((opx).v0, sb, pad + PRETTY_PAD); \
|
||||
rz_strbuf_append(sb, "\n"); \
|
||||
il_op_##sort1##_string_resolve((opx).v1, sb, pad + PRETTY_PAD); \
|
||||
rz_strbuf_append(sb, ")"); \
|
||||
} else { \
|
||||
rz_strbuf_appendf(sb, "%*.s(" name " %s\n", pad, "", rmode_str); \
|
||||
rz_strbuf_appendf(sb, "%*.s(%s\n", pad, "", name); \
|
||||
il_op_float_rmode_string_resolve(&(opx).vr, sb, pad + PRETTY_PAD); \
|
||||
rz_strbuf_append(sb, "\n"); \
|
||||
il_op_##sort0##_string_resolve(opx.v0, sb, pad + PRETTY_PAD); \
|
||||
il_op_##sort0##_string_resolve((opx).v0, sb, pad + PRETTY_PAD); \
|
||||
rz_strbuf_append(sb, "\n"); \
|
||||
il_op_##sort1##_string_resolve(opx.v1, sb, pad + PRETTY_PAD); \
|
||||
il_op_##sort1##_string_resolve((opx).v1, sb, pad + PRETTY_PAD); \
|
||||
rz_strbuf_append(sb, ")"); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define il_op_param_3_with_rmode(name, opx, sort0, v0, sort1, v1, sort2, v2, vr) \
|
||||
#define il_op_param_3_with_rmode_arg(name, opx, sort0, v0, sort1, v1, sort2, v2, vr) \
|
||||
do { \
|
||||
const char *rmode_str = rz_il_float_stringify_rmode(opx.vr); \
|
||||
if (pad < 0) { \
|
||||
rz_strbuf_append(sb, "(" name " "); \
|
||||
rz_strbuf_append(sb, rmode_str); \
|
||||
il_op_float_rmode_string_resolve(&(opx).vr, sb, pad); \
|
||||
rz_strbuf_append(sb, " "); \
|
||||
il_op_##sort0##_string_resolve(opx.v0, sb, pad); \
|
||||
il_op_##sort0##_string_resolve((opx).v0, sb, pad); \
|
||||
rz_strbuf_append(sb, " "); \
|
||||
il_op_##sort1##_string_resolve(opx.v1, sb, pad); \
|
||||
il_op_##sort1##_string_resolve((opx).v1, sb, pad); \
|
||||
rz_strbuf_append(sb, " "); \
|
||||
il_op_##sort2##_string_resolve(opx.v2, sb, pad); \
|
||||
il_op_##sort2##_string_resolve((opx).v2, sb, pad); \
|
||||
rz_strbuf_append(sb, ")"); \
|
||||
} else if ((opx).vr.kind == RZ_IL_OP_ARG_FLOAT_RMODE_STATIC) { \
|
||||
rz_strbuf_appendf(sb, "%*.s(%s ", pad, "", name); \
|
||||
il_op_float_rmode_string_resolve(&(opx).vr, sb, pad); \
|
||||
rz_strbuf_append(sb, "\n"); \
|
||||
rz_strbuf_append(sb, "\n"); \
|
||||
il_op_##sort0##_string_resolve((opx).v0, sb, pad + PRETTY_PAD); \
|
||||
rz_strbuf_append(sb, "\n"); \
|
||||
il_op_##sort1##_string_resolve((opx).v1, sb, pad + PRETTY_PAD); \
|
||||
rz_strbuf_append(sb, "\n"); \
|
||||
il_op_##sort2##_string_resolve((opx).v2, sb, pad + PRETTY_PAD); \
|
||||
rz_strbuf_append(sb, ")"); \
|
||||
} else { \
|
||||
rz_strbuf_appendf(sb, "%*.s(" name " %s\n", pad, "", rmode_str); \
|
||||
rz_strbuf_appendf(sb, "%*.s(%s\n", pad, "", name); \
|
||||
il_op_float_rmode_string_resolve(&(opx).vr, sb, pad + PRETTY_PAD); \
|
||||
rz_strbuf_append(sb, "\n"); \
|
||||
il_op_##sort0##_string_resolve(opx.v0, sb, pad + PRETTY_PAD); \
|
||||
il_op_##sort0##_string_resolve((opx).v0, sb, pad + PRETTY_PAD); \
|
||||
rz_strbuf_append(sb, "\n"); \
|
||||
il_op_##sort1##_string_resolve(opx.v1, sb, pad + PRETTY_PAD); \
|
||||
il_op_##sort1##_string_resolve((opx).v1, sb, pad + PRETTY_PAD); \
|
||||
rz_strbuf_append(sb, "\n"); \
|
||||
il_op_##sort2##_string_resolve(opx.v2, sb, pad + PRETTY_PAD); \
|
||||
il_op_##sort2##_string_resolve((opx).v2, sb, pad + PRETTY_PAD); \
|
||||
rz_strbuf_append(sb, ")"); \
|
||||
} \
|
||||
} while (0)
|
||||
|
|
@ -395,13 +437,19 @@ static void il_opdmp_fcast_int(RzILOpPure *op, RzStrBuf *sb, int pad) {
|
|||
RzILOpArgsFCastint *opx = &op->op.fcast_int;
|
||||
if (pad < 0) {
|
||||
rz_strbuf_appendf(sb, "(fcast_int %u ", opx->length);
|
||||
rz_strbuf_append(sb, rz_il_float_stringify_rmode(opx->mode));
|
||||
il_op_float_rmode_string_resolve(&opx->rmode, sb, pad);
|
||||
rz_strbuf_append(sb, " ");
|
||||
il_op_pure_string_resolve(opx->f, sb, pad);
|
||||
rz_strbuf_append(sb, ")");
|
||||
} else {
|
||||
} else if (opx->rmode.kind == RZ_IL_OP_ARG_FLOAT_RMODE_STATIC) {
|
||||
rz_strbuf_appendf(sb, "%*.s(fcast_int %u ", pad, "", opx->length);
|
||||
rz_strbuf_append(sb, rz_il_float_stringify_rmode(opx->mode));
|
||||
il_op_float_rmode_string_resolve(&opx->rmode, sb, pad);
|
||||
rz_strbuf_append(sb, "\n");
|
||||
il_op_pure_string_resolve(opx->f, sb, pad + PRETTY_PAD);
|
||||
rz_strbuf_append(sb, ")");
|
||||
} else {
|
||||
rz_strbuf_appendf(sb, "%*.s(fcast_int %u\n", pad, "", opx->length);
|
||||
il_op_float_rmode_string_resolve(&opx->rmode, sb, pad + PRETTY_PAD);
|
||||
rz_strbuf_append(sb, "\n");
|
||||
il_op_pure_string_resolve(opx->f, sb, pad + PRETTY_PAD);
|
||||
rz_strbuf_append(sb, ")");
|
||||
|
|
@ -412,13 +460,19 @@ static void il_opdmp_fcast_sint(RzILOpPure *op, RzStrBuf *sb, int pad) {
|
|||
RzILOpArgsFCastsint *opx = &op->op.fcast_sint;
|
||||
if (pad < 0) {
|
||||
rz_strbuf_appendf(sb, "(fcast_sint %u ", opx->length);
|
||||
rz_strbuf_append(sb, rz_il_float_stringify_rmode(opx->mode));
|
||||
il_op_float_rmode_string_resolve(&opx->rmode, sb, pad);
|
||||
rz_strbuf_append(sb, " ");
|
||||
il_op_pure_string_resolve(opx->f, sb, pad);
|
||||
rz_strbuf_append(sb, ")");
|
||||
} else {
|
||||
} else if (opx->rmode.kind == RZ_IL_OP_ARG_FLOAT_RMODE_STATIC) {
|
||||
rz_strbuf_appendf(sb, "%*.s(fcast_sint %u ", pad, "", opx->length);
|
||||
rz_strbuf_append(sb, rz_il_float_stringify_rmode(opx->mode));
|
||||
il_op_float_rmode_string_resolve(&opx->rmode, sb, pad);
|
||||
rz_strbuf_append(sb, "\n");
|
||||
il_op_pure_string_resolve(opx->f, sb, pad + PRETTY_PAD);
|
||||
rz_strbuf_append(sb, ")");
|
||||
} else {
|
||||
rz_strbuf_appendf(sb, "%*.s(fcast_sint %u\n", pad, "", opx->length);
|
||||
il_op_float_rmode_string_resolve(&opx->rmode, sb, pad + PRETTY_PAD);
|
||||
rz_strbuf_append(sb, "\n");
|
||||
il_op_pure_string_resolve(opx->f, sb, pad + PRETTY_PAD);
|
||||
rz_strbuf_append(sb, ")");
|
||||
|
|
@ -431,15 +485,21 @@ static void il_opdmp_fcast_float(RzILOpPure *op, RzStrBuf *sb, int pad) {
|
|||
rz_strbuf_append(sb, "(fcast_float ");
|
||||
rz_strbuf_append(sb, rz_il_float_stringify_format(opx->format));
|
||||
rz_strbuf_append(sb, " ");
|
||||
rz_strbuf_append(sb, rz_il_float_stringify_rmode(opx->mode));
|
||||
il_op_float_rmode_string_resolve(&opx->rmode, sb, pad);
|
||||
rz_strbuf_append(sb, " ");
|
||||
il_op_pure_string_resolve(opx->bv, sb, pad);
|
||||
rz_strbuf_append(sb, ")");
|
||||
} else {
|
||||
} else if (opx->rmode.kind == RZ_IL_OP_ARG_FLOAT_RMODE_STATIC) {
|
||||
rz_strbuf_appendf(sb, "%*.s(fcast_float ", pad, "");
|
||||
rz_strbuf_append(sb, rz_il_float_stringify_format(opx->format));
|
||||
rz_strbuf_append(sb, " ");
|
||||
rz_strbuf_append(sb, rz_il_float_stringify_rmode(opx->mode));
|
||||
il_op_float_rmode_string_resolve(&opx->rmode, sb, pad);
|
||||
rz_strbuf_append(sb, "\n");
|
||||
il_op_pure_string_resolve(opx->bv, sb, pad + PRETTY_PAD);
|
||||
rz_strbuf_append(sb, ")");
|
||||
} else {
|
||||
rz_strbuf_appendf(sb, "%*.s(fcast_float %s\n", pad, "", rz_il_float_stringify_format(opx->format));
|
||||
il_op_float_rmode_string_resolve(&opx->rmode, sb, pad + PRETTY_PAD);
|
||||
rz_strbuf_append(sb, "\n");
|
||||
il_op_pure_string_resolve(opx->bv, sb, pad + PRETTY_PAD);
|
||||
rz_strbuf_append(sb, ")");
|
||||
|
|
@ -452,15 +512,21 @@ static void il_opdmp_fcast_sfloat(RzILOpPure *op, RzStrBuf *sb, int pad) {
|
|||
rz_strbuf_append(sb, "(fcast_sfloat ");
|
||||
rz_strbuf_append(sb, rz_il_float_stringify_format(opx->format));
|
||||
rz_strbuf_append(sb, " ");
|
||||
rz_strbuf_append(sb, rz_il_float_stringify_rmode(opx->mode));
|
||||
il_op_float_rmode_string_resolve(&opx->rmode, sb, pad);
|
||||
rz_strbuf_append(sb, " ");
|
||||
il_op_pure_string_resolve(opx->bv, sb, pad);
|
||||
rz_strbuf_append(sb, ")");
|
||||
} else {
|
||||
} else if (opx->rmode.kind == RZ_IL_OP_ARG_FLOAT_RMODE_STATIC) {
|
||||
rz_strbuf_appendf(sb, "%*.s(fcast_sfloat ", pad, "");
|
||||
rz_strbuf_append(sb, rz_il_float_stringify_format(opx->format));
|
||||
rz_strbuf_append(sb, " ");
|
||||
rz_strbuf_append(sb, rz_il_float_stringify_rmode(opx->mode));
|
||||
il_op_float_rmode_string_resolve(&opx->rmode, sb, pad);
|
||||
rz_strbuf_append(sb, "\n");
|
||||
il_op_pure_string_resolve(opx->bv, sb, pad + PRETTY_PAD);
|
||||
rz_strbuf_append(sb, ")");
|
||||
} else {
|
||||
rz_strbuf_appendf(sb, "%*.s(fcast_sfloat %s\n", pad, "", rz_il_float_stringify_format(opx->format));
|
||||
il_op_float_rmode_string_resolve(&opx->rmode, sb, pad + PRETTY_PAD);
|
||||
rz_strbuf_append(sb, "\n");
|
||||
il_op_pure_string_resolve(opx->bv, sb, pad + PRETTY_PAD);
|
||||
rz_strbuf_append(sb, ")");
|
||||
|
|
@ -473,15 +539,21 @@ static void il_opdmp_fconvert(RzILOpPure *op, RzStrBuf *sb, int pad) {
|
|||
rz_strbuf_append(sb, "(fconvert ");
|
||||
rz_strbuf_append(sb, rz_il_float_stringify_format(opx->format));
|
||||
rz_strbuf_append(sb, " ");
|
||||
rz_strbuf_append(sb, rz_il_float_stringify_rmode(opx->mode));
|
||||
il_op_float_rmode_string_resolve(&opx->rmode, sb, pad);
|
||||
rz_strbuf_append(sb, " ");
|
||||
il_op_pure_string_resolve(opx->f, sb, pad);
|
||||
rz_strbuf_append(sb, ")");
|
||||
} else {
|
||||
} else if (opx->rmode.kind == RZ_IL_OP_ARG_FLOAT_RMODE_STATIC) {
|
||||
rz_strbuf_appendf(sb, "%*.s(fconvert ", pad, "");
|
||||
rz_strbuf_append(sb, rz_il_float_stringify_format(opx->format));
|
||||
rz_strbuf_append(sb, " ");
|
||||
rz_strbuf_append(sb, rz_il_float_stringify_rmode(opx->mode));
|
||||
il_op_float_rmode_string_resolve(&opx->rmode, sb, pad);
|
||||
rz_strbuf_append(sb, "\n");
|
||||
il_op_pure_string_resolve(opx->f, sb, pad + PRETTY_PAD);
|
||||
rz_strbuf_append(sb, ")");
|
||||
} else {
|
||||
rz_strbuf_appendf(sb, "%*.s(fconvert %s\n", pad, "", rz_il_float_stringify_format(opx->format));
|
||||
il_op_float_rmode_string_resolve(&opx->rmode, sb, pad + PRETTY_PAD);
|
||||
rz_strbuf_append(sb, "\n");
|
||||
il_op_pure_string_resolve(opx->f, sb, pad + PRETTY_PAD);
|
||||
rz_strbuf_append(sb, ")");
|
||||
|
|
@ -489,20 +561,7 @@ static void il_opdmp_fconvert(RzILOpPure *op, RzStrBuf *sb, int pad) {
|
|||
}
|
||||
|
||||
static void il_opdmp_fround(RzILOpPure *op, RzStrBuf *sb, int pad) {
|
||||
RzILOpArgsFround *opx = &op->op.fround;
|
||||
if (pad < 0) {
|
||||
rz_strbuf_append(sb, "(fround ");
|
||||
rz_strbuf_append(sb, rz_il_float_stringify_rmode(opx->rmode));
|
||||
rz_strbuf_append(sb, " ");
|
||||
il_op_pure_string_resolve(opx->f, sb, pad);
|
||||
rz_strbuf_append(sb, ")");
|
||||
} else {
|
||||
rz_strbuf_appendf(sb, "%*.s(fround ", pad, "");
|
||||
rz_strbuf_append(sb, rz_il_float_stringify_rmode(opx->rmode));
|
||||
rz_strbuf_append(sb, "\n");
|
||||
il_op_pure_string_resolve(opx->f, sb, pad + PRETTY_PAD);
|
||||
rz_strbuf_append(sb, ")");
|
||||
}
|
||||
il_op_param_1_with_rmode_arg("fround", op->op.fround, f, rmode);
|
||||
}
|
||||
|
||||
static void il_opdmp_frequal(RzILOpPure *op, RzStrBuf *sb, int pad) {
|
||||
|
|
@ -534,11 +593,11 @@ static void il_opdmp_forder(RzILOpPure *op, RzStrBuf *sb, int pad) {
|
|||
}
|
||||
|
||||
static void il_opdmp_fsqrt(RzILOpPure *op, RzStrBuf *sb, int pad) {
|
||||
il_op_param_1_with_rmode("fsqrt", op->op.fsqrt, f, rmode);
|
||||
il_op_param_1_with_rmode_arg("fsqrt", op->op.fsqrt, f, rmode);
|
||||
}
|
||||
|
||||
static void il_opdmp_frsqrt(RzILOpPure *op, RzStrBuf *sb, int pad) {
|
||||
il_op_param_1_with_rmode("frsqrt", op->op.frsqrt, f, rmode);
|
||||
il_op_param_1_with_rmode_arg("frsqrt", op->op.frsqrt, f, rmode);
|
||||
}
|
||||
|
||||
static void il_opdmp_fexcept(RzILOpPure *op, RzStrBuf *sb, int pad) {
|
||||
|
|
@ -546,47 +605,47 @@ static void il_opdmp_fexcept(RzILOpPure *op, RzStrBuf *sb, int pad) {
|
|||
}
|
||||
|
||||
static void il_opdmp_fadd(RzILOpPure *op, RzStrBuf *sb, int pad) {
|
||||
il_op_param_2_with_rmode("+.", op->op.fadd, pure, x, pure, y, rmode);
|
||||
il_op_param_2_with_rmode_arg("+.", op->op.fadd, pure, x, pure, y, rmode);
|
||||
}
|
||||
|
||||
static void il_opdmp_fsub(RzILOpPure *op, RzStrBuf *sb, int pad) {
|
||||
il_op_param_2_with_rmode("-.", op->op.fsub, pure, x, pure, y, rmode);
|
||||
il_op_param_2_with_rmode_arg("-.", op->op.fsub, pure, x, pure, y, rmode);
|
||||
}
|
||||
|
||||
static void il_opdmp_fmul(RzILOpPure *op, RzStrBuf *sb, int pad) {
|
||||
il_op_param_2_with_rmode("*.", op->op.fmul, pure, x, pure, y, rmode);
|
||||
il_op_param_2_with_rmode_arg("*.", op->op.fmul, pure, x, pure, y, rmode);
|
||||
}
|
||||
|
||||
static void il_opdmp_fdiv(RzILOpPure *op, RzStrBuf *sb, int pad) {
|
||||
il_op_param_2_with_rmode("/.", op->op.fdiv, pure, x, pure, y, rmode);
|
||||
il_op_param_2_with_rmode_arg("/.", op->op.fdiv, pure, x, pure, y, rmode);
|
||||
}
|
||||
|
||||
static void il_opdmp_fmod(RzILOpPure *op, RzStrBuf *sb, int pad) {
|
||||
il_op_param_2_with_rmode("%%.", op->op.fmod, pure, x, pure, y, rmode);
|
||||
il_op_param_2_with_rmode_arg("%.", op->op.fmod, pure, x, pure, y, rmode);
|
||||
}
|
||||
|
||||
static void il_opdmp_fhypot(RzILOpPure *op, RzStrBuf *sb, int pad) {
|
||||
il_op_param_2_with_rmode("hypot", op->op.fhypot, pure, x, pure, y, rmode);
|
||||
il_op_param_2_with_rmode_arg("hypot", op->op.fhypot, pure, x, pure, y, rmode);
|
||||
}
|
||||
|
||||
static void il_opdmp_fpow(RzILOpPure *op, RzStrBuf *sb, int pad) {
|
||||
il_op_param_2_with_rmode("pow", op->op.fpow, pure, x, pure, y, rmode);
|
||||
il_op_param_2_with_rmode_arg("pow", op->op.fpow, pure, x, pure, y, rmode);
|
||||
}
|
||||
|
||||
static void il_opdmp_fmad(RzILOpPure *op, RzStrBuf *sb, int pad) {
|
||||
il_op_param_3_with_rmode("fmad", op->op.fmad, pure, x, pure, y, pure, z, rmode);
|
||||
il_op_param_3_with_rmode_arg("fmad", op->op.fmad, pure, x, pure, y, pure, z, rmode);
|
||||
}
|
||||
|
||||
static void il_opdmp_fpown(RzILOpPure *op, RzStrBuf *sb, int pad) {
|
||||
il_op_param_2_with_rmode("fpown", op->op.fpown, pure, f, pure, n, rmode);
|
||||
il_op_param_2_with_rmode_arg("fpown", op->op.fpown, pure, f, pure, n, rmode);
|
||||
}
|
||||
|
||||
static void il_opdmp_frootn(RzILOpPure *op, RzStrBuf *sb, int pad) {
|
||||
il_op_param_2_with_rmode("frootn", op->op.frootn, pure, f, pure, n, rmode);
|
||||
il_op_param_2_with_rmode_arg("frootn", op->op.frootn, pure, f, pure, n, rmode);
|
||||
}
|
||||
|
||||
static void il_opdmp_fcompound(RzILOpPure *op, RzStrBuf *sb, int pad) {
|
||||
il_op_param_2_with_rmode("fcompound", op->op.fcompound, pure, f, pure, n, rmode);
|
||||
il_op_param_2_with_rmode_arg("fcompound", op->op.fcompound, pure, f, pure, n, rmode);
|
||||
}
|
||||
|
||||
static void il_opdmp_load(RzILOpPure *op, RzStrBuf *sb, int pad) {
|
||||
|
|
|
|||
|
|
@ -6,6 +6,18 @@
|
|||
static bool il_op_pure_string_resolve(RzILStringifyCtx *ctx, const RzILOpPure *op, RzStrBuf *sb);
|
||||
static bool il_op_effect_string_resolve(RzILStringifyCtx *ctx, const RzILOpEffect *op, RzStrBuf *sb);
|
||||
|
||||
static bool il_op_float_rmode_string_resolve(RzILStringifyCtx *ctx, const RzILOpArgFloatRMode *rmode, RzStrBuf *sb) {
|
||||
switch (rmode->kind) {
|
||||
case RZ_IL_OP_ARG_FLOAT_RMODE_STATIC:
|
||||
return rz_strbuf_append(sb, rz_il_float_stringify_rmode(rmode->value.static_mode));
|
||||
case RZ_IL_OP_ARG_FLOAT_RMODE_DYNAMIC:
|
||||
return il_op_pure_string_resolve(ctx, rmode->value.dynamic_mode, sb);
|
||||
default:
|
||||
rz_warn_if_reached();
|
||||
return rz_strbuf_append(sb, "invalid_rmode");
|
||||
}
|
||||
}
|
||||
|
||||
#define UCD_ITE "↠"
|
||||
#define UCD_LET "="
|
||||
#define UCD_BOOL_FALSE "⊥"
|
||||
|
|
@ -131,15 +143,6 @@ static bool il_op_effect_string_resolve(RzILStringifyCtx *ctx, const RzILOpEffec
|
|||
return rz_strbuf_append(sb, ")"); \
|
||||
} while (0)
|
||||
|
||||
#define il_op_param_1_with_rmode(sym, opx, v0, sort0, vr) \
|
||||
do { \
|
||||
const char *rmode_str = rz_il_float_stringify_rmode(opx.vr); \
|
||||
return_false_if_fail(rmode_str); \
|
||||
return_false_if_fail(rz_strbuf_appendf(sb, "(%s " sym " ", rmode_str)); \
|
||||
return_false_if_fail(il_op_##sort0##_string_resolve(ctx, opx.v0, sb)); \
|
||||
return rz_strbuf_append(sb, ")"); \
|
||||
} while (0)
|
||||
|
||||
#define il_op_param_1_with_fexcept(sym, opx, v0, sort0, ve) \
|
||||
do { \
|
||||
const char *str = rz_il_float_stringify_exception(opx.ve); \
|
||||
|
|
@ -149,34 +152,36 @@ static bool il_op_effect_string_resolve(RzILStringifyCtx *ctx, const RzILOpEffec
|
|||
return rz_strbuf_append(sb, ")"); \
|
||||
} while (0)
|
||||
|
||||
#define il_op_param_1_with_mode_length(sym, opx, v0, sort0, m, l) \
|
||||
#define il_op_param_1_with_rmode_arg_length(sym, opx, v0, sort0, vr, l) \
|
||||
do { \
|
||||
return_false_if_fail(rz_strbuf_appendf(sb, "(")); \
|
||||
return_false_if_fail(il_op_##sort0##_string_resolve(ctx, opx.v0, sb)); \
|
||||
return_false_if_fail(il_op_##sort0##_string_resolve(ctx, (opx).v0, sb)); \
|
||||
return_false_if_fail(rz_strbuf_append(sb, " " sym)); \
|
||||
return_false_if_fail(append_subscript(sb, opx.l)); \
|
||||
return_false_if_fail(append_subscript(sb, (opx).l)); \
|
||||
return_false_if_fail(rz_strbuf_append(sb, " ")); \
|
||||
return_false_if_fail(rz_strbuf_append(sb, rz_il_float_stringify_rmode(opx.m))); \
|
||||
return_false_if_fail(il_op_float_rmode_string_resolve(ctx, &(opx).vr, sb)); \
|
||||
return rz_strbuf_append(sb, ")"); \
|
||||
} while (0)
|
||||
|
||||
#define il_op_param_1_with_mode_format(sym, opx, v0, sort0, m, f) \
|
||||
#define il_op_param_1_with_rmode_arg(sym, opx, v0, sort0, vr) \
|
||||
do { \
|
||||
return_false_if_fail(rz_strbuf_appendf(sb, "(")); \
|
||||
return_false_if_fail(il_op_##sort0##_string_resolve(ctx, opx->v0, sb)); \
|
||||
return_false_if_fail(rz_strbuf_appendf(sb, " %s ", sym)); \
|
||||
return_false_if_fail(rz_strbuf_append(sb, rz_il_float_stringify_rmode(opx->m))); \
|
||||
return_false_if_fail(rz_strbuf_append(sb, "(")); \
|
||||
return_false_if_fail(il_op_float_rmode_string_resolve(ctx, &(opx).vr, sb)); \
|
||||
return_false_if_fail(rz_strbuf_append(sb, " " sym " ")); \
|
||||
return_false_if_fail(il_op_##sort0##_string_resolve(ctx, (opx).v0, sb)); \
|
||||
return rz_strbuf_append(sb, ")"); \
|
||||
} while (0)
|
||||
|
||||
#define il_op_param_2_with_rmode(sym, opx, v0, sort0, v1, sort1, vr) \
|
||||
#define il_op_param_2_with_rmode_arg(sym, opx, v0, sort0, v1, sort1, vr) \
|
||||
do { \
|
||||
return_false_if_fail(rz_strbuf_appendf(sb, "(%s ", rz_il_float_stringify_rmode(opx.vr))); \
|
||||
return_false_if_fail(rz_strbuf_append(sb, "(")); \
|
||||
return_false_if_fail(il_op_float_rmode_string_resolve(ctx, &(opx).vr, sb)); \
|
||||
return_false_if_fail(rz_strbuf_append(sb, " ")); \
|
||||
return_false_if_fail(il_op_##sort0##_string_resolve(ctx, opx.v0, sb)); \
|
||||
return_false_if_fail(rz_strbuf_append(sb, " " sym " ")); \
|
||||
return_false_if_fail(il_op_##sort1##_string_resolve(ctx, opx.v1, sb)); \
|
||||
return rz_strbuf_append(sb, ")"); \
|
||||
} while (0);
|
||||
} while (0)
|
||||
|
||||
// Combine a prefix with the float format's Unicode width subscript,
|
||||
// via the shared RzUtil renderer (rz_float_format_subscript) so the
|
||||
|
|
@ -419,11 +424,11 @@ static bool il_opdmp_fabs(RzILStringifyCtx *ctx, const RzILOpPure *op, RzStrBuf
|
|||
}
|
||||
|
||||
static bool il_opdmp_fcast_int(RzILStringifyCtx *ctx, const RzILOpPure *op, RzStrBuf *sb) {
|
||||
il_op_param_1_with_mode_length(UCD_FCAST_INT, op->op.fcast_int, f, pure, mode, length);
|
||||
il_op_param_1_with_rmode_arg_length(UCD_FCAST_INT, op->op.fcast_int, f, pure, rmode, length);
|
||||
}
|
||||
|
||||
static bool il_opdmp_fcast_sint(RzILStringifyCtx *ctx, const RzILOpPure *op, RzStrBuf *sb) {
|
||||
il_op_param_1_with_mode_length(UCD_FCAST_SINT, op->op.fcast_sint, f, pure, mode, length);
|
||||
il_op_param_1_with_rmode_arg_length(UCD_FCAST_SINT, op->op.fcast_sint, f, pure, rmode, length);
|
||||
}
|
||||
|
||||
static bool il_opdmp_fcast_float(RzILStringifyCtx *ctx, const RzILOpPure *op, RzStrBuf *sb) {
|
||||
|
|
@ -433,7 +438,7 @@ static bool il_opdmp_fcast_float(RzILStringifyCtx *ctx, const RzILOpPure *op, Rz
|
|||
bool ok = rz_strbuf_append(sb, "(") &&
|
||||
il_op_pure_string_resolve(ctx, opx->bv, sb) &&
|
||||
rz_strbuf_appendf(sb, " %s ", sym) &&
|
||||
rz_strbuf_append(sb, rz_il_float_stringify_rmode(opx->mode)) &&
|
||||
il_op_float_rmode_string_resolve(ctx, &opx->rmode, sb) &&
|
||||
rz_strbuf_append(sb, ")");
|
||||
free(sym);
|
||||
return ok;
|
||||
|
|
@ -446,7 +451,7 @@ static bool il_opdmp_fcast_sfloat(RzILStringifyCtx *ctx, const RzILOpPure *op, R
|
|||
bool ok = rz_strbuf_append(sb, "(") &&
|
||||
il_op_pure_string_resolve(ctx, opx->bv, sb) &&
|
||||
rz_strbuf_appendf(sb, " %s ", sym) &&
|
||||
rz_strbuf_append(sb, rz_il_float_stringify_rmode(opx->mode)) &&
|
||||
il_op_float_rmode_string_resolve(ctx, &opx->rmode, sb) &&
|
||||
rz_strbuf_append(sb, ")");
|
||||
free(sym);
|
||||
return ok;
|
||||
|
|
@ -454,20 +459,27 @@ static bool il_opdmp_fcast_sfloat(RzILStringifyCtx *ctx, const RzILOpPure *op, R
|
|||
|
||||
static bool il_opdmp_fconvert(RzILStringifyCtx *ctx, const RzILOpPure *op, RzStrBuf *sb) {
|
||||
const RzILOpArgsFconvert *opx = &op->op.fconvert;
|
||||
const char *sym = sym_with_float_format(opx->format, UCD_FCONVERT);
|
||||
il_op_param_1_with_mode_format(sym, opx, f, pure, mode, format);
|
||||
char *sym = sym_with_float_format(opx->format, UCD_FCONVERT);
|
||||
return_false_if_fail(sym);
|
||||
bool ok = rz_strbuf_append(sb, "(") &&
|
||||
il_op_pure_string_resolve(ctx, opx->f, sb) &&
|
||||
rz_strbuf_appendf(sb, " %s ", sym) &&
|
||||
il_op_float_rmode_string_resolve(ctx, &opx->rmode, sb) &&
|
||||
rz_strbuf_append(sb, ")");
|
||||
free(sym);
|
||||
return ok;
|
||||
}
|
||||
|
||||
static bool il_opdmp_fsqrt(RzILStringifyCtx *ctx, const RzILOpPure *op, RzStrBuf *sb) {
|
||||
il_op_param_1_with_rmode(UCD_FSQRT, op->op.fsqrt, f, pure, rmode);
|
||||
il_op_param_1_with_rmode_arg(UCD_FSQRT, op->op.fsqrt, f, pure, rmode);
|
||||
}
|
||||
|
||||
static bool il_opdmp_frsqrt(RzILStringifyCtx *ctx, const RzILOpPure *op, RzStrBuf *sb) {
|
||||
il_op_param_1_with_rmode(UCD_FRSQRT, op->op.frsqrt, f, pure, rmode);
|
||||
il_op_param_1_with_rmode_arg(UCD_FRSQRT, op->op.frsqrt, f, pure, rmode);
|
||||
}
|
||||
|
||||
static bool il_opdmp_fround(RzILStringifyCtx *ctx, const RzILOpPure *op, RzStrBuf *sb) {
|
||||
il_op_param_1_with_rmode(UCD_FROUND, op->op.fround, f, pure, rmode);
|
||||
il_op_param_1_with_rmode_arg(UCD_FROUND, op->op.fround, f, pure, rmode);
|
||||
}
|
||||
|
||||
static bool il_opdmp_frequal(RzILStringifyCtx *ctx, const RzILOpPure *op, RzStrBuf *sb) {
|
||||
|
|
@ -499,38 +511,38 @@ static bool il_opdmp_fexcept(RzILStringifyCtx *ctx, const RzILOpPure *op, RzStrB
|
|||
}
|
||||
|
||||
static bool il_opdmp_fadd(RzILStringifyCtx *ctx, const RzILOpPure *op, RzStrBuf *sb) {
|
||||
il_op_param_2_with_rmode(UCD_FADD, op->op.fadd, x, pure, y, pure, rmode);
|
||||
il_op_param_2_with_rmode_arg(UCD_FADD, op->op.fadd, x, pure, y, pure, rmode);
|
||||
}
|
||||
|
||||
static bool il_opdmp_fsub(RzILStringifyCtx *ctx, const RzILOpPure *op, RzStrBuf *sb) {
|
||||
il_op_param_2_with_rmode(UCD_FSUB, op->op.fsub, x, pure, y, pure, rmode);
|
||||
il_op_param_2_with_rmode_arg(UCD_FSUB, op->op.fsub, x, pure, y, pure, rmode);
|
||||
}
|
||||
|
||||
static bool il_opdmp_fmul(RzILStringifyCtx *ctx, const RzILOpPure *op, RzStrBuf *sb) {
|
||||
il_op_param_2_with_rmode(UCD_FMUL, op->op.fmul, x, pure, y, pure, rmode);
|
||||
il_op_param_2_with_rmode_arg(UCD_FMUL, op->op.fmul, x, pure, y, pure, rmode);
|
||||
}
|
||||
|
||||
static bool il_opdmp_fdiv(RzILStringifyCtx *ctx, const RzILOpPure *op, RzStrBuf *sb) {
|
||||
il_op_param_2_with_rmode(UCD_FDIV, op->op.fdiv, x, pure, y, pure, rmode);
|
||||
il_op_param_2_with_rmode_arg(UCD_FDIV, op->op.fdiv, x, pure, y, pure, rmode);
|
||||
}
|
||||
|
||||
static bool il_opdmp_fmod(RzILStringifyCtx *ctx, const RzILOpPure *op, RzStrBuf *sb) {
|
||||
il_op_param_2_with_rmode(UCD_FMOD, op->op.fmod, x, pure, y, pure, rmode);
|
||||
il_op_param_2_with_rmode_arg(UCD_FMOD, op->op.fmod, x, pure, y, pure, rmode);
|
||||
}
|
||||
|
||||
static bool il_opdmp_fhypot(RzILStringifyCtx *ctx, const RzILOpPure *op, RzStrBuf *sb) {
|
||||
il_op_param_2_with_rmode(UCD_FHYPOT, op->op.fhypot, x, pure, y, pure, rmode);
|
||||
il_op_param_2_with_rmode_arg(UCD_FHYPOT, op->op.fhypot, x, pure, y, pure, rmode);
|
||||
}
|
||||
|
||||
static bool il_opdmp_fpow(RzILStringifyCtx *ctx, const RzILOpPure *op, RzStrBuf *sb) {
|
||||
il_op_param_2_with_rmode(UCD_FPOW, op->op.fpow, x, pure, y, pure, rmode);
|
||||
il_op_param_2_with_rmode_arg(UCD_FPOW, op->op.fpow, x, pure, y, pure, rmode);
|
||||
}
|
||||
|
||||
static bool il_opdmp_fmad(RzILStringifyCtx *ctx, const RzILOpPure *op, RzStrBuf *sb) {
|
||||
const RzILOpArgsFmad *opx = &op->op.fmad;
|
||||
const char *rmode_str = rz_il_float_stringify_rmode(opx->rmode);
|
||||
return_false_if_fail(rmode_str);
|
||||
return_false_if_fail(rz_strbuf_appendf(sb, "(%s ", rmode_str));
|
||||
return_false_if_fail(rz_strbuf_append(sb, "("));
|
||||
return_false_if_fail(il_op_float_rmode_string_resolve(ctx, &opx->rmode, sb));
|
||||
return_false_if_fail(rz_strbuf_append(sb, " "));
|
||||
return_false_if_fail(il_op_pure_string_resolve(ctx, opx->x, sb));
|
||||
return_false_if_fail(rz_strbuf_append(sb, " " UCD_FMUL " "));
|
||||
return_false_if_fail(il_op_pure_string_resolve(ctx, opx->y, sb));
|
||||
|
|
@ -540,15 +552,15 @@ static bool il_opdmp_fmad(RzILStringifyCtx *ctx, const RzILOpPure *op, RzStrBuf
|
|||
}
|
||||
|
||||
static bool il_opdmp_fpown(RzILStringifyCtx *ctx, const RzILOpPure *op, RzStrBuf *sb) {
|
||||
il_op_param_2_with_rmode(UCD_FPOWN, op->op.fpown, f, pure, n, pure, rmode);
|
||||
il_op_param_2_with_rmode_arg(UCD_FPOWN, op->op.fpown, f, pure, n, pure, rmode);
|
||||
}
|
||||
|
||||
static bool il_opdmp_frootn(RzILStringifyCtx *ctx, const RzILOpPure *op, RzStrBuf *sb) {
|
||||
il_op_param_2_with_rmode(UCD_FROOTN, op->op.frootn, n, pure, f, pure, rmode);
|
||||
il_op_param_2_with_rmode_arg(UCD_FROOTN, op->op.frootn, n, pure, f, pure, rmode);
|
||||
}
|
||||
|
||||
static bool il_opdmp_fcompound(RzILStringifyCtx *ctx, const RzILOpPure *op, RzStrBuf *sb) {
|
||||
il_op_param_2_with_rmode(UCD_FCOMPOUND, op->op.fcompound, f, pure, n, pure, rmode);
|
||||
il_op_param_2_with_rmode_arg(UCD_FCOMPOUND, op->op.fcompound, f, pure, n, pure, rmode);
|
||||
}
|
||||
|
||||
static bool il_opdmp_load(RzILStringifyCtx *ctx, const RzILOpPure *op, RzStrBuf *sb) {
|
||||
|
|
|
|||
|
|
@ -16,6 +16,36 @@ static void il_op_effect_graph_resolve(RzILOpEffect *op, RzGraph /*<RzGraphNodeI
|
|||
#define graph_add_node_il(g, d) \
|
||||
rz_graph_add_node_info(g, d, NULL, UT64_MAX)
|
||||
|
||||
static RzGraphNode *il_op_graph_add_node_with_rmode(const char *name, const RzILOpArgFloatRMode *rmode,
|
||||
RzGraph /*<RzGraphNodeInfo *, NULL *>*/ *g, RzGraphNode *from) {
|
||||
char *value;
|
||||
switch (rmode->kind) {
|
||||
case RZ_IL_OP_ARG_FLOAT_RMODE_STATIC:
|
||||
value = rz_str_newf("%s %s", name, rz_il_float_stringify_rmode(rmode->value.static_mode));
|
||||
break;
|
||||
case RZ_IL_OP_ARG_FLOAT_RMODE_DYNAMIC:
|
||||
value = rz_str_dup(name);
|
||||
break;
|
||||
default:
|
||||
rz_warn_if_reached();
|
||||
value = rz_str_newf("%s invalid_rmode", name);
|
||||
break;
|
||||
}
|
||||
if (!value) {
|
||||
return NULL;
|
||||
}
|
||||
RzGraphNode *to = graph_add_node_il(g, value);
|
||||
free(value);
|
||||
if (!to) {
|
||||
return NULL;
|
||||
}
|
||||
rz_graph_add_edge(g, from, to, NULL);
|
||||
if (rmode->kind == RZ_IL_OP_ARG_FLOAT_RMODE_DYNAMIC) {
|
||||
il_op_pure_graph_resolve(rmode->value.dynamic_mode, g, to);
|
||||
}
|
||||
return to;
|
||||
}
|
||||
|
||||
#define il_op_graph_add_edge_s(g, from, string) \
|
||||
do { \
|
||||
RzGraphNode *to = graph_add_node_il(g, string); \
|
||||
|
|
@ -57,37 +87,31 @@ static void il_op_effect_graph_resolve(RzILOpEffect *op, RzGraph /*<RzGraphNodeI
|
|||
il_op_##sort2##_graph_resolve(opx.v2, g, to); \
|
||||
} while (0)
|
||||
|
||||
#define il_op_param_1_with_rmode(name, opx, v0, vr) \
|
||||
#define il_op_param_1_with_rmode_arg(name, opx, v0, vr) \
|
||||
do { \
|
||||
const char *rmode_str = rz_il_float_stringify_rmode(opx.vr); \
|
||||
char *value = rz_str_newf(name " %s", rmode_str); \
|
||||
RzGraphNode *to = graph_add_node_il(g, value); \
|
||||
free(value); \
|
||||
rz_graph_add_edge(g, from, to, NULL); \
|
||||
il_op_pure_graph_resolve(opx.v0, g, to); \
|
||||
RzGraphNode *to = il_op_graph_add_node_with_rmode(name, &(opx).vr, g, from); \
|
||||
if (to) { \
|
||||
il_op_pure_graph_resolve((opx).v0, g, to); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define il_op_param_2_with_rmode(name, opx, sort0, v0, sort1, v1, vr) \
|
||||
#define il_op_param_2_with_rmode_arg(name, opx, sort0, v0, sort1, v1, vr) \
|
||||
do { \
|
||||
const char *rmode_str = rz_il_float_stringify_rmode(opx.vr); \
|
||||
char *value = rz_str_newf(name " %s", rmode_str); \
|
||||
RzGraphNode *to = graph_add_node_il(g, value); \
|
||||
free(value); \
|
||||
rz_graph_add_edge(g, from, to, NULL); \
|
||||
il_op_##sort0##_graph_resolve(opx.v0, g, to); \
|
||||
il_op_##sort1##_graph_resolve(opx.v1, g, to); \
|
||||
RzGraphNode *to = il_op_graph_add_node_with_rmode(name, &(opx).vr, g, from); \
|
||||
if (to) { \
|
||||
il_op_##sort0##_graph_resolve((opx).v0, g, to); \
|
||||
il_op_##sort1##_graph_resolve((opx).v1, g, to); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define il_op_param_3_with_rmode(name, opx, sort0, v0, sort1, v1, sort2, v2, vr) \
|
||||
#define il_op_param_3_with_rmode_arg(name, opx, sort0, v0, sort1, v1, sort2, v2, vr) \
|
||||
do { \
|
||||
const char *rmode_str = rz_il_float_stringify_rmode(opx.vr); \
|
||||
char *value = rz_str_newf(name " %s", rmode_str); \
|
||||
RzGraphNode *to = graph_add_node_il(g, value); \
|
||||
free(value); \
|
||||
rz_graph_add_edge(g, from, to, NULL); \
|
||||
il_op_##sort0##_graph_resolve(opx.v0, g, to); \
|
||||
il_op_##sort1##_graph_resolve(opx.v1, g, to); \
|
||||
il_op_##sort2##_graph_resolve(opx.v2, g, to); \
|
||||
RzGraphNode *to = il_op_graph_add_node_with_rmode(name, &(opx).vr, g, from); \
|
||||
if (to) { \
|
||||
il_op_##sort0##_graph_resolve((opx).v0, g, to); \
|
||||
il_op_##sort1##_graph_resolve((opx).v1, g, to); \
|
||||
il_op_##sort2##_graph_resolve((opx).v2, g, to); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
static void il_op_graph_var(RzILOpPure *op, RzGraph /*<RzGraphNodeInfo *, NULL *>*/ *g, RzGraphNode *from) {
|
||||
|
|
@ -284,65 +308,74 @@ static void il_op_graph_fabs(RzILOpPure *op, RzGraph /*<RzGraphNodeInfo *, NULL
|
|||
|
||||
static void il_op_graph_fcast_int(RzILOpPure *op, RzGraph /*<RzGraphNodeInfo *, NULL *>*/ *g, RzGraphNode *from) {
|
||||
RzILOpArgsFCastint *opx = &op->op.fcast_int;
|
||||
const char *rmode_str = rz_il_float_stringify_rmode(opx->mode);
|
||||
char *value = rz_str_newf("fcast_int: %u %s", opx->length, rmode_str);
|
||||
RzGraphNode *to = graph_add_node_il(g, value);
|
||||
char *value = rz_str_newf("fcast_int: %u", opx->length);
|
||||
if (!value) {
|
||||
return;
|
||||
}
|
||||
RzGraphNode *to = il_op_graph_add_node_with_rmode(value, &opx->rmode, g, from);
|
||||
free(value);
|
||||
rz_graph_add_edge(g, from, to, NULL);
|
||||
il_op_pure_graph_resolve(opx->f, g, to);
|
||||
if (to) {
|
||||
il_op_pure_graph_resolve(opx->f, g, to);
|
||||
}
|
||||
}
|
||||
|
||||
static void il_op_graph_fcast_sint(RzILOpPure *op, RzGraph /*<RzGraphNodeInfo *, NULL *>*/ *g, RzGraphNode *from) {
|
||||
RzILOpArgsFCastsint *opx = &op->op.fcast_sint;
|
||||
const char *rmode_str = rz_il_float_stringify_rmode(opx->mode);
|
||||
char *value = rz_str_newf("fcast_sint: %u %s", opx->length, rmode_str);
|
||||
RzGraphNode *to = graph_add_node_il(g, value);
|
||||
char *value = rz_str_newf("fcast_sint: %u", opx->length);
|
||||
if (!value) {
|
||||
return;
|
||||
}
|
||||
RzGraphNode *to = il_op_graph_add_node_with_rmode(value, &opx->rmode, g, from);
|
||||
free(value);
|
||||
rz_graph_add_edge(g, from, to, NULL);
|
||||
il_op_pure_graph_resolve(opx->f, g, to);
|
||||
if (to) {
|
||||
il_op_pure_graph_resolve(opx->f, g, to);
|
||||
}
|
||||
}
|
||||
|
||||
static void il_op_graph_fcast_float(RzILOpPure *op, RzGraph /*<RzGraphNodeInfo *, NULL *>*/ *g, RzGraphNode *from) {
|
||||
RzILOpArgsFCastUFloat *opx = &op->op.fcast_float;
|
||||
const char *format_str = rz_il_float_stringify_format(opx->format);
|
||||
const char *rmode_str = rz_il_float_stringify_rmode(opx->mode);
|
||||
char *value = rz_str_newf("fcast_float: %s %s", format_str, rmode_str);
|
||||
RzGraphNode *to = graph_add_node_il(g, value);
|
||||
char *value = rz_str_newf("fcast_float: %s", format_str);
|
||||
if (!value) {
|
||||
return;
|
||||
}
|
||||
RzGraphNode *to = il_op_graph_add_node_with_rmode(value, &opx->rmode, g, from);
|
||||
free(value);
|
||||
rz_graph_add_edge(g, from, to, NULL);
|
||||
il_op_pure_graph_resolve(opx->bv, g, to);
|
||||
if (to) {
|
||||
il_op_pure_graph_resolve(opx->bv, g, to);
|
||||
}
|
||||
}
|
||||
|
||||
static void il_op_graph_fcast_sfloat(RzILOpPure *op, RzGraph /*<RzGraphNodeInfo *, NULL *>*/ *g, RzGraphNode *from) {
|
||||
RzILOpArgsFCastSFloat *opx = &op->op.fcast_sfloat;
|
||||
const char *format_str = rz_il_float_stringify_format(opx->format);
|
||||
const char *rmode_str = rz_il_float_stringify_rmode(opx->mode);
|
||||
char *value = rz_str_newf("fcast_sfloat: %s %s", format_str, rmode_str);
|
||||
RzGraphNode *to = graph_add_node_il(g, value);
|
||||
char *value = rz_str_newf("fcast_sfloat: %s", format_str);
|
||||
if (!value) {
|
||||
return;
|
||||
}
|
||||
RzGraphNode *to = il_op_graph_add_node_with_rmode(value, &opx->rmode, g, from);
|
||||
free(value);
|
||||
rz_graph_add_edge(g, from, to, NULL);
|
||||
il_op_pure_graph_resolve(opx->bv, g, to);
|
||||
if (to) {
|
||||
il_op_pure_graph_resolve(opx->bv, g, to);
|
||||
}
|
||||
}
|
||||
|
||||
static void il_op_graph_fconvert(RzILOpPure *op, RzGraph /*<RzGraphNodeInfo *, NULL *>*/ *g, RzGraphNode *from) {
|
||||
RzILOpArgsFconvert *opx = &op->op.fconvert;
|
||||
const char *format_str = rz_il_float_stringify_format(opx->format);
|
||||
const char *rmode_str = rz_il_float_stringify_rmode(opx->mode);
|
||||
char *value = rz_str_newf("fconvert: %s %s", format_str, rmode_str);
|
||||
RzGraphNode *to = graph_add_node_il(g, value);
|
||||
char *value = rz_str_newf("fconvert: %s", format_str);
|
||||
if (!value) {
|
||||
return;
|
||||
}
|
||||
RzGraphNode *to = il_op_graph_add_node_with_rmode(value, &opx->rmode, g, from);
|
||||
free(value);
|
||||
rz_graph_add_edge(g, from, to, NULL);
|
||||
il_op_pure_graph_resolve(opx->f, g, to);
|
||||
if (to) {
|
||||
il_op_pure_graph_resolve(opx->f, g, to);
|
||||
}
|
||||
}
|
||||
|
||||
static void il_op_graph_fround(RzILOpPure *op, RzGraph /*<RzGraphNodeInfo *, NULL *>*/ *g, RzGraphNode *from) {
|
||||
RzILOpArgsFround *opx = &op->op.fround;
|
||||
const char *rmode_str = rz_il_float_stringify_rmode(opx->rmode);
|
||||
char *value = rz_str_newf("fround: %s", rmode_str);
|
||||
RzGraphNode *to = graph_add_node_il(g, value);
|
||||
free(value);
|
||||
rz_graph_add_edge(g, from, to, NULL);
|
||||
il_op_pure_graph_resolve(opx->f, g, to);
|
||||
il_op_param_1_with_rmode_arg("fround:", op->op.fround, f, rmode);
|
||||
}
|
||||
|
||||
static void il_op_graph_frequal(RzILOpPure *op, RzGraph /*<RzGraphNodeInfo *, NULL *>*/ *g, RzGraphNode *from) {
|
||||
|
|
@ -367,55 +400,55 @@ static void il_op_graph_forder(RzILOpPure *op, RzGraph /*<RzGraphNodeInfo *, NUL
|
|||
}
|
||||
|
||||
static void il_op_graph_fsqrt(RzILOpPure *op, RzGraph /*<RzGraphNodeInfo *, NULL *>*/ *g, RzGraphNode *from) {
|
||||
il_op_param_1_with_rmode("fsqrt", op->op.fsqrt, f, rmode);
|
||||
il_op_param_1_with_rmode_arg("fsqrt", op->op.fsqrt, f, rmode);
|
||||
}
|
||||
|
||||
static void il_op_graph_frsqrt(RzILOpPure *op, RzGraph /*<RzGraphNodeInfo *, NULL *>*/ *g, RzGraphNode *from) {
|
||||
il_op_param_1_with_rmode("frsqrt", op->op.frsqrt, f, rmode);
|
||||
il_op_param_1_with_rmode_arg("frsqrt", op->op.frsqrt, f, rmode);
|
||||
}
|
||||
|
||||
static void il_op_graph_fadd(RzILOpPure *op, RzGraph /*<RzGraphNodeInfo *, NULL *>*/ *g, RzGraphNode *from) {
|
||||
il_op_param_2_with_rmode("fadd", op->op.fadd, pure, x, pure, y, rmode);
|
||||
il_op_param_2_with_rmode_arg("fadd", op->op.fadd, pure, x, pure, y, rmode);
|
||||
}
|
||||
|
||||
static void il_op_graph_fsub(RzILOpPure *op, RzGraph /*<RzGraphNodeInfo *, NULL *>*/ *g, RzGraphNode *from) {
|
||||
il_op_param_2_with_rmode("fsub", op->op.fsub, pure, x, pure, y, rmode);
|
||||
il_op_param_2_with_rmode_arg("fsub", op->op.fsub, pure, x, pure, y, rmode);
|
||||
}
|
||||
|
||||
static void il_op_graph_fmul(RzILOpPure *op, RzGraph /*<RzGraphNodeInfo *, NULL *>*/ *g, RzGraphNode *from) {
|
||||
il_op_param_2_with_rmode("fmul", op->op.fmul, pure, x, pure, y, rmode);
|
||||
il_op_param_2_with_rmode_arg("fmul", op->op.fmul, pure, x, pure, y, rmode);
|
||||
}
|
||||
|
||||
static void il_op_graph_fdiv(RzILOpPure *op, RzGraph /*<RzGraphNodeInfo *, NULL *>*/ *g, RzGraphNode *from) {
|
||||
il_op_param_2_with_rmode("fdiv", op->op.fdiv, pure, x, pure, y, rmode);
|
||||
il_op_param_2_with_rmode_arg("fdiv", op->op.fdiv, pure, x, pure, y, rmode);
|
||||
}
|
||||
|
||||
static void il_op_graph_fmod(RzILOpPure *op, RzGraph /*<RzGraphNodeInfo *, NULL *>*/ *g, RzGraphNode *from) {
|
||||
il_op_param_2_with_rmode("fmod", op->op.fmod, pure, x, pure, y, rmode);
|
||||
il_op_param_2_with_rmode_arg("fmod", op->op.fmod, pure, x, pure, y, rmode);
|
||||
}
|
||||
|
||||
static void il_op_graph_fhypot(RzILOpPure *op, RzGraph /*<RzGraphNodeInfo *, NULL *>*/ *g, RzGraphNode *from) {
|
||||
il_op_param_2_with_rmode("hypot", op->op.fhypot, pure, x, pure, y, rmode);
|
||||
il_op_param_2_with_rmode_arg("hypot", op->op.fhypot, pure, x, pure, y, rmode);
|
||||
}
|
||||
|
||||
static void il_op_graph_fpow(RzILOpPure *op, RzGraph /*<RzGraphNodeInfo *, NULL *>*/ *g, RzGraphNode *from) {
|
||||
il_op_param_2_with_rmode("pow", op->op.fpow, pure, x, pure, y, rmode);
|
||||
il_op_param_2_with_rmode_arg("pow", op->op.fpow, pure, x, pure, y, rmode);
|
||||
}
|
||||
|
||||
static void il_op_graph_fmad(RzILOpPure *op, RzGraph /*<RzGraphNodeInfo *, NULL *>*/ *g, RzGraphNode *from) {
|
||||
il_op_param_3_with_rmode("fmad", op->op.fmad, pure, x, pure, y, pure, z, rmode);
|
||||
il_op_param_3_with_rmode_arg("fmad", op->op.fmad, pure, x, pure, y, pure, z, rmode);
|
||||
}
|
||||
|
||||
static void il_op_graph_fpown(RzILOpPure *op, RzGraph /*<RzGraphNodeInfo *, NULL *>*/ *g, RzGraphNode *from) {
|
||||
il_op_param_2_with_rmode("fpown", op->op.fpown, pure, f, pure, n, rmode);
|
||||
il_op_param_2_with_rmode_arg("fpown", op->op.fpown, pure, f, pure, n, rmode);
|
||||
}
|
||||
|
||||
static void il_op_graph_frootn(RzILOpPure *op, RzGraph /*<RzGraphNodeInfo *, NULL *>*/ *g, RzGraphNode *from) {
|
||||
il_op_param_2_with_rmode("frootn", op->op.frootn, pure, f, pure, n, rmode);
|
||||
il_op_param_2_with_rmode_arg("frootn", op->op.frootn, pure, f, pure, n, rmode);
|
||||
}
|
||||
|
||||
static void il_op_graph_fcompound(RzILOpPure *op, RzGraph /*<RzGraphNodeInfo *, NULL *>*/ *g, RzGraphNode *from) {
|
||||
il_op_param_2_with_rmode("fcompound", op->op.fcompound, pure, f, pure, n, rmode);
|
||||
il_op_param_2_with_rmode_arg("fcompound", op->op.fcompound, pure, f, pure, n, rmode);
|
||||
}
|
||||
|
||||
static void il_op_graph_load(RzILOpPure *op, RzGraph /*<RzGraphNodeInfo *, NULL *>*/ *g, RzGraphNode *from) {
|
||||
|
|
|
|||
|
|
@ -45,6 +45,20 @@
|
|||
ret->op.s.v2 = v2; \
|
||||
} while (0)
|
||||
|
||||
static RzILOpArgFloatRMode float_rmode_static(RzFloatRMode mode) {
|
||||
return (RzILOpArgFloatRMode){
|
||||
.kind = RZ_IL_OP_ARG_FLOAT_RMODE_STATIC,
|
||||
.value.static_mode = mode,
|
||||
};
|
||||
}
|
||||
|
||||
static RzILOpArgFloatRMode float_rmode_dynamic(RzILOpBitVector *mode) {
|
||||
return (RzILOpArgFloatRMode){
|
||||
.kind = RZ_IL_OP_ARG_FLOAT_RMODE_DYNAMIC,
|
||||
.value.dynamic_mode = mode,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief op structure for `ite` (bool -> 'a pure -> 'a pure -> 'a pure)
|
||||
*
|
||||
|
|
@ -867,36 +881,81 @@ RZ_API RZ_OWN RzILOpFloat *rz_il_op_new_fabs(RZ_NONNULL RzILOpFloat *f) {
|
|||
|
||||
RZ_API RZ_OWN RzILOpBitVector *rz_il_op_new_fcast_int(ut32 length, RzFloatRMode mode, RZ_NONNULL RzILOpFloat *f) {
|
||||
rz_return_val_if_fail(f, NULL);
|
||||
RzILOpArgFloatRMode rmode = float_rmode_static(mode);
|
||||
RzILOpBitVector *ret;
|
||||
rz_il_op_new_3(BitVector, RZ_IL_OP_FCAST_INT, RzILOpArgsFCastint, fcast_int, length, mode, f);
|
||||
rz_il_op_new_3(BitVector, RZ_IL_OP_FCAST_INT, RzILOpArgsFCastint, fcast_int, length, rmode, f);
|
||||
return ret;
|
||||
}
|
||||
|
||||
RZ_API RZ_OWN RzILOpBitVector *rz_il_op_new_fcast_sint(ut32 length, RzFloatRMode mode, RZ_NONNULL RzILOpFloat *f) {
|
||||
rz_return_val_if_fail(f, NULL);
|
||||
RzILOpArgFloatRMode rmode = float_rmode_static(mode);
|
||||
RzILOpBitVector *ret;
|
||||
rz_il_op_new_3(BitVector, RZ_IL_OP_FCAST_SINT, RzILOpArgsFCastsint, fcast_sint, length, mode, f);
|
||||
rz_il_op_new_3(BitVector, RZ_IL_OP_FCAST_SINT, RzILOpArgsFCastsint, fcast_sint, length, rmode, f);
|
||||
return ret;
|
||||
}
|
||||
|
||||
RZ_API RZ_OWN RzILOpFloat *rz_il_op_new_fcast_float(RzFloatFormat format, RzFloatRMode mode, RZ_NONNULL RzILOpBitVector *bv) {
|
||||
rz_return_val_if_fail(bv, NULL);
|
||||
RzILOpArgFloatRMode rmode = float_rmode_static(mode);
|
||||
RzILOpFloat *ret;
|
||||
rz_il_op_new_3(Float, RZ_IL_OP_FCAST_FLOAT, RzILOpArgsFCastfloat, fcast_float, format, mode, bv);
|
||||
rz_il_op_new_3(Float, RZ_IL_OP_FCAST_FLOAT, RzILOpArgsFCastfloat, fcast_float, format, rmode, bv);
|
||||
return ret;
|
||||
}
|
||||
|
||||
RZ_API RZ_OWN RzILOpFloat *rz_il_op_new_fcast_sfloat(RzFloatFormat format, RzFloatRMode mode, RZ_NONNULL RzILOpBitVector *bv) {
|
||||
rz_return_val_if_fail(bv, NULL);
|
||||
RzILOpArgFloatRMode rmode = float_rmode_static(mode);
|
||||
RzILOpFloat *ret;
|
||||
rz_il_op_new_3(Float, RZ_IL_OP_FCAST_SFLOAT, RzILOpArgsFCastsfloat, fcast_sfloat, format, mode, bv);
|
||||
rz_il_op_new_3(Float, RZ_IL_OP_FCAST_SFLOAT, RzILOpArgsFCastsfloat, fcast_sfloat, format, rmode, bv);
|
||||
return ret;
|
||||
}
|
||||
|
||||
RZ_API RZ_OWN RzILOpBitVector *rz_il_op_new_fcast_int_dyn_rmode(ut32 length, RZ_NONNULL RzILOpBitVector *mode, RZ_NONNULL RzILOpFloat *f) {
|
||||
rz_return_val_if_fail(mode && f, NULL);
|
||||
RzILOpArgFloatRMode rmode = float_rmode_dynamic(mode);
|
||||
RzILOpBitVector *ret;
|
||||
rz_il_op_new_3(BitVector, RZ_IL_OP_FCAST_INT, RzILOpArgsFCastint, fcast_int, length, rmode, f);
|
||||
return ret;
|
||||
}
|
||||
|
||||
RZ_API RZ_OWN RzILOpBitVector *rz_il_op_new_fcast_sint_dyn_rmode(ut32 length, RZ_NONNULL RzILOpBitVector *mode, RZ_NONNULL RzILOpFloat *f) {
|
||||
rz_return_val_if_fail(mode && f, NULL);
|
||||
RzILOpArgFloatRMode rmode = float_rmode_dynamic(mode);
|
||||
RzILOpBitVector *ret;
|
||||
rz_il_op_new_3(BitVector, RZ_IL_OP_FCAST_SINT, RzILOpArgsFCastsint, fcast_sint, length, rmode, f);
|
||||
return ret;
|
||||
}
|
||||
|
||||
RZ_API RZ_OWN RzILOpFloat *rz_il_op_new_fcast_float_dyn_rmode(RzFloatFormat format, RZ_NONNULL RzILOpBitVector *mode, RZ_NONNULL RzILOpBitVector *bv) {
|
||||
rz_return_val_if_fail(mode && bv, NULL);
|
||||
RzILOpArgFloatRMode rmode = float_rmode_dynamic(mode);
|
||||
RzILOpFloat *ret;
|
||||
rz_il_op_new_3(Float, RZ_IL_OP_FCAST_FLOAT, RzILOpArgsFCastfloat, fcast_float, format, rmode, bv);
|
||||
return ret;
|
||||
}
|
||||
|
||||
RZ_API RZ_OWN RzILOpFloat *rz_il_op_new_fcast_sfloat_dyn_rmode(RzFloatFormat format, RZ_NONNULL RzILOpBitVector *mode, RZ_NONNULL RzILOpBitVector *bv) {
|
||||
rz_return_val_if_fail(mode && bv, NULL);
|
||||
RzILOpArgFloatRMode rmode = float_rmode_dynamic(mode);
|
||||
RzILOpFloat *ret;
|
||||
rz_il_op_new_3(Float, RZ_IL_OP_FCAST_SFLOAT, RzILOpArgsFCastsfloat, fcast_sfloat, format, rmode, bv);
|
||||
return ret;
|
||||
}
|
||||
|
||||
RZ_API RZ_OWN RzILOpFloat *rz_il_op_new_fconvert(RzFloatFormat format, RzFloatRMode mode, RZ_NONNULL RzILOpFloat *f) {
|
||||
rz_return_val_if_fail(f, NULL);
|
||||
RzILOpArgFloatRMode rmode = float_rmode_static(mode);
|
||||
RzILOpFloat *ret;
|
||||
rz_il_op_new_3(Float, RZ_IL_OP_FCONVERT, RzILOpArgsFconvert, fconvert, format, mode, f);
|
||||
rz_il_op_new_3(Float, RZ_IL_OP_FCONVERT, RzILOpArgsFconvert, fconvert, format, rmode, f);
|
||||
return ret;
|
||||
}
|
||||
|
||||
RZ_API RZ_OWN RzILOpFloat *rz_il_op_new_fconvert_dyn_rmode(RzFloatFormat format, RZ_NONNULL RzILOpBitVector *mode, RZ_NONNULL RzILOpFloat *f) {
|
||||
rz_return_val_if_fail(mode && f, NULL);
|
||||
RzILOpArgFloatRMode rmode = float_rmode_dynamic(mode);
|
||||
RzILOpFloat *ret;
|
||||
rz_il_op_new_3(Float, RZ_IL_OP_FCONVERT, RzILOpArgsFconvert, fconvert, format, rmode, f);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
@ -927,22 +986,49 @@ RZ_API RZ_OWN RzILOpBool *rz_il_op_new_forder(RZ_NONNULL RzILOpFloat *x, RZ_NONN
|
|||
return ret;
|
||||
}
|
||||
|
||||
RZ_API RZ_OWN RzILOpFloat *rz_il_op_new_fround(RzFloatRMode rmode, RZ_NONNULL RzILOpFloat *f) {
|
||||
RZ_API RZ_OWN RzILOpFloat *rz_il_op_new_fround(RzFloatRMode mode, RZ_NONNULL RzILOpFloat *f) {
|
||||
rz_return_val_if_fail(f, NULL);
|
||||
RzILOpArgFloatRMode rmode = float_rmode_static(mode);
|
||||
RzILOpFloat *ret;
|
||||
rz_il_op_new_2(Float, RZ_IL_OP_FROUND, RzILOpArgsFround, fround, rmode, f);
|
||||
return ret;
|
||||
}
|
||||
|
||||
RZ_API RZ_OWN RzILOpFloat *rz_il_op_new_fsqrt(RzFloatRMode rmode, RZ_NONNULL RzILOpFloat *f) {
|
||||
RZ_API RZ_OWN RzILOpFloat *rz_il_op_new_fsqrt(RzFloatRMode mode, RZ_NONNULL RzILOpFloat *f) {
|
||||
rz_return_val_if_fail(f, NULL);
|
||||
RzILOpArgFloatRMode rmode = float_rmode_static(mode);
|
||||
RzILOpFloat *ret;
|
||||
rz_il_op_new_2(Float, RZ_IL_OP_FSQRT, RzILOpArgsFsqrt, fsqrt, rmode, f);
|
||||
return ret;
|
||||
}
|
||||
|
||||
RZ_API RZ_OWN RzILOpFloat *rz_il_op_new_frsqrt(RzFloatRMode rmode, RZ_NONNULL RzILOpFloat *f) {
|
||||
RZ_API RZ_OWN RzILOpFloat *rz_il_op_new_fround_dyn_rmode(RZ_NONNULL RzILOpBitVector *mode, RZ_NONNULL RzILOpFloat *f) {
|
||||
rz_return_val_if_fail(mode && f, NULL);
|
||||
RzILOpArgFloatRMode rmode = float_rmode_dynamic(mode);
|
||||
RzILOpFloat *ret;
|
||||
rz_il_op_new_2(Float, RZ_IL_OP_FROUND, RzILOpArgsFround, fround, rmode, f);
|
||||
return ret;
|
||||
}
|
||||
|
||||
RZ_API RZ_OWN RzILOpFloat *rz_il_op_new_fsqrt_dyn_rmode(RZ_NONNULL RzILOpBitVector *mode, RZ_NONNULL RzILOpFloat *f) {
|
||||
rz_return_val_if_fail(mode && f, NULL);
|
||||
RzILOpArgFloatRMode rmode = float_rmode_dynamic(mode);
|
||||
RzILOpFloat *ret;
|
||||
rz_il_op_new_2(Float, RZ_IL_OP_FSQRT, RzILOpArgsFsqrt, fsqrt, rmode, f);
|
||||
return ret;
|
||||
}
|
||||
|
||||
RZ_API RZ_OWN RzILOpFloat *rz_il_op_new_frsqrt(RzFloatRMode mode, RZ_NONNULL RzILOpFloat *f) {
|
||||
rz_return_val_if_fail(f, NULL);
|
||||
RzILOpArgFloatRMode rmode = float_rmode_static(mode);
|
||||
RzILOpFloat *ret;
|
||||
rz_il_op_new_2(Float, RZ_IL_OP_FRSQRT, RzILOpArgsFrsqrt, frsqrt, rmode, f);
|
||||
return ret;
|
||||
}
|
||||
|
||||
RZ_API RZ_OWN RzILOpFloat *rz_il_op_new_frsqrt_dyn_rmode(RZ_NONNULL RzILOpBitVector *mode, RZ_NONNULL RzILOpFloat *f) {
|
||||
rz_return_val_if_fail(mode && f, NULL);
|
||||
RzILOpArgFloatRMode rmode = float_rmode_dynamic(mode);
|
||||
RzILOpFloat *ret;
|
||||
rz_il_op_new_2(Float, RZ_IL_OP_FRSQRT, RzILOpArgsFrsqrt, frsqrt, rmode, f);
|
||||
return ret;
|
||||
|
|
@ -955,57 +1041,121 @@ RZ_API RZ_OWN RzILOpBool *rz_il_op_new_fexcept(RzFloatException e, RZ_NONNULL Rz
|
|||
return ret;
|
||||
}
|
||||
|
||||
RZ_API RZ_OWN RzILOpFloat *rz_il_op_new_fadd(RzFloatRMode rmode, RZ_NONNULL RzILOpFloat *x, RZ_NONNULL RzILOpFloat *y) {
|
||||
RZ_API RZ_OWN RzILOpFloat *rz_il_op_new_fadd(RzFloatRMode mode, RZ_NONNULL RzILOpFloat *x, RZ_NONNULL RzILOpFloat *y) {
|
||||
rz_return_val_if_fail(x && y, NULL);
|
||||
RzILOpArgFloatRMode rmode = float_rmode_static(mode);
|
||||
RzILOpFloat *ret;
|
||||
rz_il_op_new_3(Float, RZ_IL_OP_FADD, RzILOpArgsFadd, fadd, rmode, x, y);
|
||||
return ret;
|
||||
}
|
||||
|
||||
RZ_API RZ_OWN RzILOpFloat *rz_il_op_new_fsub(RzFloatRMode rmode, RZ_NONNULL RzILOpFloat *x, RZ_NONNULL RzILOpFloat *y) {
|
||||
RZ_API RZ_OWN RzILOpFloat *rz_il_op_new_fsub(RzFloatRMode mode, RZ_NONNULL RzILOpFloat *x, RZ_NONNULL RzILOpFloat *y) {
|
||||
rz_return_val_if_fail(x && y, NULL);
|
||||
RzILOpArgFloatRMode rmode = float_rmode_static(mode);
|
||||
RzILOpFloat *ret;
|
||||
rz_il_op_new_3(Float, RZ_IL_OP_FSUB, RzILOpArgsFsub, fsub, rmode, x, y);
|
||||
return ret;
|
||||
}
|
||||
|
||||
RZ_API RZ_OWN RzILOpFloat *rz_il_op_new_fmul(RzFloatRMode rmode, RZ_NONNULL RzILOpFloat *x, RZ_NONNULL RzILOpFloat *y) {
|
||||
RZ_API RZ_OWN RzILOpFloat *rz_il_op_new_fmul(RzFloatRMode mode, RZ_NONNULL RzILOpFloat *x, RZ_NONNULL RzILOpFloat *y) {
|
||||
rz_return_val_if_fail(x && y, NULL);
|
||||
RzILOpArgFloatRMode rmode = float_rmode_static(mode);
|
||||
RzILOpFloat *ret;
|
||||
rz_il_op_new_3(Float, RZ_IL_OP_FMUL, RzILOpArgsFmul, fmul, rmode, x, y);
|
||||
return ret;
|
||||
}
|
||||
|
||||
RZ_API RZ_OWN RzILOpFloat *rz_il_op_new_fdiv(RzFloatRMode rmode, RZ_NONNULL RzILOpFloat *x, RZ_NONNULL RzILOpFloat *y) {
|
||||
RZ_API RZ_OWN RzILOpFloat *rz_il_op_new_fdiv(RzFloatRMode mode, RZ_NONNULL RzILOpFloat *x, RZ_NONNULL RzILOpFloat *y) {
|
||||
rz_return_val_if_fail(x && y, NULL);
|
||||
RzILOpArgFloatRMode rmode = float_rmode_static(mode);
|
||||
RzILOpFloat *ret;
|
||||
rz_il_op_new_3(Float, RZ_IL_OP_FDIV, RzILOpArgsFdiv, fdiv, rmode, x, y);
|
||||
return ret;
|
||||
}
|
||||
|
||||
RZ_API RZ_OWN RzILOpFloat *rz_il_op_new_fmod(RzFloatRMode rmode, RZ_NONNULL RzILOpFloat *x, RZ_NONNULL RzILOpFloat *y) {
|
||||
RZ_API RZ_OWN RzILOpFloat *rz_il_op_new_fmod(RzFloatRMode mode, RZ_NONNULL RzILOpFloat *x, RZ_NONNULL RzILOpFloat *y) {
|
||||
rz_return_val_if_fail(x && y, NULL);
|
||||
RzILOpArgFloatRMode rmode = float_rmode_static(mode);
|
||||
RzILOpFloat *ret;
|
||||
rz_il_op_new_3(Float, RZ_IL_OP_FMOD, RzILOpArgsFmod, fmod, rmode, x, y);
|
||||
return ret;
|
||||
}
|
||||
|
||||
RZ_API RZ_OWN RzILOpFloat *rz_il_op_new_fhypot(RzFloatRMode rmode, RZ_NONNULL RzILOpFloat *x, RZ_NONNULL RzILOpFloat *y) {
|
||||
RZ_API RZ_OWN RzILOpFloat *rz_il_op_new_fadd_dyn_rmode(RZ_NONNULL RzILOpBitVector *mode, RZ_NONNULL RzILOpFloat *x, RZ_NONNULL RzILOpFloat *y) {
|
||||
rz_return_val_if_fail(mode && x && y, NULL);
|
||||
RzILOpArgFloatRMode rmode = float_rmode_dynamic(mode);
|
||||
RzILOpFloat *ret;
|
||||
rz_il_op_new_3(Float, RZ_IL_OP_FADD, RzILOpArgsFadd, fadd, rmode, x, y);
|
||||
return ret;
|
||||
}
|
||||
|
||||
RZ_API RZ_OWN RzILOpFloat *rz_il_op_new_fsub_dyn_rmode(RZ_NONNULL RzILOpBitVector *mode, RZ_NONNULL RzILOpFloat *x, RZ_NONNULL RzILOpFloat *y) {
|
||||
rz_return_val_if_fail(mode && x && y, NULL);
|
||||
RzILOpArgFloatRMode rmode = float_rmode_dynamic(mode);
|
||||
RzILOpFloat *ret;
|
||||
rz_il_op_new_3(Float, RZ_IL_OP_FSUB, RzILOpArgsFsub, fsub, rmode, x, y);
|
||||
return ret;
|
||||
}
|
||||
|
||||
RZ_API RZ_OWN RzILOpFloat *rz_il_op_new_fmul_dyn_rmode(RZ_NONNULL RzILOpBitVector *mode, RZ_NONNULL RzILOpFloat *x, RZ_NONNULL RzILOpFloat *y) {
|
||||
rz_return_val_if_fail(mode && x && y, NULL);
|
||||
RzILOpArgFloatRMode rmode = float_rmode_dynamic(mode);
|
||||
RzILOpFloat *ret;
|
||||
rz_il_op_new_3(Float, RZ_IL_OP_FMUL, RzILOpArgsFmul, fmul, rmode, x, y);
|
||||
return ret;
|
||||
}
|
||||
|
||||
RZ_API RZ_OWN RzILOpFloat *rz_il_op_new_fdiv_dyn_rmode(RZ_NONNULL RzILOpBitVector *mode, RZ_NONNULL RzILOpFloat *x, RZ_NONNULL RzILOpFloat *y) {
|
||||
rz_return_val_if_fail(mode && x && y, NULL);
|
||||
RzILOpArgFloatRMode rmode = float_rmode_dynamic(mode);
|
||||
RzILOpFloat *ret;
|
||||
rz_il_op_new_3(Float, RZ_IL_OP_FDIV, RzILOpArgsFdiv, fdiv, rmode, x, y);
|
||||
return ret;
|
||||
}
|
||||
|
||||
RZ_API RZ_OWN RzILOpFloat *rz_il_op_new_fmod_dyn_rmode(RZ_NONNULL RzILOpBitVector *mode, RZ_NONNULL RzILOpFloat *x, RZ_NONNULL RzILOpFloat *y) {
|
||||
rz_return_val_if_fail(mode && x && y, NULL);
|
||||
RzILOpArgFloatRMode rmode = float_rmode_dynamic(mode);
|
||||
RzILOpFloat *ret;
|
||||
rz_il_op_new_3(Float, RZ_IL_OP_FMOD, RzILOpArgsFmod, fmod, rmode, x, y);
|
||||
return ret;
|
||||
}
|
||||
|
||||
RZ_API RZ_OWN RzILOpFloat *rz_il_op_new_fhypot(RzFloatRMode mode, RZ_NONNULL RzILOpFloat *x, RZ_NONNULL RzILOpFloat *y) {
|
||||
rz_return_val_if_fail(x && y, NULL);
|
||||
RzILOpArgFloatRMode rmode = float_rmode_static(mode);
|
||||
RzILOpFloat *ret;
|
||||
rz_il_op_new_3(Float, RZ_IL_OP_FHYPOT, RzILOpArgsFhypot, fhypot, rmode, x, y);
|
||||
return ret;
|
||||
}
|
||||
|
||||
RZ_API RZ_OWN RzILOpFloat *rz_il_op_new_fpow(RzFloatRMode rmode, RZ_NONNULL RzILOpFloat *x, RZ_NONNULL RzILOpFloat *y) {
|
||||
RZ_API RZ_OWN RzILOpFloat *rz_il_op_new_fpow(RzFloatRMode mode, RZ_NONNULL RzILOpFloat *x, RZ_NONNULL RzILOpFloat *y) {
|
||||
rz_return_val_if_fail(x && y, NULL);
|
||||
RzILOpArgFloatRMode rmode = float_rmode_static(mode);
|
||||
RzILOpFloat *ret;
|
||||
rz_il_op_new_3(Float, RZ_IL_OP_FPOW, RzILOpArgsFpow, fpow, rmode, x, y);
|
||||
return ret;
|
||||
}
|
||||
|
||||
RZ_API RZ_OWN RzILOpFloat *rz_il_op_new_fmad(RzFloatRMode rmode, RZ_NONNULL RzILOpFloat *x, RZ_NONNULL RzILOpFloat *y, RZ_NONNULL RzILOpFloat *z) {
|
||||
RZ_API RZ_OWN RzILOpFloat *rz_il_op_new_fhypot_dyn_rmode(RZ_NONNULL RzILOpBitVector *mode, RZ_NONNULL RzILOpFloat *x, RZ_NONNULL RzILOpFloat *y) {
|
||||
rz_return_val_if_fail(mode && x && y, NULL);
|
||||
RzILOpArgFloatRMode rmode = float_rmode_dynamic(mode);
|
||||
RzILOpFloat *ret;
|
||||
rz_il_op_new_3(Float, RZ_IL_OP_FHYPOT, RzILOpArgsFhypot, fhypot, rmode, x, y);
|
||||
return ret;
|
||||
}
|
||||
|
||||
RZ_API RZ_OWN RzILOpFloat *rz_il_op_new_fpow_dyn_rmode(RZ_NONNULL RzILOpBitVector *mode, RZ_NONNULL RzILOpFloat *x, RZ_NONNULL RzILOpFloat *y) {
|
||||
rz_return_val_if_fail(mode && x && y, NULL);
|
||||
RzILOpArgFloatRMode rmode = float_rmode_dynamic(mode);
|
||||
RzILOpFloat *ret;
|
||||
rz_il_op_new_3(Float, RZ_IL_OP_FPOW, RzILOpArgsFpow, fpow, rmode, x, y);
|
||||
return ret;
|
||||
}
|
||||
|
||||
RZ_API RZ_OWN RzILOpFloat *rz_il_op_new_fmad(RzFloatRMode mode, RZ_NONNULL RzILOpFloat *x, RZ_NONNULL RzILOpFloat *y, RZ_NONNULL RzILOpFloat *z) {
|
||||
rz_return_val_if_fail(x && y && z, NULL);
|
||||
RzILOpArgFloatRMode rmode = float_rmode_static(mode);
|
||||
RzILOpFloat *ret;
|
||||
rz_il_op_new_0(Float, RZ_IL_OP_FMAD);
|
||||
ret->op.fmad.rmode = rmode;
|
||||
|
|
@ -1015,24 +1165,63 @@ RZ_API RZ_OWN RzILOpFloat *rz_il_op_new_fmad(RzFloatRMode rmode, RZ_NONNULL RzIL
|
|||
return ret;
|
||||
}
|
||||
|
||||
RZ_API RZ_OWN RzILOpFloat *rz_il_op_new_frootn(RzFloatRMode rmode, RZ_NONNULL RzILOpFloat *f, RZ_NONNULL RzILOpBitVector *n) {
|
||||
rz_return_val_if_fail(f && n, NULL);
|
||||
RZ_API RZ_OWN RzILOpFloat *rz_il_op_new_fmad_dyn_rmode(RZ_NONNULL RzILOpBitVector *mode, RZ_NONNULL RzILOpFloat *x, RZ_NONNULL RzILOpFloat *y, RZ_NONNULL RzILOpFloat *z) {
|
||||
rz_return_val_if_fail(mode && x && y && z, NULL);
|
||||
RzILOpArgFloatRMode rmode = float_rmode_dynamic(mode);
|
||||
RzILOpFloat *ret;
|
||||
rz_il_op_new_3(Float, RZ_IL_OP_FROOTN, RzILOpArgsFrootn, frootn, rmode, n, f);
|
||||
rz_il_op_new_0(Float, RZ_IL_OP_FMAD);
|
||||
ret->op.fmad.rmode = rmode;
|
||||
ret->op.fmad.x = x;
|
||||
ret->op.fmad.y = y;
|
||||
ret->op.fmad.z = z;
|
||||
return ret;
|
||||
}
|
||||
|
||||
RZ_API RZ_OWN RzILOpFloat *rz_il_op_new_fpown(RzFloatRMode rmode, RZ_NONNULL RzILOpFloat *f, RZ_NONNULL RzILOpBitVector *n) {
|
||||
RZ_API RZ_OWN RzILOpFloat *rz_il_op_new_frootn(RzFloatRMode mode, RZ_NONNULL RzILOpFloat *f, RZ_NONNULL RzILOpBitVector *n) {
|
||||
rz_return_val_if_fail(f && n, NULL);
|
||||
RzILOpArgFloatRMode rmode = float_rmode_static(mode);
|
||||
RzILOpFloat *ret;
|
||||
rz_il_op_new_3(Float, RZ_IL_OP_FPOWN, RzILOpArgsFpown, fpown, rmode, n, f);
|
||||
rz_il_op_new_3(Float, RZ_IL_OP_FROOTN, RzILOpArgsFrootn, frootn, rmode, f, n);
|
||||
return ret;
|
||||
}
|
||||
|
||||
RZ_API RZ_OWN RzILOpFloat *rz_il_op_new_fcompound(RzFloatRMode rmode, RZ_NONNULL RzILOpFloat *f, RZ_NONNULL RzILOpBitVector *n) {
|
||||
RZ_API RZ_OWN RzILOpFloat *rz_il_op_new_fpown(RzFloatRMode mode, RZ_NONNULL RzILOpFloat *f, RZ_NONNULL RzILOpBitVector *n) {
|
||||
rz_return_val_if_fail(f && n, NULL);
|
||||
RzILOpArgFloatRMode rmode = float_rmode_static(mode);
|
||||
RzILOpFloat *ret;
|
||||
rz_il_op_new_3(Float, RZ_IL_OP_FCOMPOUND, RzILOpArgsFcompound, fcompound, rmode, n, f);
|
||||
rz_il_op_new_3(Float, RZ_IL_OP_FPOWN, RzILOpArgsFpown, fpown, rmode, f, n);
|
||||
return ret;
|
||||
}
|
||||
|
||||
RZ_API RZ_OWN RzILOpFloat *rz_il_op_new_fcompound(RzFloatRMode mode, RZ_NONNULL RzILOpFloat *f, RZ_NONNULL RzILOpBitVector *n) {
|
||||
rz_return_val_if_fail(f && n, NULL);
|
||||
RzILOpArgFloatRMode rmode = float_rmode_static(mode);
|
||||
RzILOpFloat *ret;
|
||||
rz_il_op_new_3(Float, RZ_IL_OP_FCOMPOUND, RzILOpArgsFcompound, fcompound, rmode, f, n);
|
||||
return ret;
|
||||
}
|
||||
|
||||
RZ_API RZ_OWN RzILOpFloat *rz_il_op_new_frootn_dyn_rmode(RZ_NONNULL RzILOpBitVector *mode, RZ_NONNULL RzILOpFloat *f, RZ_NONNULL RzILOpBitVector *n) {
|
||||
rz_return_val_if_fail(mode && f && n, NULL);
|
||||
RzILOpArgFloatRMode rmode = float_rmode_dynamic(mode);
|
||||
RzILOpFloat *ret;
|
||||
rz_il_op_new_3(Float, RZ_IL_OP_FROOTN, RzILOpArgsFrootn, frootn, rmode, f, n);
|
||||
return ret;
|
||||
}
|
||||
|
||||
RZ_API RZ_OWN RzILOpFloat *rz_il_op_new_fpown_dyn_rmode(RZ_NONNULL RzILOpBitVector *mode, RZ_NONNULL RzILOpFloat *f, RZ_NONNULL RzILOpBitVector *n) {
|
||||
rz_return_val_if_fail(mode && f && n, NULL);
|
||||
RzILOpArgFloatRMode rmode = float_rmode_dynamic(mode);
|
||||
RzILOpFloat *ret;
|
||||
rz_il_op_new_3(Float, RZ_IL_OP_FPOWN, RzILOpArgsFpown, fpown, rmode, f, n);
|
||||
return ret;
|
||||
}
|
||||
|
||||
RZ_API RZ_OWN RzILOpFloat *rz_il_op_new_fcompound_dyn_rmode(RZ_NONNULL RzILOpBitVector *mode, RZ_NONNULL RzILOpFloat *f, RZ_NONNULL RzILOpBitVector *n) {
|
||||
rz_return_val_if_fail(mode && f && n, NULL);
|
||||
RzILOpArgFloatRMode rmode = float_rmode_dynamic(mode);
|
||||
RzILOpFloat *ret;
|
||||
rz_il_op_new_3(Float, RZ_IL_OP_FCOMPOUND, RzILOpArgsFcompound, fcompound, rmode, f, n);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
@ -1041,6 +1230,37 @@ RZ_API RZ_OWN RzILOpFloat *rz_il_op_new_fcompound(RzFloatRMode rmode, RZ_NONNULL
|
|||
#undef rz_il_op_new_2
|
||||
#undef rz_il_op_new_3
|
||||
|
||||
static bool float_rmode_dup(RzILOpArgFloatRMode *dst, const RzILOpArgFloatRMode *src) {
|
||||
dst->kind = src->kind;
|
||||
switch (src->kind) {
|
||||
case RZ_IL_OP_ARG_FLOAT_RMODE_STATIC:
|
||||
dst->value.static_mode = src->value.static_mode;
|
||||
return true;
|
||||
case RZ_IL_OP_ARG_FLOAT_RMODE_DYNAMIC:
|
||||
if (!src->value.dynamic_mode) {
|
||||
return false;
|
||||
}
|
||||
dst->value.dynamic_mode = rz_il_op_pure_dup(src->value.dynamic_mode);
|
||||
return dst->value.dynamic_mode != NULL;
|
||||
default:
|
||||
rz_warn_if_reached();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static void float_rmode_fini(RzILOpArgFloatRMode *rmode) {
|
||||
switch (rmode->kind) {
|
||||
case RZ_IL_OP_ARG_FLOAT_RMODE_STATIC:
|
||||
break;
|
||||
case RZ_IL_OP_ARG_FLOAT_RMODE_DYNAMIC:
|
||||
rz_il_op_pure_free(rmode->value.dynamic_mode);
|
||||
break;
|
||||
default:
|
||||
rz_warn_if_reached();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Duplicate the given op recursively, for example to reuse it multiple times in another op.
|
||||
*/
|
||||
|
|
@ -1231,24 +1451,33 @@ RZ_API RzILOpPure *rz_il_op_pure_dup(RZ_NONNULL RzILOpPure *op) {
|
|||
DUP_OP1(fabs, f);
|
||||
break;
|
||||
case RZ_IL_OP_FCAST_INT:
|
||||
CONST_CP2(fcast_int, length, mode);
|
||||
DUP_OP1(fcast_int, f);
|
||||
break;
|
||||
case RZ_IL_OP_FCAST_SINT:
|
||||
CONST_CP2(fcast_sint, length, mode);
|
||||
DUP_OP1(fcast_sint, f);
|
||||
CONST_CP1(fcast_int, length);
|
||||
DUP_OP1(fcast_int, f);
|
||||
if (!float_rmode_dup(&r->op.fcast_int.rmode, &op->op.fcast_int.rmode)) {
|
||||
rz_il_op_pure_free(r->op.fcast_int.f);
|
||||
free(r);
|
||||
return NULL;
|
||||
}
|
||||
break;
|
||||
case RZ_IL_OP_FCAST_FLOAT:
|
||||
CONST_CP2(fcast_float, format, mode);
|
||||
DUP_OP1(fcast_float, bv);
|
||||
break;
|
||||
case RZ_IL_OP_FCAST_SFLOAT:
|
||||
CONST_CP2(fcast_sfloat, format, mode);
|
||||
DUP_OP1(fcast_sfloat, bv);
|
||||
CONST_CP1(fcast_float, format);
|
||||
DUP_OP1(fcast_float, bv);
|
||||
if (!float_rmode_dup(&r->op.fcast_float.rmode, &op->op.fcast_float.rmode)) {
|
||||
rz_il_op_pure_free(r->op.fcast_float.bv);
|
||||
free(r);
|
||||
return NULL;
|
||||
}
|
||||
break;
|
||||
case RZ_IL_OP_FCONVERT:
|
||||
CONST_CP2(fconvert, format, mode);
|
||||
CONST_CP1(fconvert, format);
|
||||
DUP_OP1(fconvert, f);
|
||||
if (!float_rmode_dup(&r->op.fconvert.rmode, &op->op.fconvert.rmode)) {
|
||||
rz_il_op_pure_free(r->op.fconvert.f);
|
||||
free(r);
|
||||
return NULL;
|
||||
}
|
||||
break;
|
||||
case RZ_IL_OP_FREQUAL:
|
||||
CONST_CP2(frequal, x, y);
|
||||
|
|
@ -1263,64 +1492,54 @@ RZ_API RzILOpPure *rz_il_op_pure_dup(RZ_NONNULL RzILOpPure *op) {
|
|||
DUP_OP2(forder, x, y);
|
||||
break;
|
||||
case RZ_IL_OP_FROUND:
|
||||
CONST_CP1(fround, rmode);
|
||||
DUP_OP1(fround, f);
|
||||
break;
|
||||
case RZ_IL_OP_FSQRT:
|
||||
CONST_CP1(fsqrt, rmode);
|
||||
DUP_OP1(fsqrt, f);
|
||||
break;
|
||||
case RZ_IL_OP_FRSQRT:
|
||||
CONST_CP1(frsqrt, rmode);
|
||||
DUP_OP1(frsqrt, f);
|
||||
DUP_OP1(fround, f);
|
||||
if (!float_rmode_dup(&r->op.fround.rmode, &op->op.fround.rmode)) {
|
||||
rz_il_op_pure_free(r->op.fround.f);
|
||||
free(r);
|
||||
return NULL;
|
||||
}
|
||||
break;
|
||||
case RZ_IL_OP_FEXCEPT:
|
||||
CONST_CP1(fexcept, e);
|
||||
DUP_OP1(fexcept, x);
|
||||
break;
|
||||
case RZ_IL_OP_FADD:
|
||||
CONST_CP1(fadd, rmode);
|
||||
DUP_OP2(fadd, x, y);
|
||||
break;
|
||||
case RZ_IL_OP_FSUB:
|
||||
CONST_CP1(fsub, rmode);
|
||||
DUP_OP2(fsub, x, y);
|
||||
break;
|
||||
case RZ_IL_OP_FMUL:
|
||||
CONST_CP1(fmul, rmode);
|
||||
DUP_OP2(fmul, x, y);
|
||||
break;
|
||||
case RZ_IL_OP_FDIV:
|
||||
CONST_CP1(fdiv, rmode);
|
||||
DUP_OP2(fdiv, x, y);
|
||||
break;
|
||||
case RZ_IL_OP_FMOD:
|
||||
CONST_CP1(fmod, rmode);
|
||||
DUP_OP2(fmod, x, y);
|
||||
break;
|
||||
case RZ_IL_OP_FHYPOT:
|
||||
CONST_CP1(fhypot, rmode);
|
||||
DUP_OP2(fhypot, x, y);
|
||||
break;
|
||||
case RZ_IL_OP_FPOW:
|
||||
CONST_CP1(fpow, rmode);
|
||||
DUP_OP2(fpow, x, y);
|
||||
DUP_OP2(fadd, x, y);
|
||||
if (!float_rmode_dup(&r->op.fadd.rmode, &op->op.fadd.rmode)) {
|
||||
rz_il_op_pure_free(r->op.fadd.x);
|
||||
rz_il_op_pure_free(r->op.fadd.y);
|
||||
free(r);
|
||||
return NULL;
|
||||
}
|
||||
break;
|
||||
case RZ_IL_OP_FMAD:
|
||||
CONST_CP1(fmad, rmode);
|
||||
DUP_OP3(fmad, x, y, z);
|
||||
if (!float_rmode_dup(&r->op.fmad.rmode, &op->op.fmad.rmode)) {
|
||||
rz_il_op_pure_free(r->op.fmad.x);
|
||||
rz_il_op_pure_free(r->op.fmad.y);
|
||||
rz_il_op_pure_free(r->op.fmad.z);
|
||||
free(r);
|
||||
return NULL;
|
||||
}
|
||||
break;
|
||||
case RZ_IL_OP_FROOTN:
|
||||
CONST_CP1(frootn, rmode);
|
||||
DUP_OP2(frootn, f, n);
|
||||
break;
|
||||
case RZ_IL_OP_FPOWN:
|
||||
CONST_CP1(fpown, rmode);
|
||||
DUP_OP2(fpown, f, n);
|
||||
break;
|
||||
case RZ_IL_OP_FCOMPOUND:
|
||||
CONST_CP1(fcompound, rmode);
|
||||
DUP_OP2(fcompound, f, n);
|
||||
if (!float_rmode_dup(&r->op.fcompound.rmode, &op->op.fcompound.rmode)) {
|
||||
rz_il_op_pure_free(r->op.fcompound.f);
|
||||
rz_il_op_pure_free(r->op.fcompound.n);
|
||||
free(r);
|
||||
return NULL;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
rz_warn_if_reached();
|
||||
|
|
@ -1464,11 +1683,16 @@ RZ_API void rz_il_op_pure_free(RZ_NULLABLE RzILOpPure *op) {
|
|||
break;
|
||||
case RZ_IL_OP_FCAST_INT:
|
||||
case RZ_IL_OP_FCAST_SINT:
|
||||
float_rmode_fini(&op->op.fcast_int.rmode);
|
||||
rz_il_op_free_1(pure, fcast_int, f);
|
||||
break;
|
||||
case RZ_IL_OP_FCONVERT:
|
||||
float_rmode_fini(&op->op.fconvert.rmode);
|
||||
rz_il_op_free_1(pure, fconvert, f);
|
||||
break;
|
||||
case RZ_IL_OP_FCAST_FLOAT:
|
||||
case RZ_IL_OP_FCAST_SFLOAT:
|
||||
float_rmode_fini(&op->op.fcast_float.rmode);
|
||||
rz_il_op_free_1(pure, fcast_sfloat, bv);
|
||||
break;
|
||||
case RZ_IL_OP_FREQUAL:
|
||||
|
|
@ -1479,6 +1703,7 @@ RZ_API void rz_il_op_pure_free(RZ_NULLABLE RzILOpPure *op) {
|
|||
case RZ_IL_OP_FROUND:
|
||||
case RZ_IL_OP_FSQRT:
|
||||
case RZ_IL_OP_FRSQRT:
|
||||
float_rmode_fini(&op->op.fround.rmode);
|
||||
rz_il_op_free_1(pure, fsqrt, f);
|
||||
break;
|
||||
case RZ_IL_OP_FEXCEPT:
|
||||
|
|
@ -1491,14 +1716,17 @@ RZ_API void rz_il_op_pure_free(RZ_NULLABLE RzILOpPure *op) {
|
|||
case RZ_IL_OP_FMOD:
|
||||
case RZ_IL_OP_FHYPOT:
|
||||
case RZ_IL_OP_FPOW:
|
||||
rz_il_op_free_2(pure, fpow, x, y);
|
||||
float_rmode_fini(&op->op.fadd.rmode);
|
||||
rz_il_op_free_2(pure, fadd, x, y);
|
||||
break;
|
||||
case RZ_IL_OP_FMAD:
|
||||
float_rmode_fini(&op->op.fmad.rmode);
|
||||
rz_il_op_free_3(pure, fmad, x, y, z);
|
||||
break;
|
||||
case RZ_IL_OP_FROOTN:
|
||||
case RZ_IL_OP_FPOWN:
|
||||
case RZ_IL_OP_FCOMPOUND:
|
||||
float_rmode_fini(&op->op.fcompound.rmode);
|
||||
rz_il_op_free_2(pure, fcompound, f, n);
|
||||
break;
|
||||
default:
|
||||
|
|
|
|||
|
|
@ -562,10 +562,29 @@ VALIDATOR_PURE(float_uop) {
|
|||
return true;
|
||||
}
|
||||
|
||||
static bool is_valid_float_rmode(RzFloatRMode rmode) {
|
||||
switch (rmode) {
|
||||
case RZ_FLOAT_RMODE_RNE:
|
||||
case RZ_FLOAT_RMODE_RNA:
|
||||
case RZ_FLOAT_RMODE_RTP:
|
||||
case RZ_FLOAT_RMODE_RTN:
|
||||
case RZ_FLOAT_RMODE_RTZ:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static bool validate_float_rmode(const RzILOpArgFloatRMode *rmode, RzILOpPureCode code,
|
||||
RzStrBuf *report_builder, const LocalContext *ctx, LocalPureVar *local_pure_var_stack);
|
||||
|
||||
VALIDATOR_PURE(fcast_to_int) {
|
||||
RzILOpArgsFCastint *args = &op->op.fcast_int;
|
||||
RzILSortPure sort;
|
||||
|
||||
if (!validate_float_rmode(&args->rmode, op->code, report_builder, ctx, local_pure_var_stack)) {
|
||||
return false;
|
||||
}
|
||||
VALIDATOR_DESCEND(args->f, &sort);
|
||||
VALIDATOR_ASSERT(sort.type == RZ_IL_TYPE_PURE_FLOAT, "operand of %s op is not a float.\n", rz_il_op_pure_code_stringify(op->code));
|
||||
VALIDATOR_ASSERT(args->length != 0, "length of casted bitvector should not be 0.\n");
|
||||
|
|
@ -577,6 +596,9 @@ VALIDATOR_PURE(icast_to_float) {
|
|||
RzILOpArgsFCastUFloat *args = &op->op.fcast_float;
|
||||
RzILSortPure sort;
|
||||
|
||||
if (!validate_float_rmode(&args->rmode, op->code, report_builder, ctx, local_pure_var_stack)) {
|
||||
return false;
|
||||
}
|
||||
VALIDATOR_DESCEND(args->bv, &sort);
|
||||
VALIDATOR_ASSERT(sort.type == RZ_IL_TYPE_PURE_BITVECTOR, "operand of %s op is not a bitvector.\n", rz_il_op_pure_code_stringify(op->code));
|
||||
|
||||
|
|
@ -584,10 +606,43 @@ VALIDATOR_PURE(icast_to_float) {
|
|||
return true;
|
||||
}
|
||||
|
||||
static bool validate_float_rmode(const RzILOpArgFloatRMode *rmode, RzILOpPureCode code,
|
||||
RzStrBuf *report_builder, const LocalContext *ctx, LocalPureVar *local_pure_var_stack) {
|
||||
switch (rmode->kind) {
|
||||
case RZ_IL_OP_ARG_FLOAT_RMODE_STATIC:
|
||||
VALIDATOR_ASSERT(is_valid_float_rmode(rmode->value.static_mode),
|
||||
"Static rounding-mode value of %s op is invalid.\n", rz_il_op_pure_code_stringify(code));
|
||||
return true;
|
||||
case RZ_IL_OP_ARG_FLOAT_RMODE_DYNAMIC: {
|
||||
VALIDATOR_ASSERT(rmode->value.dynamic_mode,
|
||||
"Rounding-mode operand of %s op is NULL.\n", rz_il_op_pure_code_stringify(code));
|
||||
RzILSortPure sort;
|
||||
if (!validate_pure(rmode->value.dynamic_mode, &sort, report_builder, ctx, local_pure_var_stack)) {
|
||||
return false;
|
||||
}
|
||||
VALIDATOR_ASSERT(sort.type == RZ_IL_TYPE_PURE_BITVECTOR,
|
||||
"Rounding-mode operand of %s op is not a bitvector.\n", rz_il_op_pure_code_stringify(code));
|
||||
VALIDATOR_ASSERT(sort.props.bv.length == 32,
|
||||
"Rounding-mode operand of %s op must be 32 bits, got %u.\n",
|
||||
rz_il_op_pure_code_stringify(code), (unsigned int)sort.props.bv.length);
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
VALIDATOR_ASSERT(false, "Rounding-mode argument kind of %s op is invalid.\n",
|
||||
rz_il_op_pure_code_stringify(code));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
VALIDATOR_PURE(fconvert) {
|
||||
RzILOpArgsFconvert *args = &op->op.fconvert;
|
||||
RzILSortPure sort;
|
||||
|
||||
if (!validate_float_rmode(&args->rmode, op->code, report_builder, ctx, local_pure_var_stack)) {
|
||||
return false;
|
||||
}
|
||||
VALIDATOR_ASSERT(args->format >= RZ_FLOAT_IEEE754_BIN_32 && args->format <= RZ_FLOAT_IEEE754_BIN_16,
|
||||
"Target format of %s op is invalid.\n", rz_il_op_pure_code_stringify(op->code));
|
||||
VALIDATOR_DESCEND(args->f, &sort);
|
||||
VALIDATOR_ASSERT(sort.type == RZ_IL_TYPE_PURE_FLOAT, "operand of %s op is not a float.\n", rz_il_op_pure_code_stringify(op->code));
|
||||
|
||||
|
|
@ -606,10 +661,16 @@ VALIDATOR_PURE(forder) {
|
|||
VALIDATOR_ASSERT(sy.type == RZ_IL_TYPE_PURE_FLOAT, "Right operand of %s op is not a float.\n", rz_il_op_pure_code_stringify(op->code));
|
||||
|
||||
// flatten validator assert
|
||||
VALIDATOR_ASSERT(sx.props.f.format == sy.props.f.format, "Op %s formats of left operand (%s) and right operand (%s) do not agree.\n",
|
||||
rz_il_op_pure_code_stringify(op->code),
|
||||
rz_il_sort_pure_stringify(sx),
|
||||
rz_il_sort_pure_stringify(sy));
|
||||
if (sx.props.f.format != sy.props.f.format) {
|
||||
rz_warn_if_reached();
|
||||
char *sx_s = rz_il_sort_pure_stringify(sx);
|
||||
char *sy_s = rz_il_sort_pure_stringify(sy);
|
||||
rz_strbuf_appendf(report_builder, "Op %s formats of left operand (%s) and right operand (%s) do not agree.\n",
|
||||
rz_il_op_pure_code_stringify(op->code), sx_s, sy_s);
|
||||
free(sx_s);
|
||||
free(sy_s);
|
||||
return false;
|
||||
}
|
||||
|
||||
*sort_out = rz_il_sort_pure_bool();
|
||||
return true;
|
||||
|
|
@ -624,6 +685,9 @@ VALIDATOR_PURE(float_uop_with_round) {
|
|||
RzILOpArgsFround *args = &op->op.fround;
|
||||
RzILSortPure sort;
|
||||
|
||||
if (!validate_float_rmode(&args->rmode, op->code, report_builder, ctx, local_pure_var_stack)) {
|
||||
return false;
|
||||
}
|
||||
VALIDATOR_DESCEND(args->f, &sort);
|
||||
VALIDATOR_ASSERT(sort.type == RZ_IL_TYPE_PURE_FLOAT, "operand of %s op is not a float.\n", rz_il_op_pure_code_stringify(op->code));
|
||||
|
||||
|
|
@ -646,16 +710,24 @@ VALIDATOR_PURE(float_binop_with_round) {
|
|||
RzILOpArgsFadd *args = &op->op.fadd;
|
||||
RzILSortPure sx, sy;
|
||||
|
||||
if (!validate_float_rmode(&args->rmode, op->code, report_builder, ctx, local_pure_var_stack)) {
|
||||
return false;
|
||||
}
|
||||
VALIDATOR_DESCEND(args->x, &sx);
|
||||
VALIDATOR_ASSERT(sx.type == RZ_IL_TYPE_PURE_FLOAT, "Left operand of %s op is not a float.\n", rz_il_op_pure_code_stringify(op->code));
|
||||
VALIDATOR_DESCEND(args->y, &sy);
|
||||
VALIDATOR_ASSERT(sy.type == RZ_IL_TYPE_PURE_FLOAT, "Right operand of %s op is not a float.\n", rz_il_op_pure_code_stringify(op->code));
|
||||
|
||||
// flatten validator assert
|
||||
VALIDATOR_ASSERT(sx.props.f.format == sy.props.f.format, "Op %s formats of left operand (%s) and right operand (%s) do not agree.\n",
|
||||
rz_il_op_pure_code_stringify(op->code),
|
||||
rz_il_sort_pure_stringify(sx),
|
||||
rz_il_sort_pure_stringify(sy));
|
||||
if (sx.props.f.format != sy.props.f.format) {
|
||||
char *sx_s = rz_il_sort_pure_stringify(sx);
|
||||
char *sy_s = rz_il_sort_pure_stringify(sy);
|
||||
rz_strbuf_appendf(report_builder, "Op %s formats of left operand (%s) and right operand (%s) do not agree.\n",
|
||||
rz_il_op_pure_code_stringify(op->code), sx_s, sy_s);
|
||||
free(sx_s);
|
||||
free(sy_s);
|
||||
return false;
|
||||
}
|
||||
|
||||
*sort_out = sx;
|
||||
return true;
|
||||
|
|
@ -665,6 +737,9 @@ VALIDATOR_PURE(float_terop_with_round) {
|
|||
RzILOpArgsFmad *args = &op->op.fmad;
|
||||
RzILSortPure sx, sy, sz;
|
||||
|
||||
if (!validate_float_rmode(&args->rmode, op->code, report_builder, ctx, local_pure_var_stack)) {
|
||||
return false;
|
||||
}
|
||||
VALIDATOR_DESCEND(args->x, &sx);
|
||||
VALIDATOR_ASSERT(sx.type == RZ_IL_TYPE_PURE_FLOAT, "1st operand of %s op is not a float.\n", rz_il_op_pure_code_stringify(op->code));
|
||||
|
||||
|
|
@ -674,12 +749,17 @@ VALIDATOR_PURE(float_terop_with_round) {
|
|||
VALIDATOR_DESCEND(args->z, &sz);
|
||||
VALIDATOR_ASSERT(sz.type == RZ_IL_TYPE_PURE_FLOAT, "3rd operand of %s op is not a float.\n", rz_il_op_pure_code_stringify(op->code));
|
||||
|
||||
VALIDATOR_ASSERT((sx.props.f.format == sy.props.f.format) && (sx.props.f.format == sz.props.f.format),
|
||||
"types of operand in op %s do not agree: operand1 (%s) operand2 (%s) operand3 (%s)",
|
||||
rz_il_op_pure_code_stringify(op->code),
|
||||
rz_il_sort_pure_stringify(sx),
|
||||
rz_il_sort_pure_stringify(sy),
|
||||
rz_il_sort_pure_stringify(sz));
|
||||
if ((sx.props.f.format != sy.props.f.format) || (sx.props.f.format != sz.props.f.format)) {
|
||||
char *sx_s = rz_il_sort_pure_stringify(sx);
|
||||
char *sy_s = rz_il_sort_pure_stringify(sy);
|
||||
char *sz_s = rz_il_sort_pure_stringify(sz);
|
||||
rz_strbuf_appendf(report_builder, "types of operand in op %s do not agree: operand1 (%s) operand2 (%s) operand3 (%s)",
|
||||
rz_il_op_pure_code_stringify(op->code), sx_s, sy_s, sz_s);
|
||||
free(sx_s);
|
||||
free(sy_s);
|
||||
free(sz_s);
|
||||
return false;
|
||||
}
|
||||
|
||||
*sort_out = sx;
|
||||
return true;
|
||||
|
|
@ -689,6 +769,9 @@ VALIDATOR_PURE(float_hybridop_with_round) {
|
|||
RzILOpArgsFcompound *args = &op->op.fcompound;
|
||||
RzILSortPure fs, bs;
|
||||
|
||||
if (!validate_float_rmode(&args->rmode, op->code, report_builder, ctx, local_pure_var_stack)) {
|
||||
return false;
|
||||
}
|
||||
VALIDATOR_DESCEND(args->f, &fs);
|
||||
VALIDATOR_ASSERT(fs.type == RZ_IL_TYPE_PURE_FLOAT, "1st operand of %s op is not a float.\n", rz_il_op_pure_code_stringify(op->code));
|
||||
VALIDATOR_DESCEND(args->n, &bs);
|
||||
|
|
|
|||
|
|
@ -359,6 +359,23 @@ static const char *pure_type_name(RzILTypePure type) {
|
|||
}
|
||||
}
|
||||
|
||||
static void pure_result_free(void *res, RzILTypePure type) {
|
||||
switch (type) {
|
||||
case RZ_IL_TYPE_PURE_BITVECTOR:
|
||||
rz_bv_free(res);
|
||||
break;
|
||||
case RZ_IL_TYPE_PURE_BOOL:
|
||||
rz_il_bool_free(res);
|
||||
break;
|
||||
case RZ_IL_TYPE_PURE_FLOAT:
|
||||
rz_float_free(res);
|
||||
break;
|
||||
default:
|
||||
free(res);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Evaluate the given pure op, asserting it returns a bitvector.
|
||||
* \return value in bitvector, or NULL if an error occurred (e.g. the op returned some other type)
|
||||
|
|
@ -374,6 +391,7 @@ RZ_API RZ_NULLABLE RZ_OWN RzBitVector *rz_il_evaluate_bitv(RZ_NONNULL RzILVM *vm
|
|||
}
|
||||
if (type != RZ_IL_TYPE_PURE_BITVECTOR) {
|
||||
RZ_LOG_ERROR("RzIL: type error: expected bitvector, got %s\n", pure_type_name(type));
|
||||
pure_result_free(res, type);
|
||||
return NULL;
|
||||
}
|
||||
return res;
|
||||
|
|
@ -394,6 +412,7 @@ RZ_API RZ_NULLABLE RZ_OWN RzILBool *rz_il_evaluate_bool(RZ_NONNULL RzILVM *vm, R
|
|||
}
|
||||
if (type != RZ_IL_TYPE_PURE_BOOL) {
|
||||
RZ_LOG_ERROR("RzIL: type error: expected bool, got %s\n", pure_type_name(type));
|
||||
pure_result_free(res, type);
|
||||
return NULL;
|
||||
}
|
||||
return res;
|
||||
|
|
@ -458,6 +477,7 @@ RZ_API RZ_NULLABLE RZ_OWN RzFloat *rz_il_evaluate_float(RZ_NONNULL RzILVM *vm, R
|
|||
}
|
||||
if (type != RZ_IL_TYPE_PURE_FLOAT) {
|
||||
RZ_LOG_ERROR("RzIL: type error: expected float, got %s\n", pure_type_name(type));
|
||||
pure_result_free(res, type);
|
||||
return NULL;
|
||||
}
|
||||
return res;
|
||||
|
|
|
|||
|
|
@ -161,16 +161,21 @@ void *rz_il_handler_fabs(RzILVM *vm, RzILOpPure *op, RzILTypePure *type) {
|
|||
return ret;
|
||||
}
|
||||
|
||||
static bool resolve_rmode(RzILVM *vm, RzILOpPureCode code, const RzILOpArgFloatRMode *arg, RzFloatRMode *out);
|
||||
|
||||
void *rz_il_handler_fcast_int(RzILVM *vm, RzILOpPure *op, RzILTypePure *type) {
|
||||
rz_return_val_if_fail(vm && op && type, NULL);
|
||||
|
||||
RzILOpArgsFCastint cast_int = op->op.fcast_int;
|
||||
RzFloatRMode mode;
|
||||
if (!resolve_rmode(vm, op->code, &cast_int.rmode, &mode)) {
|
||||
return NULL;
|
||||
}
|
||||
RzFloat *f = rz_il_evaluate_float(vm, cast_int.f);
|
||||
if (!f) {
|
||||
return NULL;
|
||||
}
|
||||
ut32 length = cast_int.length;
|
||||
RzFloatRMode mode = cast_int.mode;
|
||||
RzBitVector *ret = rz_float_cast_int(f, length, mode);
|
||||
|
||||
rz_float_free(f);
|
||||
|
|
@ -183,12 +188,15 @@ void *rz_il_handler_fcast_sint(RzILVM *vm, RzILOpPure *op, RzILTypePure *type) {
|
|||
rz_return_val_if_fail(vm && op && type, NULL);
|
||||
|
||||
RzILOpArgsFCastint cast_sint = op->op.fcast_sint;
|
||||
RzFloatRMode mode;
|
||||
if (!resolve_rmode(vm, op->code, &cast_sint.rmode, &mode)) {
|
||||
return NULL;
|
||||
}
|
||||
RzFloat *f = rz_il_evaluate_float(vm, cast_sint.f);
|
||||
if (!f) {
|
||||
return NULL;
|
||||
}
|
||||
ut32 length = cast_sint.length;
|
||||
RzFloatRMode mode = cast_sint.mode;
|
||||
RzBitVector *ret = rz_float_cast_sint(f, length, mode);
|
||||
|
||||
rz_float_free(f);
|
||||
|
|
@ -201,9 +209,15 @@ void *rz_il_handler_fcast_float(RzILVM *vm, RzILOpPure *op, RzILTypePure *type)
|
|||
rz_return_val_if_fail(vm && op && type, NULL);
|
||||
|
||||
RzILOpArgsFCastUFloat cast = op->op.fcast_float;
|
||||
RzFloatRMode mode;
|
||||
if (!resolve_rmode(vm, op->code, &cast.rmode, &mode)) {
|
||||
return NULL;
|
||||
}
|
||||
RzBitVector *bv = rz_il_evaluate_bitv(vm, cast.bv);
|
||||
if (!bv) {
|
||||
return NULL;
|
||||
}
|
||||
RzFloatFormat format = cast.format;
|
||||
RzFloatRMode mode = cast.mode;
|
||||
RzFloat *ret = rz_float_cast_float(bv, format, mode);
|
||||
|
||||
rz_bv_free(bv);
|
||||
|
|
@ -216,9 +230,15 @@ void *rz_il_handler_fcast_sfloat(RzILVM *vm, RzILOpPure *op, RzILTypePure *type)
|
|||
rz_return_val_if_fail(vm && op && type, NULL);
|
||||
|
||||
RzILOpArgsFCastSFloat cast = op->op.fcast_sfloat;
|
||||
RzFloatRMode mode;
|
||||
if (!resolve_rmode(vm, op->code, &cast.rmode, &mode)) {
|
||||
return NULL;
|
||||
}
|
||||
RzBitVector *bv = rz_il_evaluate_bitv(vm, cast.bv);
|
||||
if (!bv) {
|
||||
return NULL;
|
||||
}
|
||||
RzFloatFormat format = cast.format;
|
||||
RzFloatRMode mode = cast.mode;
|
||||
RzFloat *ret = rz_float_cast_sfloat(bv, format, mode);
|
||||
|
||||
rz_bv_free(bv);
|
||||
|
|
@ -227,16 +247,67 @@ void *rz_il_handler_fcast_sfloat(RzILVM *vm, RzILOpPure *op, RzILTypePure *type)
|
|||
return ret;
|
||||
}
|
||||
|
||||
static RzFloatRMode normalize_rmode(ut64 value, RzILOpPureCode code) {
|
||||
switch (value) {
|
||||
case RZ_FLOAT_RMODE_RNE:
|
||||
case RZ_FLOAT_RMODE_RNA:
|
||||
case RZ_FLOAT_RMODE_RTP:
|
||||
case RZ_FLOAT_RMODE_RTN:
|
||||
case RZ_FLOAT_RMODE_RTZ:
|
||||
return (RzFloatRMode)value;
|
||||
default:
|
||||
RZ_LOG_WARN("IL: %s got an invalid rounding-mode value 0x%" PFMT64x "; falling back to RNE\n",
|
||||
rz_il_op_pure_code_stringify(code), value);
|
||||
return RZ_FLOAT_RMODE_RNE;
|
||||
}
|
||||
}
|
||||
|
||||
static bool resolve_rmode(RzILVM *vm, RzILOpPureCode code, const RzILOpArgFloatRMode *arg, RzFloatRMode *out) {
|
||||
switch (arg->kind) {
|
||||
case RZ_IL_OP_ARG_FLOAT_RMODE_STATIC:
|
||||
*out = normalize_rmode((ut64)arg->value.static_mode, code);
|
||||
return true;
|
||||
case RZ_IL_OP_ARG_FLOAT_RMODE_DYNAMIC: {
|
||||
if (!arg->value.dynamic_mode) {
|
||||
rz_warn_if_reached();
|
||||
return false;
|
||||
}
|
||||
RzBitVector *value = rz_il_evaluate_bitv(vm, arg->value.dynamic_mode);
|
||||
if (!value) {
|
||||
return false;
|
||||
}
|
||||
ut32 length = rz_bv_len(value);
|
||||
if (length != 32) {
|
||||
RZ_LOG_ERROR("IL: rounding-mode operand of %s must be 32 bits, got %u\n",
|
||||
rz_il_op_pure_code_stringify(code), (unsigned int)length);
|
||||
rz_bv_free(value);
|
||||
return false;
|
||||
}
|
||||
*out = normalize_rmode(rz_bv_to_ut64(value), code);
|
||||
rz_bv_free(value);
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
rz_warn_if_reached();
|
||||
RZ_LOG_ERROR("IL: %s has an invalid rounding-mode argument kind %u\n",
|
||||
rz_il_op_pure_code_stringify(code), (unsigned int)arg->kind);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void *rz_il_handler_fconvert(RzILVM *vm, RzILOpPure *op, RzILTypePure *type) {
|
||||
rz_return_val_if_fail(vm && op && type, NULL);
|
||||
|
||||
RzILOpArgsFconvert convert = op->op.fconvert;
|
||||
RzFloatRMode mode;
|
||||
if (!resolve_rmode(vm, op->code, &convert.rmode, &mode)) {
|
||||
return NULL;
|
||||
}
|
||||
RzFloat *f = rz_il_evaluate_float(vm, convert.f);
|
||||
if (!f) {
|
||||
return NULL;
|
||||
}
|
||||
RzFloatFormat format = convert.format;
|
||||
RzFloatRMode mode = convert.mode;
|
||||
RzFloat *ret = rz_float_convert(f, format, mode);
|
||||
|
||||
rz_float_free(f);
|
||||
|
|
@ -301,16 +372,77 @@ void *rz_il_handler_forder(RzILVM *vm, RzILOpPure *op, RzILTypePure *type) {
|
|||
return order;
|
||||
}
|
||||
|
||||
static void float_merge_operand_exceptions(RzFloat *ret, const RzFloat *x, const RzFloat *y, const RzFloat *z);
|
||||
|
||||
/* RzIL float operations are pure, so binary80 results must not depend on the
|
||||
* caller's thread-local SoftFloat precision. */
|
||||
static bool float_full_precision_begin(bool has_binary80_operand, RzFloatRPrecision *saved) {
|
||||
*saved = RZ_FLOAT_RPREC_UNK;
|
||||
if (!has_binary80_operand) {
|
||||
return true;
|
||||
}
|
||||
*saved = rz_float_ext80_get_rounding_precision();
|
||||
if (*saved == RZ_FLOAT_RPREC_UNK || !rz_float_ext80_set_rounding_precision(RZ_FLOAT_RPREC_80)) {
|
||||
rz_warn_if_reached();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static void float_full_precision_end(RzFloatRPrecision saved) {
|
||||
if (saved != RZ_FLOAT_RPREC_UNK) {
|
||||
rz_warn_if_fail(rz_float_ext80_set_rounding_precision(saved));
|
||||
}
|
||||
}
|
||||
|
||||
static RzFloat *float_unop_full_precision(RzFloat *x, RzFloatRMode mode, RzFloat *(*operation)(RzFloat *, RzFloatRMode)) {
|
||||
RzFloatRPrecision saved;
|
||||
if (!float_full_precision_begin(x && x->r == RZ_FLOAT_IEEE754_BIN_80, &saved)) {
|
||||
return NULL;
|
||||
}
|
||||
RzFloat *ret = operation(x, mode);
|
||||
float_full_precision_end(saved);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static RzFloat *float_binop_full_precision(RzFloat *x, RzFloat *y, RzFloatRMode mode, RzFloat *(*operation)(RzFloat *, RzFloat *, RzFloatRMode)) {
|
||||
RzFloatRPrecision saved;
|
||||
bool has_binary80_operand = (x && x->r == RZ_FLOAT_IEEE754_BIN_80) || (y && y->r == RZ_FLOAT_IEEE754_BIN_80);
|
||||
if (!float_full_precision_begin(has_binary80_operand, &saved)) {
|
||||
return NULL;
|
||||
}
|
||||
RzFloat *ret = operation(x, y, mode);
|
||||
float_full_precision_end(saved);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static RzFloat *float_terop_full_precision(RzFloat *x, RzFloat *y, RzFloat *z, RzFloatRMode mode,
|
||||
RzFloat *(*operation)(RzFloat *, RzFloat *, RzFloat *, RzFloatRMode)) {
|
||||
RzFloatRPrecision saved;
|
||||
bool has_binary80_operand = (x && x->r == RZ_FLOAT_IEEE754_BIN_80) ||
|
||||
(y && y->r == RZ_FLOAT_IEEE754_BIN_80) || (z && z->r == RZ_FLOAT_IEEE754_BIN_80);
|
||||
if (!float_full_precision_begin(has_binary80_operand, &saved)) {
|
||||
return NULL;
|
||||
}
|
||||
RzFloat *ret = operation(x, y, z, mode);
|
||||
float_full_precision_end(saved);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void *rz_il_handler_fround(RzILVM *vm, RzILOpPure *op, RzILTypePure *type) {
|
||||
rz_return_val_if_fail(vm && op && type, NULL);
|
||||
|
||||
RzILOpArgsFround fround = op->op.fround;
|
||||
RzFloatRMode mode;
|
||||
if (!resolve_rmode(vm, op->code, &fround.rmode, &mode)) {
|
||||
return NULL;
|
||||
}
|
||||
RzFloat *f = rz_il_evaluate_float(vm, fround.f);
|
||||
if (!f) {
|
||||
return NULL;
|
||||
}
|
||||
RzFloatRMode mode = fround.rmode;
|
||||
RzFloat *ret = rz_float_round_to_integral(f, mode);
|
||||
float_merge_operand_exceptions(ret, f, NULL, NULL);
|
||||
|
||||
rz_float_free(f);
|
||||
|
||||
|
|
@ -321,9 +453,16 @@ void *rz_il_handler_fround(RzILVM *vm, RzILOpPure *op, RzILTypePure *type) {
|
|||
void *rz_il_handler_fsqrt(RzILVM *vm, RzILOpPure *op, RzILTypePure *type) {
|
||||
rz_return_val_if_fail(vm && op && type, NULL);
|
||||
RzILOpArgsFsqrt sqrt = op->op.fsqrt;
|
||||
RzFloatRMode mode;
|
||||
if (!resolve_rmode(vm, op->code, &sqrt.rmode, &mode)) {
|
||||
return NULL;
|
||||
}
|
||||
RzFloat *n = rz_il_evaluate_float(vm, sqrt.f);
|
||||
RzFloatRMode mode = sqrt.rmode;
|
||||
RzFloat *ret = rz_float_sqrt(n, mode);
|
||||
if (!n) {
|
||||
return NULL;
|
||||
}
|
||||
RzFloat *ret = float_unop_full_precision(n, mode, rz_float_sqrt);
|
||||
float_merge_operand_exceptions(ret, n, NULL, NULL);
|
||||
|
||||
rz_float_free(n);
|
||||
|
||||
|
|
@ -340,29 +479,33 @@ void *rz_il_handler_fexcept(RzILVM *vm, RzILOpPure *op, RzILTypePure *type) {
|
|||
}
|
||||
|
||||
bool e = false;
|
||||
RzILEventException exception = RZ_IL_EVENT_EXC_NONE;
|
||||
switch (args.e) {
|
||||
case RZ_FLOAT_E_DIV_ZERO:
|
||||
e = n->exception & RZ_FLOAT_E_DIV_ZERO;
|
||||
rz_il_vm_event_add(vm, rz_il_event_exception_new(RZ_IL_EVENT_EXC_FP_DIV_ZERO));
|
||||
exception = RZ_IL_EVENT_EXC_FP_DIV_ZERO;
|
||||
break;
|
||||
case RZ_FLOAT_E_OVERFLOW:
|
||||
e = n->exception & RZ_FLOAT_E_OVERFLOW;
|
||||
rz_il_vm_event_add(vm, rz_il_event_exception_new(RZ_IL_EVENT_EXC_FP_OVERFLOW));
|
||||
exception = RZ_IL_EVENT_EXC_FP_OVERFLOW;
|
||||
break;
|
||||
case RZ_FLOAT_E_UNDERFLOW:
|
||||
e = n->exception & RZ_FLOAT_E_UNDERFLOW;
|
||||
rz_il_vm_event_add(vm, rz_il_event_exception_new(RZ_IL_EVENT_EXC_FP_UNDERFLOW));
|
||||
exception = RZ_IL_EVENT_EXC_FP_UNDERFLOW;
|
||||
break;
|
||||
case RZ_FLOAT_E_INEXACT:
|
||||
e = n->exception & RZ_FLOAT_E_INEXACT;
|
||||
rz_il_vm_event_add(vm, rz_il_event_exception_new(RZ_IL_EVENT_EXC_FP_INEXACT));
|
||||
exception = RZ_IL_EVENT_EXC_FP_INEXACT;
|
||||
break;
|
||||
case RZ_FLOAT_E_INVALID_OP:
|
||||
e = n->exception & RZ_FLOAT_E_INVALID_OP;
|
||||
rz_il_vm_event_add(vm, rz_il_event_exception_new(RZ_IL_EVENT_EXC_FP_INVALID_OP));
|
||||
exception = RZ_IL_EVENT_EXC_FP_INVALID_OP;
|
||||
break;
|
||||
default:;
|
||||
}
|
||||
if (e) {
|
||||
rz_il_vm_event_add(vm, rz_il_event_exception_new(exception));
|
||||
}
|
||||
|
||||
RzILBool *ret = rz_il_bool_new(e);
|
||||
rz_float_free(n);
|
||||
|
|
@ -377,79 +520,68 @@ void *rz_il_handler_frsqrt(RzILVM *vm, RzILOpPure *op, RzILTypePure *type) {
|
|||
return NULL;
|
||||
}
|
||||
|
||||
void *rz_il_handler_fadd(RzILVM *vm, RzILOpPure *op, RzILTypePure *type) {
|
||||
rz_return_val_if_fail(vm && op && type, NULL);
|
||||
RzILOpArgsFadd fadd = op->op.fadd;
|
||||
RzFloat *x = rz_il_evaluate_float(vm, fadd.x);
|
||||
RzFloat *y = rz_il_evaluate_float(vm, fadd.y);
|
||||
RzFloatRMode mode = fadd.rmode;
|
||||
RzFloat *ret = rz_float_add(x, y, mode);
|
||||
static void float_merge_operand_exceptions(RzFloat *ret, const RzFloat *x, const RzFloat *y, const RzFloat *z) {
|
||||
if (!ret) {
|
||||
return;
|
||||
}
|
||||
if (x) {
|
||||
ret->exception |= x->exception;
|
||||
}
|
||||
if (y) {
|
||||
ret->exception |= y->exception;
|
||||
}
|
||||
if (z) {
|
||||
ret->exception |= z->exception;
|
||||
}
|
||||
}
|
||||
|
||||
static void *handle_float_binop(RzILVM *vm, RzILOpPure *op, RzILTypePure *type,
|
||||
const RzILOpArgsFloatAlgBinop *args,
|
||||
RzFloat *(*operation)(RzFloat *, RzFloat *, RzFloatRMode)) {
|
||||
RzFloatRMode mode;
|
||||
if (!resolve_rmode(vm, op->code, &args->rmode, &mode)) {
|
||||
return NULL;
|
||||
}
|
||||
RzFloat *x = rz_il_evaluate_float(vm, args->x);
|
||||
if (!x) {
|
||||
return NULL;
|
||||
}
|
||||
RzFloat *y = rz_il_evaluate_float(vm, args->y);
|
||||
if (!y) {
|
||||
rz_float_free(x);
|
||||
return NULL;
|
||||
}
|
||||
RzFloat *ret = float_binop_full_precision(x, y, mode, operation);
|
||||
float_merge_operand_exceptions(ret, x, y, NULL);
|
||||
rz_float_free(x);
|
||||
rz_float_free(y);
|
||||
|
||||
*type = RZ_IL_TYPE_PURE_FLOAT;
|
||||
return ret;
|
||||
}
|
||||
|
||||
void *rz_il_handler_fadd(RzILVM *vm, RzILOpPure *op, RzILTypePure *type) {
|
||||
rz_return_val_if_fail(vm && op && type, NULL);
|
||||
return handle_float_binop(vm, op, type, &op->op.binop_float_alg, rz_float_add);
|
||||
}
|
||||
|
||||
void *rz_il_handler_fsub(RzILVM *vm, RzILOpPure *op, RzILTypePure *type) {
|
||||
rz_return_val_if_fail(vm && op && type, NULL);
|
||||
RzILOpArgsFsub fsub = op->op.fsub;
|
||||
RzFloat *x = rz_il_evaluate_float(vm, fsub.x);
|
||||
RzFloat *y = rz_il_evaluate_float(vm, fsub.y);
|
||||
RzFloatRMode mode = fsub.rmode;
|
||||
RzFloat *ret = rz_float_sub(x, y, mode);
|
||||
|
||||
rz_float_free(x);
|
||||
rz_float_free(y);
|
||||
|
||||
*type = RZ_IL_TYPE_PURE_FLOAT;
|
||||
return ret;
|
||||
return handle_float_binop(vm, op, type, &op->op.binop_float_alg, rz_float_sub);
|
||||
}
|
||||
|
||||
void *rz_il_handler_fdiv(RzILVM *vm, RzILOpPure *op, RzILTypePure *type) {
|
||||
rz_return_val_if_fail(vm && op && type, NULL);
|
||||
RzILOpArgsFdiv fdiv = op->op.fdiv;
|
||||
RzFloat *x = rz_il_evaluate_float(vm, fdiv.x);
|
||||
RzFloat *y = rz_il_evaluate_float(vm, fdiv.y);
|
||||
RzFloatRMode mode = fdiv.rmode;
|
||||
RzFloat *ret = rz_float_div(x, y, mode);
|
||||
|
||||
rz_float_free(x);
|
||||
rz_float_free(y);
|
||||
|
||||
*type = RZ_IL_TYPE_PURE_FLOAT;
|
||||
return ret;
|
||||
return handle_float_binop(vm, op, type, &op->op.binop_float_alg, rz_float_div);
|
||||
}
|
||||
|
||||
void *rz_il_handler_fmul(RzILVM *vm, RzILOpPure *op, RzILTypePure *type) {
|
||||
rz_return_val_if_fail(vm && op && type, NULL);
|
||||
RzILOpArgsFmul fmul = op->op.fmul;
|
||||
RzFloat *x = rz_il_evaluate_float(vm, fmul.x);
|
||||
RzFloat *y = rz_il_evaluate_float(vm, fmul.y);
|
||||
RzFloatRMode mode = fmul.rmode;
|
||||
RzFloat *ret = rz_float_mul(x, y, mode);
|
||||
|
||||
rz_float_free(x);
|
||||
rz_float_free(y);
|
||||
|
||||
*type = RZ_IL_TYPE_PURE_FLOAT;
|
||||
return ret;
|
||||
return handle_float_binop(vm, op, type, &op->op.binop_float_alg, rz_float_mul);
|
||||
}
|
||||
|
||||
void *rz_il_handler_fmod(RzILVM *vm, RzILOpPure *op, RzILTypePure *type) {
|
||||
rz_return_val_if_fail(vm && op && type, NULL);
|
||||
RzILOpArgsFmod fmod = op->op.fmod;
|
||||
RzFloat *x = rz_il_evaluate_float(vm, fmod.x);
|
||||
RzFloat *y = rz_il_evaluate_float(vm, fmod.y);
|
||||
RzFloatRMode mode = fmod.rmode;
|
||||
RzFloat *ret = rz_float_mod(x, y, mode);
|
||||
|
||||
rz_float_free(x);
|
||||
rz_float_free(y);
|
||||
|
||||
*type = RZ_IL_TYPE_PURE_FLOAT;
|
||||
return ret;
|
||||
return handle_float_binop(vm, op, type, &op->op.binop_float_alg, rz_float_mod);
|
||||
}
|
||||
|
||||
void *rz_il_handler_fhypot(RzILVM *vm, RzILOpPure *op, RzILTypePure *type) {
|
||||
|
|
@ -467,11 +599,27 @@ void *rz_il_handler_fpow(RzILVM *vm, RzILOpPure *op, RzILTypePure *type) {
|
|||
void *rz_il_handler_fmad(RzILVM *vm, RzILOpPure *op, RzILTypePure *type) {
|
||||
rz_return_val_if_fail(vm && op && type, NULL);
|
||||
RzILOpArgsFmad fmad = op->op.fmad;
|
||||
RzFloatRMode mode;
|
||||
if (!resolve_rmode(vm, op->code, &fmad.rmode, &mode)) {
|
||||
return NULL;
|
||||
}
|
||||
RzFloat *x = rz_il_evaluate_float(vm, fmad.x);
|
||||
if (!x) {
|
||||
return NULL;
|
||||
}
|
||||
RzFloat *y = rz_il_evaluate_float(vm, fmad.y);
|
||||
if (!y) {
|
||||
rz_float_free(x);
|
||||
return NULL;
|
||||
}
|
||||
RzFloat *z = rz_il_evaluate_float(vm, fmad.z);
|
||||
RzFloatRMode mode = fmad.rmode;
|
||||
RzFloat *ret = rz_float_fma(x, y, z, mode);
|
||||
if (!z) {
|
||||
rz_float_free(x);
|
||||
rz_float_free(y);
|
||||
return NULL;
|
||||
}
|
||||
RzFloat *ret = float_terop_full_precision(x, y, z, mode, rz_float_fma);
|
||||
float_merge_operand_exceptions(ret, x, y, z);
|
||||
|
||||
rz_float_free(x);
|
||||
rz_float_free(y);
|
||||
|
|
|
|||
|
|
@ -54,42 +54,58 @@
|
|||
#define S32(val) SN(32, val)
|
||||
#define S64(val) SN(64, val)
|
||||
|
||||
#define BV2F(fmt, bv) rz_il_op_new_float(fmt, bv)
|
||||
#define F32(f32) rz_il_op_new_float_from_f32(f32)
|
||||
#define F64(f64) rz_il_op_new_float_from_f64(f64)
|
||||
#define F80(f80) rz_il_op_new_float_from_f80(f80)
|
||||
#define F2BV(fl) rz_il_op_new_fbits(fl)
|
||||
#define IS_FINITE(fl) rz_il_op_new_is_finite(fl)
|
||||
#define IS_FNAN(fl) rz_il_op_new_is_nan(fl)
|
||||
#define IS_FINF(fl) rz_il_op_new_is_inf(fl)
|
||||
#define IS_FZERO(fl) rz_il_op_new_is_fzero(fl)
|
||||
#define IS_FNEG(fl) rz_il_op_new_is_fneg(fl)
|
||||
#define IS_FPOS(fl) rz_il_op_new_is_fpos(fl)
|
||||
#define FNEG(fl) rz_il_op_new_fneg(fl)
|
||||
#define FABS(fl) rz_il_op_new_fabs(fl)
|
||||
#define F2INT(l, rmode, fl) rz_il_op_new_fcast_int(l, rmode, fl)
|
||||
#define F2SINT(l, rmode, fl) rz_il_op_new_fcast_sint(l, rmode, fl)
|
||||
#define INT2F(fmt, rmode, bv) rz_il_op_new_fcast_float(fmt, rmode, bv)
|
||||
#define SINT2F(fmt, rmode, bv) rz_il_op_new_fcast_sfloat(fmt, rmode, bv)
|
||||
#define FCONVERT(fmt, rmode, fl) rz_il_op_new_fconvert(fmt, rmode, fl)
|
||||
#define FLOATV16(bv) rz_il_op_new_float(RZ_FLOAT_IEEE754_BIN_16, bv)
|
||||
#define FLOATV32(bv) rz_il_op_new_float(RZ_FLOAT_IEEE754_BIN_32, bv)
|
||||
#define FLOATV64(bv) rz_il_op_new_float(RZ_FLOAT_IEEE754_BIN_64, bv)
|
||||
#define FLOATV128(bv) rz_il_op_new_float(RZ_FLOAT_IEEE754_BIN_128, bv)
|
||||
#define FSUCC(fl) rz_il_op_new_fsucc(fl)
|
||||
#define FPRED(fl) rz_il_op_new_fpred(fl)
|
||||
#define FORDER(flx, fly) rz_il_op_new_forder(flx, fly)
|
||||
#define FROUND(rmode, fl) rz_il_op_new_fround(rmode, fl)
|
||||
#define FSQRT(rmode, fl) rz_il_op_new_fsqrt(rmode, fl)
|
||||
#define FRSQRT(rmode, fl) rz_il_op_new_frsqrt(rmode, fl)
|
||||
#define FEXCEPT(e, fl) rz_il_op_new_fexcept(e, fl)
|
||||
#define FADD(rmode, flx, fly) rz_il_op_new_fadd(rmode, flx, fly)
|
||||
#define FSUB(rmode, flx, fly) rz_il_op_new_fsub(rmode, flx, fly)
|
||||
#define FMUL(rmode, flx, fly) rz_il_op_new_fmul(rmode, flx, fly)
|
||||
#define FDIV(rmode, flx, fly) rz_il_op_new_fdiv(rmode, flx, fly)
|
||||
#define FMOD(rmode, flx, fly) rz_il_op_new_fdiv(rmode, flx, fly)
|
||||
#define FPOW(rmode, flx, fly) rz_il_op_new_fpow(rmode, flx, fly)
|
||||
#define FMAD(rmode, flx, fly, flz) rz_il_op_new_fmad(rmode, flx, fly, flz)
|
||||
#define BV2F(fmt, bv) rz_il_op_new_float(fmt, bv)
|
||||
#define F32(f32) rz_il_op_new_float_from_f32(f32)
|
||||
#define F64(f64) rz_il_op_new_float_from_f64(f64)
|
||||
#define F80(f80) rz_il_op_new_float_from_f80(f80)
|
||||
#define F2BV(fl) rz_il_op_new_fbits(fl)
|
||||
#define IS_FINITE(fl) rz_il_op_new_is_finite(fl)
|
||||
#define IS_FNAN(fl) rz_il_op_new_is_nan(fl)
|
||||
#define IS_FINF(fl) rz_il_op_new_is_inf(fl)
|
||||
#define IS_FZERO(fl) rz_il_op_new_is_fzero(fl)
|
||||
#define IS_FNEG(fl) rz_il_op_new_is_fneg(fl)
|
||||
#define IS_FPOS(fl) rz_il_op_new_is_fpos(fl)
|
||||
#define FNEG(fl) rz_il_op_new_fneg(fl)
|
||||
#define FABS(fl) rz_il_op_new_fabs(fl)
|
||||
#define F2INT(l, rmode, fl) rz_il_op_new_fcast_int(l, rmode, fl)
|
||||
#define F2SINT(l, rmode, fl) rz_il_op_new_fcast_sint(l, rmode, fl)
|
||||
#define INT2F(fmt, rmode, bv) rz_il_op_new_fcast_float(fmt, rmode, bv)
|
||||
#define SINT2F(fmt, rmode, bv) rz_il_op_new_fcast_sfloat(fmt, rmode, bv)
|
||||
#define F2INT_DYN_RMODE(l, rmode, fl) rz_il_op_new_fcast_int_dyn_rmode(l, rmode, fl)
|
||||
#define F2SINT_DYN_RMODE(l, rmode, fl) rz_il_op_new_fcast_sint_dyn_rmode(l, rmode, fl)
|
||||
#define INT2F_DYN_RMODE(fmt, rmode, bv) rz_il_op_new_fcast_float_dyn_rmode(fmt, rmode, bv)
|
||||
#define SINT2F_DYN_RMODE(fmt, rmode, bv) rz_il_op_new_fcast_sfloat_dyn_rmode(fmt, rmode, bv)
|
||||
#define FCONVERT(fmt, rmode, fl) rz_il_op_new_fconvert(fmt, rmode, fl)
|
||||
#define FLOATV16(bv) rz_il_op_new_float(RZ_FLOAT_IEEE754_BIN_16, bv)
|
||||
#define FLOATV32(bv) rz_il_op_new_float(RZ_FLOAT_IEEE754_BIN_32, bv)
|
||||
#define FLOATV64(bv) rz_il_op_new_float(RZ_FLOAT_IEEE754_BIN_64, bv)
|
||||
#define FLOATV128(bv) rz_il_op_new_float(RZ_FLOAT_IEEE754_BIN_128, bv)
|
||||
#define FSUCC(fl) rz_il_op_new_fsucc(fl)
|
||||
#define FPRED(fl) rz_il_op_new_fpred(fl)
|
||||
#define FORDER(flx, fly) rz_il_op_new_forder(flx, fly)
|
||||
#define FROUND(rmode, fl) rz_il_op_new_fround(rmode, fl)
|
||||
#define FSQRT(rmode, fl) rz_il_op_new_fsqrt(rmode, fl)
|
||||
#define FRSQRT(rmode, fl) rz_il_op_new_frsqrt(rmode, fl)
|
||||
#define FEXCEPT(e, fl) rz_il_op_new_fexcept(e, fl)
|
||||
#define FADD(rmode, flx, fly) rz_il_op_new_fadd(rmode, flx, fly)
|
||||
#define FSUB(rmode, flx, fly) rz_il_op_new_fsub(rmode, flx, fly)
|
||||
#define FMUL(rmode, flx, fly) rz_il_op_new_fmul(rmode, flx, fly)
|
||||
#define FDIV(rmode, flx, fly) rz_il_op_new_fdiv(rmode, flx, fly)
|
||||
#define FMOD(rmode, flx, fly) rz_il_op_new_fmod(rmode, flx, fly)
|
||||
#define FPOW(rmode, flx, fly) rz_il_op_new_fpow(rmode, flx, fly)
|
||||
#define FMAD(rmode, flx, fly, flz) rz_il_op_new_fmad(rmode, flx, fly, flz)
|
||||
|
||||
#define FCONVERT_DYN_RMODE(fmt, rmode, fl) rz_il_op_new_fconvert_dyn_rmode(fmt, rmode, fl)
|
||||
#define FROUND_DYN_RMODE(rmode, fl) rz_il_op_new_fround_dyn_rmode(rmode, fl)
|
||||
#define FSQRT_DYN_RMODE(rmode, fl) rz_il_op_new_fsqrt_dyn_rmode(rmode, fl)
|
||||
#define FRSQRT_DYN_RMODE(rmode, fl) rz_il_op_new_frsqrt_dyn_rmode(rmode, fl)
|
||||
#define FADD_DYN_RMODE(rmode, flx, fly) rz_il_op_new_fadd_dyn_rmode(rmode, flx, fly)
|
||||
#define FSUB_DYN_RMODE(rmode, flx, fly) rz_il_op_new_fsub_dyn_rmode(rmode, flx, fly)
|
||||
#define FMUL_DYN_RMODE(rmode, flx, fly) rz_il_op_new_fmul_dyn_rmode(rmode, flx, fly)
|
||||
#define FDIV_DYN_RMODE(rmode, flx, fly) rz_il_op_new_fdiv_dyn_rmode(rmode, flx, fly)
|
||||
#define FMOD_DYN_RMODE(rmode, flx, fly) rz_il_op_new_fmod_dyn_rmode(rmode, flx, fly)
|
||||
#define FPOW_DYN_RMODE(rmode, flx, fly) rz_il_op_new_fpow_dyn_rmode(rmode, flx, fly)
|
||||
#define FMAD_DYN_RMODE(rmode, flx, fly, flz) rz_il_op_new_fmad_dyn_rmode(rmode, flx, fly, flz)
|
||||
|
||||
#define IL_FQNAN(f) rz_il_op_new_float_from_rz_float(rz_float_new_qnan(f))
|
||||
#define IL_FSNAN(f) rz_il_op_new_float_from_rz_float(rz_float_new_snan(f))
|
||||
|
|
|
|||
|
|
@ -38,7 +38,12 @@
|
|||
#undef F2SINT
|
||||
#undef INT2F
|
||||
#undef SINT2F
|
||||
#undef F2INT_DYN_RMODE
|
||||
#undef F2SINT_DYN_RMODE
|
||||
#undef INT2F_DYN_RMODE
|
||||
#undef SINT2F_DYN_RMODE
|
||||
#undef FCONVERT
|
||||
#undef FCONVERT_DYN_RMODE
|
||||
#undef FLOATV16
|
||||
#undef FLOATV32
|
||||
#undef FLOATV64
|
||||
|
|
@ -47,14 +52,25 @@
|
|||
#undef FPRED
|
||||
#undef FORDER
|
||||
#undef FROUND
|
||||
#undef FROUND_DYN_RMODE
|
||||
#undef FSQRT
|
||||
#undef FSQRT_DYN_RMODE
|
||||
#undef FRSQRT
|
||||
#undef FRSQRT_DYN_RMODE
|
||||
#undef FADD
|
||||
#undef FADD_DYN_RMODE
|
||||
#undef FSUB
|
||||
#undef FSUB_DYN_RMODE
|
||||
#undef FMUL
|
||||
#undef FMUL_DYN_RMODE
|
||||
#undef FDIV
|
||||
#undef FDIV_DYN_RMODE
|
||||
#undef FMOD
|
||||
#undef FMOD_DYN_RMODE
|
||||
#undef FPOW
|
||||
#undef FPOW_DYN_RMODE
|
||||
#undef FMAD
|
||||
#undef FMAD_DYN_RMODE
|
||||
#undef FNE
|
||||
#undef FEQ
|
||||
#undef FLT
|
||||
|
|
|
|||
|
|
@ -368,6 +368,29 @@ typedef RzILOpArgsUnopFloat RzILOpArgsFabs;
|
|||
typedef RzILOpArgsUnopFloat RzILOpArgsFsucc;
|
||||
typedef RzILOpArgsUnopFloat RzILOpArgsFpred;
|
||||
|
||||
/**
|
||||
* \brief Selects how a floating-point rounding-mode argument is represented.
|
||||
*/
|
||||
typedef enum rz_il_op_arg_float_rmode_kind_t {
|
||||
RZ_IL_OP_ARG_FLOAT_RMODE_STATIC, ///< Stored directly as an RzFloatRMode.
|
||||
RZ_IL_OP_ARG_FLOAT_RMODE_DYNAMIC, ///< Evaluated at run time from a 32-bit bitvector IL expression.
|
||||
} RzILOpArgFloatRModeKind;
|
||||
|
||||
/**
|
||||
* \brief A floating-point rounding-mode argument.
|
||||
*
|
||||
* Static rounding modes are stored directly as an RzFloatRMode. Dynamic
|
||||
* rounding modes are represented by a 32-bit bitvector IL expression that is
|
||||
* evaluated at run time to obtain an RzFloatRMode.
|
||||
*/
|
||||
typedef struct rz_il_op_arg_float_rmode_t {
|
||||
RzILOpArgFloatRModeKind kind;
|
||||
union {
|
||||
RzFloatRMode static_mode;
|
||||
RzILOpBitVector *dynamic_mode;
|
||||
} value;
|
||||
} RzILOpArgFloatRMode;
|
||||
|
||||
/**
|
||||
* \brief op structure for cast to bv from float
|
||||
* [FCAST_INT] `f_cast_int s rm x` returns an integer closest to x.
|
||||
|
|
@ -375,7 +398,7 @@ typedef RzILOpArgsUnopFloat RzILOpArgsFpred;
|
|||
*/
|
||||
typedef struct rz_il_op_args_float_cast_int_t {
|
||||
ut32 length;
|
||||
RzFloatRMode mode;
|
||||
RzILOpArgFloatRMode rmode;
|
||||
RzILOpFloat *f;
|
||||
} RzILOpArgsFCastInt;
|
||||
|
||||
|
|
@ -392,7 +415,7 @@ typedef RzILOpArgsFCastInt RzILOpArgsFCastsint;
|
|||
*/
|
||||
typedef struct rz_il_op_args_float_cast_float_t {
|
||||
RzFloatFormat format;
|
||||
RzFloatRMode mode;
|
||||
RzILOpArgFloatRMode rmode;
|
||||
RzILOpBitVector *bv;
|
||||
} RzILOpArgsFCastFloat;
|
||||
|
||||
|
|
@ -406,7 +429,7 @@ typedef struct rz_il_op_args_float_cast_float_t RzILOpArgsFCastSFloat;
|
|||
*/
|
||||
typedef struct rz_il_op_args_float_fconvert_t {
|
||||
RzFloatFormat format;
|
||||
RzFloatRMode mode;
|
||||
RzILOpArgFloatRMode rmode;
|
||||
RzILOpFloat *f;
|
||||
} RzILOpArgsFconvert;
|
||||
|
||||
|
|
@ -438,7 +461,7 @@ typedef struct rz_il_op_args_float_binop_t {
|
|||
* [FRSQRT] reverse sqrt, rsqrt m x is the closest floating-point number to 1 / sqrt x.
|
||||
*/
|
||||
typedef struct rz_il_op_args_float_alg_unop_t {
|
||||
RzFloatRMode rmode;
|
||||
RzILOpArgFloatRMode rmode;
|
||||
RzILOpFloat *f;
|
||||
} RzILOpArgsFloatAlgUnop;
|
||||
|
||||
|
|
@ -455,9 +478,15 @@ typedef struct rz_il_op_args_float_expect_t {
|
|||
* \brief op structure for float basic arithmetic operations (binary op with rmode)
|
||||
* rmode -> 'f float -> 'f float -> 'f float
|
||||
* [FADD]
|
||||
* [FSUB]
|
||||
* [FMUL]
|
||||
* [FDIV]
|
||||
* [FMOD]
|
||||
* [FHYPOT]
|
||||
* [FPOW]
|
||||
*/
|
||||
typedef struct rz_il_op_args_float_alg_binop_t {
|
||||
RzFloatRMode rmode;
|
||||
RzILOpArgFloatRMode rmode;
|
||||
RzILOpFloat *x;
|
||||
RzILOpFloat *y;
|
||||
} RzILOpArgsFloatAlgBinop;
|
||||
|
|
@ -475,7 +504,7 @@ typedef RzILOpArgsFloatAlgBinop RzILOpArgsFpow;
|
|||
* rmode -> 'f float -> 'f float -> 'f float -> 'f float
|
||||
*/
|
||||
typedef struct rz_il_op_args_float_alg_terop_t {
|
||||
RzFloatRMode rmode;
|
||||
RzILOpArgFloatRMode rmode;
|
||||
RzILOpFloat *x;
|
||||
RzILOpFloat *y;
|
||||
RzILOpFloat *z;
|
||||
|
|
@ -487,7 +516,7 @@ typedef RzILOpArgsFloatAlgTernop RzILOpArgsFmad;
|
|||
* rmode -> 'f float -> 'a bitv -> 'f float
|
||||
*/
|
||||
typedef struct rz_il_op_args_float_alg_hybrid_binop_t {
|
||||
RzFloatRMode rmode;
|
||||
RzILOpArgFloatRMode rmode;
|
||||
RzILOpFloat *f;
|
||||
RzILOpBitVector *n;
|
||||
} RzILOpArgsFloatAlgHybridBinop;
|
||||
|
|
@ -793,7 +822,12 @@ RZ_API RZ_OWN RzILOpBitVector *rz_il_op_new_fcast_int(ut32 length, RzFloatRMode
|
|||
RZ_API RZ_OWN RzILOpBitVector *rz_il_op_new_fcast_sint(ut32 length, RzFloatRMode mode, RZ_NONNULL RzILOpFloat *f);
|
||||
RZ_API RZ_OWN RzILOpFloat *rz_il_op_new_fcast_float(RzFloatFormat format, RzFloatRMode mode, RZ_NONNULL RzILOpBitVector *bv);
|
||||
RZ_API RZ_OWN RzILOpFloat *rz_il_op_new_fcast_sfloat(RzFloatFormat format, RzFloatRMode mode, RZ_NONNULL RzILOpBitVector *bv);
|
||||
RZ_API RZ_OWN RzILOpBitVector *rz_il_op_new_fcast_int_dyn_rmode(ut32 length, RZ_NONNULL RzILOpBitVector *rmode, RZ_NONNULL RzILOpFloat *f);
|
||||
RZ_API RZ_OWN RzILOpBitVector *rz_il_op_new_fcast_sint_dyn_rmode(ut32 length, RZ_NONNULL RzILOpBitVector *rmode, RZ_NONNULL RzILOpFloat *f);
|
||||
RZ_API RZ_OWN RzILOpFloat *rz_il_op_new_fcast_float_dyn_rmode(RzFloatFormat format, RZ_NONNULL RzILOpBitVector *rmode, RZ_NONNULL RzILOpBitVector *bv);
|
||||
RZ_API RZ_OWN RzILOpFloat *rz_il_op_new_fcast_sfloat_dyn_rmode(RzFloatFormat format, RZ_NONNULL RzILOpBitVector *rmode, RZ_NONNULL RzILOpBitVector *bv);
|
||||
RZ_API RZ_OWN RzILOpFloat *rz_il_op_new_fconvert(RzFloatFormat format, RzFloatRMode mode, RZ_NONNULL RzILOpFloat *f);
|
||||
RZ_API RZ_OWN RzILOpFloat *rz_il_op_new_fconvert_dyn_rmode(RzFloatFormat format, RZ_NONNULL RzILOpBitVector *rmode, RZ_NONNULL RzILOpFloat *f);
|
||||
RZ_API RZ_OWN RzILOpBool *rz_il_op_new_frequal(RzFloatRMode x, RzFloatRMode y);
|
||||
RZ_API RZ_OWN RzILOpFloat *rz_il_op_new_fsucc(RZ_NONNULL RzILOpFloat *f);
|
||||
RZ_API RZ_OWN RzILOpFloat *rz_il_op_new_fpred(RZ_NONNULL RzILOpFloat *f);
|
||||
|
|
@ -801,18 +835,32 @@ RZ_API RZ_OWN RzILOpBool *rz_il_op_new_forder(RZ_NONNULL RzILOpFloat *x, RZ_NONN
|
|||
RZ_API RZ_OWN RzILOpFloat *rz_il_op_new_fround(RzFloatRMode rmode, RZ_NONNULL RzILOpFloat *f);
|
||||
RZ_API RZ_OWN RzILOpFloat *rz_il_op_new_fsqrt(RzFloatRMode rmode, RZ_NONNULL RzILOpFloat *f);
|
||||
RZ_API RZ_OWN RzILOpFloat *rz_il_op_new_frsqrt(RzFloatRMode rmode, RZ_NONNULL RzILOpFloat *f);
|
||||
RZ_API RZ_OWN RzILOpFloat *rz_il_op_new_fround_dyn_rmode(RZ_NONNULL RzILOpBitVector *rmode, RZ_NONNULL RzILOpFloat *f);
|
||||
RZ_API RZ_OWN RzILOpFloat *rz_il_op_new_fsqrt_dyn_rmode(RZ_NONNULL RzILOpBitVector *rmode, RZ_NONNULL RzILOpFloat *f);
|
||||
RZ_API RZ_OWN RzILOpFloat *rz_il_op_new_frsqrt_dyn_rmode(RZ_NONNULL RzILOpBitVector *rmode, RZ_NONNULL RzILOpFloat *f);
|
||||
RZ_API RZ_OWN RzILOpBool *rz_il_op_new_fexcept(RzFloatException e, RZ_NONNULL RzILOpFloat *x);
|
||||
RZ_API RZ_OWN RzILOpFloat *rz_il_op_new_fadd(RzFloatRMode rmode, RZ_NONNULL RzILOpFloat *x, RZ_NONNULL RzILOpFloat *y);
|
||||
RZ_API RZ_OWN RzILOpFloat *rz_il_op_new_fsub(RzFloatRMode rmode, RZ_NONNULL RzILOpFloat *x, RZ_NONNULL RzILOpFloat *y);
|
||||
RZ_API RZ_OWN RzILOpFloat *rz_il_op_new_fmul(RzFloatRMode rmode, RZ_NONNULL RzILOpFloat *x, RZ_NONNULL RzILOpFloat *y);
|
||||
RZ_API RZ_OWN RzILOpFloat *rz_il_op_new_fdiv(RzFloatRMode rmode, RZ_NONNULL RzILOpFloat *x, RZ_NONNULL RzILOpFloat *y);
|
||||
RZ_API RZ_OWN RzILOpFloat *rz_il_op_new_fmod(RzFloatRMode rmode, RZ_NONNULL RzILOpFloat *x, RZ_NONNULL RzILOpFloat *y);
|
||||
RZ_API RZ_OWN RzILOpFloat *rz_il_op_new_fadd_dyn_rmode(RZ_NONNULL RzILOpBitVector *rmode, RZ_NONNULL RzILOpFloat *x, RZ_NONNULL RzILOpFloat *y);
|
||||
RZ_API RZ_OWN RzILOpFloat *rz_il_op_new_fsub_dyn_rmode(RZ_NONNULL RzILOpBitVector *rmode, RZ_NONNULL RzILOpFloat *x, RZ_NONNULL RzILOpFloat *y);
|
||||
RZ_API RZ_OWN RzILOpFloat *rz_il_op_new_fmul_dyn_rmode(RZ_NONNULL RzILOpBitVector *rmode, RZ_NONNULL RzILOpFloat *x, RZ_NONNULL RzILOpFloat *y);
|
||||
RZ_API RZ_OWN RzILOpFloat *rz_il_op_new_fdiv_dyn_rmode(RZ_NONNULL RzILOpBitVector *rmode, RZ_NONNULL RzILOpFloat *x, RZ_NONNULL RzILOpFloat *y);
|
||||
RZ_API RZ_OWN RzILOpFloat *rz_il_op_new_fmod_dyn_rmode(RZ_NONNULL RzILOpBitVector *rmode, RZ_NONNULL RzILOpFloat *x, RZ_NONNULL RzILOpFloat *y);
|
||||
RZ_API RZ_OWN RzILOpFloat *rz_il_op_new_fhypot(RzFloatRMode rmode, RZ_NONNULL RzILOpFloat *x, RZ_NONNULL RzILOpFloat *y);
|
||||
RZ_API RZ_OWN RzILOpFloat *rz_il_op_new_fpow(RzFloatRMode rmode, RZ_NONNULL RzILOpFloat *x, RZ_NONNULL RzILOpFloat *y);
|
||||
RZ_API RZ_OWN RzILOpFloat *rz_il_op_new_fhypot_dyn_rmode(RZ_NONNULL RzILOpBitVector *rmode, RZ_NONNULL RzILOpFloat *x, RZ_NONNULL RzILOpFloat *y);
|
||||
RZ_API RZ_OWN RzILOpFloat *rz_il_op_new_fpow_dyn_rmode(RZ_NONNULL RzILOpBitVector *rmode, RZ_NONNULL RzILOpFloat *x, RZ_NONNULL RzILOpFloat *y);
|
||||
RZ_API RZ_OWN RzILOpFloat *rz_il_op_new_fmad(RzFloatRMode rmode, RZ_NONNULL RzILOpFloat *x, RZ_NONNULL RzILOpFloat *y, RZ_NONNULL RzILOpFloat *z);
|
||||
RZ_API RZ_OWN RzILOpFloat *rz_il_op_new_fmad_dyn_rmode(RZ_NONNULL RzILOpBitVector *rmode, RZ_NONNULL RzILOpFloat *x, RZ_NONNULL RzILOpFloat *y, RZ_NONNULL RzILOpFloat *z);
|
||||
RZ_API RZ_OWN RzILOpFloat *rz_il_op_new_frootn(RzFloatRMode rmode, RZ_NONNULL RzILOpFloat *x, RZ_NONNULL RzILOpBitVector *n);
|
||||
RZ_API RZ_OWN RzILOpFloat *rz_il_op_new_fpown(RzFloatRMode rmode, RZ_NONNULL RzILOpFloat *x, RZ_NONNULL RzILOpBitVector *n);
|
||||
RZ_API RZ_OWN RzILOpFloat *rz_il_op_new_fcompound(RzFloatRMode rmode, RZ_NONNULL RzILOpFloat *x, RZ_NONNULL RzILOpBitVector *n);
|
||||
RZ_API RZ_OWN RzILOpFloat *rz_il_op_new_frootn_dyn_rmode(RZ_NONNULL RzILOpBitVector *rmode, RZ_NONNULL RzILOpFloat *x, RZ_NONNULL RzILOpBitVector *n);
|
||||
RZ_API RZ_OWN RzILOpFloat *rz_il_op_new_fpown_dyn_rmode(RZ_NONNULL RzILOpBitVector *rmode, RZ_NONNULL RzILOpFloat *x, RZ_NONNULL RzILOpBitVector *n);
|
||||
RZ_API RZ_OWN RzILOpFloat *rz_il_op_new_fcompound_dyn_rmode(RZ_NONNULL RzILOpBitVector *rmode, RZ_NONNULL RzILOpFloat *x, RZ_NONNULL RzILOpBitVector *n);
|
||||
|
||||
RZ_API RZ_OWN RzILOpBitVector *rz_il_extract32(RZ_BORROW RzILOpBitVector *value, RZ_BORROW RzILOpBitVector *start, RZ_BORROW RzILOpBitVector *length);
|
||||
RZ_API RZ_OWN RzILOpBitVector *rz_il_extract64(RZ_BORROW RzILOpBitVector *value, RZ_BORROW RzILOpBitVector *start, RZ_BORROW RzILOpBitVector *length);
|
||||
|
|
|
|||
|
|
@ -72,6 +72,14 @@ typedef enum rz_float_round_enum {
|
|||
RZ_FLOAT_RMODE_UNK ///< end
|
||||
} RzFloatRMode; ///< Rounding Mode
|
||||
|
||||
/** Precision used by SoftFloat binary80 arithmetic. */
|
||||
typedef enum rz_float_rprecision_enum {
|
||||
RZ_FLOAT_RPREC_32 = 32, ///< 24-bit significand precision
|
||||
RZ_FLOAT_RPREC_64 = 64, ///< 53-bit significand precision
|
||||
RZ_FLOAT_RPREC_80 = 80, ///< full binary80 precision
|
||||
RZ_FLOAT_RPREC_UNK = 0
|
||||
} RzFloatRPrecision;
|
||||
|
||||
typedef enum rz_float_exception_enum {
|
||||
RZ_FLOAT_E_INVALID_OP = 1, ///< Invalid operation
|
||||
RZ_FLOAT_E_DIV_ZERO = 2, ///< Divide zero
|
||||
|
|
@ -205,4 +213,6 @@ RZ_API RZ_OWN RzFloat *rz_float_cast_sfloat(RZ_NONNULL RzBitVector *bv, RzFloatF
|
|||
RZ_API RZ_OWN RzBitVector *rz_float_cast_int(RZ_NONNULL RzFloat *f, ut32 length, RzFloatRMode mode);
|
||||
RZ_API RZ_OWN RzBitVector *rz_float_cast_sint(RZ_NONNULL RzFloat *f, ut32 length, RzFloatRMode mode);
|
||||
RZ_API RZ_OWN RzFloat *rz_float_convert(RZ_NONNULL RzFloat *f, RzFloatFormat format, RzFloatRMode mode);
|
||||
RZ_API RzFloatRPrecision rz_float_ext80_get_rounding_precision(void);
|
||||
RZ_API bool rz_float_ext80_set_rounding_precision(RzFloatRPrecision precision);
|
||||
#endif // RZ_FLOAT_H
|
||||
|
|
|
|||
|
|
@ -16,13 +16,39 @@
|
|||
* exponent value range : -16382 ~ 16383
|
||||
**/
|
||||
|
||||
#include "float_internal.c"
|
||||
#include "rz_util/rz_float.h"
|
||||
|
||||
static RzFloat *float_overflow_result(RzFloatFormat format, bool sign, RzFloatRMode mode);
|
||||
|
||||
#include "float_internal.c"
|
||||
#include <rz_userconf.h>
|
||||
#include <math.h>
|
||||
#include <fenv.h>
|
||||
#include <softfloat.h>
|
||||
|
||||
RZ_API RzFloatRPrecision rz_float_ext80_get_rounding_precision(void) {
|
||||
switch (extF80_roundingPrecision) {
|
||||
case RZ_FLOAT_RPREC_32:
|
||||
case RZ_FLOAT_RPREC_64:
|
||||
case RZ_FLOAT_RPREC_80:
|
||||
return (RzFloatRPrecision)extF80_roundingPrecision;
|
||||
default:
|
||||
return RZ_FLOAT_RPREC_UNK;
|
||||
}
|
||||
}
|
||||
|
||||
RZ_API bool rz_float_ext80_set_rounding_precision(RzFloatRPrecision precision) {
|
||||
switch (precision) {
|
||||
case RZ_FLOAT_RPREC_32:
|
||||
case RZ_FLOAT_RPREC_64:
|
||||
case RZ_FLOAT_RPREC_80:
|
||||
extF80_roundingPrecision = precision;
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* \defgroup Generate Nan and infinite for float/double/long double
|
||||
* @ {
|
||||
|
|
@ -59,6 +85,14 @@ define_types_gen_inf(f128, long double);
|
|||
/** \defgroup Helper utilities to inter-operate with SoftFloat.
|
||||
* @ {
|
||||
*/
|
||||
static inline float16_t to_float16(RzFloat *f16) {
|
||||
rz_warn_if_fail(f16->r == RZ_FLOAT_IEEE754_BIN_16);
|
||||
float16_t ret = {
|
||||
.v = (ut16)rz_bv_to_ut32(f16->s)
|
||||
};
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline float32_t to_float32(RzFloat *f32) {
|
||||
rz_warn_if_fail(f32->r == RZ_FLOAT_IEEE754_BIN_32);
|
||||
float32_t ret = {
|
||||
|
|
@ -128,6 +162,13 @@ static inline RzFloat *set_exception_flags(RzFloat *f) {
|
|||
return f;
|
||||
}
|
||||
|
||||
static inline RzFloat *of_float16(float16_t f16) {
|
||||
RzFloat *ret = rz_float_new(RZ_FLOAT_IEEE754_BIN_16);
|
||||
|
||||
rz_bv_set_from_ut64(ret->s, f16.v);
|
||||
return set_exception_flags(ret);
|
||||
}
|
||||
|
||||
static inline RzFloat *of_float32(float32_t f32) {
|
||||
RzFloat *ret = rz_float_new(RZ_FLOAT_IEEE754_BIN_32);
|
||||
|
||||
|
|
@ -176,11 +217,48 @@ static int8_t rounding_mode_mapping[] = {
|
|||
[RZ_FLOAT_RMODE_RTP] = softfloat_round_max,
|
||||
[RZ_FLOAT_RMODE_RTN] = softfloat_round_min,
|
||||
[RZ_FLOAT_RMODE_RTZ] = softfloat_round_minMag,
|
||||
[RZ_FLOAT_RMODE_UNK] = 6,
|
||||
};
|
||||
|
||||
static inline void set_float_rounding_mode(RzFloatRMode mode) {
|
||||
static inline bool set_float_rounding_mode(RzFloatRMode mode) {
|
||||
if (mode < RZ_FLOAT_RMODE_RNE || mode >= RZ_FLOAT_RMODE_UNK) {
|
||||
return false;
|
||||
}
|
||||
softfloat_roundingMode = rounding_mode_mapping[mode];
|
||||
return true;
|
||||
}
|
||||
|
||||
static RzFloat *float_overflow_result(RzFloatFormat format, bool sign, RzFloatRMode mode) {
|
||||
bool to_infinity = mode == RZ_FLOAT_RMODE_RNE || mode == RZ_FLOAT_RMODE_RNA ||
|
||||
(!sign && mode == RZ_FLOAT_RMODE_RTP) || (sign && mode == RZ_FLOAT_RMODE_RTN);
|
||||
RzFloat *ret = to_infinity ? rz_float_new_inf(format, sign) : rz_float_new(format);
|
||||
if (!ret) {
|
||||
return NULL;
|
||||
}
|
||||
if (!to_infinity) {
|
||||
ut32 total_len = rz_float_get_format_info(format, RZ_FLOAT_INFO_TOTAL_LEN);
|
||||
ut32 exp_len = rz_float_get_format_info(format, RZ_FLOAT_INFO_EXP_LEN);
|
||||
ut32 man_len = rz_float_get_format_info(format, RZ_FLOAT_INFO_MAN_LEN);
|
||||
rz_bv_set_range(ret->s, 0, man_len - 1, true);
|
||||
rz_bv_set_range(ret->s, man_len + 1, man_len + exp_len - 1, true);
|
||||
if (format == RZ_FLOAT_IEEE754_BIN_80) {
|
||||
rz_bv_set(ret->s, man_len - 1, true);
|
||||
}
|
||||
rz_bv_set(ret->s, total_len - 1, sign);
|
||||
}
|
||||
ret->exception = RZ_FLOAT_E_OVERFLOW | RZ_FLOAT_E_INEXACT;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline bool f128_is_special(float128_t value) {
|
||||
return (value.v[1] & UINT64_C(0x7fff000000000000)) == UINT64_C(0x7fff000000000000);
|
||||
}
|
||||
|
||||
static inline bool f128_is_zero(float128_t value) {
|
||||
return !value.v[0] && !(value.v[1] & UINT64_C(0x7fffffffffffffff));
|
||||
}
|
||||
|
||||
static inline bool ext_f80_is_special(extFloat80_t value) {
|
||||
return (value.signExp & 0x7fff) == 0x7fff;
|
||||
}
|
||||
/**@}*/
|
||||
|
||||
|
|
@ -928,19 +1006,36 @@ RZ_API RzFloatSpec rz_float_detect_spec(RZ_NONNULL RzFloat *f) {
|
|||
bool sign = get_sign(f->s, f->r);
|
||||
|
||||
if (rz_bv_is_all_one(exp_squashed)) {
|
||||
bool mantissa_is_zero;
|
||||
bool is_quiet;
|
||||
if (f->r == RZ_FLOAT_IEEE754_BIN_80) {
|
||||
/* The binary80 integer bit is explicit. Infinity and NaN encodings
|
||||
* require it to be set; a clear integer bit is non-canonical. */
|
||||
bool integer_bit = rz_bv_get(mantissa_squashed, 63);
|
||||
rz_bv_set(mantissa_squashed, 63, false);
|
||||
mantissa_is_zero = integer_bit && rz_bv_is_zero_vector(mantissa_squashed);
|
||||
is_quiet = !integer_bit || rz_bv_get(mantissa_squashed, 62);
|
||||
} else {
|
||||
mantissa_is_zero = rz_bv_is_zero_vector(mantissa_squashed);
|
||||
is_quiet = rz_bv_msb(mantissa_squashed);
|
||||
}
|
||||
// full exp with 0 mantissa -> inf
|
||||
if (rz_bv_is_zero_vector(mantissa_squashed)) {
|
||||
if (mantissa_is_zero) {
|
||||
ret = sign ? RZ_FLOAT_SPEC_NINF : RZ_FLOAT_SPEC_PINF;
|
||||
} else {
|
||||
// detect signal or quiet nan
|
||||
bool is_quiet = rz_bv_msb(mantissa_squashed);
|
||||
ret = is_quiet ? RZ_FLOAT_SPEC_QNAN : RZ_FLOAT_SPEC_SNAN;
|
||||
}
|
||||
}
|
||||
|
||||
if (rz_bv_is_zero_vector(exp_squashed)) {
|
||||
if (rz_bv_is_zero_vector(mantissa_squashed))
|
||||
bool mantissa_is_zero = rz_bv_is_zero_vector(mantissa_squashed);
|
||||
if (f->r == RZ_FLOAT_IEEE754_BIN_80) {
|
||||
mantissa_is_zero = !rz_bv_get(mantissa_squashed, 63) && mantissa_is_zero;
|
||||
}
|
||||
if (mantissa_is_zero) {
|
||||
ret = RZ_FLOAT_SPEC_ZERO;
|
||||
}
|
||||
}
|
||||
|
||||
rz_bv_free(exp_squashed);
|
||||
|
|
@ -983,6 +1078,14 @@ RZ_API bool rz_float_is_zero(RZ_NONNULL RzFloat *f) {
|
|||
return type == RZ_FLOAT_SPEC_ZERO;
|
||||
}
|
||||
|
||||
static inline bool is_f80_infinity(RzFloat *f) {
|
||||
if (f->r != RZ_FLOAT_IEEE754_BIN_80) {
|
||||
return false;
|
||||
}
|
||||
RzFloatSpec spec = rz_float_detect_spec(f);
|
||||
return spec == RZ_FLOAT_SPEC_PINF || spec == RZ_FLOAT_SPEC_NINF;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Compares 2 float numbers allowing imperfect bits
|
||||
*
|
||||
|
|
@ -1001,8 +1104,20 @@ RZ_API bool rz_float_is_equal(RZ_NONNULL RzFloat *x, RZ_NONNULL RzFloat *y) {
|
|||
return false;
|
||||
}
|
||||
|
||||
/* Only binary80 needs the pseudo-infinity canonicalization below; keep
|
||||
* other formats on the plain bit-wise compare. */
|
||||
bool x_f80_inf = false;
|
||||
bool y_f80_inf = false;
|
||||
ut32 f80_integer_bit = 0;
|
||||
if (x->r == RZ_FLOAT_IEEE754_BIN_80) {
|
||||
x_f80_inf = is_f80_infinity(x);
|
||||
y_f80_inf = is_f80_infinity(y);
|
||||
f80_integer_bit = rz_float_get_format_info(RZ_FLOAT_IEEE754_BIN_80, RZ_FLOAT_INFO_MAN_LEN) - 1;
|
||||
}
|
||||
for (ut32 i = 1; i < xb->len; ++i) {
|
||||
if (rz_bv_get(xb, i) != rz_bv_get(yb, i)) {
|
||||
bool x_bit = x_f80_inf && i == f80_integer_bit ? true : rz_bv_get(xb, i);
|
||||
bool y_bit = y_f80_inf && i == f80_integer_bit ? true : rz_bv_get(yb, i);
|
||||
if (x_bit != y_bit) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -1015,10 +1130,15 @@ static void set_inf(RzFloat *f, bool is_negative) {
|
|||
ut32 exp_start = rz_float_get_format_info(f->r, RZ_FLOAT_INFO_MAN_LEN);
|
||||
ut32 exp_end = exp_start + rz_float_get_format_info(f->r, RZ_FLOAT_INFO_EXP_LEN);
|
||||
|
||||
rz_bv_set_all(bv, false);
|
||||
// set exponent part to all 1
|
||||
rz_bv_set_range(bv, exp_start, exp_end - 1, true);
|
||||
if (f->r == RZ_FLOAT_IEEE754_BIN_80) {
|
||||
// binary80 stores its integer bit explicitly.
|
||||
rz_bv_set(bv, exp_start - 1, true);
|
||||
}
|
||||
|
||||
// set sign bit (MSB), keep mantissa as zero-bv
|
||||
// set sign bit (MSB), keep the fractional mantissa as zero-bv
|
||||
rz_bv_set(bv, bv->len - 1, is_negative);
|
||||
}
|
||||
|
||||
|
|
@ -1027,11 +1147,18 @@ static void set_qnan(RzFloat *f) {
|
|||
ut32 exp_start = rz_float_get_format_info(f->r, RZ_FLOAT_INFO_MAN_LEN);
|
||||
ut32 exp_end = exp_start + rz_float_get_format_info(f->r, RZ_FLOAT_INFO_EXP_LEN);
|
||||
|
||||
rz_bv_set_all(bv, false);
|
||||
// set exponent part to all 1
|
||||
rz_bv_set_range(bv, exp_start, exp_end - 1, true);
|
||||
|
||||
// set is_quiet to 1
|
||||
rz_bv_set(bv, exp_start - 1, true);
|
||||
if (f->r == RZ_FLOAT_IEEE754_BIN_80) {
|
||||
// Set the explicit integer bit and the distinct quiet bit.
|
||||
rz_bv_set(bv, exp_start - 1, true);
|
||||
rz_bv_set(bv, exp_start - 2, true);
|
||||
} else {
|
||||
// set is_quiet to 1
|
||||
rz_bv_set(bv, exp_start - 1, true);
|
||||
}
|
||||
|
||||
// set sig as non-zero
|
||||
rz_bv_set(bv, 0, true);
|
||||
|
|
@ -1042,11 +1169,14 @@ static void set_snan(RzFloat *f) {
|
|||
ut32 exp_start = rz_float_get_format_info(f->r, RZ_FLOAT_INFO_MAN_LEN);
|
||||
ut32 exp_end = exp_start + rz_float_get_format_info(f->r, RZ_FLOAT_INFO_EXP_LEN);
|
||||
|
||||
rz_bv_set_all(bv, false);
|
||||
// set exponent part to all 1
|
||||
rz_bv_set_range(bv, exp_start, exp_end - 1, true);
|
||||
|
||||
// set is_quiet to 0 (msb of mantissa part)
|
||||
rz_bv_set(bv, exp_start - 1, false);
|
||||
if (f->r == RZ_FLOAT_IEEE754_BIN_80) {
|
||||
// The explicit integer bit is set; the quiet bit remains clear.
|
||||
rz_bv_set(bv, exp_start - 1, true);
|
||||
}
|
||||
|
||||
// set sig as non-zero
|
||||
rz_bv_set(bv, 0, true);
|
||||
|
|
@ -1174,9 +1304,13 @@ RZ_API RZ_OWN RzFloat *rz_float_add_ieee_bin(RZ_NONNULL RzFloat *left, RZ_NONNUL
|
|||
rz_return_val_if_fail(left && right && left->r == right->r, NULL);
|
||||
|
||||
RzFloatFormat format = left->r;
|
||||
set_float_rounding_mode(mode);
|
||||
if (!set_float_rounding_mode(mode)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
switch (format) {
|
||||
case RZ_FLOAT_IEEE754_BIN_16:
|
||||
return of_float16(f16_add(to_float16(left), to_float16(right)));
|
||||
case RZ_FLOAT_IEEE754_BIN_32:
|
||||
return of_float32(f32_add(to_float32(left), to_float32(right)));
|
||||
case RZ_FLOAT_IEEE754_BIN_64:
|
||||
|
|
@ -1200,9 +1334,13 @@ RZ_API RZ_OWN RzFloat *rz_float_sub_ieee_bin(RZ_NONNULL RzFloat *left, RZ_NONNUL
|
|||
rz_return_val_if_fail(left && right && left->r == right->r, NULL);
|
||||
|
||||
RzFloatFormat format = left->r;
|
||||
set_float_rounding_mode(mode);
|
||||
if (!set_float_rounding_mode(mode)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
switch (format) {
|
||||
case RZ_FLOAT_IEEE754_BIN_16:
|
||||
return of_float16(f16_sub(to_float16(left), to_float16(right)));
|
||||
case RZ_FLOAT_IEEE754_BIN_32:
|
||||
return of_float32(f32_sub(to_float32(left), to_float32(right)));
|
||||
case RZ_FLOAT_IEEE754_BIN_64:
|
||||
|
|
@ -1226,9 +1364,13 @@ RZ_API RZ_OWN RzFloat *rz_float_mul_ieee_bin(RZ_NONNULL RzFloat *left, RZ_NONNUL
|
|||
rz_return_val_if_fail(left && right && left->r == right->r, NULL);
|
||||
|
||||
RzFloatFormat format = left->r;
|
||||
set_float_rounding_mode(mode);
|
||||
if (!set_float_rounding_mode(mode)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
switch (format) {
|
||||
case RZ_FLOAT_IEEE754_BIN_16:
|
||||
return of_float16(f16_mul(to_float16(left), to_float16(right)));
|
||||
case RZ_FLOAT_IEEE754_BIN_32:
|
||||
return of_float32(f32_mul(to_float32(left), to_float32(right)));
|
||||
case RZ_FLOAT_IEEE754_BIN_64:
|
||||
|
|
@ -1258,9 +1400,13 @@ RZ_API RZ_OWN RzFloat *rz_float_div_ieee_bin(RZ_NONNULL RzFloat *left, RZ_NONNUL
|
|||
rz_return_val_if_fail(left && right && left->r == right->r, NULL);
|
||||
|
||||
RzFloatFormat format = left->r;
|
||||
set_float_rounding_mode(mode);
|
||||
if (!set_float_rounding_mode(mode)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
switch (format) {
|
||||
case RZ_FLOAT_IEEE754_BIN_16:
|
||||
return of_float16(f16_div(to_float16(left), to_float16(right)));
|
||||
case RZ_FLOAT_IEEE754_BIN_32:
|
||||
return of_float32(f32_div(to_float32(left), to_float32(right)));
|
||||
case RZ_FLOAT_IEEE754_BIN_64:
|
||||
|
|
@ -1291,9 +1437,13 @@ RZ_API RZ_OWN RzFloat *rz_float_rem_ieee_bin(RZ_NONNULL RzFloat *left, RZ_NONNUL
|
|||
rz_return_val_if_fail(left && right && left->r == right->r, NULL);
|
||||
|
||||
RzFloatFormat format = left->r;
|
||||
set_float_rounding_mode(mode);
|
||||
if (!set_float_rounding_mode(mode)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
switch (format) {
|
||||
case RZ_FLOAT_IEEE754_BIN_16:
|
||||
return of_float16(f16_rem(to_float16(left), to_float16(right)));
|
||||
case RZ_FLOAT_IEEE754_BIN_32:
|
||||
return of_float32(f32_rem(to_float32(left), to_float32(right)));
|
||||
case RZ_FLOAT_IEEE754_BIN_64:
|
||||
|
|
@ -1327,6 +1477,9 @@ RZ_API RZ_OWN RzFloat *rz_float_mod_ieee_bin(RZ_NONNULL RzFloat *left, RZ_NONNUL
|
|||
rz_return_val_if_fail(left && right && left->r == right->r, NULL);
|
||||
|
||||
RzFloat *ret = rz_float_rem_ieee_bin(left, right, mode);
|
||||
if (!ret) {
|
||||
return NULL;
|
||||
}
|
||||
if (rz_float_get_sign(ret) != rz_float_get_sign(left)) {
|
||||
if (rz_float_is_zero(ret)) {
|
||||
/* If a zero is returned, it should still have the same sign as the dividend. */
|
||||
|
|
@ -1341,6 +1494,7 @@ RZ_API RZ_OWN RzFloat *rz_float_mod_ieee_bin(RZ_NONNULL RzFloat *left, RZ_NONNUL
|
|||
same_sign = rz_float_sub(ret, right_abs, mode);
|
||||
}
|
||||
|
||||
rz_float_free(right_abs);
|
||||
rz_float_free(ret);
|
||||
ret = same_sign;
|
||||
}
|
||||
|
|
@ -1358,24 +1512,91 @@ RZ_API RZ_OWN RzFloat *rz_float_fma_ieee_bin(RZ_NONNULL RzFloat *a, RZ_NONNULL R
|
|||
rz_return_val_if_fail(a && b && c && a->r == b->r && b->r == c->r, NULL);
|
||||
|
||||
RzFloatFormat format = a->r;
|
||||
set_float_rounding_mode(mode);
|
||||
if (!set_float_rounding_mode(mode)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
switch (format) {
|
||||
case RZ_FLOAT_IEEE754_BIN_16:
|
||||
return of_float16(f16_mulAdd(to_float16(a), to_float16(b), to_float16(c)));
|
||||
case RZ_FLOAT_IEEE754_BIN_32:
|
||||
return of_float32(f32_mulAdd(to_float32(a), to_float32(b), to_float32(c)));
|
||||
case RZ_FLOAT_IEEE754_BIN_64:
|
||||
return of_float64(f64_mulAdd(to_float64(a), to_float64(b), to_float64(c)));
|
||||
case RZ_FLOAT_IEEE754_BIN_80: {
|
||||
/* We don't have a 80-bit FMA available in SoftFloat, so we cast the
|
||||
* float to 128-bit, perform FMA and cast it back. This should be fine
|
||||
* since th 80-bit and the 128-bit format differ only in the size of
|
||||
* their mantissa. */
|
||||
* float to 128-bit, perform FMA and cast it back. The exact product of
|
||||
* two 64-bit significands needs up to 128 significant bits while f128
|
||||
* carries only 113, so a plain f128_mulAdd + f128_to_extF80 rounds
|
||||
* twice and can be off by 1 ulp. Use a round-to-odd intermediate
|
||||
* instead: 113 bits keep enough information (more than 64 + 2) for
|
||||
* the final extF80 rounding to produce the correctly rounded result. */
|
||||
float128_t a_resized = extF80_to_f128(to_float80(a));
|
||||
float128_t b_resized = extF80_to_f128(to_float80(b));
|
||||
float128_t c_resized = extF80_to_f128(to_float80(c));
|
||||
|
||||
uint_fast8_t requested_mode = softfloat_roundingMode;
|
||||
uint_fast8_t accumulated_flags = softfloat_exceptionFlags;
|
||||
softfloat_exceptionFlags = 0;
|
||||
softfloat_roundingMode = softfloat_round_minMag;
|
||||
|
||||
float128_t fma_resized = f128_mulAdd(a_resized, b_resized, c_resized);
|
||||
return of_float80(f128_to_extF80(fma_resized));
|
||||
uint_fast8_t fma_flags = softfloat_exceptionFlags;
|
||||
if (!(fma_flags & softfloat_flag_inexact) && f128_is_zero(fma_resized)) {
|
||||
/* Exact cancellation gets its zero sign from the rounding mode. */
|
||||
softfloat_exceptionFlags = 0;
|
||||
softfloat_roundingMode = requested_mode;
|
||||
fma_resized = f128_mulAdd(a_resized, b_resized, c_resized);
|
||||
fma_flags |= softfloat_exceptionFlags;
|
||||
softfloat_roundingMode = softfloat_round_minMag;
|
||||
}
|
||||
if ((fma_flags & softfloat_flag_inexact) && !f128_is_special(fma_resized)) {
|
||||
fma_resized.v[0] |= 1;
|
||||
}
|
||||
|
||||
if (extF80_roundingPrecision != RZ_FLOAT_RPREC_32 && extF80_roundingPrecision != RZ_FLOAT_RPREC_64) {
|
||||
/* Full binary80 precision: the round-to-odd intermediate rounds
|
||||
* exactly once to the 64-bit significand with the requested mode. */
|
||||
softfloat_exceptionFlags = 0;
|
||||
softfloat_roundingMode = requested_mode;
|
||||
extFloat80_t rounded = f128_to_extF80(fma_resized);
|
||||
uint_fast8_t convert_flags = softfloat_exceptionFlags;
|
||||
|
||||
/* Intermediate underflow is decided by this final conversion,
|
||||
* while all other accumulated flags are preserved. */
|
||||
fma_flags &= ~softfloat_flag_underflow;
|
||||
softfloat_exceptionFlags |= accumulated_flags | fma_flags | convert_flags;
|
||||
return of_float80(rounded);
|
||||
}
|
||||
|
||||
/* SoftFloat's f128-to-extF80 conversion always rounds to full binary80
|
||||
* precision. Chain another round-to-odd intermediate so that the final
|
||||
* extF80 operation can round once to the requested reduced precision
|
||||
* without a double-rounding error. */
|
||||
softfloat_exceptionFlags = 0;
|
||||
extFloat80_t fma_f80 = f128_to_extF80(fma_resized);
|
||||
uint_fast8_t convert_flags = softfloat_exceptionFlags;
|
||||
if ((convert_flags & softfloat_flag_inexact) && !ext_f80_is_special(fma_f80)) {
|
||||
fma_f80.signif |= 1;
|
||||
}
|
||||
|
||||
softfloat_exceptionFlags = 0;
|
||||
softfloat_roundingMode = requested_mode;
|
||||
extFloat80_t rounded = fma_f80;
|
||||
if (!ext_f80_is_special(fma_f80) && ((fma_f80.signExp & 0x7fff) || fma_f80.signif)) {
|
||||
extFloat80_t zero = {
|
||||
.signif = 0,
|
||||
.signExp = fma_f80.signExp & 0x8000,
|
||||
};
|
||||
rounded = extF80_add(fma_f80, zero);
|
||||
}
|
||||
|
||||
/* Intermediate underflow is precision-dependent. Let the final extF80
|
||||
* rounding decide it, while preserving all other accumulated flags. */
|
||||
fma_flags &= ~softfloat_flag_underflow;
|
||||
convert_flags &= ~softfloat_flag_underflow;
|
||||
softfloat_exceptionFlags |= accumulated_flags | fma_flags | convert_flags;
|
||||
return of_float80(rounded);
|
||||
}
|
||||
case RZ_FLOAT_IEEE754_BIN_128:
|
||||
return of_float128(f128_mulAdd(to_float128(a), to_float128(b), to_float128(c)));
|
||||
|
|
@ -1394,9 +1615,13 @@ RZ_API RZ_OWN RzFloat *rz_float_sqrt_ieee_bin(RZ_NONNULL RzFloat *n, RzFloatRMod
|
|||
rz_return_val_if_fail(n, NULL);
|
||||
|
||||
RzFloatFormat format = n->r;
|
||||
set_float_rounding_mode(mode);
|
||||
if (!set_float_rounding_mode(mode)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
switch (format) {
|
||||
case RZ_FLOAT_IEEE754_BIN_16:
|
||||
return of_float16(f16_sqrt(to_float16(n)));
|
||||
case RZ_FLOAT_IEEE754_BIN_32:
|
||||
return of_float32(f32_sqrt(to_float32(n)));
|
||||
case RZ_FLOAT_IEEE754_BIN_64:
|
||||
|
|
@ -1470,9 +1695,13 @@ RZ_API RZ_OWN RzFloat *rz_float_round_to_integral(RZ_NONNULL RzFloat *f, RzFloat
|
|||
rz_return_val_if_fail(f, NULL);
|
||||
|
||||
RzFloatFormat format = f->r;
|
||||
set_float_rounding_mode(mode);
|
||||
if (!set_float_rounding_mode(mode)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
switch (format) {
|
||||
case RZ_FLOAT_IEEE754_BIN_16:
|
||||
return of_float16(f16_roundToInt(to_float16(f), softfloat_roundingMode, false));
|
||||
case RZ_FLOAT_IEEE754_BIN_32:
|
||||
return of_float32(f32_roundToInt(to_float32(f), softfloat_roundingMode, false));
|
||||
case RZ_FLOAT_IEEE754_BIN_64:
|
||||
|
|
@ -1495,25 +1724,26 @@ RZ_API RZ_OWN RzFloat *rz_float_round_to_integral(RZ_NONNULL RzFloat *f, RzFloat
|
|||
* \param mode rounding mode
|
||||
* \return closest float of given integer
|
||||
*/
|
||||
RZ_API RZ_OWN RzFloat *rz_float_cast_float(RZ_NONNULL RzBitVector *bv, RzFloatFormat format, RzFloatRMode mode) {
|
||||
rz_return_val_if_fail(bv, NULL);
|
||||
static RzFloat *float_cast_from_abs(RzBitVector *bv, RzFloatFormat format, RzFloatRMode mode, bool sign) {
|
||||
ut32 bias = rz_float_get_format_info(format, RZ_FLOAT_INFO_BIAS);
|
||||
ut32 exp_max_no_bias = bias;
|
||||
|
||||
ut32 width = rz_bv_len(bv) - rz_bv_clz(bv);
|
||||
// Zero has no highest set bit; handle it before width - 1 underflows.
|
||||
if (width == 0) {
|
||||
return rz_float_new_zero(format, false);
|
||||
return rz_float_new_zero(format, sign);
|
||||
}
|
||||
ut32 order = width - 1;
|
||||
if (order > exp_max_no_bias) {
|
||||
// error: not representable
|
||||
return rz_float_new_inf(format, 0);
|
||||
return float_overflow_result(format, sign, mode);
|
||||
}
|
||||
|
||||
// unsigned bv, as positive one
|
||||
RzFloat *cast_float = rz_float_round_bv_and_pack(0, order + bias, bv, format, mode);
|
||||
return cast_float;
|
||||
return rz_float_round_bv_and_pack(sign, order + bias, bv, format, mode);
|
||||
}
|
||||
|
||||
RZ_API RZ_OWN RzFloat *rz_float_cast_float(RZ_NONNULL RzBitVector *bv, RzFloatFormat format, RzFloatRMode mode) {
|
||||
rz_return_val_if_fail(bv, NULL);
|
||||
return float_cast_from_abs(bv, format, mode, false);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -1533,15 +1763,8 @@ RZ_API RZ_OWN RzFloat *rz_float_cast_sfloat(RZ_NONNULL RzBitVector *bv, RzFloatF
|
|||
bool sign = rz_bv_msb(bv);
|
||||
bv_abs = sign ? rz_bv_complement_2(bv) : rz_bv_dup(bv);
|
||||
|
||||
RzFloat *cast_float = rz_float_cast_float(bv_abs, format, mode);
|
||||
RzFloat *cast_float = float_cast_from_abs(bv_abs, format, mode, sign);
|
||||
rz_bv_free(bv_abs);
|
||||
|
||||
if (!cast_float) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// set sign of float
|
||||
rz_float_set_sign(cast_float, sign);
|
||||
return cast_float;
|
||||
}
|
||||
|
||||
|
|
@ -1585,6 +1808,7 @@ RZ_API RZ_OWN RzBitVector *rz_float_cast_sint(RZ_NONNULL RzFloat *f, ut32 length
|
|||
// drop extra bits or append zeros
|
||||
// 1.MM..M * 2^exp = 1MM..M * 2^0 (integer)
|
||||
bool should_inc = false;
|
||||
bool inexact = false;
|
||||
RzBitVector *sig = rz_float_get_mantissa(f);
|
||||
bool is_zero = rz_bv_is_zero_vector(sig) && exp == 0;
|
||||
|
||||
|
|
@ -1596,7 +1820,7 @@ RZ_API RZ_OWN RzBitVector *rz_float_cast_sint(RZ_NONNULL RzFloat *f, ut32 length
|
|||
|
||||
if (exp_no_bias >= 0) {
|
||||
// has `exp_no_bias` + 3 + 1 length
|
||||
tmp = round_significant(sign, sig, exp_no_bias, mode, &should_inc);
|
||||
tmp = round_significant(sign, sig, exp_no_bias, mode, &should_inc, &inexact);
|
||||
} else {
|
||||
// float 1.M..M * 2^exp, when exp < 0
|
||||
// flatten it and we have 0.0..1M..M (|exp|+1 zeros before 1MMM...)
|
||||
|
|
@ -1610,7 +1834,7 @@ RZ_API RZ_OWN RzBitVector *rz_float_cast_sint(RZ_NONNULL RzFloat *f, ut32 length
|
|||
fake_f = rz_bv_dup(sig);
|
||||
}
|
||||
rz_bv_set(fake_f, rz_bv_len(fake_f) - 1, true);
|
||||
tmp = round_significant(sign, fake_f, 0, mode, &should_inc);
|
||||
tmp = round_significant(sign, fake_f, 0, mode, &should_inc, &inexact);
|
||||
|
||||
// unset the fake 1 in tmp
|
||||
// tmp has 3 + 1 + precision = 4
|
||||
|
|
@ -1649,6 +1873,74 @@ RZ_API RZ_OWN RzBitVector *rz_float_cast_sint(RZ_NONNULL RzFloat *f, ut32 length
|
|||
return ret;
|
||||
}
|
||||
|
||||
/* SoftFloat conversion thunks for rz_float_convert. One row per source format
|
||||
* keeps the dispatch in a single place: adding a conversion path means adding
|
||||
* one thunk and one table cell instead of touching symmetric switch cases. */
|
||||
typedef RzFloat *(*RzFloatConvertThunk)(RzFloat *f);
|
||||
|
||||
#define RZ_FLOAT_CONVERT_THUNK(name, FROM, TO, FN) \
|
||||
static RzFloat *name(RzFloat *f) { \
|
||||
return of_float##TO(FN(to_float##FROM(f))); \
|
||||
}
|
||||
|
||||
RZ_FLOAT_CONVERT_THUNK(convert_f16_to_f32, 16, 32, f16_to_f32)
|
||||
RZ_FLOAT_CONVERT_THUNK(convert_f16_to_f64, 16, 64, f16_to_f64)
|
||||
RZ_FLOAT_CONVERT_THUNK(convert_f16_to_f80, 16, 80, f16_to_extF80)
|
||||
RZ_FLOAT_CONVERT_THUNK(convert_f16_to_f128, 16, 128, f16_to_f128)
|
||||
RZ_FLOAT_CONVERT_THUNK(convert_f32_to_f16, 32, 16, f32_to_f16)
|
||||
RZ_FLOAT_CONVERT_THUNK(convert_f32_to_f64, 32, 64, f32_to_f64)
|
||||
RZ_FLOAT_CONVERT_THUNK(convert_f32_to_f80, 32, 80, f32_to_extF80)
|
||||
RZ_FLOAT_CONVERT_THUNK(convert_f32_to_f128, 32, 128, f32_to_f128)
|
||||
RZ_FLOAT_CONVERT_THUNK(convert_f64_to_f16, 64, 16, f64_to_f16)
|
||||
RZ_FLOAT_CONVERT_THUNK(convert_f64_to_f32, 64, 32, f64_to_f32)
|
||||
RZ_FLOAT_CONVERT_THUNK(convert_f64_to_f80, 64, 80, f64_to_extF80)
|
||||
RZ_FLOAT_CONVERT_THUNK(convert_f64_to_f128, 64, 128, f64_to_f128)
|
||||
RZ_FLOAT_CONVERT_THUNK(convert_f80_to_f16, 80, 16, extF80_to_f16)
|
||||
RZ_FLOAT_CONVERT_THUNK(convert_f80_to_f32, 80, 32, extF80_to_f32)
|
||||
RZ_FLOAT_CONVERT_THUNK(convert_f80_to_f64, 80, 64, extF80_to_f64)
|
||||
RZ_FLOAT_CONVERT_THUNK(convert_f80_to_f128, 80, 128, extF80_to_f128)
|
||||
RZ_FLOAT_CONVERT_THUNK(convert_f128_to_f16, 128, 16, f128_to_f16)
|
||||
RZ_FLOAT_CONVERT_THUNK(convert_f128_to_f32, 128, 32, f128_to_f32)
|
||||
RZ_FLOAT_CONVERT_THUNK(convert_f128_to_f64, 128, 64, f128_to_f64)
|
||||
RZ_FLOAT_CONVERT_THUNK(convert_f128_to_f80, 128, 80, f128_to_extF80)
|
||||
|
||||
/* The five IEEE-754 binary formats are the leading, densely numbered members
|
||||
* of RzFloatFormat. */
|
||||
#define RZ_FLOAT_BINARY_FORMAT_COUNT (RZ_FLOAT_IEEE754_BIN_16 + 1)
|
||||
|
||||
static RzFloatConvertThunk const softfloat_convert_table[RZ_FLOAT_BINARY_FORMAT_COUNT][RZ_FLOAT_BINARY_FORMAT_COUNT] = {
|
||||
[RZ_FLOAT_IEEE754_BIN_16] = {
|
||||
[RZ_FLOAT_IEEE754_BIN_32] = convert_f16_to_f32,
|
||||
[RZ_FLOAT_IEEE754_BIN_64] = convert_f16_to_f64,
|
||||
[RZ_FLOAT_IEEE754_BIN_80] = convert_f16_to_f80,
|
||||
[RZ_FLOAT_IEEE754_BIN_128] = convert_f16_to_f128,
|
||||
},
|
||||
[RZ_FLOAT_IEEE754_BIN_32] = {
|
||||
[RZ_FLOAT_IEEE754_BIN_16] = convert_f32_to_f16,
|
||||
[RZ_FLOAT_IEEE754_BIN_64] = convert_f32_to_f64,
|
||||
[RZ_FLOAT_IEEE754_BIN_80] = convert_f32_to_f80,
|
||||
[RZ_FLOAT_IEEE754_BIN_128] = convert_f32_to_f128,
|
||||
},
|
||||
[RZ_FLOAT_IEEE754_BIN_64] = {
|
||||
[RZ_FLOAT_IEEE754_BIN_16] = convert_f64_to_f16,
|
||||
[RZ_FLOAT_IEEE754_BIN_32] = convert_f64_to_f32,
|
||||
[RZ_FLOAT_IEEE754_BIN_80] = convert_f64_to_f80,
|
||||
[RZ_FLOAT_IEEE754_BIN_128] = convert_f64_to_f128,
|
||||
},
|
||||
[RZ_FLOAT_IEEE754_BIN_80] = {
|
||||
[RZ_FLOAT_IEEE754_BIN_16] = convert_f80_to_f16,
|
||||
[RZ_FLOAT_IEEE754_BIN_32] = convert_f80_to_f32,
|
||||
[RZ_FLOAT_IEEE754_BIN_64] = convert_f80_to_f64,
|
||||
[RZ_FLOAT_IEEE754_BIN_128] = convert_f80_to_f128,
|
||||
},
|
||||
[RZ_FLOAT_IEEE754_BIN_128] = {
|
||||
[RZ_FLOAT_IEEE754_BIN_16] = convert_f128_to_f16,
|
||||
[RZ_FLOAT_IEEE754_BIN_32] = convert_f128_to_f32,
|
||||
[RZ_FLOAT_IEEE754_BIN_64] = convert_f128_to_f64,
|
||||
[RZ_FLOAT_IEEE754_BIN_80] = convert_f128_to_f80,
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* convert float from format A to a new format B
|
||||
* \param f float
|
||||
|
|
@ -1660,38 +1952,55 @@ RZ_API RZ_OWN RzFloat *rz_float_convert(RZ_NONNULL RzFloat *f, RzFloatFormat for
|
|||
rz_return_val_if_fail(f, NULL);
|
||||
|
||||
if (rz_float_is_nan(f)) {
|
||||
return rz_float_new_qnan(format);
|
||||
RzFloat *ret = rz_float_new_qnan(format);
|
||||
if (ret) {
|
||||
rz_float_set_sign(ret, rz_float_get_sign(f));
|
||||
ret->exception |= f->exception;
|
||||
if (rz_float_detect_spec(f) == RZ_FLOAT_SPEC_SNAN) {
|
||||
/* Quieting a signaling NaN raises the invalid-operation
|
||||
* exception, like the SoftFloat conversions below do. */
|
||||
ret->exception |= RZ_FLOAT_E_INVALID_OP;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (rz_float_is_inf(f)) {
|
||||
return rz_float_new_inf(format, rz_float_get_sign(f));
|
||||
RzFloat *ret = rz_float_new_inf(format, rz_float_get_sign(f));
|
||||
if (ret) {
|
||||
ret->exception |= f->exception;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (rz_float_is_zero(f)) {
|
||||
RzFloat *ret_zero = rz_float_new_zero(format, rz_float_get_sign(f));
|
||||
if (ret_zero) {
|
||||
ret_zero->exception |= f->exception;
|
||||
}
|
||||
return ret_zero;
|
||||
}
|
||||
|
||||
ut32 exp = float_exponent(f);
|
||||
RzFloatFormat old_format = f->r;
|
||||
bool sign = get_sign(f->s, old_format);
|
||||
ut32 man_len = rz_float_get_format_info(old_format, RZ_FLOAT_INFO_MAN_LEN);
|
||||
if (old_format == RZ_FLOAT_IEEE754_BIN_80) {
|
||||
/* Special case, see [rz_float_info_bin80] for more. */
|
||||
man_len--;
|
||||
if (f->r == format) {
|
||||
return rz_float_dup(f);
|
||||
}
|
||||
|
||||
// recover hidden bit if it's a normal float
|
||||
// for sub-normal, we also set a fake hidden bit 1 to use round_float
|
||||
RzBitVector *sig = rz_float_get_mantissa(f);
|
||||
rz_bv_set(sig, man_len, 1);
|
||||
|
||||
// shift to make significant a integer
|
||||
// 1.MM..M * 2^exp_no_bias == 1MM..M * 2^(exp_no_bias - man_len)
|
||||
// 0.MM..M * 2^exp_no_bias == 00..1X..X * 2^(exp_no_bias - man_len)
|
||||
RzFloat *ret = round_float_bv_new(sign, exp, sig, old_format, format, mode);
|
||||
|
||||
rz_bv_free(sig);
|
||||
if (!set_float_rounding_mode(mode)) {
|
||||
return NULL;
|
||||
}
|
||||
softfloat_exceptionFlags = 0;
|
||||
RzFloat *ret = NULL;
|
||||
if (f->r < RZ_FLOAT_BINARY_FORMAT_COUNT && format < RZ_FLOAT_BINARY_FORMAT_COUNT) {
|
||||
RzFloatConvertThunk thunk = softfloat_convert_table[f->r][format];
|
||||
if (thunk) {
|
||||
ret = thunk(f);
|
||||
}
|
||||
}
|
||||
if (ret) {
|
||||
ret->exception |= f->exception;
|
||||
} else {
|
||||
softfloat_exceptionFlags = 0;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
@ -1798,6 +2107,14 @@ RZ_API RZ_OWN RzFloat *rz_float_neg(RZ_NONNULL RzFloat *f) {
|
|||
return ret;
|
||||
}
|
||||
|
||||
static void normalize_f80_infinity(RzFloat *f, RzBitVector *bv) {
|
||||
if (!is_f80_infinity(f)) {
|
||||
return;
|
||||
}
|
||||
ut32 significand_len = rz_float_get_format_info(f->r, RZ_FLOAT_INFO_MAN_LEN);
|
||||
rz_bv_set(bv, significand_len - 1, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* get least floating-point number representable in (sort x) that is greater than given float
|
||||
* BAP ref: val fsucc : 'f float -> 'f float
|
||||
|
|
@ -1872,6 +2189,8 @@ RZ_API RZ_OWN st32 rz_float_cmp(RZ_NONNULL RzFloat *x, RZ_NONNULL RzFloat *y) {
|
|||
|
||||
RZ_BORROW RzBitVector *x_bv = rz_bv_dup(x->s);
|
||||
RZ_BORROW RzBitVector *y_bv = rz_bv_dup(y->s);
|
||||
normalize_f80_infinity(x, x_bv);
|
||||
normalize_f80_infinity(y, y_bv);
|
||||
|
||||
bool x_sign = rz_bv_msb(x_bv);
|
||||
bool y_sign = rz_bv_msb(y_bv);
|
||||
|
|
@ -1919,7 +2238,8 @@ RZ_API RZ_OWN st32 rz_float_cmp(RZ_NONNULL RzFloat *x, RZ_NONNULL RzFloat *y) {
|
|||
* \return new bitvector would be 0001MM...M, which length is `precision + 1 + 3`
|
||||
*/
|
||||
RZ_API RZ_OWN RzBitVector *rz_float_round_significant(bool sign, RzBitVector *sig, ut32 precision, RzFloatRMode mode, bool *should_inc) {
|
||||
return round_significant(sign, sig, precision, mode, should_inc);
|
||||
bool inexact = false;
|
||||
return round_significant(sign, sig, precision, mode, should_inc, &inexact);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -119,7 +119,7 @@ static bool rz_bv_shift_right_jammed(RzBitVector *bv, ut32 dist) {
|
|||
}
|
||||
|
||||
rz_bv_rshift(bv, dist);
|
||||
rz_bv_set(bv, 0, lsb);
|
||||
rz_bv_set(bv, 0, rz_bv_get(bv, 0) || lsb);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -295,8 +295,8 @@ static RZ_OWN RzBitVector *pack_float_bv(bool sign, RZ_BORROW RZ_NONNULL const R
|
|||
* 1 means caller should round by adding ULP to `return bitv`
|
||||
* \return new bitvector would be 0001MM...M, which length is `precision + 1 + 3`
|
||||
*/
|
||||
static RzBitVector *round_significant(bool sign, RzBitVector *sig, ut32 precision, RzFloatRMode mode, bool *should_inc) {
|
||||
rz_return_val_if_fail(sig && should_inc, NULL);
|
||||
static RzBitVector *round_significant(bool sign, RzBitVector *sig, ut32 precision, RzFloatRMode mode, bool *should_inc, bool *inexact) {
|
||||
rz_return_val_if_fail(sig && should_inc && inexact, NULL);
|
||||
|
||||
ut32 sig_len = rz_bv_len(sig) - rz_bv_clz(sig);
|
||||
ut32 mantissa_len = sig_len - 1;
|
||||
|
|
@ -322,6 +322,7 @@ static RzBitVector *round_significant(bool sign, RzBitVector *sig, ut32 precisio
|
|||
|
||||
// default is drop
|
||||
*should_inc = false;
|
||||
*inexact = rz_bv_get(ret, 2) || rz_bv_get(ret, 1) || rz_bv_get(ret, 0);
|
||||
|
||||
if (mode == RZ_FLOAT_RMODE_RNE || mode == RZ_FLOAT_RMODE_RNA) {
|
||||
bool guard_bit = rz_bv_get(ret, 2);
|
||||
|
|
@ -352,7 +353,7 @@ static RzBitVector *round_significant(bool sign, RzBitVector *sig, ut32 precisio
|
|||
}
|
||||
|
||||
if (mode == (sign ? RZ_FLOAT_RMODE_RTN : RZ_FLOAT_RMODE_RTP)) {
|
||||
*should_inc = 1;
|
||||
*should_inc = *inexact;
|
||||
// rshift to remove RGS
|
||||
rz_bv_rshift(ret, 3);
|
||||
return ret;
|
||||
|
|
@ -392,9 +393,7 @@ static RZ_OWN RzFloat *round_float_bv_new(bool sign, st32 exp, RzBitVector *sig,
|
|||
|
||||
// check overflow and underflow
|
||||
if (exp_val > exp_max) {
|
||||
ret = rz_float_new_inf(new_format, sign);
|
||||
ret->exception = RZ_FLOAT_E_OVERFLOW;
|
||||
return ret;
|
||||
return float_overflow_result(new_format, sign, mode);
|
||||
}
|
||||
if (exp_val < exp_min) {
|
||||
ret = rz_float_new_qnan(new_format);
|
||||
|
|
@ -403,8 +402,9 @@ static RZ_OWN RzFloat *round_float_bv_new(bool sign, st32 exp, RzBitVector *sig,
|
|||
}
|
||||
|
||||
bool should_inc = false;
|
||||
bool inexact = false;
|
||||
ut32 bit_prec = new_man_len;
|
||||
RzBitVector *rounded_tmp = round_significant(sign, sig, bit_prec, mode, &should_inc);
|
||||
RzBitVector *rounded_tmp = round_significant(sign, sig, bit_prec, mode, &should_inc, &inexact);
|
||||
|
||||
if (rounded_tmp == NULL) {
|
||||
// TODO: error and report in code
|
||||
|
|
@ -431,8 +431,7 @@ static RZ_OWN RzFloat *round_float_bv_new(bool sign, st32 exp, RzBitVector *sig,
|
|||
exp_val += 1;
|
||||
if (exp_val > exp_max) {
|
||||
// overflow
|
||||
ret = rz_float_new_inf(new_format, sign);
|
||||
ret->exception = RZ_FLOAT_E_OVERFLOW;
|
||||
ret = float_overflow_result(new_format, sign, mode);
|
||||
rz_bv_free(rounded_sig);
|
||||
rz_bv_free(rounded_tmp);
|
||||
return ret;
|
||||
|
|
@ -469,6 +468,9 @@ static RZ_OWN RzFloat *round_float_bv_new(bool sign, st32 exp, RzBitVector *sig,
|
|||
}
|
||||
ret->s = pack_float_bv(sign, exp_bv, rounded_sig, new_format);
|
||||
ret->r = new_format;
|
||||
if (inexact) {
|
||||
ret->exception |= RZ_FLOAT_E_INEXACT;
|
||||
}
|
||||
|
||||
rz_bv_free(exp_bv);
|
||||
rz_bv_free(rounded_sig);
|
||||
|
|
|
|||
44
meson.build
44
meson.build
|
|
@ -744,8 +744,48 @@ if r.returncode() == 1 and get_option('subprojects_check')
|
|||
error(subproject_clean_error_msg)
|
||||
endif
|
||||
|
||||
softfloat_dep = dependency('softfloat', required: get_option('use_sys_softfloat'), static: is_static_build)
|
||||
if not softfloat_dep.found()
|
||||
use_sys_softfloat = get_option('use_sys_softfloat')
|
||||
# Keep the system probes method-specific so a rejected system dependency does
|
||||
# not occupy the generic name overridden by the bundled subproject.
|
||||
softfloat_probe_required = use_sys_softfloat.disabled() ? use_sys_softfloat : false
|
||||
softfloat_dep = dependency('softfloat', required: softfloat_probe_required, static: is_static_build, method: 'pkg-config')
|
||||
if not softfloat_dep.found() and not use_sys_softfloat.disabled() and host_machine.system() in ['darwin', 'ios', 'tvos', 'visionos', 'watchos']
|
||||
softfloat_dep = dependency('softfloat', required: false, static: is_static_build, method: 'extraframework')
|
||||
endif
|
||||
if not softfloat_dep.found() and not use_sys_softfloat.disabled()
|
||||
softfloat_dep = dependency('softfloat', required: use_sys_softfloat, static: is_static_build, method: 'cmake')
|
||||
endif
|
||||
use_bundled_softfloat = not softfloat_dep.found()
|
||||
if softfloat_dep.found()
|
||||
softfloat_tls_probe = '''
|
||||
#include <stdint.h>
|
||||
#include <softfloat.h>
|
||||
#if defined(_MSC_VER)
|
||||
#define RZ_SOFTFLOAT_THREAD_LOCAL __declspec(thread)
|
||||
#else
|
||||
#define RZ_SOFTFLOAT_THREAD_LOCAL __thread
|
||||
#endif
|
||||
/* Fails to compile unless <softfloat.h> itself declares the mutable state
|
||||
* as thread-local, and fails to link unless the library definitions agree
|
||||
* with those declarations. */
|
||||
extern RZ_SOFTFLOAT_THREAD_LOCAL uint_fast8_t softfloat_detectTininess;
|
||||
extern RZ_SOFTFLOAT_THREAD_LOCAL uint_fast8_t softfloat_roundingMode;
|
||||
extern RZ_SOFTFLOAT_THREAD_LOCAL uint_fast8_t softfloat_exceptionFlags;
|
||||
extern RZ_SOFTFLOAT_THREAD_LOCAL uint_fast8_t extF80_roundingPrecision;
|
||||
int main(void) {
|
||||
return softfloat_detectTininess + softfloat_roundingMode +
|
||||
softfloat_exceptionFlags + extF80_roundingPrecision;
|
||||
}
|
||||
'''
|
||||
if not cc.links(softfloat_tls_probe, dependencies: softfloat_dep, name: 'system SoftFloat has thread-local state')
|
||||
if use_sys_softfloat.enabled()
|
||||
error('System SoftFloat must define its mutable state as thread-local')
|
||||
endif
|
||||
warning('System SoftFloat does not provide thread-local state; using the bundled dependency')
|
||||
use_bundled_softfloat = true
|
||||
endif
|
||||
endif
|
||||
if use_bundled_softfloat
|
||||
softfloat_proj = subproject('softfloat', default_options: ['default_library=static'])
|
||||
softfloat_dep = softfloat_proj.get_variable('softfloat_dep')
|
||||
endif
|
||||
|
|
|
|||
76
subprojects/packagefiles/softfloat/include/platform.h
Normal file
76
subprojects/packagefiles/softfloat/include/platform.h
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
|
||||
/*============================================================================
|
||||
|
||||
This C header file is part of the SoftFloat IEEE Floating-Point Arithmetic
|
||||
Package, Release 3e, by John R. Hauser.
|
||||
|
||||
Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017 The Regents of the
|
||||
University of California. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions, and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions, and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
3. Neither the name of the University nor the names of its contributors may
|
||||
be used to endorse or promote products derived from this software without
|
||||
specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS "AS IS", AND ANY
|
||||
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
=============================================================================*/
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
*----------------------------------------------------------------------------*/
|
||||
#define LITTLEENDIAN 1
|
||||
|
||||
/* SOFTFLOAT_FAST_INT64: (as sourced from http://www.jhauser.us/arithmetic/SoftFloat-3/doc/SoftFloat-source.html)
|
||||
* Can be defined to indicate that the build target's implementation of 64-bit
|
||||
* arithmetic is efficient. For newer 64-bit processors, this macro should
|
||||
* usually be defined. For very small microprocessors whose buses and registers
|
||||
* are 8-bit or 16-bit in size, this macro should usually not be defined. Whether
|
||||
* this macro should be defined for a 32-bit processor may depend on the target
|
||||
* machine and the applications that will use SoftFloat. */
|
||||
#define SOFTFLOAT_FAST_INT64 1
|
||||
/* For Rizin, we assume that the only architectures we'll be running on will have
|
||||
* a 64-bit integer. The fast part is not too relevant either, since we don't
|
||||
* care too much about efficiency when running on fringe architectures.
|
||||
*/
|
||||
|
||||
#ifndef THREAD_LOCAL
|
||||
#if defined(__TINYC__)
|
||||
#define THREAD_LOCAL
|
||||
#elif defined(_MSC_VER)
|
||||
#define THREAD_LOCAL __declspec(thread)
|
||||
#elif defined(__has_feature)
|
||||
#if __has_feature(tls)
|
||||
#define THREAD_LOCAL __thread
|
||||
#else
|
||||
#define THREAD_LOCAL
|
||||
#endif
|
||||
#else
|
||||
#define THREAD_LOCAL __thread
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
*----------------------------------------------------------------------------*/
|
||||
#ifdef __GNUC_STDC_INLINE__
|
||||
#define INLINE inline
|
||||
#else
|
||||
#define INLINE extern inline
|
||||
#endif
|
||||
|
|
@ -3,6 +3,7 @@ url = https://github.com/rizinorg/softfloat
|
|||
revision = 537d18e71a51aea70f6b54334854f7014c6458c7
|
||||
directory = softfloat
|
||||
depth = 1
|
||||
patch_directory = softfloat
|
||||
|
||||
[provide]
|
||||
softfloat=softfloat_dep
|
||||
|
|
|
|||
|
|
@ -3,3 +3,4 @@ url = https://github.com/rizinorg/softfloat
|
|||
revision = 537d18e71a51aea70f6b54334854f7014c6458c7
|
||||
directory = softfloat_cross_native
|
||||
depth = 1
|
||||
patch_directory = softfloat
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@ EOF
|
|||
RUN
|
||||
|
||||
NAME=dbg.manycontback
|
||||
TIMEOUT=1200
|
||||
FILE=bins/elf/analysis/ls-linux-x86_64-zlul
|
||||
ARGS=-d -e dbg.bpsysign=true
|
||||
CMDS=<<EOF
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -181,7 +181,7 @@ if get_option('enable_tests')
|
|||
rz_mark_dep,
|
||||
rzheap_dep,
|
||||
lrt,
|
||||
],
|
||||
] + (test == 'unum' ? [mth] : []),
|
||||
install: false,
|
||||
install_rpath: rpath_exe,
|
||||
implicit_include_directories: false,
|
||||
|
|
|
|||
|
|
@ -13,6 +13,103 @@ void print_float(RzFloat *f) {
|
|||
free(str);
|
||||
}
|
||||
|
||||
/* TinyCC builds leave SoftFloat's THREAD_LOCAL empty, so floating-point state
|
||||
* is process-global and the thread-isolation test does not apply. */
|
||||
#if !defined(__TINYC__)
|
||||
typedef struct float_precision_thread_ctx_t {
|
||||
RzFloatRPrecision precision;
|
||||
RzFloatRPrecision saved;
|
||||
RzFloatRPrecision observed;
|
||||
RzThreadSemaphore *ready;
|
||||
RzThreadSemaphore *release;
|
||||
bool set_ok;
|
||||
bool restore_ok;
|
||||
} FloatPrecisionThreadCtx;
|
||||
|
||||
static void *float_precision_thread(void *user) {
|
||||
FloatPrecisionThreadCtx *ctx = user;
|
||||
ctx->saved = rz_float_ext80_get_rounding_precision();
|
||||
ctx->set_ok = rz_float_ext80_set_rounding_precision(ctx->precision);
|
||||
rz_th_sem_post(ctx->ready);
|
||||
rz_th_sem_wait(ctx->release);
|
||||
ctx->observed = rz_float_ext80_get_rounding_precision();
|
||||
ctx->restore_ok = rz_float_ext80_set_rounding_precision(ctx->saved);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static bool ext80_rounding_precision_thread_local_test(void) {
|
||||
RzThreadSemaphore *ready = rz_th_sem_new(0);
|
||||
RzThreadSemaphore *release_32 = rz_th_sem_new(0);
|
||||
RzThreadSemaphore *release_64 = rz_th_sem_new(0);
|
||||
bool semaphores_ok = ready && release_32 && release_64;
|
||||
if (!semaphores_ok) {
|
||||
rz_th_sem_free(ready);
|
||||
rz_th_sem_free(release_32);
|
||||
rz_th_sem_free(release_64);
|
||||
mu_assert_true(semaphores_ok, "create precision test semaphores");
|
||||
}
|
||||
|
||||
FloatPrecisionThreadCtx ctx_32 = {
|
||||
.precision = RZ_FLOAT_RPREC_32,
|
||||
.ready = ready,
|
||||
.release = release_32,
|
||||
};
|
||||
FloatPrecisionThreadCtx ctx_64 = {
|
||||
.precision = RZ_FLOAT_RPREC_64,
|
||||
.ready = ready,
|
||||
.release = release_64,
|
||||
};
|
||||
|
||||
bool main_set_ok = rz_float_ext80_set_rounding_precision(RZ_FLOAT_RPREC_80);
|
||||
RzThread *thread_32 = rz_th_new(float_precision_thread, &ctx_32);
|
||||
if (!thread_32) {
|
||||
rz_th_sem_free(ready);
|
||||
rz_th_sem_free(release_32);
|
||||
rz_th_sem_free(release_64);
|
||||
mu_assert_notnull(thread_32, "create 32-bit precision thread");
|
||||
}
|
||||
rz_th_sem_wait(ready);
|
||||
|
||||
RzThread *thread_64 = rz_th_new(float_precision_thread, &ctx_64);
|
||||
if (!thread_64) {
|
||||
rz_th_sem_post(release_32);
|
||||
rz_th_wait(thread_32);
|
||||
rz_th_free(thread_32);
|
||||
rz_th_sem_free(ready);
|
||||
rz_th_sem_free(release_32);
|
||||
rz_th_sem_free(release_64);
|
||||
mu_assert_notnull(thread_64, "create 64-bit precision thread");
|
||||
}
|
||||
rz_th_sem_wait(ready);
|
||||
|
||||
RzFloatRPrecision main_during_scopes = rz_float_ext80_get_rounding_precision();
|
||||
rz_th_sem_post(release_32);
|
||||
bool wait_32_ok = rz_th_wait(thread_32);
|
||||
rz_th_free(thread_32);
|
||||
rz_th_sem_post(release_64);
|
||||
bool wait_64_ok = rz_th_wait(thread_64);
|
||||
rz_th_free(thread_64);
|
||||
RzFloatRPrecision main_after_scopes = rz_float_ext80_get_rounding_precision();
|
||||
|
||||
rz_th_sem_free(ready);
|
||||
rz_th_sem_free(release_32);
|
||||
rz_th_sem_free(release_64);
|
||||
rz_float_ext80_set_rounding_precision(RZ_FLOAT_RPREC_80);
|
||||
|
||||
mu_assert_true(main_set_ok, "set main thread precision");
|
||||
mu_assert_true(wait_32_ok && wait_64_ok, "join precision test threads");
|
||||
mu_assert_true(ctx_32.set_ok && ctx_64.set_ok, "set worker thread precisions");
|
||||
mu_assert_true(ctx_32.restore_ok && ctx_64.restore_ok, "restore worker thread precisions");
|
||||
mu_assert_eq(ctx_32.saved, RZ_FLOAT_RPREC_80, "32-bit worker starts with default precision");
|
||||
mu_assert_eq(ctx_64.saved, RZ_FLOAT_RPREC_80, "64-bit worker starts with default precision");
|
||||
mu_assert_eq(ctx_32.observed, RZ_FLOAT_RPREC_32, "32-bit worker retains its precision");
|
||||
mu_assert_eq(ctx_64.observed, RZ_FLOAT_RPREC_64, "64-bit worker retains its precision");
|
||||
mu_assert_eq(main_during_scopes, RZ_FLOAT_RPREC_80, "worker scopes do not alter main thread precision");
|
||||
mu_assert_eq(main_after_scopes, RZ_FLOAT_RPREC_80, "interleaved restores do not alter main thread precision");
|
||||
mu_end;
|
||||
}
|
||||
#endif
|
||||
|
||||
bool f32_ieee_format_test(void) {
|
||||
float val = 1.5f;
|
||||
RzFloat *f = rz_float_new_from_f32(val);
|
||||
|
|
@ -79,6 +176,39 @@ bool rz_float_detect_spec_test(void) {
|
|||
mu_end;
|
||||
}
|
||||
|
||||
bool f16_ieee_arithmetic_test(void) {
|
||||
RzFloat *source = rz_float_new_from_f32(1.0f);
|
||||
RzFloat *one = rz_float_convert(source, RZ_FLOAT_IEEE754_BIN_16, RZ_FLOAT_RMODE_RNE);
|
||||
rz_float_free(source);
|
||||
source = rz_float_new_from_f32(2.0f);
|
||||
RzFloat *two = rz_float_convert(source, RZ_FLOAT_IEEE754_BIN_16, RZ_FLOAT_RMODE_RNE);
|
||||
rz_float_free(source);
|
||||
source = rz_float_new_from_f32(3.0f);
|
||||
RzFloat *three = rz_float_convert(source, RZ_FLOAT_IEEE754_BIN_16, RZ_FLOAT_RMODE_RNE);
|
||||
rz_float_free(source);
|
||||
RzFloat *sum = rz_float_add(one, two, RZ_FLOAT_RMODE_RNE);
|
||||
RzFloat *product = rz_float_mul(one, two, RZ_FLOAT_RMODE_RNE);
|
||||
RzFloat *quotient = rz_float_div(two, two, RZ_FLOAT_RMODE_RNE);
|
||||
RzFloat *root = rz_float_sqrt(one, RZ_FLOAT_RMODE_RNE);
|
||||
RzFloat *fma = rz_float_fma(one, two, one, RZ_FLOAT_RMODE_RNE);
|
||||
|
||||
mu_assert_true(rz_float_is_equal(sum, three), "binary16 add");
|
||||
mu_assert_true(rz_float_is_equal(product, two), "binary16 multiply");
|
||||
mu_assert_true(rz_float_is_equal(quotient, one), "binary16 divide");
|
||||
mu_assert_true(rz_float_is_equal(root, one), "binary16 square root");
|
||||
mu_assert_true(rz_float_is_equal(fma, three), "binary16 fused multiply-add");
|
||||
|
||||
rz_float_free(fma);
|
||||
rz_float_free(root);
|
||||
rz_float_free(quotient);
|
||||
rz_float_free(product);
|
||||
rz_float_free(sum);
|
||||
rz_float_free(three);
|
||||
rz_float_free(two);
|
||||
rz_float_free(one);
|
||||
mu_end;
|
||||
}
|
||||
|
||||
bool f32_ieee_add_test(void) {
|
||||
|
||||
RzFloat *f0 = rz_float_new_from_f32(1.5f);
|
||||
|
|
@ -1440,6 +1570,33 @@ bool f32_ieee_cast_test(void) {
|
|||
rz_float_free(expect);
|
||||
rz_bv_free(val);
|
||||
|
||||
// Preserve a lone bit shifted into the sticky position.
|
||||
val = rz_bv_new_from_ut64(32, 134217730);
|
||||
cast_val = rz_float_cast_float(val, RZ_FLOAT_IEEE754_BIN_32, RZ_FLOAT_RMODE_RNE);
|
||||
mu_assert_streq_free(rz_float_as_hex_string(cast_val, true), "0x4d000000", "sticky bit rounds integer conversion down with rne");
|
||||
mu_assert_true(cast_val->exception & RZ_FLOAT_E_INEXACT, "sticky bit marks integer conversion inexact");
|
||||
rz_float_free(cast_val);
|
||||
cast_val = rz_float_cast_float(val, RZ_FLOAT_IEEE754_BIN_32, RZ_FLOAT_RMODE_RTP);
|
||||
mu_assert_streq_free(rz_float_as_hex_string(cast_val, true), "0x4d000001", "sticky bit rounds integer conversion up with rtp");
|
||||
mu_assert_true(cast_val->exception & RZ_FLOAT_E_INEXACT, "directed integer conversion remains inexact");
|
||||
rz_float_free(cast_val);
|
||||
rz_bv_free(val);
|
||||
|
||||
// Integer overflow follows the requested rounding direction and is inexact.
|
||||
val = rz_bv_new(129);
|
||||
rz_bv_set(val, 128, true);
|
||||
cast_val = rz_float_cast_float(val, RZ_FLOAT_IEEE754_BIN_32, RZ_FLOAT_RMODE_RNE);
|
||||
mu_assert_streq_free(rz_float_as_hex_string(cast_val, true), "0x7f800000", "nearest integer overflow produces infinity");
|
||||
mu_assert_true(cast_val->exception & RZ_FLOAT_E_OVERFLOW, "integer overflow raises overflow");
|
||||
mu_assert_true(cast_val->exception & RZ_FLOAT_E_INEXACT, "integer overflow raises inexact");
|
||||
rz_float_free(cast_val);
|
||||
cast_val = rz_float_cast_float(val, RZ_FLOAT_IEEE754_BIN_32, RZ_FLOAT_RMODE_RTZ);
|
||||
mu_assert_streq_free(rz_float_as_hex_string(cast_val, true), "0x7f7fffff", "round-zero integer overflow produces the largest finite value");
|
||||
mu_assert_true(cast_val->exception & RZ_FLOAT_E_OVERFLOW, "directed integer overflow raises overflow");
|
||||
mu_assert_true(cast_val->exception & RZ_FLOAT_E_INEXACT, "directed integer overflow raises inexact");
|
||||
rz_float_free(cast_val);
|
||||
rz_bv_free(val);
|
||||
|
||||
// 1-4. cast-float negative
|
||||
// 1111 1111 1111 1111 -> unsigned : 2^16 - 1 = 65535, signed : -1
|
||||
val = rz_bv_new_from_st64(16, -1);
|
||||
|
|
@ -1479,6 +1636,14 @@ bool f32_ieee_cast_test(void) {
|
|||
rz_bv_free(cast_bv);
|
||||
rz_bv_free(expect_bv);
|
||||
|
||||
fval = rz_float_new_from_f32(1.125f);
|
||||
expect_bv = rz_bv_new_from_ut64(32, 2);
|
||||
cast_bv = rz_float_cast_sint(fval, 32, RZ_FLOAT_RMODE_RTP);
|
||||
mu_assert_true(rz_bv_eq(expect_bv, cast_bv), "sticky bit rounds 1.125f toward positive infinity");
|
||||
rz_float_free(fval);
|
||||
rz_bv_free(cast_bv);
|
||||
rz_bv_free(expect_bv);
|
||||
|
||||
// 2-3. huge
|
||||
fval = rz_float_new_from_f32(1.2345679E8f);
|
||||
expect_bv = rz_bv_new_from_ut64(32, 123456792);
|
||||
|
|
@ -1586,6 +1751,219 @@ static RzFloat *new_f80_from_bytes(const char *bytes) {
|
|||
return ret;
|
||||
}
|
||||
|
||||
bool float_convert_range_test(void) {
|
||||
/* Exact values in the target subnormal range must survive narrowing. */
|
||||
RzFloat *source = rz_float_new_from_f64(0x1p-127);
|
||||
RzFloat *converted = rz_float_convert(source, RZ_FLOAT_IEEE754_BIN_32, RZ_FLOAT_RMODE_RNE);
|
||||
mu_assert_streq_free(rz_float_as_hex_string(converted, true), "0x00400000", "exact binary32 subnormal conversion");
|
||||
mu_assert_eq(converted->exception, 0, "exact subnormal conversion has no IEEE exception");
|
||||
rz_float_free(converted);
|
||||
rz_float_free(source);
|
||||
|
||||
/* A half-ULP below the minimum subnormal exercises gradual underflow and
|
||||
* the sign-dependent directed rounding modes. */
|
||||
source = rz_float_new_from_f64(0x1p-150);
|
||||
converted = rz_float_convert(source, RZ_FLOAT_IEEE754_BIN_32, RZ_FLOAT_RMODE_RNE);
|
||||
mu_assert_streq_free(rz_float_as_hex_string(converted, true), "0x00000000", "ties-to-even rounds half a minimum subnormal to zero");
|
||||
mu_assert_true(converted->exception & RZ_FLOAT_E_UNDERFLOW, "tiny inexact conversion raises underflow");
|
||||
mu_assert_true(converted->exception & RZ_FLOAT_E_INEXACT, "tiny inexact conversion raises inexact");
|
||||
rz_float_free(converted);
|
||||
converted = rz_float_convert(source, RZ_FLOAT_IEEE754_BIN_32, RZ_FLOAT_RMODE_RTP);
|
||||
mu_assert_streq_free(rz_float_as_hex_string(converted, true), "0x00000001", "round-positive produces the minimum positive subnormal");
|
||||
rz_float_free(converted);
|
||||
converted = rz_float_convert(source, RZ_FLOAT_IEEE754_BIN_32, RZ_FLOAT_RMODE_RTZ);
|
||||
mu_assert_streq_free(rz_float_as_hex_string(converted, true), "0x00000000", "round-zero produces positive zero");
|
||||
rz_float_free(converted);
|
||||
rz_float_free(source);
|
||||
|
||||
source = rz_float_new_from_f64(-0x1p-150);
|
||||
converted = rz_float_convert(source, RZ_FLOAT_IEEE754_BIN_32, RZ_FLOAT_RMODE_RTN);
|
||||
mu_assert_streq_free(rz_float_as_hex_string(converted, true), "0x80000001", "round-negative produces the minimum negative subnormal");
|
||||
rz_float_free(converted);
|
||||
converted = rz_float_convert(source, RZ_FLOAT_IEEE754_BIN_32, RZ_FLOAT_RMODE_RTP);
|
||||
mu_assert_streq_free(rz_float_as_hex_string(converted, true), "0x80000000", "round-positive produces negative zero");
|
||||
rz_float_free(converted);
|
||||
rz_float_free(source);
|
||||
|
||||
/* Narrowing must round once at the target precision. */
|
||||
source = rz_float_new_from_f64(1.0 + 0x1p-24);
|
||||
converted = rz_float_convert(source, RZ_FLOAT_IEEE754_BIN_32, RZ_FLOAT_RMODE_RNE);
|
||||
mu_assert_streq_free(rz_float_as_hex_string(converted, true), "0x3f800000", "ties-to-even rounds to 1.0f");
|
||||
mu_assert_true(converted->exception & RZ_FLOAT_E_INEXACT, "precision loss raises inexact");
|
||||
rz_float_free(converted);
|
||||
converted = rz_float_convert(source, RZ_FLOAT_IEEE754_BIN_32, RZ_FLOAT_RMODE_RTP);
|
||||
mu_assert_streq_free(rz_float_as_hex_string(converted, true), "0x3f800001", "round-positive selects the next binary32 value");
|
||||
rz_float_free(converted);
|
||||
rz_float_free(source);
|
||||
|
||||
/* Overflow chooses infinity or the largest finite value according to the
|
||||
* rounding direction, while always reporting overflow and inexact. */
|
||||
source = rz_float_new_from_f64(0x1p128);
|
||||
converted = rz_float_convert(source, RZ_FLOAT_IEEE754_BIN_32, RZ_FLOAT_RMODE_RNE);
|
||||
mu_assert_streq_free(rz_float_as_hex_string(converted, true), "0x7f800000", "nearest overflow produces positive infinity");
|
||||
mu_assert_true(converted->exception & RZ_FLOAT_E_OVERFLOW, "overflow conversion raises overflow");
|
||||
mu_assert_true(converted->exception & RZ_FLOAT_E_INEXACT, "overflow conversion raises inexact");
|
||||
rz_float_free(converted);
|
||||
converted = rz_float_convert(source, RZ_FLOAT_IEEE754_BIN_32, RZ_FLOAT_RMODE_RTZ);
|
||||
mu_assert_streq_free(rz_float_as_hex_string(converted, true), "0x7f7fffff", "round-zero overflow produces the largest finite value");
|
||||
rz_float_free(converted);
|
||||
rz_float_free(source);
|
||||
|
||||
source = rz_float_new_from_f64(-0x1p128);
|
||||
converted = rz_float_convert(source, RZ_FLOAT_IEEE754_BIN_32, RZ_FLOAT_RMODE_RTN);
|
||||
mu_assert_streq_free(rz_float_as_hex_string(converted, true), "0xff800000", "round-negative overflow produces negative infinity");
|
||||
rz_float_free(converted);
|
||||
converted = rz_float_convert(source, RZ_FLOAT_IEEE754_BIN_32, RZ_FLOAT_RMODE_RTP);
|
||||
mu_assert_streq_free(rz_float_as_hex_string(converted, true), "0xff7fffff", "round-positive overflow produces the largest finite negative value");
|
||||
rz_float_free(converted);
|
||||
rz_float_free(source);
|
||||
|
||||
/* Exercise the binary80 source path used by the M68K lifter. */
|
||||
source = new_f80_from_bytes("\x3f\x80\x80\x00\x00\x00\x00\x00\x00\x00");
|
||||
converted = rz_float_convert(source, RZ_FLOAT_IEEE754_BIN_32, RZ_FLOAT_RMODE_RNE);
|
||||
mu_assert_streq_free(rz_float_as_hex_string(converted, true), "0x00400000", "binary80 narrows to an exact binary32 subnormal");
|
||||
mu_assert_eq(converted->exception, 0, "exact binary80 subnormal conversion has no IEEE exception");
|
||||
rz_float_free(converted);
|
||||
rz_float_free(source);
|
||||
|
||||
/* Existing exception metadata is cumulative across conversions. */
|
||||
source = rz_float_new_from_f64(1.0);
|
||||
source->exception = RZ_FLOAT_E_DIV_ZERO;
|
||||
converted = rz_float_convert(source, RZ_FLOAT_IEEE754_BIN_32, RZ_FLOAT_RMODE_RNE);
|
||||
mu_assert_true(converted->exception & RZ_FLOAT_E_DIV_ZERO, "conversion preserves source exception metadata");
|
||||
rz_float_free(converted);
|
||||
rz_float_free(source);
|
||||
|
||||
/* Keep the binary16 dispatch covered as well. */
|
||||
source = rz_float_new_from_f32(1.5f);
|
||||
converted = rz_float_convert(source, RZ_FLOAT_IEEE754_BIN_16, RZ_FLOAT_RMODE_RNE);
|
||||
mu_assert_streq_free(rz_float_as_hex_string(converted, true), "0x3e00", "binary32 converts to binary16");
|
||||
rz_float_free(converted);
|
||||
rz_float_free(source);
|
||||
|
||||
mu_end;
|
||||
}
|
||||
|
||||
bool float_convert_snan_test(void) {
|
||||
/* Converting a signaling NaN quiets it and must raise the
|
||||
* invalid-operation exception, like the SoftFloat conversion paths do. */
|
||||
RzFloat *snan32 = rz_float_new_snan(RZ_FLOAT_IEEE754_BIN_32);
|
||||
RzFloat *converted = rz_float_convert(snan32, RZ_FLOAT_IEEE754_BIN_64, RZ_FLOAT_RMODE_RNE);
|
||||
mu_assert_true(rz_float_is_nan(converted), "sNaN converts to a NaN");
|
||||
mu_assert_eq(rz_float_detect_spec(converted), RZ_FLOAT_SPEC_QNAN, "sNaN conversion quiets the NaN");
|
||||
mu_assert_true(converted->exception & RZ_FLOAT_E_INVALID_OP, "sNaN conversion raises invalid operation");
|
||||
rz_float_free(converted);
|
||||
|
||||
converted = rz_float_convert(snan32, RZ_FLOAT_IEEE754_BIN_32, RZ_FLOAT_RMODE_RNE);
|
||||
mu_assert_eq(rz_float_detect_spec(converted), RZ_FLOAT_SPEC_QNAN, "same-format sNaN conversion quiets the NaN");
|
||||
mu_assert_true(converted->exception & RZ_FLOAT_E_INVALID_OP, "same-format sNaN conversion raises invalid operation");
|
||||
rz_float_free(converted);
|
||||
rz_float_free(snan32);
|
||||
|
||||
/* Quiet NaNs convert without raising invalid operation. */
|
||||
RzFloat *qnan32 = rz_float_new_qnan(RZ_FLOAT_IEEE754_BIN_32);
|
||||
converted = rz_float_convert(qnan32, RZ_FLOAT_IEEE754_BIN_64, RZ_FLOAT_RMODE_RNE);
|
||||
mu_assert_eq(rz_float_detect_spec(converted), RZ_FLOAT_SPEC_QNAN, "qNaN converts to a quiet NaN");
|
||||
mu_assert_false(converted->exception & RZ_FLOAT_E_INVALID_OP, "qNaN conversion does not raise invalid operation");
|
||||
rz_float_free(converted);
|
||||
rz_float_free(qnan32);
|
||||
mu_end;
|
||||
}
|
||||
|
||||
bool f80_ieee_fma_test(void) {
|
||||
/* The exact product-sum (1 + 2^-63) * 1 + (2^-64 - 2^-113) rounds to
|
||||
* 1 + 2^-63 in a single rounding. Staging it through an f128 FMA double
|
||||
* rounds (113 bits, then 64) and the staged tie carries twice, yielding
|
||||
* 1 + 2^-62 instead; a round-to-odd intermediate prevents that. */
|
||||
RzFloatRPrecision saved_precision = rz_float_ext80_get_rounding_precision();
|
||||
rz_float_ext80_set_rounding_precision(RZ_FLOAT_RPREC_80);
|
||||
RzFloat *a = new_f80_from_bytes("\x3f\xff\x80\x00\x00\x00\x00\x00\x00\x01");
|
||||
RzFloat *b = new_f80_from_bytes("\x3f\xff\x80\x00\x00\x00\x00\x00\x00\x00");
|
||||
RzFloat *c = new_f80_from_bytes("\x3f\xbe\xff\xff\xff\xff\xff\xff\x80\x00");
|
||||
RzFloat *res = rz_float_fma(a, b, c, RZ_FLOAT_RMODE_RNE);
|
||||
mu_assert_streq_free(rz_float_as_hex_string(res, true), "0x3fff8000000000000001",
|
||||
"binary80 fma rounds the product-sum once");
|
||||
mu_assert_true(res->exception & RZ_FLOAT_E_INEXACT, "binary80 fma of an inexact result raises inexact");
|
||||
rz_float_free(res);
|
||||
rz_float_free(c);
|
||||
rz_float_free(b);
|
||||
rz_float_free(a);
|
||||
rz_float_ext80_set_rounding_precision(saved_precision);
|
||||
mu_end;
|
||||
}
|
||||
|
||||
bool f80_ieee_special_num_test(void) {
|
||||
RzFloat *pinf = rz_float_new_inf(RZ_FLOAT_IEEE754_BIN_80, false);
|
||||
RzFloat *pseudo_inf = new_f80_from_bytes("\x7f\xff\x00\x00\x00\x00\x00\x00\x00\x00");
|
||||
RzFloat *ninf = rz_float_new_inf(RZ_FLOAT_IEEE754_BIN_80, true);
|
||||
RzFloat *pseudo_ninf = new_f80_from_bytes("\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00");
|
||||
RzFloat *qnan = rz_float_new_qnan(RZ_FLOAT_IEEE754_BIN_80);
|
||||
RzFloat *snan = rz_float_new_snan(RZ_FLOAT_IEEE754_BIN_80);
|
||||
RzFloat *one = new_f80_from_bytes("\x3f\xff\x80\x00\x00\x00\x00\x00\x00\x00");
|
||||
RzFloat *pseudo_denormal = new_f80_from_bytes("\x00\x00\x80\x00\x00\x00\x00\x00\x00\x00");
|
||||
|
||||
mu_assert_streq_free(rz_float_as_hex_string(pinf, true), "0x7fff8000000000000000", "binary80 infinity has an explicit integer bit");
|
||||
mu_assert_streq_free(rz_float_as_hex_string(qnan, true), "0x7fffc000000000000001", "binary80 quiet NaN has distinct integer and quiet bits");
|
||||
mu_assert_streq_free(rz_float_as_hex_string(snan, true), "0x7fff8000000000000001", "binary80 signaling NaN keeps the quiet bit clear");
|
||||
mu_assert_true(rz_float_is_inf(pinf), "detect canonical binary80 infinity");
|
||||
mu_assert_true(rz_float_is_nan(pseudo_inf), "detect unsupported binary80 pseudo-infinity as NaN");
|
||||
mu_assert_true(rz_float_is_nan(pseudo_ninf), "detect unsupported negative binary80 pseudo-infinity as NaN");
|
||||
mu_assert_true(rz_float_cmp(pinf, pseudo_inf) != 0, "canonical and unsupported positive infinity differ");
|
||||
mu_assert_true(rz_float_cmp(ninf, pseudo_ninf) != 0, "canonical and unsupported negative infinity differ");
|
||||
mu_assert_false(rz_float_is_equal(pinf, pseudo_inf), "canonical and unsupported positive infinity are unequal");
|
||||
mu_assert_false(rz_float_is_equal(pseudo_inf, pinf), "unsupported positive infinity equality is symmetric");
|
||||
mu_assert_false(rz_float_is_equal(ninf, pseudo_ninf), "canonical and unsupported negative infinity are unequal");
|
||||
mu_assert_false(rz_float_is_equal(pseudo_ninf, ninf), "unsupported negative infinity equality is symmetric");
|
||||
mu_assert_true(rz_float_detect_spec(qnan) == RZ_FLOAT_SPEC_QNAN, "detect binary80 quiet NaN");
|
||||
mu_assert_true(rz_float_detect_spec(snan) == RZ_FLOAT_SPEC_SNAN, "detect binary80 signaling NaN");
|
||||
mu_assert_false(rz_float_is_zero(pseudo_denormal), "binary80 pseudo-denormal is nonzero");
|
||||
RzFloat *pseudo_qnan = rz_float_dup(qnan);
|
||||
rz_bv_set(pseudo_qnan->s, 63, false);
|
||||
mu_assert_true(rz_float_is_nan(pseudo_qnan), "detect binary80 NaN with a clear integer bit");
|
||||
mu_assert_false(rz_float_is_equal(qnan, pseudo_qnan), "binary80 NaN integer bits are not normalized by equality");
|
||||
|
||||
RzFloat *pred_inf = rz_float_pred(pinf);
|
||||
RzFloat *succ_pred_inf = rz_float_succ(pred_inf);
|
||||
RzFloat *succ_ninf = rz_float_succ(ninf);
|
||||
mu_assert_streq_free(rz_float_as_hex_string(pred_inf, true), "0x7fff7fffffffffffffff", "predecessor follows the binary80 encoding order");
|
||||
mu_assert_streq_free(rz_float_as_hex_string(succ_pred_inf, true), "0x7fff8000000000000000", "successor returns canonical binary80 infinity");
|
||||
mu_assert_true(rz_float_is_equal(pinf, succ_pred_inf), "successor is canonical infinity");
|
||||
mu_assert_true(rz_float_is_equal(succ_pred_inf, pinf), "successor infinity equality is symmetric");
|
||||
mu_assert_streq_free(rz_float_as_hex_string(succ_ninf, true), "0xffff7fffffffffffffff", "successor follows the binary80 encoding order");
|
||||
|
||||
RzFloat *inf_product = rz_float_mul(pinf, pinf, RZ_FLOAT_RMODE_RNE);
|
||||
mu_assert_true(rz_float_is_inf(inf_product), "binary80 infinity multiplied by infinity stays infinite");
|
||||
mu_assert_false(inf_product->exception & RZ_FLOAT_E_INVALID_OP, "infinity multiplied by infinity is valid");
|
||||
|
||||
RzFloat *nan_sum = rz_float_add(qnan, one, RZ_FLOAT_RMODE_RNE);
|
||||
mu_assert_true(rz_float_is_nan(nan_sum), "binary80 quiet NaN propagates through arithmetic");
|
||||
mu_assert_false(nan_sum->exception & RZ_FLOAT_E_INVALID_OP, "quiet NaN propagation does not raise invalid");
|
||||
|
||||
RzFloat *neg_nan = rz_float_neg(qnan);
|
||||
RzFloat *nan32 = rz_float_convert(neg_nan, RZ_FLOAT_IEEE754_BIN_32, RZ_FLOAT_RMODE_RNE);
|
||||
RzFloat *roundtrip_nan = rz_float_convert(nan32, RZ_FLOAT_IEEE754_BIN_80, RZ_FLOAT_RMODE_RNE);
|
||||
mu_assert_true(rz_float_get_sign(roundtrip_nan), "NaN conversion preserves its sign");
|
||||
|
||||
rz_float_free(roundtrip_nan);
|
||||
rz_float_free(nan32);
|
||||
rz_float_free(neg_nan);
|
||||
rz_float_free(nan_sum);
|
||||
rz_float_free(inf_product);
|
||||
rz_float_free(succ_ninf);
|
||||
rz_float_free(succ_pred_inf);
|
||||
rz_float_free(pred_inf);
|
||||
rz_float_free(pseudo_qnan);
|
||||
rz_float_free(pseudo_denormal);
|
||||
rz_float_free(one);
|
||||
rz_float_free(snan);
|
||||
rz_float_free(qnan);
|
||||
rz_float_free(pseudo_ninf);
|
||||
rz_float_free(ninf);
|
||||
rz_float_free(pseudo_inf);
|
||||
rz_float_free(pinf);
|
||||
mu_end;
|
||||
}
|
||||
|
||||
bool f80_ieee_cast_sint_test(void) {
|
||||
// binary80 has no hidden bit: the integer part of the significand is stored
|
||||
// explicitly, unlike other IEEE-754 formats where it is implicit for normals
|
||||
|
|
@ -1757,9 +2135,15 @@ bool f80_ieee_cast_test(void) {
|
|||
}
|
||||
|
||||
bool all_tests() {
|
||||
/* TinyCC builds use process-global SoftFloat state because THREAD_LOCAL is
|
||||
* empty, so the thread-isolation test is intentionally excluded. */
|
||||
#if !defined(__TINYC__)
|
||||
mu_run_test(ext80_rounding_precision_thread_local_test);
|
||||
#endif
|
||||
mu_run_test(rz_float_new_from_hex_test);
|
||||
mu_run_test(f32_ieee_format_test);
|
||||
mu_run_test(rz_float_detect_spec_test);
|
||||
mu_run_test(f16_ieee_arithmetic_test);
|
||||
mu_run_test(f32_ieee_add_test);
|
||||
mu_run_test(f32_ieee_sub_test);
|
||||
mu_run_test(f32_ieee_mul_test);
|
||||
|
|
@ -1783,12 +2167,16 @@ bool all_tests() {
|
|||
mu_run_test(f32_ieee_cast_test);
|
||||
mu_run_test(f80_ieee_cast_sint_test);
|
||||
mu_run_test(f80_ieee_cast_sint_large_test);
|
||||
mu_run_test(float_convert_range_test);
|
||||
mu_run_test(f80_ieee_special_num_test);
|
||||
mu_run_test(f80_ieee_add_test);
|
||||
mu_run_test(f80_ieee_sub_test);
|
||||
mu_run_test(f80_ieee_mul_test);
|
||||
mu_run_test(f80_ieee_div_test);
|
||||
mu_run_test(f80_ieee_sqrt_test);
|
||||
mu_run_test(f80_ieee_cast_test);
|
||||
mu_run_test(float_convert_snan_test);
|
||||
mu_run_test(f80_ieee_fma_test);
|
||||
mu_run_test(f128_ieee_div_test);
|
||||
return tests_passed != tests_run;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -317,11 +317,18 @@ static bool test_il_value_eq() {
|
|||
RzILVal *bv16_42_dup = rz_il_value_new_bitv(rz_bv_new_from_ut64(16, 42));
|
||||
RzILVal *bv16_43 = rz_il_value_new_bitv(rz_bv_new_from_ut64(16, 43));
|
||||
RzILVal *bv8_42 = rz_il_value_new_bitv(rz_bv_new_from_ut64(8, 42));
|
||||
RzFloat *canonical_inf = rz_float_new_inf(RZ_FLOAT_IEEE754_BIN_80, false);
|
||||
RzFloat *largest_finite = rz_float_pred(canonical_inf);
|
||||
RzFloat *pseudo_inf = rz_float_succ(largest_finite);
|
||||
RzILVal *canonical_inf_val = rz_il_value_new_float(canonical_inf);
|
||||
RzILVal *pseudo_inf_val = rz_il_value_new_float(pseudo_inf);
|
||||
|
||||
mu_assert_true(rz_il_value_eq(b0, b0), "eq");
|
||||
mu_assert_true(rz_il_value_eq(b0, b0_dup), "eq");
|
||||
mu_assert_true(rz_il_value_eq(bv16_42, bv16_42), "eq");
|
||||
mu_assert_true(rz_il_value_eq(bv16_42, bv16_42_dup), "eq");
|
||||
mu_assert_true(rz_il_value_eq(canonical_inf_val, pseudo_inf_val), "canonical and pseudo float infinity eq");
|
||||
mu_assert_true(rz_il_value_eq(pseudo_inf_val, canonical_inf_val), "float infinity eq is symmetric");
|
||||
|
||||
mu_assert_false(rz_il_value_eq(b0, b1), "not eq");
|
||||
mu_assert_false(rz_il_value_eq(b0, bv16_42), "not eq");
|
||||
|
|
@ -335,6 +342,9 @@ static bool test_il_value_eq() {
|
|||
rz_il_value_free(bv16_42_dup);
|
||||
rz_il_value_free(bv16_43);
|
||||
rz_il_value_free(bv8_42);
|
||||
rz_il_value_free(canonical_inf_val);
|
||||
rz_il_value_free(pseudo_inf_val);
|
||||
rz_float_free(largest_finite);
|
||||
|
||||
mu_end;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1732,6 +1732,170 @@ static bool test_il_validate_pure_fconvert() {
|
|||
mu_end;
|
||||
}
|
||||
|
||||
static bool test_il_validate_float_rmode_argument() {
|
||||
RzILValidateGlobalContext *ctx = rz_il_validate_global_context_new_empty(24);
|
||||
RzILSortPure sort;
|
||||
RzILValidateReport report = NULL;
|
||||
|
||||
const RzFloatRMode valid_modes[] = {
|
||||
RZ_FLOAT_RMODE_RNE,
|
||||
RZ_FLOAT_RMODE_RNA,
|
||||
RZ_FLOAT_RMODE_RTP,
|
||||
RZ_FLOAT_RMODE_RTN,
|
||||
RZ_FLOAT_RMODE_RTZ,
|
||||
};
|
||||
for (size_t i = 0; i < RZ_ARRAY_SIZE(valid_modes); i++) {
|
||||
RzILOpPure *valid_op = rz_il_op_new_fadd(
|
||||
valid_modes[i],
|
||||
rz_il_op_new_float_from_f32(1.0f),
|
||||
rz_il_op_new_float_from_f32(2.0f));
|
||||
mu_assert_true(rz_il_validate_pure(valid_op, ctx, &sort, &report), "explicitly supported static rounding mode");
|
||||
mu_assert_null(report, "no report for a supported static rounding mode");
|
||||
rz_il_op_pure_free(valid_op);
|
||||
}
|
||||
|
||||
RzILOpPure *op = rz_il_op_new_fadd(
|
||||
RZ_FLOAT_RMODE_UNK,
|
||||
rz_il_op_new_float_from_f32(1.0f),
|
||||
rz_il_op_new_float_from_f32(2.0f));
|
||||
bool val = rz_il_validate_pure(op, ctx, &sort, &report);
|
||||
mu_assert_false(val, "invalid static rounding-mode value");
|
||||
mu_assert_streq_free(report, "Static rounding-mode value of fadd op is invalid.", "report");
|
||||
rz_il_op_pure_free(op);
|
||||
|
||||
op = rz_il_op_new_fadd(
|
||||
(RzFloatRMode)(RZ_FLOAT_RMODE_UNK + 7),
|
||||
rz_il_op_new_float_from_f32(1.0f),
|
||||
rz_il_op_new_float_from_f32(2.0f));
|
||||
val = rz_il_validate_pure(op, ctx, &sort, &report);
|
||||
mu_assert_false(val, "positive unknown static rounding-mode value");
|
||||
mu_assert_streq_free(report, "Static rounding-mode value of fadd op is invalid.", "report");
|
||||
rz_il_op_pure_free(op);
|
||||
|
||||
#define CHECK_INVALID_STATIC_RMODE(op_expr, opcode_name) \
|
||||
do { \
|
||||
op = (op_expr); \
|
||||
val = rz_il_validate_pure(op, ctx, &sort, &report); \
|
||||
mu_assert_false(val, "invalid static rounding mode reaches the shared validator"); \
|
||||
mu_assert_streq_free(report, "Static rounding-mode value of " opcode_name " op is invalid.", "report"); \
|
||||
rz_il_op_pure_free(op); \
|
||||
} while (0)
|
||||
|
||||
CHECK_INVALID_STATIC_RMODE(
|
||||
rz_il_op_new_fcast_int(32, RZ_FLOAT_RMODE_UNK, rz_il_op_new_float_from_f32(1.0f)),
|
||||
"fcast_int");
|
||||
CHECK_INVALID_STATIC_RMODE(
|
||||
rz_il_op_new_fcast_float(RZ_FLOAT_IEEE754_BIN_32, RZ_FLOAT_RMODE_UNK, rz_il_op_new_bitv_from_ut64(32, 1)),
|
||||
"fcast_float");
|
||||
CHECK_INVALID_STATIC_RMODE(
|
||||
rz_il_op_new_frsqrt(RZ_FLOAT_RMODE_UNK, rz_il_op_new_float_from_f32(1.0f)),
|
||||
"frsqrt");
|
||||
CHECK_INVALID_STATIC_RMODE(
|
||||
rz_il_op_new_fhypot(RZ_FLOAT_RMODE_UNK, rz_il_op_new_float_from_f32(1.0f), rz_il_op_new_float_from_f32(2.0f)),
|
||||
"fhypot");
|
||||
CHECK_INVALID_STATIC_RMODE(
|
||||
rz_il_op_new_fmad(RZ_FLOAT_RMODE_UNK, rz_il_op_new_float_from_f32(1.0f), rz_il_op_new_float_from_f32(2.0f), rz_il_op_new_float_from_f32(3.0f)),
|
||||
"fmad");
|
||||
CHECK_INVALID_STATIC_RMODE(
|
||||
rz_il_op_new_fcompound(RZ_FLOAT_RMODE_UNK, rz_il_op_new_float_from_f32(1.0f), rz_il_op_new_bitv_from_ut64(32, 2)),
|
||||
"fcompound");
|
||||
|
||||
#undef CHECK_INVALID_STATIC_RMODE
|
||||
|
||||
op = rz_il_op_new_fadd(
|
||||
(RzFloatRMode)-1,
|
||||
rz_il_op_new_float_from_f32(1.0f),
|
||||
rz_il_op_new_float_from_f32(2.0f));
|
||||
val = rz_il_validate_pure(op, ctx, &sort, &report);
|
||||
mu_assert_false(val, "negative static rounding-mode value");
|
||||
mu_assert_streq_free(report, "Static rounding-mode value of fadd op is invalid.", "report");
|
||||
rz_il_op_pure_free(op);
|
||||
|
||||
op = rz_il_op_new_fadd(
|
||||
RZ_FLOAT_RMODE_RNE,
|
||||
rz_il_op_new_float_from_f32(1.0f),
|
||||
rz_il_op_new_float_from_f32(2.0f));
|
||||
op->op.fadd.rmode.kind = (RzILOpArgFloatRModeKind)(RZ_IL_OP_ARG_FLOAT_RMODE_DYNAMIC + 1);
|
||||
val = rz_il_validate_pure(op, ctx, &sort, &report);
|
||||
mu_assert_false(val, "unknown rounding-mode argument tag");
|
||||
mu_assert_streq_free(report, "Rounding-mode argument kind of fadd op is invalid.", "report");
|
||||
op->op.fadd.rmode.kind = RZ_IL_OP_ARG_FLOAT_RMODE_STATIC;
|
||||
op->op.fadd.rmode.value.static_mode = RZ_FLOAT_RMODE_RNE;
|
||||
rz_il_op_pure_free(op);
|
||||
|
||||
op = rz_il_op_new_fadd(
|
||||
RZ_FLOAT_RMODE_RNE,
|
||||
rz_il_op_new_float_from_f32(1.0f),
|
||||
rz_il_op_new_float_from_f32(2.0f));
|
||||
op->op.fadd.rmode.kind = RZ_IL_OP_ARG_FLOAT_RMODE_DYNAMIC;
|
||||
op->op.fadd.rmode.value.dynamic_mode = NULL;
|
||||
val = rz_il_validate_pure(op, ctx, &sort, &report);
|
||||
mu_assert_false(val, "dynamic rounding-mode argument must not be NULL");
|
||||
mu_assert_streq_free(report, "Rounding-mode operand of fadd op is NULL.", "report");
|
||||
rz_il_op_pure_free(op);
|
||||
|
||||
op = rz_il_op_new_fadd_dyn_rmode(
|
||||
rz_il_op_new_bitv_from_ut64(32, RZ_FLOAT_RMODE_RNE),
|
||||
rz_il_op_new_float_from_f32(1.0f),
|
||||
rz_il_op_new_float_from_f32(2.0f));
|
||||
val = rz_il_validate_pure(op, ctx, &sort, &report);
|
||||
mu_assert_true(val, "32-bit runtime rounding mode");
|
||||
mu_assert_true(rz_il_sort_pure_eq(sort, rz_il_sort_pure_float(RZ_FLOAT_IEEE754_BIN_32)), "sort");
|
||||
mu_assert_null(report, "no report");
|
||||
rz_il_op_pure_free(op);
|
||||
|
||||
op = rz_il_op_new_fadd_dyn_rmode(
|
||||
rz_il_op_new_bitv_from_ut64(8, RZ_FLOAT_RMODE_RNE),
|
||||
rz_il_op_new_float_from_f32(1.0f),
|
||||
rz_il_op_new_float_from_f32(2.0f));
|
||||
val = rz_il_validate_pure(op, ctx, &sort, &report);
|
||||
mu_assert_false(val, "runtime rounding mode must be 32-bit");
|
||||
mu_assert_streq_free(report, "Rounding-mode operand of fadd op must be 32 bits, got 8.", "report");
|
||||
rz_il_op_pure_free(op);
|
||||
|
||||
op = rz_il_op_new_fadd_dyn_rmode(
|
||||
rz_il_op_new_b0(),
|
||||
rz_il_op_new_float_from_f32(1.0f),
|
||||
rz_il_op_new_float_from_f32(2.0f));
|
||||
val = rz_il_validate_pure(op, ctx, &sort, &report);
|
||||
mu_assert_false(val, "runtime rounding mode must be a bitvector");
|
||||
mu_assert_streq_free(report, "Rounding-mode operand of fadd op is not a bitvector.", "report");
|
||||
rz_il_op_pure_free(op);
|
||||
|
||||
op = rz_il_op_new_fadd_dyn_rmode(
|
||||
rz_il_op_new_bitv_from_ut64(32, RZ_FLOAT_RMODE_RNE),
|
||||
rz_il_op_new_float_from_f32(1.0f),
|
||||
rz_il_op_new_float_from_f64(2.0));
|
||||
val = rz_il_validate_pure(op, ctx, &sort, &report);
|
||||
mu_assert_false(val, "runtime-rmode binary operands retain static format checks");
|
||||
mu_assert_streq_free(report,
|
||||
"Op fadd formats of left operand (float:0) and right operand (float:1) do not agree.",
|
||||
"report");
|
||||
rz_il_op_pure_free(op);
|
||||
|
||||
op = rz_il_op_new_fconvert_dyn_rmode(
|
||||
RZ_FLOAT_IEEE754_BIN_32,
|
||||
rz_il_op_new_bitv_from_ut64(32, RZ_FLOAT_RMODE_RNE),
|
||||
rz_il_op_new_float_from_f64(1.0));
|
||||
val = rz_il_validate_pure(op, ctx, &sort, &report);
|
||||
mu_assert_true(val, "runtime-rmode fconvert retains target format");
|
||||
mu_assert_true(rz_il_sort_pure_eq(sort, rz_il_sort_pure_float(RZ_FLOAT_IEEE754_BIN_32)), "converted sort");
|
||||
mu_assert_null(report, "no report");
|
||||
rz_il_op_pure_free(op);
|
||||
|
||||
op = rz_il_op_new_fconvert_dyn_rmode(
|
||||
RZ_FLOAT_UNK,
|
||||
rz_il_op_new_bitv_from_ut64(32, RZ_FLOAT_RMODE_RNE),
|
||||
rz_il_op_new_float_from_f64(1.0));
|
||||
val = rz_il_validate_pure(op, ctx, &sort, &report);
|
||||
mu_assert_false(val, "runtime-rmode fconvert rejects an invalid target format");
|
||||
mu_assert_streq_free(report, "Target format of fconvert op is invalid.", "report");
|
||||
rz_il_op_pure_free(op);
|
||||
|
||||
rz_il_validate_global_context_free(ctx);
|
||||
mu_end;
|
||||
}
|
||||
|
||||
bool all_tests() {
|
||||
mu_run_test(test_il_validate_pure_null);
|
||||
mu_run_test(test_il_validate_pure_bitv);
|
||||
|
|
@ -1775,6 +1939,7 @@ bool all_tests() {
|
|||
mu_run_test(test_il_validate_pure_fcast_to_int);
|
||||
mu_run_test(test_il_validate_pure_icast_to_float);
|
||||
mu_run_test(test_il_validate_pure_fconvert);
|
||||
mu_run_test(test_il_validate_float_rmode_argument);
|
||||
|
||||
return tests_passed != tests_run;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
#include <rz_il.h>
|
||||
#include <rz_util.h>
|
||||
#include <rz_util/rz_graph_drawable.h>
|
||||
#include "minunit.h"
|
||||
#include "rz_il/rz_il_events.h"
|
||||
#include "rz_il/rz_il_opcodes.h"
|
||||
|
|
@ -1090,8 +1091,11 @@ static bool test_rzil_vm_op_fexcept() {
|
|||
|
||||
mu_assert_true(rz_float_is_inf(result), "div by zero should result in infinity");
|
||||
mu_assert_true(e->b, "div by zero exception should be set");
|
||||
mu_assert_eq(rz_pvector_len(vm->events), 1, "div by zero exception event");
|
||||
rz_float_free(result);
|
||||
rz_il_op_pure_free(eop);
|
||||
rz_il_bool_free(e);
|
||||
rz_il_vm_clear_events(vm);
|
||||
|
||||
// 2. Test overflow
|
||||
op = rz_il_op_new_fmul(RZ_FLOAT_RMODE_RNE,
|
||||
|
|
@ -1103,9 +1107,11 @@ static bool test_rzil_vm_op_fexcept() {
|
|||
|
||||
mu_assert_true(rz_float_is_inf(result), "overflow should result in infinity");
|
||||
mu_assert_true(e->b, "overflow exception should be set");
|
||||
mu_assert_eq(rz_pvector_len(vm->events), 1, "overflow exception event");
|
||||
rz_float_free(result);
|
||||
rz_il_op_pure_free(eop);
|
||||
rz_il_bool_free(e);
|
||||
rz_il_vm_clear_events(vm);
|
||||
|
||||
// 3. Test underflow
|
||||
op = rz_il_op_new_fmul(RZ_FLOAT_RMODE_RNE,
|
||||
|
|
@ -1114,8 +1120,10 @@ static bool test_rzil_vm_op_fexcept() {
|
|||
eop = rz_il_op_new_fexcept(RZ_FLOAT_E_UNDERFLOW, op);
|
||||
e = rz_il_evaluate_bool(vm, eop);
|
||||
mu_assert_true(e->b, "underflow exception should be set");
|
||||
mu_assert_eq(rz_pvector_len(vm->events), 1, "underflow exception event");
|
||||
rz_il_op_pure_free(eop);
|
||||
rz_il_bool_free(e);
|
||||
rz_il_vm_clear_events(vm);
|
||||
|
||||
// 4. Test inexact result
|
||||
op = rz_il_op_new_fdiv(RZ_FLOAT_RMODE_RNE,
|
||||
|
|
@ -1124,6 +1132,53 @@ static bool test_rzil_vm_op_fexcept() {
|
|||
eop = rz_il_op_new_fexcept(RZ_FLOAT_E_INEXACT, op);
|
||||
e = rz_il_evaluate_bool(vm, eop);
|
||||
mu_assert_true(e->b, "inexact exception should be set");
|
||||
mu_assert_eq(rz_pvector_len(vm->events), 1, "inexact exception event");
|
||||
rz_il_op_pure_free(eop);
|
||||
rz_il_bool_free(e);
|
||||
rz_il_vm_clear_events(vm);
|
||||
|
||||
// Querying an exception that is not present must not emit an event.
|
||||
op = rz_il_op_new_fdiv(RZ_FLOAT_RMODE_RNE,
|
||||
rz_il_op_new_float_from_f64(1.0),
|
||||
rz_il_op_new_float_from_f64(1.0));
|
||||
eop = rz_il_op_new_fexcept(RZ_FLOAT_E_INVALID_OP, op);
|
||||
e = rz_il_evaluate_bool(vm, eop);
|
||||
mu_assert_false(e->b, "invalid-operation exception should be clear");
|
||||
mu_assert_eq(rz_pvector_len(vm->events), 0, "absent float exception emits no event");
|
||||
rz_il_op_pure_free(eop);
|
||||
rz_il_bool_free(e);
|
||||
|
||||
// Float conversions must preserve exceptions from nested operations.
|
||||
op = rz_il_op_new_fconvert(RZ_FLOAT_IEEE754_BIN_64, RZ_FLOAT_RMODE_RNE,
|
||||
rz_il_op_new_fconvert(RZ_FLOAT_IEEE754_BIN_32, RZ_FLOAT_RMODE_RNE,
|
||||
rz_il_op_new_float_from_f64(1.0 + 0x1p-30)));
|
||||
eop = rz_il_op_new_fexcept(RZ_FLOAT_E_INEXACT, op);
|
||||
e = rz_il_evaluate_bool(vm, eop);
|
||||
mu_assert_true(e->b, "float conversion preserves nested inexact exception");
|
||||
rz_il_op_pure_free(eop);
|
||||
rz_il_bool_free(e);
|
||||
rz_il_vm_clear_events(vm);
|
||||
|
||||
// Arithmetic composition must preserve exceptions raised by child operations.
|
||||
op = rz_il_op_new_fadd(RZ_FLOAT_RMODE_RNE,
|
||||
rz_il_op_new_float_from_f32(0.0f),
|
||||
rz_il_op_new_fmul(RZ_FLOAT_RMODE_RNE,
|
||||
rz_il_op_new_float_from_f32(1e38f),
|
||||
rz_il_op_new_float_from_f32(1e38f)));
|
||||
eop = rz_il_op_new_fexcept(RZ_FLOAT_E_OVERFLOW, op);
|
||||
e = rz_il_evaluate_bool(vm, eop);
|
||||
mu_assert_true(e->b, "float arithmetic preserves nested overflow exception");
|
||||
rz_il_op_pure_free(eop);
|
||||
rz_il_bool_free(e);
|
||||
rz_il_vm_clear_events(vm);
|
||||
|
||||
op = rz_il_op_new_fconvert(RZ_FLOAT_IEEE754_BIN_32, RZ_FLOAT_RMODE_RNE,
|
||||
rz_il_op_new_fsub(RZ_FLOAT_RMODE_RNE,
|
||||
rz_il_op_new_float_from_f64(F64_PINF),
|
||||
rz_il_op_new_float_from_f64(F64_PINF)));
|
||||
eop = rz_il_op_new_fexcept(RZ_FLOAT_E_INVALID_OP, op);
|
||||
e = rz_il_evaluate_bool(vm, eop);
|
||||
mu_assert_true(e->b, "float conversion preserves nested invalid-operation exception");
|
||||
rz_il_op_pure_free(eop);
|
||||
rz_il_bool_free(e);
|
||||
|
||||
|
|
@ -1131,6 +1186,467 @@ static bool test_rzil_vm_op_fexcept() {
|
|||
mu_end;
|
||||
}
|
||||
|
||||
static bool test_rzil_vm_binary80_full_precision() {
|
||||
RzILVM *vm = rz_il_vm_new(0, 32, false, RZ_IL_EVENT_EXC_NONE);
|
||||
RzFloatRPrecision original_precision = rz_float_ext80_get_rounding_precision();
|
||||
mu_assert_true(original_precision != RZ_FLOAT_RPREC_UNK, "initial binary80 precision is valid");
|
||||
|
||||
const struct {
|
||||
RzFloatRPrecision ambient_precision;
|
||||
long double input;
|
||||
} mul_cases[] = {
|
||||
{ RZ_FLOAT_RPREC_32, 1.0L + 0x1.8p-23L },
|
||||
{ RZ_FLOAT_RPREC_64, 1.0L + 0x1.8p-52L },
|
||||
};
|
||||
for (size_t i = 0; i < RZ_ARRAY_SIZE(mul_cases); i++) {
|
||||
mu_assert_true(rz_float_ext80_set_rounding_precision(mul_cases[i].ambient_precision), "set reduced ambient precision");
|
||||
RzILOpFloat *op = rz_il_op_new_fmul(RZ_FLOAT_RMODE_RNE,
|
||||
rz_il_op_new_float_from_f80(mul_cases[i].input),
|
||||
rz_il_op_new_float_from_f80(1.0L));
|
||||
RzFloat *actual = rz_il_evaluate_float(vm, op);
|
||||
RzFloat *expected = rz_float_new_from_f80(mul_cases[i].input);
|
||||
mu_assert_notnull(actual, "binary80 multiply");
|
||||
mu_assert_false(rz_float_cmp(actual, expected), "RzIL binary80 multiply uses full precision");
|
||||
mu_assert_eq(rz_float_ext80_get_rounding_precision(), mul_cases[i].ambient_precision, "multiply restores ambient precision");
|
||||
rz_float_free(actual);
|
||||
rz_float_free(expected);
|
||||
rz_il_op_pure_free(op);
|
||||
}
|
||||
|
||||
mu_assert_true(rz_float_ext80_set_rounding_precision(RZ_FLOAT_RPREC_80), "set full precision for reference sqrt");
|
||||
RzFloat *sqrt_input = rz_float_new_from_f80(2.0L);
|
||||
RzFloat *expected = rz_float_sqrt(sqrt_input, RZ_FLOAT_RMODE_RNE);
|
||||
rz_float_free(sqrt_input);
|
||||
mu_assert_notnull(expected, "full-precision sqrt reference");
|
||||
mu_assert_true(rz_float_ext80_set_rounding_precision(RZ_FLOAT_RPREC_32), "set reduced precision before RzIL sqrt");
|
||||
RzILOpFloat *op = rz_il_op_new_fsqrt(RZ_FLOAT_RMODE_RNE, rz_il_op_new_float_from_f80(2.0L));
|
||||
RzFloat *actual = rz_il_evaluate_float(vm, op);
|
||||
mu_assert_notnull(actual, "binary80 sqrt");
|
||||
mu_assert_false(rz_float_cmp(actual, expected), "RzIL binary80 sqrt uses full precision");
|
||||
mu_assert_eq(rz_float_ext80_get_rounding_precision(), RZ_FLOAT_RPREC_32, "sqrt restores ambient precision");
|
||||
rz_float_free(actual);
|
||||
rz_float_free(expected);
|
||||
rz_il_op_pure_free(op);
|
||||
|
||||
mu_assert_true(rz_float_ext80_set_rounding_precision(RZ_FLOAT_RPREC_32), "set reduced precision before RzIL FMA");
|
||||
op = rz_il_op_new_fmad(RZ_FLOAT_RMODE_RNE,
|
||||
rz_il_op_new_float_from_f80(1.0L),
|
||||
rz_il_op_new_float_from_f80(1.0L),
|
||||
rz_il_op_new_float_from_f80(0x1p-30L));
|
||||
actual = rz_il_evaluate_float(vm, op);
|
||||
expected = rz_float_new_from_f80(1.0L + 0x1p-30L);
|
||||
mu_assert_notnull(actual, "binary80 fused multiply-add");
|
||||
mu_assert_false(rz_float_cmp(actual, expected), "RzIL binary80 FMA uses full precision");
|
||||
mu_assert_eq(rz_float_ext80_get_rounding_precision(), RZ_FLOAT_RPREC_32, "FMA restores ambient precision");
|
||||
rz_float_free(actual);
|
||||
rz_float_free(expected);
|
||||
rz_il_op_pure_free(op);
|
||||
|
||||
mu_assert_true(rz_float_ext80_set_rounding_precision(original_precision), "restore initial binary80 precision");
|
||||
rz_il_vm_free(vm);
|
||||
mu_end;
|
||||
}
|
||||
|
||||
static bool runtime_rmode_result_is(RzILVM *vm, RzILOpFloat *op, const char *expected) {
|
||||
if (!op) {
|
||||
return false;
|
||||
}
|
||||
RzFloat *actual = rz_il_evaluate_float(vm, op);
|
||||
char *hex = actual ? rz_float_as_hex_string(actual, true) : NULL;
|
||||
bool matches = hex && !strcmp(hex, expected);
|
||||
free(hex);
|
||||
rz_float_free(actual);
|
||||
rz_il_op_pure_free(op);
|
||||
return matches;
|
||||
}
|
||||
|
||||
static bool test_rzil_vm_float_rmode_arguments() {
|
||||
RzILOpFloat *op = rz_il_op_new_fadd(
|
||||
RZ_FLOAT_RMODE_RTZ,
|
||||
rz_il_op_new_float_from_f32(1.0f),
|
||||
rz_il_op_new_float_from_f32(2.0f));
|
||||
mu_assert_notnull(op, "static-rmode op");
|
||||
mu_assert_eq(op->code, RZ_IL_OP_FADD, "static constructor uses base opcode");
|
||||
mu_assert_eq(op->op.fadd.rmode.kind, RZ_IL_OP_ARG_FLOAT_RMODE_STATIC, "static rmode tag");
|
||||
mu_assert_eq(op->op.fadd.rmode.value.static_mode, RZ_FLOAT_RMODE_RTZ, "static rmode value");
|
||||
|
||||
RzILOpPure *copy = rz_il_op_pure_dup(op);
|
||||
mu_assert_notnull(copy, "static-rmode op copy");
|
||||
mu_assert_eq(copy->code, RZ_IL_OP_FADD, "copied static op keeps base opcode");
|
||||
mu_assert_eq(copy->op.fadd.rmode.kind, RZ_IL_OP_ARG_FLOAT_RMODE_STATIC, "copied static rmode tag");
|
||||
mu_assert_eq(copy->op.fadd.rmode.value.static_mode, RZ_FLOAT_RMODE_RTZ, "copied static rmode value");
|
||||
|
||||
RzStrBuf text;
|
||||
rz_strbuf_init(&text);
|
||||
rz_il_op_pure_stringify(copy, &text, false);
|
||||
mu_assert_strcontains(rz_strbuf_get(&text), "(+. rtz ", "static rmode keeps the canonical compact form");
|
||||
rz_strbuf_fini(&text);
|
||||
|
||||
PJ *pj = pj_new();
|
||||
rz_il_op_pure_json(copy, pj);
|
||||
char *json = pj_drain(pj);
|
||||
mu_assert_strcontains(json, "\"opcode\":\"+.\"", "static rmode keeps the canonical JSON opcode");
|
||||
mu_assert_strcontains(json, "\"rmode\":\"rtz\"", "static rmode stays a JSON string");
|
||||
free(json);
|
||||
rz_il_op_pure_free(copy);
|
||||
rz_il_op_pure_free(op);
|
||||
|
||||
op = rz_il_op_new_fmod_dyn_rmode(
|
||||
rz_il_op_new_bitv_from_ut64(32, RZ_FLOAT_RMODE_RNE),
|
||||
rz_il_op_new_float_from_f32(5.0f),
|
||||
rz_il_op_new_float_from_f32(2.0f));
|
||||
rz_strbuf_init(&text);
|
||||
rz_il_op_pure_stringify(op, &text, false);
|
||||
mu_assert_strcontains(rz_strbuf_get(&text), "(%. (bv 32", "dynamic fmod keeps the canonical compact opcode");
|
||||
rz_strbuf_fini(&text);
|
||||
rz_strbuf_init(&text);
|
||||
rz_il_op_pure_stringify(op, &text, true);
|
||||
mu_assert_strcontains(rz_strbuf_get(&text), "(%.\n", "dynamic fmod keeps the canonical pretty opcode");
|
||||
rz_strbuf_fini(&text);
|
||||
rz_il_op_pure_free(op);
|
||||
mu_end;
|
||||
}
|
||||
|
||||
static bool test_rzil_vm_all_float_rmode_payloads() {
|
||||
RzILValidateGlobalContext *validate_ctx = rz_il_validate_global_context_new_empty(32);
|
||||
RzILSortPure sort;
|
||||
|
||||
#define CHECK_DYNAMIC_RMODE_OP(op_expr, expected_code, member) \
|
||||
do { \
|
||||
RzILOpPure *dyn_op = (RzILOpPure *)(op_expr); \
|
||||
mu_assert_notnull(dyn_op, "dynamic-rmode constructor"); \
|
||||
mu_assert_eq(dyn_op->code, expected_code, "dynamic constructor uses the base opcode"); \
|
||||
mu_assert_eq(dyn_op->op.member.rmode.kind, RZ_IL_OP_ARG_FLOAT_RMODE_DYNAMIC, "dynamic rmode tag"); \
|
||||
mu_assert_eq(dyn_op->op.member.rmode.value.dynamic_mode->code, RZ_IL_OP_BITV, "dynamic rmode child"); \
|
||||
RzILValidateReport report = NULL; \
|
||||
mu_assert_true(rz_il_validate_pure(dyn_op, validate_ctx, &sort, &report), "dynamic-rmode payload validates"); \
|
||||
mu_assert_null(report, "no dynamic-rmode validation report"); \
|
||||
RzILOpPure *dyn_copy = rz_il_op_pure_dup(dyn_op); \
|
||||
mu_assert_notnull(dyn_copy, "dynamic-rmode payload copy"); \
|
||||
mu_assert_eq(dyn_copy->op.member.rmode.kind, RZ_IL_OP_ARG_FLOAT_RMODE_DYNAMIC, "copied dynamic rmode tag"); \
|
||||
mu_assert_ptrneq(dyn_copy->op.member.rmode.value.dynamic_mode, dyn_op->op.member.rmode.value.dynamic_mode, "dynamic rmode child is deep-copied"); \
|
||||
RzStrBuf compact; \
|
||||
rz_strbuf_init(&compact); \
|
||||
rz_il_op_pure_stringify(dyn_op, &compact, false); \
|
||||
mu_assert_strcontains(rz_strbuf_get(&compact), "(bv 32", "compact export includes the dynamic rmode child"); \
|
||||
rz_strbuf_fini(&compact); \
|
||||
PJ *pj = pj_new(); \
|
||||
rz_il_op_pure_json(dyn_op, pj); \
|
||||
char *json = pj_drain(pj); \
|
||||
mu_assert_strcontains(json, "\"rmode\":{\"opcode\":\"bitv\"", "JSON export represents the dynamic rmode as an IL child"); \
|
||||
free(json); \
|
||||
RzILStringifyCtx stringify_ctx = { .indent = 0, .indent_inc = 2 }; \
|
||||
RzStrBuf unicode; \
|
||||
rz_strbuf_init(&unicode); \
|
||||
mu_assert_true(rz_il_op_pure_stringify_unicode(&stringify_ctx, dyn_op, &unicode), "Unicode dynamic-rmode export"); \
|
||||
rz_strbuf_fini(&unicode); \
|
||||
RzGraph *graph = rz_il_op_pure_graph(dyn_op, "dynamic-rmode"); \
|
||||
mu_assert_notnull(graph, "graph dynamic-rmode export"); \
|
||||
rz_graph_free(graph); \
|
||||
rz_il_op_pure_free(dyn_copy); \
|
||||
rz_il_op_pure_free(dyn_op); \
|
||||
} while (0)
|
||||
|
||||
CHECK_DYNAMIC_RMODE_OP(
|
||||
rz_il_op_new_fcast_int_dyn_rmode(32, rz_il_op_new_bitv_from_ut64(32, RZ_FLOAT_RMODE_RNE), rz_il_op_new_float_from_f32(1.0f)),
|
||||
RZ_IL_OP_FCAST_INT, fcast_int);
|
||||
CHECK_DYNAMIC_RMODE_OP(
|
||||
rz_il_op_new_fcast_sint_dyn_rmode(32, rz_il_op_new_bitv_from_ut64(32, RZ_FLOAT_RMODE_RNE), rz_il_op_new_float_from_f32(-1.0f)),
|
||||
RZ_IL_OP_FCAST_SINT, fcast_sint);
|
||||
CHECK_DYNAMIC_RMODE_OP(
|
||||
rz_il_op_new_fcast_float_dyn_rmode(RZ_FLOAT_IEEE754_BIN_32, rz_il_op_new_bitv_from_ut64(32, RZ_FLOAT_RMODE_RNE), rz_il_op_new_bitv_from_ut64(32, 1)),
|
||||
RZ_IL_OP_FCAST_FLOAT, fcast_float);
|
||||
CHECK_DYNAMIC_RMODE_OP(
|
||||
rz_il_op_new_fcast_sfloat_dyn_rmode(RZ_FLOAT_IEEE754_BIN_32, rz_il_op_new_bitv_from_ut64(32, RZ_FLOAT_RMODE_RNE), rz_il_op_new_bitv_from_st64(32, -1)),
|
||||
RZ_IL_OP_FCAST_SFLOAT, fcast_sfloat);
|
||||
CHECK_DYNAMIC_RMODE_OP(
|
||||
rz_il_op_new_fconvert_dyn_rmode(RZ_FLOAT_IEEE754_BIN_64, rz_il_op_new_bitv_from_ut64(32, RZ_FLOAT_RMODE_RNE), rz_il_op_new_float_from_f32(1.0f)),
|
||||
RZ_IL_OP_FCONVERT, fconvert);
|
||||
CHECK_DYNAMIC_RMODE_OP(
|
||||
rz_il_op_new_frsqrt_dyn_rmode(rz_il_op_new_bitv_from_ut64(32, RZ_FLOAT_RMODE_RNE), rz_il_op_new_float_from_f32(1.0f)),
|
||||
RZ_IL_OP_FRSQRT, frsqrt);
|
||||
CHECK_DYNAMIC_RMODE_OP(
|
||||
rz_il_op_new_fhypot_dyn_rmode(rz_il_op_new_bitv_from_ut64(32, RZ_FLOAT_RMODE_RNE), rz_il_op_new_float_from_f32(3.0f), rz_il_op_new_float_from_f32(4.0f)),
|
||||
RZ_IL_OP_FHYPOT, fhypot);
|
||||
CHECK_DYNAMIC_RMODE_OP(
|
||||
rz_il_op_new_fpow_dyn_rmode(rz_il_op_new_bitv_from_ut64(32, RZ_FLOAT_RMODE_RNE), rz_il_op_new_float_from_f32(2.0f), rz_il_op_new_float_from_f32(3.0f)),
|
||||
RZ_IL_OP_FPOW, fpow);
|
||||
CHECK_DYNAMIC_RMODE_OP(
|
||||
rz_il_op_new_fmad_dyn_rmode(rz_il_op_new_bitv_from_ut64(32, RZ_FLOAT_RMODE_RNE), rz_il_op_new_float_from_f32(1.0f), rz_il_op_new_float_from_f32(2.0f), rz_il_op_new_float_from_f32(3.0f)),
|
||||
RZ_IL_OP_FMAD, fmad);
|
||||
CHECK_DYNAMIC_RMODE_OP(
|
||||
rz_il_op_new_frootn_dyn_rmode(rz_il_op_new_bitv_from_ut64(32, RZ_FLOAT_RMODE_RNE), rz_il_op_new_float_from_f32(4.0f), rz_il_op_new_bitv_from_ut64(32, 2)),
|
||||
RZ_IL_OP_FROOTN, frootn);
|
||||
CHECK_DYNAMIC_RMODE_OP(
|
||||
rz_il_op_new_fpown_dyn_rmode(rz_il_op_new_bitv_from_ut64(32, RZ_FLOAT_RMODE_RNE), rz_il_op_new_float_from_f32(2.0f), rz_il_op_new_bitv_from_ut64(32, 3)),
|
||||
RZ_IL_OP_FPOWN, fpown);
|
||||
CHECK_DYNAMIC_RMODE_OP(
|
||||
rz_il_op_new_fcompound_dyn_rmode(rz_il_op_new_bitv_from_ut64(32, RZ_FLOAT_RMODE_RNE), rz_il_op_new_float_from_f32(0.5f), rz_il_op_new_bitv_from_ut64(32, 2)),
|
||||
RZ_IL_OP_FCOMPOUND, fcompound);
|
||||
|
||||
#undef CHECK_DYNAMIC_RMODE_OP
|
||||
|
||||
rz_il_validate_global_context_free(validate_ctx);
|
||||
mu_end;
|
||||
}
|
||||
|
||||
static bool test_rzil_vm_runtime_rmode_helpers() {
|
||||
RzILVM *vm = rz_il_vm_new(0, 32, false, RZ_IL_EVENT_EXC_NONE);
|
||||
const struct {
|
||||
ut32 mode;
|
||||
const char *expected;
|
||||
} add_cases[] = {
|
||||
{ RZ_FLOAT_RMODE_RNE, "0x3f800000" },
|
||||
{ RZ_FLOAT_RMODE_RNA, "0x3f800001" },
|
||||
{ RZ_FLOAT_RMODE_RTP, "0x3f800001" },
|
||||
{ RZ_FLOAT_RMODE_RTN, "0x3f800000" },
|
||||
{ RZ_FLOAT_RMODE_RTZ, "0x3f800000" },
|
||||
};
|
||||
for (size_t i = 0; i < RZ_ARRAY_SIZE(add_cases); i++) {
|
||||
RzILOpFloat *op = rz_il_op_new_fadd_dyn_rmode(
|
||||
rz_il_op_new_bitv_from_ut64(32, add_cases[i].mode),
|
||||
rz_il_op_new_float_from_f32(1.0f),
|
||||
rz_il_op_new_float_from_f32(0x1p-24f));
|
||||
mu_assert_true(runtime_rmode_result_is(vm, op, add_cases[i].expected), "runtime fadd rounding-mode selection");
|
||||
}
|
||||
|
||||
RzILOpFloat *op = rz_il_op_new_fadd_dyn_rmode(
|
||||
rz_il_op_new_bitv_from_ut64(32, RZ_FLOAT_RMODE_UNK + 1),
|
||||
rz_il_op_new_float_from_f32(1.0f),
|
||||
rz_il_op_new_float_from_f32(0x1p-24f));
|
||||
mu_assert_true(runtime_rmode_result_is(vm, op, "0x3f800000"), "invalid runtime rounding mode falls back to RNE");
|
||||
|
||||
op = rz_il_op_new_fadd(
|
||||
(RzFloatRMode)(RZ_FLOAT_RMODE_UNK + 1),
|
||||
rz_il_op_new_float_from_f32(1.0f),
|
||||
rz_il_op_new_float_from_f32(0x1p-24f));
|
||||
mu_assert_true(runtime_rmode_result_is(vm, op, "0x3f800000"), "invalid static rounding mode falls back to RNE");
|
||||
|
||||
op = rz_il_op_new_fadd_dyn_rmode(
|
||||
rz_il_op_new_add(
|
||||
rz_il_op_new_bitv_from_ut64(32, RZ_FLOAT_RMODE_RTP),
|
||||
rz_il_op_new_bitv_from_ut64(32, 0)),
|
||||
rz_il_op_new_float_from_f32(1.0f),
|
||||
rz_il_op_new_float_from_f32(0x1p-24f));
|
||||
mu_assert_true(runtime_rmode_result_is(vm, op, "0x3f800001"), "runtime rounding-mode expressions are evaluated");
|
||||
|
||||
RzILOpFloat *bad_op = rz_il_op_new_fadd_dyn_rmode(
|
||||
rz_il_op_new_bitv_from_ut64(8, RZ_FLOAT_RMODE_RNE),
|
||||
rz_il_op_new_float_from_f32(1.0f),
|
||||
rz_il_op_new_float_from_f32(0.0f));
|
||||
RzFloat *bad_result = rz_il_evaluate_float(vm, bad_op);
|
||||
mu_assert_null(bad_result, "non-32-bit runtime rounding mode fails direct evaluation");
|
||||
rz_il_op_pure_free(bad_op);
|
||||
|
||||
op = rz_il_op_new_fconvert_dyn_rmode(
|
||||
RZ_FLOAT_IEEE754_BIN_32,
|
||||
rz_il_op_new_bitv_from_ut64(32, RZ_FLOAT_RMODE_RTP),
|
||||
rz_il_op_new_float_from_f64(1.0 + 0x1p-24));
|
||||
mu_assert_true(runtime_rmode_result_is(vm, op, "0x3f800001"), "runtime fconvert rounding mode");
|
||||
|
||||
op = rz_il_op_new_fround_dyn_rmode(
|
||||
rz_il_op_new_bitv_from_ut64(32, RZ_FLOAT_RMODE_RNA),
|
||||
rz_il_op_new_float_from_f64(0.5));
|
||||
mu_assert_true(runtime_rmode_result_is(vm, op, "0x3ff0000000000000"), "runtime fround rounding mode");
|
||||
|
||||
op = rz_il_op_new_fsqrt_dyn_rmode(
|
||||
rz_il_op_new_bitv_from_ut64(32, RZ_FLOAT_RMODE_RTZ),
|
||||
rz_il_op_new_float_from_f64(4.0));
|
||||
mu_assert_true(runtime_rmode_result_is(vm, op, "0x4000000000000000"), "runtime fsqrt rounding mode");
|
||||
|
||||
op = rz_il_op_new_fsub_dyn_rmode(
|
||||
rz_il_op_new_bitv_from_ut64(32, RZ_FLOAT_RMODE_RNE),
|
||||
rz_il_op_new_float_from_f32(3.0f),
|
||||
rz_il_op_new_float_from_f32(2.0f));
|
||||
mu_assert_true(runtime_rmode_result_is(vm, op, "0x3f800000"), "runtime fsub rounding mode");
|
||||
|
||||
op = rz_il_op_new_fmul_dyn_rmode(
|
||||
rz_il_op_new_bitv_from_ut64(32, RZ_FLOAT_RMODE_RNE),
|
||||
rz_il_op_new_float_from_f32(3.0f),
|
||||
rz_il_op_new_float_from_f32(2.0f));
|
||||
mu_assert_true(runtime_rmode_result_is(vm, op, "0x40c00000"), "runtime fmul rounding mode");
|
||||
|
||||
op = rz_il_op_new_fdiv_dyn_rmode(
|
||||
rz_il_op_new_bitv_from_ut64(32, RZ_FLOAT_RMODE_RNE),
|
||||
rz_il_op_new_float_from_f32(6.0f),
|
||||
rz_il_op_new_float_from_f32(2.0f));
|
||||
mu_assert_true(runtime_rmode_result_is(vm, op, "0x40400000"), "runtime fdiv rounding mode");
|
||||
|
||||
op = rz_il_op_new_fmod_dyn_rmode(
|
||||
rz_il_op_new_bitv_from_ut64(32, RZ_FLOAT_RMODE_RNE),
|
||||
rz_il_op_new_float_from_f32(5.0f),
|
||||
rz_il_op_new_float_from_f32(2.0f));
|
||||
mu_assert_true(runtime_rmode_result_is(vm, op, "0x3f800000"), "runtime fmod rounding mode");
|
||||
|
||||
RzILOpBitVector *cast_int_op = rz_il_op_new_fcast_int_dyn_rmode(
|
||||
32,
|
||||
rz_il_op_new_bitv_from_ut64(32, RZ_FLOAT_RMODE_RTP),
|
||||
rz_il_op_new_float_from_f32(1.125f));
|
||||
RzBitVector *cast_int = rz_il_evaluate_bitv(vm, cast_int_op);
|
||||
mu_assert_notnull(cast_int, "runtime unsigned float-to-integer cast");
|
||||
mu_assert_eq(rz_bv_to_ut64(cast_int), 2, "runtime unsigned float-to-integer rounding mode");
|
||||
rz_bv_free(cast_int);
|
||||
rz_il_op_pure_free(cast_int_op);
|
||||
|
||||
RzILOpBitVector *cast_sint_op = rz_il_op_new_fcast_sint_dyn_rmode(
|
||||
32,
|
||||
rz_il_op_new_bitv_from_ut64(32, RZ_FLOAT_RMODE_RTN),
|
||||
rz_il_op_new_float_from_f32(-0.125f));
|
||||
RzBitVector *cast_sint = rz_il_evaluate_bitv(vm, cast_sint_op);
|
||||
mu_assert_notnull(cast_sint, "runtime signed float-to-integer cast");
|
||||
mu_assert_eq(rz_bv_to_ut32(cast_sint), UT32_MAX, "runtime signed float-to-integer rounding mode");
|
||||
rz_bv_free(cast_sint);
|
||||
rz_il_op_pure_free(cast_sint_op);
|
||||
|
||||
op = rz_il_op_new_fcast_float_dyn_rmode(
|
||||
RZ_FLOAT_IEEE754_BIN_32,
|
||||
rz_il_op_new_bitv_from_ut64(32, RZ_FLOAT_RMODE_RTP),
|
||||
rz_il_op_new_bitv_from_ut64(32, 134217730));
|
||||
mu_assert_true(runtime_rmode_result_is(vm, op, "0x4d000001"), "runtime unsigned integer-to-float rounding mode");
|
||||
|
||||
op = rz_il_op_new_fcast_sfloat_dyn_rmode(
|
||||
RZ_FLOAT_IEEE754_BIN_32,
|
||||
rz_il_op_new_bitv_from_ut64(32, RZ_FLOAT_RMODE_RTP),
|
||||
rz_il_op_new_bitv_from_ut64(32, 134217730));
|
||||
mu_assert_true(runtime_rmode_result_is(vm, op, "0x4d000001"), "runtime signed integer-to-float rounding mode");
|
||||
|
||||
op = rz_il_op_new_fmad_dyn_rmode(
|
||||
rz_il_op_new_bitv_from_ut64(32, RZ_FLOAT_RMODE_RTP),
|
||||
rz_il_op_new_float_from_f32(1.0f),
|
||||
rz_il_op_new_float_from_f32(1.0f),
|
||||
rz_il_op_new_float_from_f32(0x1p-24f));
|
||||
mu_assert_true(runtime_rmode_result_is(vm, op, "0x3f800001"), "runtime fused multiply-add rounding mode");
|
||||
|
||||
rz_il_vm_free(vm);
|
||||
mu_end;
|
||||
}
|
||||
|
||||
#include <rz_il/rz_il_opbuilder_begin.h>
|
||||
|
||||
static bool test_rzil_opbuilder_float_rmode_aliases() {
|
||||
RzILOpFloat *op = FMOD(
|
||||
RZ_FLOAT_RMODE_RNE,
|
||||
F32(5.0f),
|
||||
F32(2.0f));
|
||||
mu_assert_notnull(op, "static fmod opbuilder alias");
|
||||
mu_assert_eq(op->code, RZ_IL_OP_FMOD, "static fmod alias maps to fmod");
|
||||
rz_il_op_pure_free(op);
|
||||
|
||||
op = FRSQRT_DYN_RMODE(
|
||||
U32(RZ_FLOAT_RMODE_RNE),
|
||||
F32(1.0f));
|
||||
mu_assert_notnull(op, "dynamic frsqrt opbuilder alias");
|
||||
mu_assert_eq(op->code, RZ_IL_OP_FRSQRT, "dynamic frsqrt alias maps to frsqrt");
|
||||
rz_il_op_pure_free(op);
|
||||
|
||||
op = FMAD_DYN_RMODE(
|
||||
U32(RZ_FLOAT_RMODE_RNE),
|
||||
F32(1.0f),
|
||||
F32(2.0f),
|
||||
F32(3.0f));
|
||||
mu_assert_notnull(op, "dynamic fmad opbuilder alias");
|
||||
mu_assert_eq(op->code, RZ_IL_OP_FMAD, "dynamic fmad alias maps to fmad");
|
||||
rz_il_op_pure_free(op);
|
||||
mu_end;
|
||||
}
|
||||
|
||||
#include <rz_il/rz_il_opbuilder_end.h>
|
||||
|
||||
static bool test_rzil_vm_runtime_rmode_avoids_let_capture() {
|
||||
const char *name = "_rz_il_runtime_rmode";
|
||||
RzILOpFloat *op = rz_il_op_new_let(
|
||||
name,
|
||||
rz_il_op_new_bitv_from_ut64(32, 0x3f800000),
|
||||
rz_il_op_new_fadd_dyn_rmode(
|
||||
rz_il_op_new_bitv_from_ut64(32, RZ_FLOAT_RMODE_RTZ),
|
||||
rz_il_op_new_float(
|
||||
RZ_FLOAT_IEEE754_BIN_32,
|
||||
rz_il_op_new_var(name, RZ_IL_VAR_KIND_LOCAL_PURE)),
|
||||
rz_il_op_new_float_from_f32(0.0f)));
|
||||
|
||||
RzILValidateGlobalContext *ctx = rz_il_validate_global_context_new_empty(32);
|
||||
RzILSortPure sort;
|
||||
RzILValidateReport report = NULL;
|
||||
mu_assert_true(rz_il_validate_pure(op, ctx, &sort, &report), "same-named outer let validates");
|
||||
mu_assert_true(rz_il_sort_pure_eq(sort, rz_il_sort_pure_float(RZ_FLOAT_IEEE754_BIN_32)), "captured expression sort");
|
||||
mu_assert_null(report, "no validation report");
|
||||
rz_il_validate_global_context_free(ctx);
|
||||
|
||||
RzILVM *vm = rz_il_vm_new(0, 32, false, RZ_IL_EVENT_EXC_NONE);
|
||||
RzFloat *result = rz_il_evaluate_float(vm, op);
|
||||
char *hex = result ? rz_float_as_hex_string(result, true) : NULL;
|
||||
mu_assert_streq(hex, "0x3f800000", "runtime rmode does not capture the outer let value");
|
||||
free(hex);
|
||||
rz_float_free(result);
|
||||
rz_il_op_pure_free(op);
|
||||
rz_il_vm_free(vm);
|
||||
mu_end;
|
||||
}
|
||||
|
||||
static bool test_rzil_vm_runtime_rmode_compact_composition() {
|
||||
RzILOpFloat *op = rz_il_op_new_float_from_f32(1.0f);
|
||||
for (size_t i = 0; i < 8; i++) {
|
||||
RzILOpBitVector *rmode = i == 7
|
||||
? rz_il_op_new_add(
|
||||
rz_il_op_new_bitv_from_ut64(32, RZ_FLOAT_RMODE_RNE),
|
||||
rz_il_op_new_bitv_from_ut64(32, 0))
|
||||
: rz_il_op_new_bitv_from_ut64(32, RZ_FLOAT_RMODE_RNE);
|
||||
op = rz_il_op_new_fadd_dyn_rmode(
|
||||
rmode,
|
||||
op,
|
||||
rz_il_op_new_float_from_f32(0.0f));
|
||||
}
|
||||
mu_assert_notnull(op, "nested runtime-rmode expression");
|
||||
mu_assert_eq(op->code, RZ_IL_OP_FADD, "runtime-rmode constructor uses base opcode");
|
||||
mu_assert_streq(rz_il_op_pure_code_stringify(op->code), "fadd", "runtime-rmode opcode string");
|
||||
mu_assert_eq(op->op.fadd.rmode.kind, RZ_IL_OP_ARG_FLOAT_RMODE_DYNAMIC, "dynamic rmode tag");
|
||||
mu_assert_eq(op->op.fadd.rmode.value.dynamic_mode->code, RZ_IL_OP_ADD, "dynamic rmode expression");
|
||||
|
||||
RzILOpPure *copy = rz_il_op_pure_dup(op);
|
||||
mu_assert_notnull(copy, "runtime-rmode expression copy");
|
||||
mu_assert_eq(copy->op.fadd.rmode.kind, RZ_IL_OP_ARG_FLOAT_RMODE_DYNAMIC, "copied dynamic rmode tag");
|
||||
mu_assert_ptrneq(copy->op.fadd.rmode.value.dynamic_mode, op->op.fadd.rmode.value.dynamic_mode, "dynamic rmode expression is deep-copied");
|
||||
|
||||
RzStrBuf original;
|
||||
RzStrBuf copied;
|
||||
rz_strbuf_init(&original);
|
||||
rz_strbuf_init(&copied);
|
||||
rz_il_op_pure_stringify(op, &original, false);
|
||||
rz_il_op_pure_stringify(copy, &copied, false);
|
||||
mu_assert("nested runtime-rmode stringify stays below 64 KiB", rz_strbuf_length(&original) < 64 * 1024);
|
||||
mu_assert_streq(rz_strbuf_get(&copied), rz_strbuf_get(&original), "copied runtime-rmode expression output");
|
||||
mu_assert_strcontains(rz_strbuf_get(&original), "(+. (+ (bv 32", "dynamic rmode uses the canonical compact opcode");
|
||||
rz_strbuf_fini(&original);
|
||||
rz_strbuf_fini(&copied);
|
||||
|
||||
PJ *pj = pj_new();
|
||||
rz_il_op_pure_json(copy, pj);
|
||||
char *json = pj_drain(pj);
|
||||
mu_assert_strcontains(json, "\"opcode\":\"+.\"", "dynamic rmode uses the canonical JSON opcode");
|
||||
mu_assert_strcontains(json, "\"rmode\":{\"opcode\":\"+\"", "dynamic rmode is a JSON child node");
|
||||
free(json);
|
||||
|
||||
RzILStringifyCtx stringify_ctx = { .indent = 0, .indent_inc = 2 };
|
||||
RzStrBuf unicode;
|
||||
rz_strbuf_init(&unicode);
|
||||
mu_assert_true(rz_il_op_pure_stringify_unicode(&stringify_ctx, copy, &unicode), "runtime-rmode Unicode export");
|
||||
mu_assert("runtime-rmode Unicode output stays compact", rz_strbuf_length(&unicode) < 64 * 1024);
|
||||
mu_assert_strcontains(rz_strbuf_get(&unicode), "((0x0", "Unicode export includes the dynamic rmode expression");
|
||||
rz_strbuf_fini(&unicode);
|
||||
|
||||
RzGraph *graph = rz_il_op_pure_graph(copy, "runtime-rmode");
|
||||
mu_assert_notnull(graph, "runtime-rmode graph export");
|
||||
char *dot = rz_graph_drawable_to_dot(graph, NULL, NULL);
|
||||
mu_assert_notnull(dot, "runtime-rmode graph rendering");
|
||||
mu_assert_strcontains(dot, "label=\"fadd\"", "graph keeps the canonical opcode node");
|
||||
mu_assert_strcontains(dot, "label=\"add\"", "graph includes the dynamic rmode expression");
|
||||
free(dot);
|
||||
rz_graph_free(graph);
|
||||
rz_il_op_pure_free(copy);
|
||||
rz_il_op_pure_free(op);
|
||||
mu_end;
|
||||
}
|
||||
|
||||
bool all_tests() {
|
||||
mu_run_test(test_rzil_vm_init);
|
||||
mu_run_test(test_rzil_vm_global_vars);
|
||||
|
|
@ -1160,6 +1676,13 @@ bool all_tests() {
|
|||
mu_run_test(test_rzil_vm_op_float);
|
||||
mu_run_test(test_rzil_vm_op_fcast);
|
||||
mu_run_test(test_rzil_vm_op_fexcept);
|
||||
mu_run_test(test_rzil_vm_binary80_full_precision);
|
||||
mu_run_test(test_rzil_vm_float_rmode_arguments);
|
||||
mu_run_test(test_rzil_vm_all_float_rmode_payloads);
|
||||
mu_run_test(test_rzil_vm_runtime_rmode_helpers);
|
||||
mu_run_test(test_rzil_opbuilder_float_rmode_aliases);
|
||||
mu_run_test(test_rzil_vm_runtime_rmode_avoids_let_capture);
|
||||
mu_run_test(test_rzil_vm_runtime_rmode_compact_composition);
|
||||
mu_run_test(test_rzil_vm_halt_on_exc);
|
||||
return tests_passed != tests_run;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue