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:
billow 2026-08-10 01:06:17 +08:00 committed by GitHub
parent e33674578c
commit 19b1783c88
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
27 changed files with 2714 additions and 492 deletions

View file

@ -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)))),

View file

@ -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`

View file

@ -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) {

View file

@ -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) {

View file

@ -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) {

View file

@ -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) {

View file

@ -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:

View file

@ -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);

View file

@ -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;

View file

@ -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);

View file

@ -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))

View file

@ -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

View file

@ -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);

View file

@ -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

View file

@ -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);
}
/**

View file

@ -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);

View file

@ -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

View 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

View file

@ -3,6 +3,7 @@ url = https://github.com/rizinorg/softfloat
revision = 537d18e71a51aea70f6b54334854f7014c6458c7
directory = softfloat
depth = 1
patch_directory = softfloat
[provide]
softfloat=softfloat_dep

View file

@ -3,3 +3,4 @@ url = https://github.com/rizinorg/softfloat
revision = 537d18e71a51aea70f6b54334854f7014c6458c7
directory = softfloat_cross_native
depth = 1
patch_directory = softfloat

View file

@ -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

View file

@ -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,

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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;
}