Lower boolean conditions directly (skip boxing) + thumb fast paths#11336
Lower boolean conditions directly (skip boxing) + thumb fast paths#11336humanapp wants to merge 2 commits into
Conversation
riknoll
left a comment
There was a problem hiding this comment.
this change looks good (thanks for all the comments!) but agree the blast radius is high. i wish we had a test suite for generated ARM code! we'll just have to bang on it a lot.
@abchatra not sure if you want this in the mbit release or not, could be risky.
|
@abchatra - where are we in the testing for next release of microbit - do we have enough runway to test this change more fully? |
|
@humanapp - the fromBool function is provided just as a helper for the user; it is not needed for the optimization, correct? |
There was a problem hiding this comment.
Pull request overview
This PR optimizes boolean handling in the PXT compiler by lowering boolean conditions directly into short-circuiting control flow (avoiding boxed booleans in branch-decision paths) and by adding Thumb inline fast paths for common boolean boxing/unboxing and truthiness checks.
Changes:
- Update
emitCondition()to lower&&/||used as conditions into short-circuiting jumps that produce raw0/1, and lower!exprin condition context viaBoolean_::bangon the raw condition result. - Add Thumb assembly helpers for
pxt::fromBool,Boolean_::bang,numops::toBool, andnumops::toBoolDecr, and route calls to these helpers where applicable.
Show a summary per file
| File | Description |
|---|---|
| pxtcompiler/emitter/emitter.ts | Lowers &&/` |
| pxtcompiler/emitter/backthumb.ts | Adds Thumb fast paths for boolean helpers (fromBool, bang, toBool, toBoolDecr) and updates comparison helper path to use the new toBoolDecr fast path. |
Copilot's findings
- Files reviewed: 2/2 changed files
- Comments generated: 0
|
Good to see you @humanapp 😃 |
@thomasjball, the presence of "pxt::fromBool" in the |
|
@humanapp - I don't quite follow the logic here:
|
|
I will create a MakeCode program to test the various conditions. |
This code is checking the lowest bit of
|
|
@humanapp - so my confusion is how the sequence does two checks (it is a tagged int and that int is non zero). |
The cmp r0, #1 ; integer 0 ((0<<1)|1) -> false
beq .falseWith what possibility filtered out, |
|
Thanks. I chatted with Jonny Austin - we will keep this PR for after new version of MakeCode for mbit ships. |
Hey folks! Here is another compiler optimization for your consideration. This is a change that makes every MakeCode program smaller and faster. It reduces the MicroCode hex file size by 44k, and makes the user experience on hardware noticeably more responsive. It does come with some risk: low odds of a bug, but it touches low-level branch code everywhere, so you'll want to test it broadly before shipping.
Summary
The MakeCode compiler currently compiles booleans inefficiently. In an expression like
if (!ready)orwhile (a || b), the expression is turned into a "boxed" boolean value that is then passed to a helper function, where it is unboxed and tested for truthiness. This PR changes the compiler to evaluate those conditions directly, and adds small fast-paths that speed up boxing/unboxing for a few common cases where boxing is still needed. The result is generated code that is both significantly smaller and measurably faster.Testing with the microcode build, this change produces a hex file that is ~44k smaller, and a user experience that is noticeably more responsive.
What it changes
There are two layers to this change:
!xin a condition now negatesx's truth test directly, instead of producing a boxed boolean just to invert it.a && b/a || bin a condition now compile to ordinary short-circuit branches that yield a plain0/1, instead of boxing each intermediate boolean and converting it back.Only code used to decide a branch is impacted by this change. Boolean expressions used as values (e.g.
const flag = a && b,return !x) are untouched.Risk assessment
I'd put this at low/moderate risk. The changes are small and not complicated, but the blast radius if a bug does surface is broad. In a way, the broad blast radius is risk-mitigating. If this regresses something, it would be caught pretty quickly.
The biggest risk here is misclassification of a candidate for fast-path. Sending a value that should have been boxed down the fast path would lead to incorrect truthiness evals.
Where do these classifications happen?
Fast-path filtering happens in
_numops_toBooland_numops_toBoolDecr. These methods are extensively commented to make their logic clear.Mitigating factors
Tests Performed
I compiled and ran many Arcade games on hardware, including:
cc: @thomasjball