New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

LLVM loop optimization can make safe programs crash #28728

Open
RalfJung opened this Issue Sep 29, 2015 · 37 comments

Comments

Projects
None yet
@RalfJung
Member

RalfJung commented Sep 29, 2015

The following snippet crashes when compiled in release mode on current stable, beta and nightly:

enum Null {}

fn foo() -> Null { loop { } }

fn create_null() -> Null {
    let n = foo();

    let mut i = 0;
    while i < 100 { i += 1; }
    return n;
}

fn use_null(n: Null) -> ! {
    match n { }
}


fn main() {
    use_null(create_null());
}

https://play.rust-lang.org/?gist=1f99432e4f2dccdf7d7e&version=stable

This is based on the following example of LLVM removing a loop that I was made aware of: https://github.com/simnalamburt/snippets/blob/master/rust/src/bin/infinite.rs.
What seems to happen is that since C allows LLVM to remove endless loops that have no side-effect, we end up executing a match that has to arms.

@steveklabnik steveklabnik added the A-LLVM label Sep 29, 2015

@ranma42

This comment has been minimized.

Show comment
Hide comment
@ranma42

ranma42 Sep 29, 2015

Contributor

The LLVM IR of the optimised code is

; Function Attrs: noreturn nounwind readnone uwtable
define internal void @_ZN4main20h5ec738167109b800UaaE() unnamed_addr #0 {
entry-block:
  unreachable
}

This kind of optimisation breaks the main assumption that should normally hold on uninhabited types: it should be impossible to have a value of that type.
rust-lang/rfcs#1216 proposes to explicitly handle such types in Rust. It might be effective in ensuring that LLVM never has to handle them and in injecting the appropriate code to ensure divergence when needed (IIUIC this could be achieved with appropriate attributes or intrinsic calls).
This topic has also been recently discussed in the LLVM mailing list: http://lists.llvm.org/pipermail/llvm-dev/2015-July/088095.html

Contributor

ranma42 commented Sep 29, 2015

The LLVM IR of the optimised code is

; Function Attrs: noreturn nounwind readnone uwtable
define internal void @_ZN4main20h5ec738167109b800UaaE() unnamed_addr #0 {
entry-block:
  unreachable
}

This kind of optimisation breaks the main assumption that should normally hold on uninhabited types: it should be impossible to have a value of that type.
rust-lang/rfcs#1216 proposes to explicitly handle such types in Rust. It might be effective in ensuring that LLVM never has to handle them and in injecting the appropriate code to ensure divergence when needed (IIUIC this could be achieved with appropriate attributes or intrinsic calls).
This topic has also been recently discussed in the LLVM mailing list: http://lists.llvm.org/pipermail/llvm-dev/2015-July/088095.html

@alexcrichton

This comment has been minimized.

Show comment
Hide comment
@alexcrichton

alexcrichton Sep 29, 2015

Member

triage: I-nominated

Seems bad! If LLVM doesn't have a way to say "yes, this loop really is infinite" though then we may just have to sit-and-wait for the upstream discussion to settle.

Member

alexcrichton commented Sep 29, 2015

triage: I-nominated

Seems bad! If LLVM doesn't have a way to say "yes, this loop really is infinite" though then we may just have to sit-and-wait for the upstream discussion to settle.

@ranma42

This comment has been minimized.

Show comment
Hide comment
@ranma42

ranma42 Sep 29, 2015

Contributor

A way to prevent infinite loops from being optimised away is to add unsafe {asm!("" :::: "volatile")} inside of them. This is similar to the llvm.noop.sideeffect intrinsic that has been proposed in the LLVM mailing list, but it might prevent some optimisations.
In order to avoid the performance loss and to still guarantee that diverging functions/loops are not optimised away, I believe that it should be sufficient to insert an empty non-optimisable loop (i.e. loop { unsafe { asm!("" :::: "volatile") } }) if uninhabited values are in scope.
If LLVM optimises the code which should diverge to the point that it does not diverge anymore, such loops will ensure that the control flow is still unable to proceed.
In "lucky" case in which LLVM is unable to optimise the diverging code, such loop will be removed by DCE.

Contributor

ranma42 commented Sep 29, 2015

A way to prevent infinite loops from being optimised away is to add unsafe {asm!("" :::: "volatile")} inside of them. This is similar to the llvm.noop.sideeffect intrinsic that has been proposed in the LLVM mailing list, but it might prevent some optimisations.
In order to avoid the performance loss and to still guarantee that diverging functions/loops are not optimised away, I believe that it should be sufficient to insert an empty non-optimisable loop (i.e. loop { unsafe { asm!("" :::: "volatile") } }) if uninhabited values are in scope.
If LLVM optimises the code which should diverge to the point that it does not diverge anymore, such loops will ensure that the control flow is still unable to proceed.
In "lucky" case in which LLVM is unable to optimise the diverging code, such loop will be removed by DCE.

@geofft

This comment has been minimized.

Show comment
Hide comment
@geofft

geofft Sep 29, 2015

Contributor

Is this related to #18785? That one's about infinite recursion to be UB, but it sounds like the fundamental cause might be similar: LLVM doesn't consider not halting to be a side effect, so if a function has no side effects other than not halting, it's happy to optimize it away.

Contributor

geofft commented Sep 29, 2015

Is this related to #18785? That one's about infinite recursion to be UB, but it sounds like the fundamental cause might be similar: LLVM doesn't consider not halting to be a side effect, so if a function has no side effects other than not halting, it's happy to optimize it away.

@arielb1

This comment has been minimized.

Show comment
Hide comment
@arielb1

arielb1 Sep 29, 2015

Contributor

@geofft

It's the same issue.

Contributor

arielb1 commented Sep 29, 2015

@geofft

It's the same issue.

@RalfJung

This comment has been minimized.

Show comment
Hide comment
@RalfJung

RalfJung Sep 29, 2015

Member

Yes, looks like it's the same. Further down that issue, they show how to get undef, from which I assume it's not hard to make a (seemingly safe) program crash.

Member

RalfJung commented Sep 29, 2015

Yes, looks like it's the same. Further down that issue, they show how to get undef, from which I assume it's not hard to make a (seemingly safe) program crash.

@simnalamburt

This comment has been minimized.

Show comment
Hide comment
@simnalamburt

simnalamburt Sep 29, 2015

Contributor

👍

Contributor

simnalamburt commented Sep 29, 2015

👍

@ranma42

This comment has been minimized.

Show comment
Hide comment
@ranma42

ranma42 Sep 29, 2015

Contributor
Contributor

ranma42 commented Sep 29, 2015

@bluss bluss added the I-wrong label Sep 29, 2015

@nikomatsakis

This comment has been minimized.

Show comment
Hide comment
@nikomatsakis

nikomatsakis Oct 1, 2015

Contributor

So I've been wondering how long until somebody reports this. :) In my opinion, the best solution would of course be if we could tell LLVM not to be so aggressive about potentially infinite loops. Otherwise, the only thing I think we can do is to do a conservative analysis in Rust itself that determines whether:

  1. the loop will terminate OR
  2. the loop will have side-effects (I/O operations etc, I forget precisely how this is defined in C)

Either of this should be enough to avoid undefined behavior.

Contributor

nikomatsakis commented Oct 1, 2015

So I've been wondering how long until somebody reports this. :) In my opinion, the best solution would of course be if we could tell LLVM not to be so aggressive about potentially infinite loops. Otherwise, the only thing I think we can do is to do a conservative analysis in Rust itself that determines whether:

  1. the loop will terminate OR
  2. the loop will have side-effects (I/O operations etc, I forget precisely how this is defined in C)

Either of this should be enough to avoid undefined behavior.

@nikomatsakis

This comment has been minimized.

Show comment
Hide comment
@nikomatsakis

nikomatsakis Oct 1, 2015

Contributor

triage: P-medium

We'd like to see what LLVM will do before we invest a lot of effort on our side, and this seems relatively unlikely to cause problems in practice (though I have personally hit this while developing the compiler as well). There are no backwards incomatibility issues to be concerned about.

Contributor

nikomatsakis commented Oct 1, 2015

triage: P-medium

We'd like to see what LLVM will do before we invest a lot of effort on our side, and this seems relatively unlikely to cause problems in practice (though I have personally hit this while developing the compiler as well). There are no backwards incomatibility issues to be concerned about.

@rust-highfive rust-highfive added P-medium and removed I-nominated labels Oct 1, 2015

@dotdash

This comment has been minimized.

Show comment
Hide comment
@dotdash

dotdash Oct 1, 2015

Contributor

Quoting from the LLVM mailing list discussion:

 The implementation may assume that any thread will eventually do one of the following:
   - terminate
   - make a call to a library I/O function
   - access or modify a volatile object, or
   - perform a synchronization operation or an atomic operation

 [Note: This is intended to allow compiler transformations such as removal of empty loops, even
  when termination cannot be proven. — end note ]
Contributor

dotdash commented Oct 1, 2015

Quoting from the LLVM mailing list discussion:

 The implementation may assume that any thread will eventually do one of the following:
   - terminate
   - make a call to a library I/O function
   - access or modify a volatile object, or
   - perform a synchronization operation or an atomic operation

 [Note: This is intended to allow compiler transformations such as removal of empty loops, even
  when termination cannot be proven. — end note ]
@ranma42

This comment has been minimized.

Show comment
Hide comment
@ranma42

ranma42 Oct 2, 2015

Contributor

@dotdash The excerpt you are quoting comes from the C++ specification; it is basically the answer to "how it [having side effects] is defined in C" (also confirmed by the standard committee: http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1528.htm ).

Regarding what is the expected behaviour of the LLVM IR there is some confusion. https://llvm.org/bugs/show_bug.cgi?id=24078 shows that there seems to be no accurate & explicit specification of the semantics of infinite loops in LLVM IR. It aligns with the semantics of C++, most likely for historical reasons and for convenience (I only managed to track down https://groups.google.com/forum/#!topic/llvm-dev/j2vlIECKkdE which apparently refers to a time when infinite loops were not optimised away, some time before the C/C++ specs were updated to allow it).

From the thread it is clear that there is the desire to optimise C++ code as effectively as possible (i.e. also taking into account the opportunity to remove infinite loops), but in the same thread several developers (including some that actively contribute to LLVM) have shown interest in the ability to preserve infinite loops, as they are needed for other languages.

Contributor

ranma42 commented Oct 2, 2015

@dotdash The excerpt you are quoting comes from the C++ specification; it is basically the answer to "how it [having side effects] is defined in C" (also confirmed by the standard committee: http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1528.htm ).

Regarding what is the expected behaviour of the LLVM IR there is some confusion. https://llvm.org/bugs/show_bug.cgi?id=24078 shows that there seems to be no accurate & explicit specification of the semantics of infinite loops in LLVM IR. It aligns with the semantics of C++, most likely for historical reasons and for convenience (I only managed to track down https://groups.google.com/forum/#!topic/llvm-dev/j2vlIECKkdE which apparently refers to a time when infinite loops were not optimised away, some time before the C/C++ specs were updated to allow it).

From the thread it is clear that there is the desire to optimise C++ code as effectively as possible (i.e. also taking into account the opportunity to remove infinite loops), but in the same thread several developers (including some that actively contribute to LLVM) have shown interest in the ability to preserve infinite loops, as they are needed for other languages.

@dotdash

This comment has been minimized.

Show comment
Hide comment
@dotdash

dotdash Oct 2, 2015

Contributor

@ranma42 I'm aware of that, I just quoted that for reference, because one possibility to work-around this would be to detect such loops in rust and add one of the above to it to stop LLVM from performing this optimization.

Contributor

dotdash commented Oct 2, 2015

@ranma42 I'm aware of that, I just quoted that for reference, because one possibility to work-around this would be to detect such loops in rust and add one of the above to it to stop LLVM from performing this optimization.

@bstrie

This comment has been minimized.

Show comment
Hide comment
@bstrie

bstrie Nov 30, 2015

Contributor

Is this a soundness issue? If so, we should tag it as such.

Contributor

bstrie commented Nov 30, 2015

Is this a soundness issue? If so, we should tag it as such.

@bluss

This comment has been minimized.

Show comment
Hide comment
@bluss

bluss Nov 30, 2015

Contributor

Yes, following @ranma42's example, this way shows how it readily defeats array bounds checks. playground link

Contributor

bluss commented Nov 30, 2015

Yes, following @ranma42's example, this way shows how it readily defeats array bounds checks. playground link

@bluss bluss added I-unsound 💥 and removed I-wrong labels Nov 30, 2015

@arielb1 arielb1 added I-wrong and removed I-unsound 💥 labels Dec 2, 2015

@arielb1

This comment has been minimized.

Show comment
Hide comment
@arielb1

arielb1 Dec 2, 2015

Contributor

@bluss

The policy is that wrong-code issues that are also soundness issues (i.e. most of them) should be tagged I-wrong.

Contributor

arielb1 commented Dec 2, 2015

@bluss

The policy is that wrong-code issues that are also soundness issues (i.e. most of them) should be tagged I-wrong.

@nikomatsakis

This comment has been minimized.

Show comment
Hide comment
@nikomatsakis

nikomatsakis Aug 4, 2016

Contributor

So just to recap prior discussion, there are really two choices here that I can see:

  • Wait for LLVM to provide a solution.
  • Introduce no-op asm statements wherever there may be an infinite loop or infinite recursion (#18785).

The latter is kind of bad because it can inhibit optimization, so we'd want to do it somewhat sparingly -- basically wherever we can't prove termination ourselves. You could also imaging tying it a bit more to how LLVM optimizes -- i.e., introducing only if we can detect a scenario that LLVM might consider to be an infinite loop/recursion -- but that would (a) require tracking LLVM and (b) require deeper knowledge than I, at least, possess.

Contributor

nikomatsakis commented Aug 4, 2016

So just to recap prior discussion, there are really two choices here that I can see:

  • Wait for LLVM to provide a solution.
  • Introduce no-op asm statements wherever there may be an infinite loop or infinite recursion (#18785).

The latter is kind of bad because it can inhibit optimization, so we'd want to do it somewhat sparingly -- basically wherever we can't prove termination ourselves. You could also imaging tying it a bit more to how LLVM optimizes -- i.e., introducing only if we can detect a scenario that LLVM might consider to be an infinite loop/recursion -- but that would (a) require tracking LLVM and (b) require deeper knowledge than I, at least, possess.

@gnzlbg

This comment has been minimized.

Show comment
Hide comment
@gnzlbg

gnzlbg Sep 1, 2016

Contributor

Wait for LLVM to provide a solution.

What is the LLVM bug tracking this issue?

Contributor

gnzlbg commented Sep 1, 2016

Wait for LLVM to provide a solution.

What is the LLVM bug tracking this issue?

@oli-obk

This comment has been minimized.

Show comment
Hide comment
@oli-obk

oli-obk Oct 11, 2016

Contributor

side-note: while true {} exhibits this behaviour. Maybe the lint should be upgraded to error-by-default and get a note stating that this currently can exhibit undefined behaviour?

Contributor

oli-obk commented Oct 11, 2016

side-note: while true {} exhibits this behaviour. Maybe the lint should be upgraded to error-by-default and get a note stating that this currently can exhibit undefined behaviour?

@ubsan

This comment has been minimized.

Show comment
Hide comment
@ubsan

ubsan Nov 30, 2016

Contributor

Also, note that this is invalid for C. LLVM making this argument means that there is a bug in clang.

void foo() { while (1) { } }

void create_null() {
        foo();

        int i = 0;
        while (i < 100) { i += 1; }
}

__attribute__((noreturn))
void use_null() {
        __builtin_unreachable();
}


int main() {
        create_null();
        use_null();
}

This crashes with optimizations; this is invalid behavior under the C11 standard:

An iteration statement whose controlling expression is not a constant
expression, [note 156] that performs no  input/output  operations,
does  not  access  volatile  objects,  and  performs  no synchronization or
atomic operations in its body, controlling expression, or (in the case of
a for statement) its expression-3, may be   assumed   by   the
implementation to terminate. [note 157]

156: An omitted controlling expression is replaced by a nonzero constant,
     which is a constant expression.
157: This  is  intended  to  allow  compiler  transformations  such  as
     removal  of  empty  loops  even  when termination cannot be proven. 

Note the "whose controlling expression is not a constant expression" - while (1) { }, 1 is a constant expression, and thus may not be removed.

Contributor

ubsan commented Nov 30, 2016

Also, note that this is invalid for C. LLVM making this argument means that there is a bug in clang.

void foo() { while (1) { } }

void create_null() {
        foo();

        int i = 0;
        while (i < 100) { i += 1; }
}

__attribute__((noreturn))
void use_null() {
        __builtin_unreachable();
}


int main() {
        create_null();
        use_null();
}

This crashes with optimizations; this is invalid behavior under the C11 standard:

An iteration statement whose controlling expression is not a constant
expression, [note 156] that performs no  input/output  operations,
does  not  access  volatile  objects,  and  performs  no synchronization or
atomic operations in its body, controlling expression, or (in the case of
a for statement) its expression-3, may be   assumed   by   the
implementation to terminate. [note 157]

156: An omitted controlling expression is replaced by a nonzero constant,
     which is a constant expression.
157: This  is  intended  to  allow  compiler  transformations  such  as
     removal  of  empty  loops  even  when termination cannot be proven. 

Note the "whose controlling expression is not a constant expression" - while (1) { }, 1 is a constant expression, and thus may not be removed.

@oli-obk

This comment has been minimized.

Show comment
Hide comment
@oli-obk

oli-obk Nov 30, 2016

Contributor

Is the loop removal an optimization pass that we could simply remove?

Contributor

oli-obk commented Nov 30, 2016

Is the loop removal an optimization pass that we could simply remove?

@zackw

This comment has been minimized.

Show comment
Hide comment
@zackw

zackw May 15, 2017

Contributor

Repeating myself from #42009: this bug can, under some circumstances, cause the emission of an externally callable function containing no machine instructions at all. This should never happen. If LLVM deduces that a pub fn can never be called by correct code, it should emit at least a trap instruction as the body of that function.

Contributor

zackw commented May 15, 2017

Repeating myself from #42009: this bug can, under some circumstances, cause the emission of an externally callable function containing no machine instructions at all. This should never happen. If LLVM deduces that a pub fn can never be called by correct code, it should emit at least a trap instruction as the body of that function.

@steveklabnik

This comment has been minimized.

Show comment
Hide comment
@sunfishcode

This comment has been minimized.

Show comment
Hide comment
@sunfishcode

sunfishcode Sep 22, 2017

Contributor

The LLVM bug for this is https://bugs.llvm.org/show_bug.cgi?id=965 (opened in 2006).

Contributor

sunfishcode commented Sep 22, 2017

The LLVM bug for this is https://bugs.llvm.org/show_bug.cgi?id=965 (opened in 2006).

@sunfishcode

This comment has been minimized.

Show comment
Hide comment
@sunfishcode

sunfishcode Sep 27, 2017

Contributor

@zackw LLVM has a flag for that: TrapUnreachable. I haven't tested this, but it looks adding Options.TrapUnreachable = true; to LLVMRustCreateTargetMachine ought to address your concern. It's likely that this has a low enough cost that it could be done by default, though I haven't made any measurements.

@oli-obk It's unfortunately not just a loop-deletion pass. The problem arises from broad assumptions, for example: (a) branches have no side effects, (b) functions that contain no instructions with side effects have no side effects, and (c) calls to functions with no side effects can be moved or deleted.

Contributor

sunfishcode commented Sep 27, 2017

@zackw LLVM has a flag for that: TrapUnreachable. I haven't tested this, but it looks adding Options.TrapUnreachable = true; to LLVMRustCreateTargetMachine ought to address your concern. It's likely that this has a low enough cost that it could be done by default, though I haven't made any measurements.

@oli-obk It's unfortunately not just a loop-deletion pass. The problem arises from broad assumptions, for example: (a) branches have no side effects, (b) functions that contain no instructions with side effects have no side effects, and (c) calls to functions with no side effects can be moved or deleted.

@steveklabnik

This comment has been minimized.

Show comment
Hide comment
@steveklabnik

steveklabnik Sep 28, 2017

Member

Looks like there's a patch: https://reviews.llvm.org/D38336

Member

steveklabnik commented Sep 28, 2017

Looks like there's a patch: https://reviews.llvm.org/D38336

@bstrie

This comment has been minimized.

Show comment
Hide comment
@bstrie

bstrie Nov 2, 2017

Contributor

@sunfishcode , looks like your LLVM patch at https://reviews.llvm.org/D38336 was "accepted" on October 3, can you give an update on what that means regarding LLVM's release process? What's the next step beyond acceptance, and do you have an idea of what future LLVM release will contain this patch?

Contributor

bstrie commented Nov 2, 2017

@sunfishcode , looks like your LLVM patch at https://reviews.llvm.org/D38336 was "accepted" on October 3, can you give an update on what that means regarding LLVM's release process? What's the next step beyond acceptance, and do you have an idea of what future LLVM release will contain this patch?

@sunfishcode

This comment has been minimized.

Show comment
Hide comment
@sunfishcode

sunfishcode Nov 2, 2017

Contributor

I talked with some people offline who suggested we have an llvmdev thread. The thread is here:

http://lists.llvm.org/pipermail/llvm-dev/2017-October/118558.html

It's now concluded, with the result being that I need to make additional changes. I think the changes will be good, though they'll take me a little more time to do.

Contributor

sunfishcode commented Nov 2, 2017

I talked with some people offline who suggested we have an llvmdev thread. The thread is here:

http://lists.llvm.org/pipermail/llvm-dev/2017-October/118558.html

It's now concluded, with the result being that I need to make additional changes. I think the changes will be good, though they'll take me a little more time to do.

@bstrie

This comment has been minimized.

Show comment
Hide comment
@bstrie

bstrie Nov 2, 2017

Contributor

Thanks for the update, and thanks so much for your efforts!

Contributor

bstrie commented Nov 2, 2017

Thanks for the update, and thanks so much for your efforts!

@bstrie

This comment has been minimized.

Show comment
Hide comment
@bstrie

bstrie Nov 9, 2017

Contributor

Note that https://reviews.llvm.org/rL317729 has landed in LLVM. This patch is planned to have a follow-up patch which makes infinite loops exhibit defined behavior by default, so AFAICT all we need to do is wait and eventually this will be resolved for us upstream.

Contributor

bstrie commented Nov 9, 2017

Note that https://reviews.llvm.org/rL317729 has landed in LLVM. This patch is planned to have a follow-up patch which makes infinite loops exhibit defined behavior by default, so AFAICT all we need to do is wait and eventually this will be resolved for us upstream.

@sunfishcode

This comment has been minimized.

Show comment
Hide comment
@sunfishcode

sunfishcode Nov 10, 2017

Contributor

@zackw I've now created #45920 to fix the problem of functions containing no code.

@bstrie Yes, the first step is landed, and I'm working on the second step of making LLVM give infinite loops defined behavior by default. It's a complex change, and I don't yet know how long it'll take to finish, but I'll post updates here.

Contributor

sunfishcode commented Nov 10, 2017

@zackw I've now created #45920 to fix the problem of functions containing no code.

@bstrie Yes, the first step is landed, and I'm working on the second step of making LLVM give infinite loops defined behavior by default. It's a complex change, and I don't yet know how long it'll take to finish, but I'll post updates here.

pusherofbrooms added a commit to pusherofbrooms/blink that referenced this issue Nov 12, 2017

Prevent LLVM from optimizing away busy loop
Idea is from rust-lang/rust#28728.
Also, increase the loop to 400000 cycles so the blink is visible.

shepmaster added a commit to avr-rust/blink that referenced this issue Nov 13, 2017

Prevent LLVM from optimizing away busy loop (#4)
Idea is from rust-lang/rust#28728.
Also, increase the loop to 400000 cycles so the blink is visible.

bors added a commit that referenced this issue Nov 15, 2017

Auto merge of #45920 - sunfishcode:trap-on-unreachable, r=Zoxc
Enable TrapUnreachable in LLVM.

This patch enables LLVM's TrapUnreachable flag, which tells it to translate `unreachable` instructions into hardware trap instructions, rather than allowing control flow to "fall through" into whatever code happens to follow it in memory.

This follows up on #28728 (comment). For example, for @zackw's testcase [here](#42009 (comment)), the output function contains a `ud2` instead of no code, so it won't "fall through" into whatever happens to be next in memory.

(I'm also working on the problem of LLVM optimizing away infinite loops, but the patch here is useful independently.)

I tested this patch on a few different codebases, and the code size increase ranged from 0.0% to 0.1%.

bors added a commit that referenced this issue Nov 16, 2017

Auto merge of #45920 - sunfishcode:trap-on-unreachable, r=Zoxc
Enable TrapUnreachable in LLVM.

This patch enables LLVM's TrapUnreachable flag, which tells it to translate `unreachable` instructions into hardware trap instructions, rather than allowing control flow to "fall through" into whatever code happens to follow it in memory.

This follows up on #28728 (comment). For example, for @zackw's testcase [here](#42009 (comment)), the output function contains a `ud2` instead of no code, so it won't "fall through" into whatever happens to be next in memory.

(I'm also working on the problem of LLVM optimizing away infinite loops, but the patch here is useful independently.)

I tested this patch on a few different codebases, and the code size increase ranged from 0.0% to 0.1%.
@jsgf

This comment has been minimized.

Show comment
Hide comment
Contributor

jsgf commented Feb 8, 2018

@kennytm

This comment has been minimized.

Show comment
Hide comment
@kennytm

kennytm Feb 8, 2018

Member

@jsgf Still repro. Have you selected Release mode?

Member

kennytm commented Feb 8, 2018

@jsgf Still repro. Have you selected Release mode?

@jsgf

This comment has been minimized.

Show comment
Hide comment
@jsgf

jsgf Feb 15, 2018

Contributor

@kennytm Woops, never mind.

Contributor

jsgf commented Feb 15, 2018

@kennytm Woops, never mind.

@japaric

This comment has been minimized.

Show comment
Hide comment
@japaric

japaric Aug 7, 2018

Member

Note that https://reviews.llvm.org/rL317729 has landed in LLVM. This patch is planned to have a follow-up patch which makes infinite loops exhibit defined behavior by default, so AFAICT all we need to do is wait and eventually this will be resolved for us upstream.

It has been several months since this comment. Anyone knows if the follow-up patch happened or will still happen?

Alternatively, it seems that the llvm.sideeffect intrinsic exists in the LLVM version we are using: could we fix this ourselves by translating Rust infinite loops into LLVM loops that contain the sideeffect intrinsic?

Member

japaric commented Aug 7, 2018

Note that https://reviews.llvm.org/rL317729 has landed in LLVM. This patch is planned to have a follow-up patch which makes infinite loops exhibit defined behavior by default, so AFAICT all we need to do is wait and eventually this will be resolved for us upstream.

It has been several months since this comment. Anyone knows if the follow-up patch happened or will still happen?

Alternatively, it seems that the llvm.sideeffect intrinsic exists in the LLVM version we are using: could we fix this ourselves by translating Rust infinite loops into LLVM loops that contain the sideeffect intrinsic?

bors bot added a commit to rust-embedded/cortex-m-rt that referenced this issue Aug 8, 2018

Merge #85
85: [WIP] provide defaults for DefaultHandler and HardFault r=korken89 a=japaric

`exception!(HardFault, ..)` and `exception!(*, ..)` can now be omitted from
programs to pick up the default behavior of an infinite loop. Existing programs
that define these exception handlers will continue to work w/o any functional
change.

closes #72

--

Annoyances:

- The handlers can't be *just* an infinite loop because of rust-lang/rust#28728.
If we define the handlers as just `loop {}` they will become an abort (UDF)
instruction. And that would turn HardFault into infinite recursion. For that
reason I have made them into an infinite loop that does some side effect

- If you stick to the defaults then the symbol name of the default handler
changes from `DefaultHandler` (override) to `DefaultDefaultHandler` (default).
We can make these two names more similar but I think we can not prevent the
rename. Something similar happens with UserHardFault (which becomes
DefaultUserHardFault when not overridden).

cc @rust-embedded/cortex-m

Co-authored-by: Jorge Aparicio <jorge@japaric.io>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment