Skip to content

feat(arxjit): lower expressions to astx - #109

Open
Jaskirat-s7 wants to merge 1 commit into
arxlang:mainfrom
Jaskirat-s7:feat/arxjit-lowering-expr
Open

feat(arxjit): lower expressions to astx#109
Jaskirat-s7 wants to merge 1 commit into
arxlang:mainfrom
Jaskirat-s7:feat/arxjit-lowering-expr

Conversation

@Jaskirat-s7

Copy link
Copy Markdown
Contributor

Second stage of the Sprint 3 lowering work, after #107 covered the function
shell. Adds parameter reads, arithmetic, and unary operators, so the wiki's
own example lowers end to end:

def add(a, b):
    return a + b

Operand types propagate: both sides of an operator are lowered at the expected
type, so a literal buried in an expression takes the width of its context
exactly as a returned one does. A variable read carries no type of its own,
because the prototype already declares it and reconciling it with its context
is IRx's to do. That is the deliberate difference from a literal, whose width
this stage chooses.

Two disagreements between validation and the backend

I read the operator vocabulary off astx and IRx rather than off the Python
grammar, which turned up two constructs the subset admits but the backend
cannot express:

  1. // and ** have no astx operator. astx maps op_codes to specialized
    nodes through _BINARY_OP_TYPES, which has no entry for ast.FloorDiv or
    ast.Pow. An unmapped op_code does not raise: it stays a plain BinaryOp
    and reaches codegen as Binary op ** not implemented yet. Validation
    accepts both, so they are rejected here rather than lowered into a module
    that cannot compile. A test reads astx's table directly so ours cannot
    drift from it.
  2. IRx has no unary minus. unary_ops.py implements ++, -- and !,
    and raises for anything else. A unary plus needs no node, being the
    identity, and a negated literal is folded into one negative literal, which
    is the only form a negative constant takes in Python. Negating anything
    else is rejected.

Both are worth a decision beyond this PR: either validation should stop
accepting these, or astx/IRx should gain the operators. Rejecting at lowering
keeps them visible as diagnostics meanwhile, but the subset and the backend
disagreeing is not a good resting state. Happy to open an issue for whichever
direction you prefer.

The negative-literal trap from #107

Folding also closes the trap #107 flagged and deliberately left a failing test
for: a range check applied to the magnitude alone would refuse -2147483648,
whose magnitude is one larger than the i32 maximum. That test is flipped here,
with a boundary partner confirming the fold widens what is accepted by exactly
that one value and no further.

Scope

Comparisons stay unlowered. Validation accepts them, but they only mean
something alongside the conditionals they belong to, so they lower with those
in the next PR. @jit still does not call this stage; that wiring is the last
PR of the sprint.

251 tests, arxjit coverage 100% line and branch. Every operator verified
through irx.analysis.api.analyze, and lowering verified on CPython 3.10,
3.11 and 3.14.

Second stage of the Sprint 3 lowering work, after arxlang#107 covered the
function shell. Adds parameter reads, arithmetic, and unary operators, so
the wiki's own example lowers end to end:

    def add(a, b):
        return a + b

Operand types propagate: both sides of an operator are lowered at the
expected type, so a literal buried in an expression takes the width of its
context exactly as a returned one does. A variable read carries no type,
because the prototype already declares it and reconciling it with its
context is IRx's to do; that is the difference from a literal, whose width
this stage chooses.

What the operator tables admit was read off astx and IRx rather than off
the Python grammar, and this turned up two disagreements with validation:

- astx maps operators to specialized nodes through _BINARY_OP_TYPES, and
  has no entry for ast.FloorDiv or ast.Pow. An unmapped op_code does not
  raise; it stays a plain BinaryOp and reaches codegen as "not implemented
  yet". Validation accepts both operators, so the subset currently admits
  two things the backend cannot express. They are rejected here, with a
  test reading astx's table directly so ours cannot drift from it.
- IRx implements three unary operators, "++", "--" and "!", and no
  negation. A unary plus needs no node, being the identity, and a negated
  literal is folded into a single negative literal, which is the only form
  a negative constant takes in Python. Negating anything else is rejected.

The fold also closes the trap arxlang#107 flagged and left a failing test for: a
range check applied to the magnitude alone would refuse -2147483648, whose
magnitude is one larger than the i32 maximum. That test is flipped here,
with a boundary partner confirming the fold widens what is accepted by
exactly that one value.

Comparisons stay unlowered. Validation accepts them, but they only mean
something with the conditionals they belong to, so they lower with those.

251 tests, arxjit coverage 100% line and branch. Every operator verified
through irx.analysis.api.analyze, and lowering verified on CPython 3.10,
3.11 and 3.14.
@yuvimittal

Copy link
Copy Markdown
Member

@Jaskirat-s7 , can you please rebase your branch ?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants