Skip to content

Ordering-predicate algebra: collapse boolean combinations of comparisons over one operand pair #314

Description

@Unisay

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 ~= ba > b (the golden instance above) and dually a <= b and a ~= ba < b;
  • a < b or a == ba <= b and dually a > b or a == ba >= b;
  • a <= b and a >= ba == b; a < b or a > bnot (a == b);
  • absorptions like a >= b and a == ba == b, contradictions like a < b and a > bfalse, tautologies like a <= b or a >= btrue.

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 < bb > 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 ~= 0n > 0); eval goldens unchanged.

Metadata

Metadata

Assignees

Labels

OptimisationA Compiler Optimisationarea: irIR / optimizer / DCE / inlinerenhancementNew feature or request

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions