[codegen] Unnecessary panicking branches in resumption of infinite generator (stored in static variable) #66100
Labels
A-async-await
Area: Async & Await
A-codegen
Area: Code generation
A-coroutines
Area: Coroutines
AsyncAwait-Triaged
Async-await issues that have been triaged during a working group meeting.
C-enhancement
Category: An issue proposing an enhancement or a PR with one.
F-coroutines
`#![feature(coroutines)]`
F-type_alias_impl_trait
`#[feature(type_alias_impl_trait)]`
I-heavy
Issue: Problems and improvements with respect to binary size of generated code.
I-slow
Issue: Problems and improvements with respect to performance of generated code.
T-compiler
Relevant to the compiler team, which will review and decide on the PR/issue.
WG-embedded
Working group: Embedded systems
Tested with
nightly-2019-11-04
Observed behavior
The following
no_std
program which stores a generator in a static variable andthen resumes it (no indirection / dynamic-dispatch involved):
when fully optimized for the
thumbv7m-none-eabi
target (panic = abort
)produces the following machine code for the
SysTick
function:This function resumes the generator stored in the static variable
X
. Thegenerator is extremely simple and contains no explicit panicking branches yet
the machine code contains 2 panicking branches which are not necessary for
memory safety (more on this later).
If the program is slightly modified like this (semantics are not changed):
Then
SysTick
is optimized to the following machine code, which is free ofpanicking branches:
Expected behavior
This second machine code (the one with 5 instructions) is what one would expect
from the first version of the program. Changing the shape / complexity of unrelated code
should not introduce panicking branches.
Cause
The
Generator.resume
function decides which part of the generator body toexecute based on the value of a discriminant. As of nightly-2019-11-04 the code
generated for the
resume
function contains 2 "built-in" panicking branches,which are present in all generators:
The first built-in panicking branch, which corresponds to discriminant
1
, isused to panic a generator that has been
resume
-d beyond completion -- agenerator that has reached a
return
statement has reached completion.The second built-in branch, which corresponds to discriminant
2
, is used topanic a generator that has been
resume
-d after panicking -- I'm not sure whenthis can occur in practice but looking at the compiler codegen unwinding is required to
reach this "poisoned" state.
However, neither of these situations can occur in the example. The discriminant
of the generator can never become
1
because it does not contain areturn
statement in its body. Its discriminant can not become
2
either because thetarget has been configured for
panic = abort
meaning that unwinding is notsupported at the codegen level (
UnwindResume
functions are never generated). Thiscan be confirmed by scanning the optimized LLVM IR produced by the compiler: the
discriminant is stored in static memory (
.bss
) and the only values written tothis memory are
0
and3
.Possible fixes (?)
Naively, I would improve the MIR codegen by:
Not producing the
discriminant == 1
branch if theReturn
type of thegenerator is
!
(or, I guess, any other empty type likeenum Void {}
). Thistype indicates that the generator does not contain a
return
statement.Not producing the
discriminant == 2
branch if the target has set itspanic
option to
abort
. Alternatively, one could check if the unwind-relatedResume
function is generated and omit the branch if it's not generated.Relevant code:
rust/src/librustc_mir/transform/generator.rs
Lines 1061 to 1068 in 881ebeb
Does these seem like sensible fixes?
STR
Steps to produce the above machine code. This is not a minified repro because a
certain complexity is required to get the inferior codegen -- trivial programs
get properly optimized.
The text was updated successfully, but these errors were encountered: