Skip to content

feat(linter): linter prototype#48

Merged
Boshen merged 1 commit into
mainfrom
linter-prototype
Feb 25, 2023
Merged

feat(linter): linter prototype#48
Boshen merged 1 commit into
mainfrom
linter-prototype

Conversation

@Boshen

@Boshen Boshen commented Feb 25, 2023

Copy link
Copy Markdown
Member

No description provided.

@Boshen Boshen merged commit c86cca3 into main Feb 25, 2023
@Boshen Boshen deleted the linter-prototype branch February 25, 2023 08:56
@github-actions

github-actions Bot commented Feb 25, 2023

Copy link
Copy Markdown
Contributor

Parser Benchmark Results - ubuntu-latest\n

group                    main                                   pr
-----                    ----                                   --
parser/babylon.max.js    1.00    154.8±4.72ms    66.7 MB/sec    1.02    158.1±5.52ms    65.3 MB/sec
parser/d3.js             1.00     19.7±0.72ms    27.8 MB/sec    1.05     20.6±0.63ms    26.5 MB/sec
parser/lodash.js         1.00      5.6±0.27ms    92.0 MB/sec    1.01      5.6±0.21ms    91.4 MB/sec
parser/pdf.js            1.00     11.3±0.44ms    35.5 MB/sec    1.01     11.5±0.39ms    35.0 MB/sec
parser/typescript.js     1.00    152.9±5.37ms    62.9 MB/sec    1.04    159.2±4.68ms    60.4 MB/sec

@github-actions

github-actions Bot commented Feb 25, 2023

Copy link
Copy Markdown
Contributor

Parser Benchmark Results - macos-latest\n

group                    main                                   pr
-----                    ----                                   --
parser/babylon.max.js    1.00    161.8±8.99ms    63.8 MB/sec    1.00    161.3±4.06ms    64.0 MB/sec
parser/d3.js             1.00     19.9±0.47ms    27.5 MB/sec    1.02     20.2±0.75ms    27.0 MB/sec
parser/lodash.js         1.00      5.5±0.33ms    93.6 MB/sec    1.03      5.6±0.36ms    91.2 MB/sec
parser/pdf.js            1.00     11.5±0.40ms    34.9 MB/sec    1.00     11.5±0.46ms    34.9 MB/sec
parser/typescript.js     1.00    159.4±5.09ms    60.3 MB/sec    1.02    162.0±6.62ms    59.4 MB/sec

@github-actions

github-actions Bot commented Feb 25, 2023

Copy link
Copy Markdown
Contributor

Parser Benchmark Results - windows-latest\n

group                    main                                   pr
-----                    ----                                   --
parser/babylon.max.js    1.01    141.1±3.26ms    73.2 MB/sec    1.00    140.2±3.03ms    73.6 MB/sec
parser/d3.js             1.01     17.6±0.10ms    31.1 MB/sec    1.00     17.5±0.28ms    31.3 MB/sec
parser/lodash.js         1.00      4.8±0.18ms   106.5 MB/sec    1.01      4.9±0.16ms   106.0 MB/sec
parser/pdf.js            1.00      9.9±0.13ms    40.7 MB/sec    1.00      9.9±0.15ms    40.5 MB/sec
parser/typescript.js     1.00    139.4±1.85ms    69.0 MB/sec    1.00    139.3±2.10ms    69.0 MB/sec

graphite-app Bot pushed a commit that referenced this pull request Jun 11, 2026
## Summary

- Change `Codegen::wrap` from `FnMut` to `FnOnce`, matching how the helper is used: every closure passed to `wrap` is invoked exactly once.
- Add an assembly comparison note showing the optimized codegen difference before and after the change.

## `Fn`, `FnMut`, and `FnOnce` ?

`Fn`, `FnMut`, and `FnOnce` all describe how a closure may be called:

- `Fn` is the most restrictive for the closure body: it is callable through `&self`, can be called repeatedly, and cannot require mutable or consuming access to captured state.
- `FnMut` is callable through `&mut self`, can be called repeatedly, and may mutate captured state.
- `FnOnce` is callable by value, may consume captured state, and is only guaranteed to be callable once.

The trait relationship goes from most specific to most general call capability:

```rust
Fn: FnMut
FnMut: FnOnce
```

So accepting `FnOnce` is the least restrictive bound for a callback that is only invoked once. It still accepts `Fn` and `FnMut` closures, but it also tells the optimizer that `wrap` does not need a reusable mutable closure object.

## Assembly Impact

`Codegen::wrap` is an inline generic helper, so there is no stable standalone `wrap` assembly symbol in release output. The impact shows up in monomorphized call sites such as `Class::gen`, `Function::gen`, and expression `gen_expr` closures.

Before, several call sites materialized a closure environment on the stack before calling the closure:

```asm
strb    w9, [sp, #15]
add     x9, sp, #15
stp     x0, x9, [sp, #16]
add     x0, sp, #16
bl      <...>::{{closure}}
```

After the `FnOnce` bound, the same shape can pass the one-shot closure state directly through registers:

```asm
and     w1, w2, #0xfffffffd
mov     x2, x19
bl      <...>::{{closure}}
```

Some wrapper frames also shrink. For example, representative `Class::gen` / `Function::gen` paths go from a `64` byte frame to a `48` byte frame:

```asm
- sub     sp, sp, #64
- stp     x20, x19, [sp, #32]
- stp     x29, x30, [sp, #48]
+ sub     sp, sp, #48
+ stp     x20, x19, [sp, #16]
+ stp     x29, x30, [sp, #32]
```

Several restored-frame return paths also become tail calls:

```asm
ldp     x29, x30, [sp, #32]
ldp     x20, x19, [sp, #16]
add     sp, sp, #48
b       <closure or push_slow target>
```

The assembly diff also contains expected local label renumbering noise, such as switch-table suffixes changing from `.318` to `.330`; those are not behavior changes.
camc314 added a commit that referenced this pull request Jul 3, 2026
## Summary

- Change `Codegen::wrap` from `FnMut` to `FnOnce`, matching how the helper is used: every closure passed to `wrap` is invoked exactly once.
- Add an assembly comparison note showing the optimized codegen difference before and after the change.

## `Fn`, `FnMut`, and `FnOnce` ?

`Fn`, `FnMut`, and `FnOnce` all describe how a closure may be called:

- `Fn` is the most restrictive for the closure body: it is callable through `&self`, can be called repeatedly, and cannot require mutable or consuming access to captured state.
- `FnMut` is callable through `&mut self`, can be called repeatedly, and may mutate captured state.
- `FnOnce` is callable by value, may consume captured state, and is only guaranteed to be callable once.

The trait relationship goes from most specific to most general call capability:

```rust
Fn: FnMut
FnMut: FnOnce
```

So accepting `FnOnce` is the least restrictive bound for a callback that is only invoked once. It still accepts `Fn` and `FnMut` closures, but it also tells the optimizer that `wrap` does not need a reusable mutable closure object.

## Assembly Impact

`Codegen::wrap` is an inline generic helper, so there is no stable standalone `wrap` assembly symbol in release output. The impact shows up in monomorphized call sites such as `Class::gen`, `Function::gen`, and expression `gen_expr` closures.

Before, several call sites materialized a closure environment on the stack before calling the closure:

```asm
strb    w9, [sp, #15]
add     x9, sp, #15
stp     x0, x9, [sp, #16]
add     x0, sp, #16
bl      <...>::{{closure}}
```

After the `FnOnce` bound, the same shape can pass the one-shot closure state directly through registers:

```asm
and     w1, w2, #0xfffffffd
mov     x2, x19
bl      <...>::{{closure}}
```

Some wrapper frames also shrink. For example, representative `Class::gen` / `Function::gen` paths go from a `64` byte frame to a `48` byte frame:

```asm
- sub     sp, sp, #64
- stp     x20, x19, [sp, #32]
- stp     x29, x30, [sp, #48]
+ sub     sp, sp, #48
+ stp     x20, x19, [sp, #16]
+ stp     x29, x30, [sp, #32]
```

Several restored-frame return paths also become tail calls:

```asm
ldp     x29, x30, [sp, #32]
ldp     x20, x19, [sp, #16]
add     sp, sp, #48
b       <closure or push_slow target>
```

The assembly diff also contains expected local label renumbering noise, such as switch-table suffixes changing from `.318` to `.330`; those are not behavior changes.
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.

1 participant