/* * QBE Helper Functions * * Thin C wrappers called from QBE-generated code for operations * that are too complex to inline: float arithmetic, float comparison, * string comparison, bitwise ops on floats, and boolean conversion. */ #include "quickjs-internal.h" #include /* Comparison op IDs (must match qbe.cm float_cmp_op_id values) */ enum { QBE_CMP_EQ = 0, QBE_CMP_NE = 1, QBE_CMP_LT = 2, QBE_CMP_LE = 3, QBE_CMP_GT = 4, QBE_CMP_GE = 5 }; /* ============================================================ Float binary arithmetic ============================================================ */ static inline JSValue qbe_float_binop(JSContext *ctx, JSValue a, JSValue b, double (*op)(double, double)) { double da, db; JS_ToFloat64(ctx, &da, a); JS_ToFloat64(ctx, &db, b); double r = op(da, db); if (!isfinite(r)) return JS_NULL; return JS_NewFloat64(ctx, r); } static double op_add(double a, double b) { return a + b; } static double op_sub(double a, double b) { return a - b; } static double op_mul(double a, double b) { return a * b; } JSValue qbe_float_add(JSContext *ctx, JSValue a, JSValue b) { return qbe_float_binop(ctx, a, b, op_add); } JSValue qbe_float_sub(JSContext *ctx, JSValue a, JSValue b) { return qbe_float_binop(ctx, a, b, op_sub); } JSValue qbe_float_mul(JSContext *ctx, JSValue a, JSValue b) { return qbe_float_binop(ctx, a, b, op_mul); } JSValue qbe_float_div(JSContext *ctx, JSValue a, JSValue b) { double da, db; JS_ToFloat64(ctx, &da, a); JS_ToFloat64(ctx, &db, b); if (db == 0.0) return JS_NULL; double r = da / db; if (!isfinite(r)) return JS_NULL; return JS_NewFloat64(ctx, r); } JSValue qbe_float_mod(JSContext *ctx, JSValue a, JSValue b) { double da, db; JS_ToFloat64(ctx, &da, a); JS_ToFloat64(ctx, &db, b); if (db == 0.0) return JS_NULL; double r = fmod(da, db); if (!isfinite(r)) return JS_NULL; return JS_NewFloat64(ctx, r); } JSValue qbe_float_pow(JSContext *ctx, JSValue a, JSValue b) { double da, db; JS_ToFloat64(ctx, &da, a); JS_ToFloat64(ctx, &db, b); double r = pow(da, db); if (!isfinite(r) && isfinite(da) && isfinite(db)) return JS_NULL; return JS_NewFloat64(ctx, r); } /* ============================================================ Float unary ops ============================================================ */ JSValue qbe_float_neg(JSContext *ctx, JSValue v) { double d; JS_ToFloat64(ctx, &d, v); return JS_NewFloat64(ctx, -d); } JSValue qbe_float_inc(JSContext *ctx, JSValue v) { double d; JS_ToFloat64(ctx, &d, v); return JS_NewFloat64(ctx, d + 1); } JSValue qbe_float_dec(JSContext *ctx, JSValue v) { double d; JS_ToFloat64(ctx, &d, v); return JS_NewFloat64(ctx, d - 1); } /* ============================================================ Float comparison — returns 0 or 1 for QBE branching ============================================================ */ int qbe_float_cmp(JSContext *ctx, int op, JSValue a, JSValue b) { double da, db; JS_ToFloat64(ctx, &da, a); JS_ToFloat64(ctx, &db, b); switch (op) { case QBE_CMP_EQ: return da == db; case QBE_CMP_NE: return da != db; case QBE_CMP_LT: return da < db; case QBE_CMP_LE: return da <= db; case QBE_CMP_GT: return da > db; case QBE_CMP_GE: return da >= db; default: return 0; } } /* ============================================================ Boolean conversion wrapper ============================================================ */ int qbe_to_bool(JSContext *ctx, JSValue v) { return JS_ToBool(ctx, v); } /* ============================================================ Bitwise not on non-int (float -> int32 -> ~) ============================================================ */ JSValue qbe_bnot(JSContext *ctx, JSValue v) { int32_t i; JS_ToInt32(ctx, &i, v); return JS_NewInt32(ctx, ~i); } /* ============================================================ Bitwise binary ops on floats (convert both to int32, apply, re-tag) ============================================================ */ JSValue qbe_bitwise_and(JSContext *ctx, JSValue a, JSValue b) { int32_t ia, ib; JS_ToInt32(ctx, &ia, a); JS_ToInt32(ctx, &ib, b); return JS_NewInt32(ctx, ia & ib); } JSValue qbe_bitwise_or(JSContext *ctx, JSValue a, JSValue b) { int32_t ia, ib; JS_ToInt32(ctx, &ia, a); JS_ToInt32(ctx, &ib, b); return JS_NewInt32(ctx, ia | ib); } JSValue qbe_bitwise_xor(JSContext *ctx, JSValue a, JSValue b) { int32_t ia, ib; JS_ToInt32(ctx, &ia, a); JS_ToInt32(ctx, &ib, b); return JS_NewInt32(ctx, ia ^ ib); } /* ============================================================ Shift ops on floats (convert to int32, shift, re-tag) ============================================================ */ JSValue qbe_shift_shl(JSContext *ctx, JSValue a, JSValue b) { int32_t ia, ib; JS_ToInt32(ctx, &ia, a); JS_ToInt32(ctx, &ib, b); return JS_NewInt32(ctx, ia << (ib & 31)); } JSValue qbe_shift_sar(JSContext *ctx, JSValue a, JSValue b) { int32_t ia, ib; JS_ToInt32(ctx, &ia, a); JS_ToInt32(ctx, &ib, b); return JS_NewInt32(ctx, ia >> (ib & 31)); } JSValue qbe_shift_shr(JSContext *ctx, JSValue a, JSValue b) { int32_t ia, ib; JS_ToInt32(ctx, &ia, a); JS_ToInt32(ctx, &ib, b); return JS_NewInt32(ctx, (uint32_t)ia >> (ib & 31)); }