195 lines
5.3 KiB
C
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));
|
|
}
|