Problem
After #238 (PR #313) pushed not through ordering comparisons, the dominant residual of a strict comparison is a conjunction of two comparisons over the same operands. A PureScript n > 0 on Int reaches Lua as (verbatim from Golden.Primops.Test/golden.lua):
if n >= 0 and n ~= 0 then acc, n = acc + n, n - 1 else return acc end
where a single fused comparison would do:
if n > 0 then acc, n = acc + n, n - 1 else return acc end
This fusion is one instance of a family pslua does not simplify: boolean combinations (and, or, not) of comparison primops applied to one and the same operand pair.
Approach
Treat the six primitive predicates over a totally ordered pair (a, b) as the subsets of the three-way comparison outcome {LT, EQ, GT} they accept: < = {LT}, <= = {LT,EQ}, == = {EQ}, ~= = {LT,GT}, >= = {EQ,GT}, > = {GT}. Then and is intersection, or is union, not is complement, and any combination of same-pair predicates normalizes to one of the eight subsets — each expressible as a single primitive predicate, a negated equality, or a boolean literal. The #238 complement flip is exactly the not fragment of this algebra; this issue adds the binary connectives. Representative members:
a >= b and a ~= b → a > b (the golden instance above) and dually a <= b and a ~= b → a < b;
a < b or a == b → a <= b and dually a > b or a == b → a >= b;
a <= b and a >= b → a == b; a < b or a > b → not (a == b);
- absorptions like
a >= b and a == b → a == b, contradictions like a < b and a > b → false, tautologies like a <= b or a >= b → true.
Three gates, in a peephole rule in IR/Optimizer.hs next to the #238 flip:
- Total-order witness, reused from the flip (
foldPrimNot): one operand is an Int, Char, or String literal. For numbers the fusion happens to be NaN-transparent (every comparison against NaN is false, so e.g. both a >= b and a ~= b and a > b evaluate false), but strings need the witness for a different reason: Lua 5.1 orders strings by strcoll (locale-sensitive) while ==/~= test byte equality, so two distinct strings collating equal would distinguish the fused and unfused forms. The one gate covers both hazards.
- Operand identity: both predicates compare the same
(a, b) syntactically (alpha-equal, same orientation; normalizing mirrored orientation a < b ≡ b > a can be a later extension).
- Purity of the dropped copy: fusing evaluates
a and b once where the unfused form evaluates them up to twice, so the operands must be free to drop — trivially true for the reference-or-literal operands the pipeline emits (the isInlinableValue tier), which is the natural guard.
The two rules compose: once > residuals fuse to a single PrimGt, the #238 flip turns a surrounding not (a > b) into a <= b.
Prerequisites / Relations
Builds on the witness gate shipped with #238 (PR #313). The conjunction shapes it targets are produced by the half-literal boolean-if collapses of #203. Independent of the rest of the backlog.
Verification / Measurement
Focused optimizer tests per connective (fusion, absorption, contradiction, tautology), plus decline cases for a missing witness, mismatched operands, and a non-trivial operand. The eight goldens PR #313 touched shrink again (n >= 0 and n ~= 0 → n > 0); eval goldens unchanged.
Problem
After #238 (PR #313) pushed
notthrough ordering comparisons, the dominant residual of a strict comparison is a conjunction of two comparisons over the same operands. A PureScriptn > 0onIntreaches Lua as (verbatim fromGolden.Primops.Test/golden.lua):where a single fused comparison would do:
This fusion is one instance of a family pslua does not simplify: boolean combinations (
and,or,not) of comparison primops applied to one and the same operand pair.Approach
Treat the six primitive predicates over a totally ordered pair
(a, b)as the subsets of the three-way comparison outcome{LT, EQ, GT}they accept:<= {LT},<== {LT,EQ},=== {EQ},~== {LT,GT},>== {EQ,GT},>= {GT}. Thenandis intersection,oris union,notis complement, and any combination of same-pair predicates normalizes to one of the eight subsets — each expressible as a single primitive predicate, a negated equality, or a boolean literal. The #238 complement flip is exactly thenotfragment of this algebra; this issue adds the binary connectives. Representative members:a >= b and a ~= b→a > b(the golden instance above) and duallya <= b and a ~= b→a < b;a < b or a == b→a <= band duallya > b or a == b→a >= b;a <= b and a >= b→a == b;a < b or a > b→not (a == b);a >= b and a == b→a == b, contradictions likea < b and a > b→false, tautologies likea <= b or a >= b→true.Three gates, in a peephole rule in
IR/Optimizer.hsnext to the #238 flip:foldPrimNot): one operand is anInt,Char, orStringliteral. For numbers the fusion happens to be NaN-transparent (every comparison against NaN is false, so e.g. botha >= b and a ~= banda > bevaluate false), but strings need the witness for a different reason: Lua 5.1 orders strings bystrcoll(locale-sensitive) while==/~=test byte equality, so two distinct strings collating equal would distinguish the fused and unfused forms. The one gate covers both hazards.(a, b)syntactically (alpha-equal, same orientation; normalizing mirrored orientationa < b≡b > acan be a later extension).aandbonce where the unfused form evaluates them up to twice, so the operands must be free to drop — trivially true for the reference-or-literal operands the pipeline emits (theisInlinableValuetier), which is the natural guard.The two rules compose: once
>residuals fuse to a singlePrimGt, the #238 flip turns a surroundingnot (a > b)intoa <= b.Prerequisites / Relations
Builds on the witness gate shipped with #238 (PR #313). The conjunction shapes it targets are produced by the half-literal boolean-if collapses of #203. Independent of the rest of the backlog.
Verification / Measurement
Focused optimizer tests per connective (fusion, absorption, contradiction, tautology), plus decline cases for a missing witness, mismatched operands, and a non-trivial operand. The eight goldens PR #313 touched shrink again (
n >= 0 and n ~= 0→n > 0); eval goldens unchanged.