mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Merge.
This commit is contained in:
commit
173caec5ba
@ -1,4 +1,4 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* vim: set ts=4 sw=4 et tw=99:
|
||||
*
|
||||
* ***** BEGIN LICENSE BLOCK *****
|
||||
@ -4541,8 +4541,45 @@ static struct {
|
||||
|
||||
JS_STATIC_ASSERT(sizeof(binary_imacros) < IMACRO_PC_ADJ_LIMIT);
|
||||
|
||||
void
|
||||
TraceRecorder::strictEquality(bool equal)
|
||||
{
|
||||
jsval& r = stackval(-1);
|
||||
jsval& l = stackval(-2);
|
||||
LIns* l_ins = get(&l);
|
||||
LIns* r_ins = get(&r);
|
||||
|
||||
uint8 ltag = getPromotedType(l);
|
||||
if (ltag != getPromotedType(r)) {
|
||||
set(&l, lir->insImm(!equal));
|
||||
return;
|
||||
}
|
||||
|
||||
LIns* x;
|
||||
if (ltag == JSVAL_STRING) {
|
||||
LIns* args[] = { r_ins, l_ins };
|
||||
/*
|
||||
* FIXME: js_EqualStrings returns 0/1, so we really shouldn't have to
|
||||
* always compare to 0. The problem is there are assertions
|
||||
* that require the instruction here to be a condition; this is
|
||||
* easily fixable by stealing a bit from CallInfo to return
|
||||
* condition-ness and making sure such calls set the right
|
||||
* condition flags for a comparison to immediately follow. See
|
||||
* bug 467995.
|
||||
*/
|
||||
x = lir->ins_eq0(lir->insCall(&js_EqualStrings_ci, args));
|
||||
} else {
|
||||
LOpcode op = (ltag != JSVAL_DOUBLE) ? LIR_eq : LIR_feq;
|
||||
x = lir->ins2(op, l_ins, r_ins);
|
||||
}
|
||||
if (!equal)
|
||||
x = lir->ins_eq0(x);
|
||||
|
||||
set(&l, x);
|
||||
}
|
||||
|
||||
bool
|
||||
TraceRecorder::cmp(LOpcode op, int flags)
|
||||
TraceRecorder::equality(int flags)
|
||||
{
|
||||
jsval& r = stackval(-1);
|
||||
jsval& l = stackval(-2);
|
||||
@ -4553,28 +4590,10 @@ TraceRecorder::cmp(LOpcode op, int flags)
|
||||
LIns* r_ins = get(&r);
|
||||
bool fp = false;
|
||||
|
||||
if (op != LIR_feq) {
|
||||
if (JSVAL_IS_OBJECT(l) && hasValueOfMethod(l)) {
|
||||
if (JSVAL_IS_OBJECT(r) && hasValueOfMethod(r))
|
||||
return call_imacro(binary_imacros.obj_obj);
|
||||
return call_imacro(binary_imacros.obj_any);
|
||||
}
|
||||
if (JSVAL_IS_OBJECT(r) && hasValueOfMethod(r))
|
||||
return call_imacro(binary_imacros.any_obj);
|
||||
}
|
||||
|
||||
// CMP_STRICT is set only for JSOP_STRICTEQ and JSOP_STRICTNE, which correspond to the
|
||||
// === and !== operators. negate is true for !== and false for ===. The strict equality
|
||||
// operators produce false if the types of the operands differ, i.e. if only one of
|
||||
// them is a number.
|
||||
if ((flags & CMP_STRICT) && getPromotedType(l) != getPromotedType(r)) {
|
||||
x = INS_CONST(negate);
|
||||
cond = negate;
|
||||
} else if (JSVAL_IS_STRING(l) || JSVAL_IS_STRING(r)) {
|
||||
if (JSVAL_IS_STRING(l) || JSVAL_IS_STRING(r)) {
|
||||
// Comparing equality of a string against null always produces false.
|
||||
if (op == LIR_feq &&
|
||||
((JSVAL_IS_NULL(l) && l_ins->isconst()) ||
|
||||
(JSVAL_IS_NULL(r) && r_ins->isconst()))) {
|
||||
if ((JSVAL_IS_NULL(l) && l_ins->isconst()) ||
|
||||
(JSVAL_IS_NULL(r) && r_ins->isconst())) {
|
||||
x = INS_CONST(negate);
|
||||
cond = negate;
|
||||
} else {
|
||||
@ -4582,12 +4601,9 @@ TraceRecorder::cmp(LOpcode op, int flags)
|
||||
ABORT_TRACE("unsupported type for cmp vs string");
|
||||
|
||||
LIns* args[] = { r_ins, l_ins };
|
||||
if (op == LIR_feq)
|
||||
l_ins = lir->ins_eq0(lir->insCall(&js_EqualStrings_ci, args));
|
||||
else
|
||||
l_ins = lir->insCall(&js_CompareStrings_ci, args);
|
||||
l_ins = lir->ins_eq0(lir->insCall(&js_EqualStrings_ci, args));
|
||||
r_ins = lir->insImm(0);
|
||||
cond = evalCmp(op, JSVAL_TO_STRING(l), JSVAL_TO_STRING(r));
|
||||
cond = js_EqualStrings(JSVAL_TO_STRING(l), JSVAL_TO_STRING(r));
|
||||
}
|
||||
} else if (isNumber(l) || isNumber(r)) {
|
||||
jsval tmp[2] = {l, r};
|
||||
@ -4601,7 +4617,7 @@ TraceRecorder::cmp(LOpcode op, int flags)
|
||||
LIns* args[] = { l_ins, cx_ins };
|
||||
if (l == JSVAL_NULL && l_ins->isconst()) {
|
||||
jsdpun u;
|
||||
u.d = (op == LIR_feq) ? js_NaN : 0.0;
|
||||
u.d = js_NaN;
|
||||
l_ins = lir->insImmq(u.u64);
|
||||
} else if (JSVAL_IS_STRING(l)) {
|
||||
l_ins = lir->insCall(&js_StringToNumber_ci, args);
|
||||
@ -4623,7 +4639,7 @@ TraceRecorder::cmp(LOpcode op, int flags)
|
||||
args[1] = cx_ins;
|
||||
if (r == JSVAL_NULL && r_ins->isconst()) {
|
||||
jsdpun u;
|
||||
u.d = (op == LIR_feq) ? js_NaN : 0.0;
|
||||
u.d = js_NaN;
|
||||
r_ins = lir->insImmq(u.u64);
|
||||
} else if (JSVAL_IS_STRING(r)) {
|
||||
r_ins = lir->insCall(&js_StringToNumber_ci, args);
|
||||
@ -4634,21 +4650,13 @@ TraceRecorder::cmp(LOpcode op, int flags)
|
||||
ABORT_TRACE("unsupported RHS type for cmp vs number");
|
||||
}
|
||||
rnum = js_ValueToNumber(cx, &tmp[1]);
|
||||
cond = evalCmp(op, lnum, rnum);
|
||||
} else if ((JSVAL_TAG(l) == JSVAL_BOOLEAN) && (JSVAL_TAG(r) == JSVAL_BOOLEAN)) {
|
||||
cond = (lnum == rnum);
|
||||
} else if (JSVAL_TAG(l) == JSVAL_BOOLEAN && JSVAL_TAG(r) == JSVAL_BOOLEAN) {
|
||||
// The well-known values of JSVAL_TRUE and JSVAL_FALSE make this very easy.
|
||||
// In particular: JSVAL_TO_BOOLEAN(0) < JSVAL_TO_BOOLEAN(1) so all of these comparisons do
|
||||
// the right thing.
|
||||
cond = evalCmp(op, l, r);
|
||||
// For ==, !=, ===, and !=== the result is magically correct even if undefined (2) is
|
||||
// involved. For the relational operations we need some additional cmov magic to make
|
||||
// the result always false (since undefined becomes NaN per ECMA and that doesn't
|
||||
// compare to anything, even itself). The code for this is emitted a few lines down.
|
||||
cond = (l == r);
|
||||
} else if (JSVAL_IS_OBJECT(l) && JSVAL_IS_OBJECT(r)) {
|
||||
if (op != LIR_feq) {
|
||||
JS_NOT_REACHED("we should have converted to numbers already");
|
||||
return false;
|
||||
}
|
||||
cond = (l == r);
|
||||
} else {
|
||||
ABORT_TRACE("unsupported operand types for cmp");
|
||||
@ -4657,52 +4665,160 @@ TraceRecorder::cmp(LOpcode op, int flags)
|
||||
/* If we didn't generate a constant result yet, then emit the comparison now. */
|
||||
if (!x) {
|
||||
/* If the result is not a number or it's not a quad, we must use an integer compare. */
|
||||
if (!fp) {
|
||||
JS_ASSERT(op >= LIR_feq && op <= LIR_fge);
|
||||
op = LOpcode(op + (LIR_eq - LIR_feq));
|
||||
}
|
||||
LOpcode op = fp ? LIR_feq : LIR_eq;
|
||||
x = lir->ins2(op, l_ins, r_ins);
|
||||
if (negate) {
|
||||
x = lir->ins_eq0(x);
|
||||
cond = !cond;
|
||||
}
|
||||
// For boolean comparison we need a bit post-processing to make the result false if
|
||||
// either side is undefined.
|
||||
if (op != LIR_eq && (JSVAL_TAG(l) == JSVAL_BOOLEAN) && (JSVAL_TAG(r) == JSVAL_BOOLEAN)) {
|
||||
x = lir->ins_choose(lir->ins2i(LIR_eq,
|
||||
lir->ins2i(LIR_and,
|
||||
lir->ins2(LIR_or, l_ins, r_ins),
|
||||
JSVAL_TO_BOOLEAN(JSVAL_VOID)),
|
||||
JSVAL_TO_BOOLEAN(JSVAL_VOID)),
|
||||
lir->insImm(JSVAL_TO_BOOLEAN(JSVAL_FALSE)),
|
||||
x);
|
||||
x = lir->ins_eq0(lir->ins_eq0(x));
|
||||
if ((l == JSVAL_VOID) || (r == JSVAL_VOID))
|
||||
cond = false;
|
||||
}
|
||||
}
|
||||
|
||||
/* Don't guard if the same path is always taken. */
|
||||
if (!x->isconst()) {
|
||||
if (flags & CMP_CASE) {
|
||||
if (flags & CMP_CASE) {
|
||||
/* Only guard if the same path may not always be taken. */
|
||||
if (!x->isconst())
|
||||
guard(cond, x, BRANCH_EXIT);
|
||||
return true;
|
||||
}
|
||||
|
||||
/* The interpreter fuses comparisons and the following branch,
|
||||
so we have to do that here as well. */
|
||||
if (flags & CMP_TRY_BRANCH_AFTER_COND) {
|
||||
fuseIf(cx->fp->regs->pc + 1, cond, x);
|
||||
}
|
||||
} else if (flags & CMP_CASE) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/* We update the stack after the guard. This is safe since
|
||||
the guard bails out at the comparison and the interpreter
|
||||
will therefore re-execute the comparison. This way the
|
||||
value of the condition doesn't have to be calculated and
|
||||
saved on the stack in most cases. */
|
||||
/*
|
||||
* Don't guard if the same path is always taken. If it isn't, we have to
|
||||
* fuse comparisons and the following branch, because the interpreter does
|
||||
* that.
|
||||
*/
|
||||
if ((flags & CMP_TRY_BRANCH_AFTER_COND) && !x->isconst())
|
||||
fuseIf(cx->fp->regs->pc + 1, cond, x);
|
||||
|
||||
/*
|
||||
* We update the stack after the guard. This is safe since the guard bails
|
||||
* out at the comparison and the interpreter will therefore re-execute the
|
||||
* comparison. This way the value of the condition doesn't have to be
|
||||
* calculated and saved on the stack in most cases.
|
||||
*/
|
||||
set(&l, x);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
TraceRecorder::relational(LOpcode op, int flags)
|
||||
{
|
||||
jsval& r = stackval(-1);
|
||||
jsval& l = stackval(-2);
|
||||
LIns* x = NULL;
|
||||
bool negate = !!(flags & CMP_NEGATE);
|
||||
bool cond;
|
||||
LIns* l_ins = get(&l);
|
||||
LIns* r_ins = get(&r);
|
||||
bool fp = false;
|
||||
jsdouble lnum, rnum;
|
||||
|
||||
/*
|
||||
* 11.8.5 if either argument is an object with a function-valued valueOf
|
||||
* property; if both arguments are objects with non-function-valued valueOf
|
||||
* properties, abort.
|
||||
*/
|
||||
if (JSVAL_IS_OBJECT(l) && hasValueOfMethod(l)) {
|
||||
if (JSVAL_IS_OBJECT(r) && hasValueOfMethod(r))
|
||||
return call_imacro(binary_imacros.obj_obj);
|
||||
return call_imacro(binary_imacros.obj_any);
|
||||
}
|
||||
if (JSVAL_IS_OBJECT(r) && hasValueOfMethod(r))
|
||||
return call_imacro(binary_imacros.any_obj);
|
||||
if (JSVAL_IS_OBJECT(l) || JSVAL_IS_OBJECT(r))
|
||||
ABORT_TRACE("comparing two objects with non-function valueOf");
|
||||
|
||||
/* 11.8.5 steps 3, 16-21. */
|
||||
if (JSVAL_IS_STRING(l) && JSVAL_IS_STRING(r)) {
|
||||
LIns* args[] = { r_ins, l_ins };
|
||||
l_ins = lir->insCall(&js_CompareStrings_ci, args);
|
||||
r_ins = lir->insImm(0);
|
||||
cond = evalCmp(op, JSVAL_TO_STRING(l), JSVAL_TO_STRING(r));
|
||||
goto do_comparison;
|
||||
}
|
||||
|
||||
/* 11.8.5 steps 4-5. */
|
||||
if (!JSVAL_IS_NUMBER(l)) {
|
||||
LIns* args[] = { l_ins, cx_ins };
|
||||
switch (JSVAL_TAG(l)) {
|
||||
case JSVAL_BOOLEAN:
|
||||
l_ins = lir->insCall(&js_BooleanOrUndefinedToNumber_ci, args);
|
||||
break;
|
||||
case JSVAL_STRING:
|
||||
l_ins = lir->insCall(&js_StringToNumber_ci, args);
|
||||
break;
|
||||
case JSVAL_INT:
|
||||
case JSVAL_DOUBLE:
|
||||
case JSVAL_OBJECT:
|
||||
default:
|
||||
JS_NOT_REACHED("JSVAL_IS_NUMBER if int/double, objects should "
|
||||
"have been handled at start of method");
|
||||
ABORT_TRACE("safety belt");
|
||||
}
|
||||
}
|
||||
if (!JSVAL_IS_NUMBER(r)) {
|
||||
LIns* args[] = { r_ins, cx_ins };
|
||||
switch (JSVAL_TAG(r)) {
|
||||
case JSVAL_BOOLEAN:
|
||||
r_ins = lir->insCall(&js_BooleanOrUndefinedToNumber_ci, args);
|
||||
break;
|
||||
case JSVAL_STRING:
|
||||
r_ins = lir->insCall(&js_StringToNumber_ci, args);
|
||||
break;
|
||||
case JSVAL_INT:
|
||||
case JSVAL_DOUBLE:
|
||||
case JSVAL_OBJECT:
|
||||
default:
|
||||
JS_NOT_REACHED("JSVAL_IS_NUMBER if int/double, objects should "
|
||||
"have been handled at start of method");
|
||||
ABORT_TRACE("safety belt");
|
||||
}
|
||||
}
|
||||
{
|
||||
jsval tmp = JSVAL_NULL;
|
||||
JSAutoTempValueRooter tvr(cx, 1, &tmp);
|
||||
|
||||
tmp = l;
|
||||
lnum = js_ValueToNumber(cx, &tmp);
|
||||
tmp = r;
|
||||
rnum = js_ValueToNumber(cx, &tmp);
|
||||
}
|
||||
cond = evalCmp(op, lnum, rnum);
|
||||
fp = true;
|
||||
|
||||
/* 11.8.5 steps 6-15. */
|
||||
do_comparison:
|
||||
/* If the result is not a number or it's not a quad, we must use an integer compare. */
|
||||
if (!fp) {
|
||||
JS_ASSERT(op >= LIR_feq && op <= LIR_fge);
|
||||
op = LOpcode(op + (LIR_eq - LIR_feq));
|
||||
}
|
||||
if (negate) {
|
||||
JS_STATIC_ASSERT((LIR_flt ^ 3) == LIR_fge);
|
||||
JS_STATIC_ASSERT((LIR_fgt ^ 3) == LIR_fle);
|
||||
JS_STATIC_ASSERT((LIR_lt ^ 3) == LIR_ge);
|
||||
JS_STATIC_ASSERT((LIR_gt ^ 3) == LIR_le);
|
||||
JS_STATIC_ASSERT((LIR_ult ^ 3) == LIR_uge);
|
||||
JS_STATIC_ASSERT((LIR_ugt ^ 3) == LIR_ule);
|
||||
|
||||
x = lir->ins2(LOpcode(op ^ 3), l_ins, r_ins);
|
||||
cond = !cond;
|
||||
} else {
|
||||
x = lir->ins2(op, l_ins, r_ins);
|
||||
}
|
||||
|
||||
/*
|
||||
* Don't guard if the same path is always taken. If it isn't, we have to
|
||||
* fuse comparisons and the following branch, because the interpreter does
|
||||
* that.
|
||||
*/
|
||||
if ((flags & CMP_TRY_BRANCH_AFTER_COND) && !x->isconst())
|
||||
fuseIf(cx->fp->regs->pc + 1, cond, x);
|
||||
|
||||
/*
|
||||
* We update the stack after the guard. This is safe since the guard bails
|
||||
* out at the comparison and the interpreter will therefore re-execute the
|
||||
* comparison. This way the value of the condition doesn't have to be
|
||||
* calculated and saved on the stack in most cases.
|
||||
*/
|
||||
set(&l, x);
|
||||
return true;
|
||||
}
|
||||
@ -5506,37 +5622,37 @@ TraceRecorder::record_JSOP_BITAND()
|
||||
bool
|
||||
TraceRecorder::record_JSOP_EQ()
|
||||
{
|
||||
return cmp(LIR_feq, CMP_TRY_BRANCH_AFTER_COND);
|
||||
return equality(CMP_TRY_BRANCH_AFTER_COND);
|
||||
}
|
||||
|
||||
bool
|
||||
TraceRecorder::record_JSOP_NE()
|
||||
{
|
||||
return cmp(LIR_feq, CMP_NEGATE | CMP_TRY_BRANCH_AFTER_COND);
|
||||
return equality(CMP_NEGATE | CMP_TRY_BRANCH_AFTER_COND);
|
||||
}
|
||||
|
||||
bool
|
||||
TraceRecorder::record_JSOP_LT()
|
||||
{
|
||||
return cmp(LIR_flt, CMP_TRY_BRANCH_AFTER_COND);
|
||||
return relational(LIR_flt, CMP_TRY_BRANCH_AFTER_COND);
|
||||
}
|
||||
|
||||
bool
|
||||
TraceRecorder::record_JSOP_LE()
|
||||
{
|
||||
return cmp(LIR_fle, CMP_TRY_BRANCH_AFTER_COND);
|
||||
return relational(LIR_fle, CMP_TRY_BRANCH_AFTER_COND);
|
||||
}
|
||||
|
||||
bool
|
||||
TraceRecorder::record_JSOP_GT()
|
||||
{
|
||||
return cmp(LIR_fgt, CMP_TRY_BRANCH_AFTER_COND);
|
||||
return relational(LIR_fgt, CMP_TRY_BRANCH_AFTER_COND);
|
||||
}
|
||||
|
||||
bool
|
||||
TraceRecorder::record_JSOP_GE()
|
||||
{
|
||||
return cmp(LIR_fge, CMP_TRY_BRANCH_AFTER_COND);
|
||||
return relational(LIR_fge, CMP_TRY_BRANCH_AFTER_COND);
|
||||
}
|
||||
|
||||
bool
|
||||
@ -7001,13 +7117,15 @@ TraceRecorder::record_JSOP_LOOKUPSWITCH()
|
||||
bool
|
||||
TraceRecorder::record_JSOP_STRICTEQ()
|
||||
{
|
||||
return cmp(LIR_feq, CMP_STRICT);
|
||||
strictEquality(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
TraceRecorder::record_JSOP_STRICTNE()
|
||||
{
|
||||
return cmp(LIR_feq, CMP_STRICT | CMP_NEGATE);
|
||||
strictEquality(false);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
@ -7505,7 +7623,7 @@ TraceRecorder::record_JSOP_CONDSWITCH()
|
||||
bool
|
||||
TraceRecorder::record_JSOP_CASE()
|
||||
{
|
||||
return cmp(LIR_feq, CMP_CASE);
|
||||
return equality(CMP_CASE);
|
||||
}
|
||||
|
||||
bool
|
||||
@ -7698,7 +7816,7 @@ TraceRecorder::record_JSOP_GOSUBX()
|
||||
bool
|
||||
TraceRecorder::record_JSOP_CASEX()
|
||||
{
|
||||
return cmp(LIR_feq, CMP_CASE);
|
||||
return equality(CMP_CASE);
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* vim: set ts=8 sw=4 et tw=99 ft=cpp:
|
||||
*
|
||||
* ***** BEGIN LICENSE BLOCK *****
|
||||
@ -354,8 +354,10 @@ class TraceRecorder : public avmplus::GCObject {
|
||||
bool incElem(jsint incr, bool pre = true);
|
||||
bool incName(jsint incr, bool pre = true);
|
||||
|
||||
enum { CMP_NEGATE = 1, CMP_TRY_BRANCH_AFTER_COND = 2, CMP_CASE = 4, CMP_STRICT = 8 };
|
||||
bool cmp(nanojit::LOpcode op, int flags = 0);
|
||||
enum { CMP_NEGATE = 1, CMP_TRY_BRANCH_AFTER_COND = 2, CMP_CASE = 4 };
|
||||
void strictEquality(bool equal);
|
||||
bool equality(int flags);
|
||||
bool relational(nanojit::LOpcode op, int flags);
|
||||
|
||||
bool unary(nanojit::LOpcode op);
|
||||
bool binary(nanojit::LOpcode op);
|
||||
|
@ -70,6 +70,8 @@ namespace nanojit
|
||||
NanoStaticAssert((LIR_le ^ 3) == LIR_gt);
|
||||
NanoStaticAssert((LIR_ult ^ 3) == LIR_uge);
|
||||
NanoStaticAssert((LIR_ule ^ 3) == LIR_ugt);
|
||||
NanoStaticAssert((LIR_flt ^ 3) == LIR_fge);
|
||||
NanoStaticAssert((LIR_fle ^ 3) == LIR_fgt);
|
||||
|
||||
/* Opcodes must be strictly increasing without holes. */
|
||||
uint32_t count = 0;
|
||||
|
@ -104,9 +104,13 @@ OPDEF(ji, 25, 2) // jump indirect
|
||||
OPDEF(ldcs, 26, 2) // non-volatile 16-bit load
|
||||
|
||||
/*
|
||||
* feq though fge must only be used on float arguments. They
|
||||
* return integers. NB: These opcodes must remain continuous
|
||||
* so that comparison-opcode detection works correctly.
|
||||
* feq though fge must only be used on float arguments. They return integers.
|
||||
* For all except feq, (op ^ 1) is the op which flips the
|
||||
* left and right sides of the comparison, so (lt ^ 1) == gt, or the operator
|
||||
* "<" is xored with 1 to get ">". Similarly, (op ^ 3) is the complement of
|
||||
* op, so (lt ^ 1) == ge, or the complement of the operator "<" is ">=" xored
|
||||
* with 3. NB: These opcodes must remain continuous so that comparison-opcode
|
||||
* detection works correctly.
|
||||
*/
|
||||
OPDEF(feq, 27, 2) // floating-point equality [2 float inputs]
|
||||
OPDEF(flt, 28, 2) // floating-point less than: arg1 < arg2
|
||||
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user