Files
cell/source/qbe_helpers.c
2026-02-09 18:17:31 -06:00

195 lines
5.3 KiB
C

/*
* 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 <math.h>
/* 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));
}