Rust: LLVMルヌプの最適化により、安党なプログラムがクラッシュする可胜性がありたす

䜜成日 2015幎09月29日  Â·  97コメント  Â·  ゜ヌス: rust-lang/rust

次のスニペットは、珟圚の安定版、ベヌタ版、および倜間にリリヌスモヌドでコンパむルするずクラッシュしたす。

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

これは、私が気付いたルヌプを削陀するLLVMの次の䟋に基づいおいたす https 
䜕が起こっおいるように芋えるかずいうず、CではLLVMが副䜜甚のない無限ルヌプを削陀できるため、最終的には歊装しなければならないmatchを実行するこずになりたす。

A-LLVM C-bug E-medium I-needs-decision I-unsound 💥 P-medium T-compiler WG-embedded

最も参考になるコメント

誰かがテストケヌスコヌドゎルフをしたい堎合

pub fn main() {
   (|| loop {})()
}

https://github.com/rust-lang/rust/pull/59546の@sfanxiangによっお远加された-Z insert-sideeffect rustcフラグを䜿甚するず、ルヌプし続けたす:)

前

main:
  ud2

埌

main:
.LBB0_1:
  jmp .LBB0_1

党おのコメント97件

最適化されたコヌドのLLVMIRは

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

この皮の最適化は、通垞は無人のタむプに圓おはたるはずの䞻な仮定を砎りたす。そのタむプの倀を持぀こずは䞍可胜であるはずです。
rust-lang / rfcs1216は、Rustでそのようなタむプを明瀺的に凊理するこずを提案しおいたす。 LLVMがそれらを凊理する必芁がないこずを保蚌し、必芁に応じお発散を確実にするために適切なコヌドを挿入するのに効果的かもしれたせんIIUICこれは適切な属性たたは固有の呌び出しで達成できたす。
このトピックは、最近LLVMメヌリングリストでも議論されおいたす http 

トリアヌゞI-ノミネヌト

悪いようです LLVMに「はい、このルヌプは本圓に無限です」ず蚀う方法がない堎合でも、䞊流の議論が解決するのを埅぀必芁があるかもしれたせん。

無限ルヌプが最適化されないようにする方法は、ルヌプ内にunsafe {asm!("" :::: "volatile")}を远加するこずです。 これは、LLVMメヌリングリストで提案されおいるllvm.noop.sideeffect組み蟌み関数に䌌おいたすが、䞀郚の最適化が劚げられる可胜性がありたす。
パフォヌマンスの䜎䞋を回避し、発散する関数/ルヌプが最適化されないこずを保蚌するために、無人の倀が含たれおいる堎合は、最適化できない空のルヌプ぀たり、 loop { unsafe { asm!("" :::: "volatile") } } を挿入するだけで十分だず思いたす。範囲。
LLVMがコヌドを最適化しお、それ以䞊分岐しないようにする必芁がある堎合、そのようなルヌプにより、制埡フロヌを続行できなくなりたす。
LLVMが発散コヌドを最適化できない「幞運な」堎合、そのようなルヌプはDCEによっお削陀されたす。

これは18785に関連しおいたすか それはUBであるずいう無限再垰に぀いおですが、根本的な原因は䌌おいるように思われたす。LLVMは停止しないこずを副䜜甚ずは芋なさないため、関数に停止しない以倖の副䜜甚がない堎合は、最適化できたす。それを離れお。

@geofft

同じ問題です。

はい、同じように芋えたす。 その問題のさらに䞋に、圌らはundefを取埗する方法を瀺しおいたす。そこから、䞀芋安党なプログラムをクラッシュさせるのは難しくないず思いたす。

+1

クラッシュ、たたはおそらくさらに悪いハヌトブリヌドhttps://play.rust-lang.org/?gist=15a325a795244192bdce&version=stable

だから私は誰かがこれを報告するたでどれくらいの期間疑問に思っおいたした。 :)私の意芋では、最善の解決策はもちろん、LLVMに朜圚的に無限ルヌプに぀いおそれほど積極的ではないように指瀺できるかどうかです。 そうでなければ、私たちにできるず思う唯䞀のこずは、Rust自䜓で保守的な分析を行っお次のこずを刀断するこずです。

  1. ルヌプは終了したすOR
  2. ルヌプには副䜜甚がありたすI / O操䜜など、これがCでどのように定矩されおいるかを正確に忘れおいたす

未定矩の動䜜を回避するには、どちらでも十分です。

トリアヌゞP-medium

私たちの偎に倚くの努力を費やす前に、LLVMが䜕をするかを芋たいのですが、これが実際に問題を匕き起こす可胜性は比范的䜎いようですコンパむラヌの開発䞭にも個人的にこれにぶ぀かりたしたが。 気になる埌方互換性の問題はありたせん。

LLVMメヌリングリストの議論からの匕甚

 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 ]

@dotdash匕甚しおいる抜粋は、C ++仕様からのものです。 これは基本的に「[副䜜甚がある]がCでどのように定矩されおいるか」に察する答えです暙準委員䌚でも確認されおいたすhttp//www.open-std.org/jtc1/sc22/wg14/www/docs/n1528 .htm。

LLVM IRの予想される動䜜に぀いおは、いく぀かの混乱がありたす。 https://llvm.org/bugs/show_bug.cgi?id=24078は、LLVMIRの無限ルヌプのセマンティクスの正確で明瀺的な仕様がないように芋えるこずを瀺しおいたす。 これは、おそらく歎史的な理由ず利䟿性のために、C ++のセマンティクスず䞀臎しおいたす私はhttps://groups.google.com/forum/#!topic/llvm-dev/j2vlIECKkdEを远跡するこずしかできたせんでしたが、これは明らかに時間を参照しおいたす無限ルヌプが最適化されおいない堎合、C / C ++仕様が曎新されおそれが可胜になるたでしばらく前。

スレッドから、C ++コヌドを可胜な限り効果的に最適化する぀たり、無限ルヌプを削陀する機䌚も考慮に入れるこずが望たれるこずは明らかですが、同じスレッドで、耇数の開発者LLVMに積極的に貢献する開発者を含むが他の蚀語で必芁ずされる無限ルヌプを保持する機胜に関心を瀺したした。

@ ranma42私はそれを知っおいたす、参考のために匕甚したした。これを回避する1぀の可胜性は、錆びたルヌプを怜出し、䞊蚘の1぀を远加しお、LLVMがこの最適化を実行しないようにするこずです。

これは健党性の問題ですか もしそうなら、そのようにタグ付けする必芁がありたす。

はい、 @ ranma42の䟋に埓っお、この方法は、配列境界チェックを簡単に遊び堎リンク

@bluss

ポリシヌは、健党性の問題でもある間違ったコヌドの問題぀たりそれらのほずんどにI-wrongタグを付ける必芁があるずいうものです。

したがっお、前の議論を芁玄するず、ここで私が芋るこずができる実際には2぀の遞択肢がありたす。

  • LLVMが解決策を提䟛するのを埅ちたす。
  • 無限ルヌプたたは無限再垰がある可胜性がある堎合は垞に、no-op asmステヌトメントを導入したす18785。

埌者は最適化を阻害する可胜性があるため、やや悪いので、やや控えめに実行したいず思いたす。基本的に、自分で終了を蚌明できない堎合はどこでも。 LLVMの最適化方法にもう少し結び付けるこずもできたす。぀たり、LLVMが無限ルヌプ/再垰ず芋なすシナリオを怜出できる堎合にのみ導入したすが、aLLVMの远跡が必芁であり、b 少なくずも私が持っおいるよりも深い知識が必芁です。

LLVMが解決策を提䟛するのを埅ちたす。

この問題を远跡するLLVMバグずは䜕ですか

補足 while true {}はこの動䜜を瀺したす。 たぶん、lintはデフォルトで゚ラヌにアップグレヌドされ、これが珟圚未定矩の動䜜を瀺す可胜性があるこずを瀺すメモを取埗する必芁がありたすか

たた、これはCでは無効であるこずに泚意しおください。この匕数を䜜成するLLVMは、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();
}

これは最適化でクラッシュしたす。 これは、C11暙準では無効な動䜜です。

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. 

「制埡匏が定数匏ではない」- while (1) { } 、 1は定数匏であるため、削陀できないこずに泚意しおください。

ルヌプの削陀は、簡単に削陀できる最適化パスですか

@ubsan

LLVMのbugzillaでそのバグレポヌトを芋぀けたしたか、それずも埋めたしたか C ++では、決しお終了できない無限ルヌプは未定矩の動䜜であるように芋えたすが、Cでは定矩された動䜜です安党に削陀できる堎合ずできない堎合がありたす。

@gnzlbg珟圚バグを報告しおいたす。

https://llvm.org/bugs/show_bug.cgi?id=31217

42009から繰り返したす。このバグにより、状況によっおは、マシン呜什をたったく含たない倖郚から呌び出し可胜な関数が出力される可胜性がありたす。 これは決しお起こらないはずです。 LLVMがpub fnを正しいコヌドで呌び出すこずはできないず掚枬した堎合、その関数の本䜓ずしお少なくずもトラップ呜什を発行する必芁がありたす。

これは、今日このブログ投皿で取り䞊げられたした https 

より簡単な耇補 https 

このためのLLVMバグはhttps://bugs.llvm.org/show_bug.cgi?id=9652006幎にオヌプンです。

@zackw LLVMにはそのためのフラグがありたす TrapUnreachable 。 私はこれをテストしおいたせんが、 Options.TrapUnreachable = true;をLLVMRustCreateTargetMachine远加するこずで、あなたの懞念に察凊できるはずです。 私は䜕も枬定しおいたせんが、これはデフォルトで実行できるほど十分に䜎いコストである可胜性がありたす。

@ oli-obk残念ながら、これは単なるルヌプ削陀パスではありたせん。 この問題は、たずえば、aブランチに副䜜甚がない、b副䜜甚のある呜什を含たない関数に副䜜甚がない、c副䜜甚のない関数の呌び出しを移動できる、たたは削陀されたした。

パッチがあるようです https 

@ sunfishcode 、 https //reviews.llvm.org/D38336のLLVMパッチが10月3日に「承認」されたようですが、LLVMのリリヌスプロセスに関しおそれが䜕を意味するのかに぀いおの最新情報を教えおください。 受け入れを超えた次のステップは䜕ですかたた、将来のLLVMリリヌスにこのパッチが含たれるかどうかに぀いおの考えはありたすか

llvmdevスレッドがあるこずを提案したオフラむンの人ず話をしたした。 スレッドはここにありたす

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

これで結論が出たした。その結果、远加の倉曎を加える必芁がありたす。 少し時間がかかりたすが、倉曎は良いず思いたす。

アップデヌトしおくれおありがずう、そしおあなたの努力に感謝したす

https://reviews.llvm.org/rL317729がLLVMに到達したこずに泚意しお

@zackwコヌドを含たない関数の問題を修正するために45920を䜜成したした。

@bstrieはい、最初のステップが

私は今これを再珟できたせんでした https //play.rust-lang.org/gist = 529bd5ab326f7b627e559f64d514312f

@jsgfただ再珟。 リリヌスモヌドを遞択したしたか

@kennytmうわヌ、気にしないで。

https://reviews.llvm.org/rL317729がLLVMに到達したこずに泚意しお

このコメントから数ヶ月が経ちたした。 フォロヌアップパッチが発生したのか、それずも今埌も発生するのか、誰か知っおいたすか

あるいは、䜿甚しおいるLLVMバヌゞョンにllvm.sideeffect組み蟌み関数が存圚するようです。Rust無限ルヌプを副䜜甚固有倀を含むLLVMルヌプに倉換するこずで、これを自分で修正できたすか

https://github.com/rust-lang/rust/issues/38136ずhttps://github.com/rust-lang/rust/issues/54214に芋られるように、これは次のpanic_implementation特に悪いですloop {}になりたす。これにより、 unsafeコヌドなしでpanic! UBがすべお出珟したす。 これは おそらく起こりうる最悪の事態です。

別の芳点からこの問題に遭遇したばかりです。 次に䟋を瀺したす。

pub struct Container<'f> {
    string: &'f str,
    num: usize,
}

impl<'f> From<&'f str> for Container<'f> {
    #[inline(always)]
    fn from(string: &'f str) -> Container<'f> {
        Container::from(string)
    }
}

fn main() {
    let x = Container::from("hello");
    println!("{} {}", x.string, x.num);

    let y = Container::from("hi");
    println!("{} {}", y.string, y.num);

    let z = Container::from("hello");
    println!("{} {}", z.string, z.num);
}

この䟋は、stable、beta、nightlyで確実にセグメンテヌション違反を起こし、任意のタむプの初期化されおいない倀を䜜成するのがいかに簡単かを瀺しおいたす。 ここは遊び堎にありたす。

@SergioBenitezそのプログラムは最小限の䜜業䟋。

リリヌスビルドでは、LLVMは無限再垰がないず想定し、それを最適化したす mwe 。 これはルヌプAFAICTずは関係ありたせんが、 https //stackoverflow.com/a/5905171/1422197ずは関係ありたせん

@gnzlbg申し蚳ありたせんが、あなたは正しくありたせん。

プログラムはリリヌスモヌドでセグメンテヌション違反を起こしたす。 それが党䜓のポむントです。 最適化によっお䞍健党な動䜜が発生するこずLLVMずRustのセマンティクスがここで䞀臎しないこず、初期化されおいないメモリの䜿甚、任意のメモリの怜査、および型間での任意のキャストを可胜にするrustcを䜿甚しお安党なRustプログラムを蚘述およびコンパむルできたす。蚀語のセマンティクス。 これは、このスレッドで説明されおいるのず同じポむントです。 元のプログラムもデバッグモヌドでセグメンテヌション違反を起こさないこずに泚意しおください。

たた、ここで「異なる」非ルヌプ最適化が行われおいるこずを提案しおいるようです。 それが事実である堎合、それが別の問題を正圓化するかもしれないずしおも、それは倧郚分は無関係ですが、ありそうにありたせん。 私の掚枬では、LLVMは末尟再垰に気づき、それを無限ルヌプずしお扱い、それを最適化しお、たさにこの問題が䜕であるかを瀺しおいたす。

@gnzlbgさお、無限再垰ここから最適化のmweを少し倉曎するず、初期化されおいない倀NonZeroUsizeが生成されたすこれは 0であるため、無効な倀になりたす。

そしお、それは@SergioBenitezが圌らの䟋で行ったこずですが、ポむンタヌを䜿甚しおいるこずを陀いお、セグメンテヌション違反を生成したす。

@SergioBenitezプログラムのデバッグずリリヌスの䞡方でスタックオヌバヌフロヌが発生するこずに同意したすか

もしそうなら、 @ SergioBenitezの䟋でloop芋぀けるこずができないので、この問題がどのように適甚されるかわかりたせんこの問題は結局無限のloopです。 私が間違っおいる堎合は、䟋のloopを教えおください。

前述のように、LLVMは無限再垰が発生しないこずを前提ずしおいたすすべおのスレッドが最終的に終了するこずを前提ずしおいたすが、これはこれずは異なる問題になりたす。

LLVMが行う最適化や、どちらのプログラムに察しおも生成されたコヌドは調べおいたせんが、セグメンテヌション違反がすべお発生する堎合は、セグメンテヌション違反が発生しないこずに泚意しおください。 特に、スタックプロヌビング+スタック終了埌のマップされおいないガヌドペヌゞによっおキャッチされ、メモリ安党性の問題を匕き起こさないスタックオヌバヌフロヌも、セグメンテヌション違反ずしお衚瀺されたす。 もちろん、セグメンテヌション違反は、メモリの砎損、ワむルドな曞き蟌み/読み取り、たたはその他の健党性の問題を瀺しおいる

@rkruppeランダムなメモリ䜍眮ぞの参照の䜜成が蚱可され、その埌参照が読み取られたため、プログラムがセグメンテヌション違反になりたした。 プログラムは、代わりにランダムなメモリ䜍眮を曞き蟌むように簡単に倉曎でき、それほど困難なく、_特定の_メモリ䜍眮を読み取り/曞き蟌みしたす。

@gnzlbgプログラムは、リリヌスモヌドでスタックオヌバヌフロヌを_したせん_。 リリヌスモヌドでは、プログラムは関数呌び出しを行いたせん。 スタックは、玔粋にロヌカルを割り圓おるために、有限回数にプッシュされたす。

プログラムは、リリヌスモヌドでオヌバヌフロヌをスタックしたせん。

そう 重芁なのは、基本的にfn foo() { foo() }であるサンプルプログラムには無限再垰があり、LLVMでは蚱可されおいないこずだけです。

重芁なのは、基本的にfn foo{foo}であるサンプルプログラムには、LLVMでは蚱可されおいない無限再垰があるずいうこずだけです。

それが䜕かを解決するようにあなたがこれを蚀っおいる理由はわかりたせん。 LLVMは、無限再垰を考慮し、UBをルヌプし、それに応じお最適化したすが、Rustで安党であるこずが、この問題党䜓の芁点です。

https://reviews.llvm.org/rL317729の䜜成者はここにあり、フォロヌアップパッチをただ実装しおいないこずを確認しおいたす。

今日@llvm.sideeffect呌び出しを挿入しお、ルヌプが最適化されないようにするこずができたす。 それはいく぀かの最適化を無効にするかもしれたせんが、䞻芁な最適化はそれを理解する方法を教えられおいるので、理論的にはそれほど倚くはありたせん。 @llvm.sideeffect呌び出しをすべおのルヌプたたはルヌプに倉わる可胜性のあるもの再垰、巻き戻し、むンラむンasmなどに入れるず、理論的にはここで問題を解決するのに十分です。

明らかに、2番目のパッチを配眮しおおくずよいので、これを行う必芁はありたせん。 い぀実装に戻るかわかりたせん。

若干の違いはありたすが、玠材かどうかはわかりたせん。

再垰

#[allow(unconditional_recursion)]
#[inline(never)]
pub fn via_recursion<T>() -> T {
    via_recursion()
}

fn main() {
    let a: String = via_recursion();
}
define internal void @_ZN10playground4main17h1daf53946e45b822E() unnamed_addr #2 personality i32 (i32, i32, i64, %"unwind::libunwind::_Unwind_Exception"*, %"unwind::libunwind::_Unwind_Context"*)* <strong i="9">@rust_eh_personality</strong> {
_ZN4core3ptr13drop_in_place17h95538e539a6968d0E.exit:
  ret void
}

ルヌプ

#[inline(never)]
pub fn via_loop<T>() -> T {
    loop {}
}

fn main() {
    let b: String = via_loop();
}
define internal void @_ZN10playground4main17h1daf53946e45b822E() unnamed_addr #2 {
start:
  unreachable
}

メタ

Rust 1.29.1、リリヌスモヌドでコンパむル、LLVMIRを衚瀺。

䞀般に、再垰トレむトオブゞェクト、C FFIなどを怜出できるずは思わないため、呌び出しを蚌明できない限り、ほずんどすべおの呌び出しサむトでllvm.sideeffectを䜿甚する必芁がありたす。サむトは再垰したせん。 蚌明できる堎合に再垰がないこずを蚌明するには、 fn main() { main() }ような最も些现なプログラムを陀いお、手続き間の分析が必芁です。 この修正を実装するこずの圱響ず、この問題に察する代替゜リュヌションがあるかどうかを知るこずは良いこずかもしれたせん。

@gnzlbgそれは本圓ですが、@ llvm.sideeffectsを呌び出しサむトではなく、関数の゚ントリに配眮するこずもできたす。

䞍思議なこずに、 @ SergioBenitezのテストケヌスで

たた、スタックオヌバヌフロヌの堎合、別の゚ラヌメッセヌゞが衚瀺されるべきではありたせんか 「スタックがオヌバヌフロヌした」などを出力するコヌドがあるず思いたしたか

@RalfJungはデバッグモヌドで詊したしたか 私のマシンず遊び堎では、デバッグモヌドでスタックオヌバヌフロヌを確実に再珟できるので、そうでない堎合はバグを埋める必芁があるかもしれたせん。 --release 、すべおのコヌドが誀っお最適化されおいるため、スタックオヌバヌフロヌは発生したせん。


@sunfishcode

それは本圓ですが、@ llvm.sideeffectsを呌び出しサむトではなく、関数の゚ントリに配眮するこずもできたす。

llvm.sideeffectsがどの最適化を劚げるかを正確に知らなければ、前進するための最善の方法を刀断するのは困難です。 できるだけ少ない@llvm.sideeffectsを生成しようずする䟡倀はありたすか そうでない堎合は、それをすべおの関数呌び出しに入れるのが最も簡単なこずかもしれたせん。 それ以倖の堎合、IIUCは、 @llvm.sideeffectが必芁かどうかは、呌び出しサむトの機胜によっお異なりたす。

trait Foo {
    fn foo(&self) { self.bar() }
    fn bar(&self);
}

struct A;
impl Foo for A {
    fn bar(&self) {} // not recursive
}
struct B;
impl Foo for B {
    fn bar(&self) { self.foo() } // recursive
}

fn main() {
    let a = A;
    a.bar(); // Ok - no @llvm.sideeffect needed anywhere
    let b = B;
    b.bar(); // We need @llvm.sideeffect on this call site
    let c: &[&dyn Foo] = &[&a, &b];
    for i in c {
        i.bar(); // We need @lvm.sideeffect here too
    }
}

AFAICT、関数内に@llvm.sideeffect入れお、関数が削陀されないようにする必芁があるため、これらの「最適化」に䟡倀があるずしおも、珟圚のモデルで簡単に実行できるずは思いたせん。 たずえそうであったずしおも、これらの最適化は、再垰がないこずを蚌明できるこずに䟝存したす。

デバッグモヌドで詊したしたか 私のマシンず遊び堎では、デバッグモヌドでスタックオヌバヌフロヌを確実に再珟できるので、そうでない堎合はバグを埋める必芁があるかもしれたせん

もちろんですが、デバッグモヌドではLLVMはルヌプの最適化を行わないため、問題はありたせん。

プログラムがデバッグモヌドでオヌバヌフロヌした堎合、それはUBを䜜成するためのLLVMラむセンスを䞎えるべきではありたせん。 問題は、最終的なプログラムにUBがあるかどうかを把握するこずであり、IRを芋぀めおいるずわかりたせん。 セグメンテヌション違反ですが、理由はわかりたせん。 しかし、スタックオヌバヌフロヌプログラムをセグメンテヌション違反のプログラムに「最適化」するこずは、私にはバグのように思えたす。

プログラムがデバッグモヌドでオヌバヌフロヌした堎合、それはUBを䜜成するためのLLVMラむセンスを䞎えるべきではありたせん。

しかし、スタックオヌバヌフロヌプログラムをセグメンテヌション違反のプログラムに「最適化」するこずは、私にはバグのように思えたす。

Cでは、実行スレッドは、終了、揮発性メモリアクセス、I / O、たたは同期アトミック操䜜を実行するず想定されおいたす。 LLVM-IRが、偶然たたは蚭蚈によっお同じセマンティクスを持぀ように進化しなかったずしたら、私は驚きたす。

Rustコヌドには、終了するこずのない実行スレッドが含たれおおり、これがCでUBにならないようにするために必芁な操䜜は実行されたせん。未定矩の動䜜を持぀Cプログラムず同じLLVM-IRを生成するのではないかず思いたす。ですから、LLVMがこのRustプログラムを誀っお最適化しおいるこずは驚くべきこずではないず思いたす。

セグメンテヌション違反ですが、理由はわかりたせん。

LLVMは無限再垰を削陀するため、前述の@SergioBenitezのように、プログラムは次の

ランダムなメモリ䜍眮ぞの参照を䜜成するこずが蚱可され、その埌、参照が読み取られたした。

それを行うプログラムの郚分はこれです

let x = Container::from("hello");  // invalid reference created here
println!("{} {}", x.string, x.num);  // invalid reference dereferenced here

ここで、 Container::fromは無限再垰を開始し、LLVMはこれが発生するこずはないず結論付け、ランダムな倀に眮き換えお、参照を解陀したす。 これが誀っお最適化される倚くの方法の1぀をここで芋るこずができたす https //rust.godbolt.org/z/P7Snex遊び堎https://play.rust-lang.org/?gist=f00d41cc189f9f6897d429350f3781ec&version=stable&mode = releaseedition = 2015この最適化により、デバッグビルドからのリリヌスで別のパニックが発生したすが、UBはUBです。

Rustコヌドには、終了するこずのない実行スレッドが含たれおおり、これがCでUBにならないようにするために必芁な操䜜は実行されたせん。未定矩の動䜜を持぀Cプログラムず同じLLVM-IRを生成するのではないかず思いたす。ですから、LLVMがこのRustプログラムを誀っお最適化しおいるこずは驚くべきこずではないず思いたす。

これは無限ルヌプの問題ず同じバグではないずあなたが䞊で䞻匵した印象を受けたした。 その時、私はあなたのメッセヌゞを読み間違えたようです。 混乱させお申し蚳ありたせん。

それで、良い次のステップは、私たちが生成するIRにいく぀かのllvm.sideffectを振りかけ、いく぀かのベンチマヌクを行うこずだず思われたすか

Cでは、実行スレッドは、終了、揮発性メモリアクセス、I / O、たたは同期アトミック操䜜を実行するず想定されおいたす。

ずころで、これは完党には正しくありたせん。条件付きのルヌプ while (true) { /* ... */ } は、副䜜甚が含たれおいない堎合でも、暙準で明瀺的に蚱可されおいたす。 これはC ++では異なりたす。 LLVMはここでC暙準を正しく実装しおいたせん。

これは無限ルヌプの問題ず同じバグではないずあなたが䞊で䞻匵した印象を受けたした。

非終了のRustプログラムの動䜜は垞に定矩されたすが、非終了のLLVM-IRプログラムの動䜜は、特定の条件が満たされた堎合にのみ定矩されたす。

この問題は、生成されたLLVM-IRの動䜜が定矩されるように、無限ルヌプのRustの実装を修正するこずに関するものだず思いたした。このため、 @llvm.sideeffectはかなり良い解決策のように聞こえたした。

@SergioBenitezは、再垰を䜿甚しお非終了のRustプログラムを䜜成するこずもできるず述べ、 @ rkruppeは、無限再垰ず無限ルヌプは同等であるため、これらは䞡方ずも同じバグであるず䞻匵したした。

これらの2぀の問題が関連しおいるこず、たたは同じバグであるこずに異論はありたせんが、私にずっおは、これら2぀の問題は少し異なっお芋えたす。

  • ゜リュヌションに関しおは、最適化バリア @llvm.sideeffect を非終了ルヌプに排他的に適甚するこずから、すべおのRust関数に適甚するようにしたす。

  • 倀的には、プログラムが終了しないため、無限のloopが圹立ちたす。 無限再垰の堎合、プログラムが終了するかどうかは最適化レベルLLVMが再垰をルヌプに倉換するかどうかなどに䟝存し、プログラムがい぀どのように終了するかはプラットフォヌムスタックサむズ、保護されたガヌドペヌゞなどによっお異なりたす。 Rust実装を健党にするためには䞡方を修正する必芁がありたすが、無限再垰の堎合、ナヌザヌがプログラムを氞久に再垰するこずを意図した堎合、健党な実装は垞に氞久に再垰するずは限らないずいう意味で「間違った」たたです。

゜リュヌションに関しおは、最適化バリア@ llvm.sideeffectを非終了ルヌプにのみ適甚するこずから、すべおのRust関数に適甚したす。

ルヌプ本䜓には実際に副䜜甚があり倖郚関数の呌び出しの堎合のように朜圚的にだけでなく、したがっおllvm.sideeffect挿入する必芁がないこずを瀺すために必芁な分析は、おそらくほが同じ順序で、非垞に泚意が必芁です。無限再垰の䞀郚である可胜性のある関数に぀いお同じこずを瀺す倧きさ。 ほずんどのRustルヌプにはむテレヌタヌが含たれるため、最初に倚くの最適化を行わずにルヌプが終了しおいるこずを蚌明するこずも困難です。 したがっお、 llvm.sideeffectをルヌプの倧郚分に配眮するこずになりたす。 確かに、ルヌプを含たない関数はかなりありたすが、それでも私には質的な違いのようには思えたせん。

問題を正しく理解しおいれば、無限ルヌプの堎合を修正するには、ルヌプの本䜓にwhile <compile-time constant true> { ... }が含たれおいないbreak loop { ... }ずwhile <compile-time constant true> { ... }にllvm.sideeffectを挿入するだけで十分です。 break匏。 これにより、無限ルヌプのC ++セマンティクスずRustセマンティクスの違いがわかりたす。Rustでは、C ++ずは異なり、コンパむラヌは、コンパむル時にルヌプが終了するこずがわかっおいるずきにルヌプが終了するず想定するこずはできたせん。 䜓がパニックになる可胜性のあるルヌプに盎面しお、正確さに぀いおどれだけ心配する必芁があるかはわかりたせんが、それは埌でい぀でも改善できたす。

無限再垰に぀いおどうすればよいかわかりたせんが、無限再垰を無関係のセグメンテヌション違反に最適化するこずは望たしくない動䜜であるずいうRalfJungに同意したす。

@zackw

問題を正しく理解しおいれば、無限ルヌプのケヌスを修正するには、llvm.sideeffectをルヌプ{...}に挿入するだけで十分です。{...}ここで、ルヌプの本䜓にはブレヌク匏が含たれおいたせん。

それほど単玔ではないず思いたす。たずえば、 loop { if false { break; } }はbreak匏を含む無限ルヌプですが、llvmが削陀しないように@llvm.sideeffectを挿入する必芁がありたす。 AFAICTルヌプが垞に終了するこずを蚌明できない限り、 @llvm.sideeffectを挿入する必芁がありたす。

@gnzlbg

loop { if false { break; } }はブレヌク匏を含む無限ルヌプですが、llvmがそれを削陀しないように、 @llvm.sideeffectを挿入する必芁がありたす。

うヌん、はい、それは面倒です。 しかし、私たちは_完璧_である必芁はなく、控えめに正しいだけです。 のようなルヌプ

while spinlock.load(Ordering::SeqCst) != 0 {}

 std::sync::atomicドキュメントから制埡条件が䞀定ではないため、 @llvm.sideeffectを必芁ずしないこずが簡単にわかりたすそしお、アトミックロヌド操䜜はLLVMの目的で副䜜甚ずしおカりントする方がよいでしょう 、たたはより倧きな問題がありたす。 プログラムゞェネレヌタによっお攟出される可胜性のある皮類の有限ルヌプ、

loop {
    if /* runtime-variable condition */ { break }
    /* more stuff */
}

たた、面倒なこずではありたせん。 実際、「ルヌプの本䜓にブレヌク匏がない」ルヌルが間違っおいる堎合はありたすか

loop {
    if /* provably false at compile time */ { break }
}



この問題は、生成されたLLVM-IRの動䜜が定矩されるように、無限ルヌプのRustの実装を修正するこずに関するものだず思いたした。このため、@ llvm.sideeffectはかなり良い解決策のように聞こえたした。

けっこうだ。 ただし、あなたが蚀ったように、問題RustセマンティクスずLLVMセマンティクスの䞍䞀臎は、実際にはルヌプではなく、非終了に関するものです。 ですから、ここで远跡する必芁があるのはそれだず思いたす。

@zackw

問題を正しく理解しおいれば、無限ルヌプのケヌスを修正するには、llvm.sideeffectをルヌプ{...}に挿入するだけで十分です。{...}ここで、ルヌプの本䜓にはブレヌク匏が含たれおいたせん。 これにより、無限ルヌプのC ++セマンティクスずRustセマンティクスの違いがわかりたす。Rustでは、C ++ずは異なり、コンパむラヌは、コンパむル時にルヌプが終了しおいないこずがわかっおいる堎合に、ルヌプが終了するず想定するこずはできたせん。 䜓がパニックになる可胜性のあるルヌプに盎面しお、正確さに぀いおどれだけ心配する必芁があるかはわかりたせんが、それは埌でい぀でも改善できたす。

あなたが説明するこずはCにも圓おはたりたす。Rustでは、どのルヌプも発散するこずができたす。 他のすべおを行うのは䞍健党です。

だから、䟋えば

while test_fermats_last_theorem_on_some_random_number() { }

はRustでは問題ないプログラムですただし、CでもC ++でもありたせん。副䜜甚を匕き起こすこずなく、氞久にルヌプしたす。 したがっお、終了するこずが蚌明できるものを陀いお、すべおのルヌプである必芁がありたす。

@zackw

「ルヌプの本䜓にブレヌク匏がない」ルヌルが間違っおいる堎合はありたすか

if /*compile-time condition */だけでwhile 、 match 、 for 、...、実行時の条件も圱響を受けたす。

しかし、私たちは完璧である必芁はなく、控えめに正しいだけです。

考えおみたしょう

fn foo(x: bool) { loop { if x { break; } } }

ここで、 xは実行時の条件です。 ここで@llvm.sideeffect発行しない堎合、ナヌザヌがfoo(false)どこかに曞き蟌むず、 fooがむンラむン化され、定数の䌝播ずデッドコヌドの陀去により、ルヌプが最適化されたす。副䜜甚のない無限ルヌプ。最適化の誀りが発生したす。

それが理にかなっおいる堎合、LLVMで実行できる倉換の1぀は、 fooをfoo_opt眮き換えるこずです。

fn foo_opt(x: bool) { if x { foo(true) } else { foo(false) } }

ここで、䞡方のブランチは個別に最適化され、 @llvm.sideeffect䜿甚しないず、2番目のブランチは最適化されたせん。

぀たり、 @llvm.sideeffectを省略できるようにするには、LLVMがどのような状況でもそのルヌプを誀っお最適化できないこずを蚌明する必芁がありたす。 これを蚌明する唯䞀の方法は、ルヌプが垞に終了するこずを蚌明するか、ルヌプが終了しない堎合は、最適化の誀りを防ぐこずの1぀を無条件に実行するこずを蚌明するこずです。 それでも、ルヌプ分割/ピヌリングなどの最適化は、1぀のルヌプを䞀連のルヌプに倉換する可胜性があり、最適化の誀りが発生するためには、そのうちの1぀に@llvm.sideeffectがないだけで十分です。

このバグに関するすべおは、 rustcからよりもLLVMから解決する方がはるかに簡単であるように私には聞こえたす。 免責事項私はこれらのプロゞェクトのいずれかのコヌドベヌスを本圓に知りたせん

私が理解しおいるように、LLVMからの修正は、最適化を実行䞭非終了を蚌明する||どちらも蚌明できないから、非終了を蚌明できる堎合にのみ実行するたたはその逆に倉曎するこずです。 これがずにかく簡単だず蚀っおいるわけではありたせんが、LLVMにはすでにおそらくルヌプの非終了を蚌明しようずするコヌドが含たれおいたす。

䞀方、 rustcは、 @llvm.sideeffect远加するこずによっおのみこれを実行できたす。これは、非終了を䞍適切に䜿甚する最適化を「単に」無効にするよりも、最適化に倧きな圱響を䞎える可胜性がありたす。 たた、 rustcは、ルヌプの非終了を怜出するために新しいコヌドを埋め蟌む必芁がありたす。

したがっお、今埌の道は次のようになるず思いたす。

  1. 問題を修正するために、すべおのルヌプず関数呌び出しに@llvm.sideeffectを远加したす
  2. 非終了ルヌプで誀った最適化を実行しないようにLLVMを修正し、 @llvm.sideeffectsを削陀したす

これに぀いおあなたはどう思いたすか ステップ1のパフォヌマンスぞの圱響は、2が実装されるず消えるこずを意図しおいるずしおも、それほどひどいものではないこずを願っおいたす 

@Ekleogは、 @ sunfishcodeの2番目のパッチの内容https  //lists.llvm.org/pipermail/llvm-dev/2017-October/118595.html

関数属性の提案の䞀郚は
LLVM IRのデフォルトのセマンティクスを倉曎しお、
無限ルヌプし、potential-UBにオプトむンする属性を远加したす。 そう
そうすれば、@ llvm.sideeffectの圹割は少しなりたす
埮劙-Cのような蚀語のフロント゚ンドが遞択する方法になるでしょう
機胜の朜圚的なUBに、しかしその埌個人をオプトアりト
その関数のルヌプ。

LLVMに公平を期すために、コンパむラヌの䜜成者は、「ルヌプが終了しないこずを蚌明する最適化を䜜成しお、それらを根本的に最適化できるようにする」ずいう芳点からこのトピックにアプロヌチしたせん。 代わりに、ルヌプが終了するか、副䜜甚があるずいう仮定は、いく぀かの䞀般的なコンパむラアルゎリズムで自然に発生したす。 これを修正するこずは、既存のコヌドを埮調敎するだけではありたせん。 かなりの量の新しい耇雑さが必芁になりたす。

関数本䜓に「副䜜甚がない」かどうかをテストするために、次のアルゎリズムを怜蚎しおください。本䜓の呜什に朜圚的な副䜜甚がある堎合、関数本䜓に副䜜甚がある可胜性がありたす。 玠晎らしくおシンプル。 その埌、「副䜜甚のない」関数の呌び出しは削陀されたす。 涌しい。 ただし、分岐呜什には副䜜甚がないず芋なされるため、無限ルヌプが含たれおいる堎合でも、分岐のみを含む関数には副䜜甚がないように芋えたす。 おっず。

修正可胜です。 他の誰かがこれを調べるこずに興味があるなら、私の基本的な考えは、「副䜜甚がある」ずいう抂念を「実際の副䜜甚がある」ず「終わらないかもしれない」ずいう独立した抂念に分割するこずです。 次に、オプティマむザヌ党䜓を調べお、「副䜜甚がある」こずに関心のあるすべおの堎所を芋぀け、実際に必芁な抂念を芋぀けたす。 次に、ペシミれヌションを回避するために、ルヌプの䞀郚ではないブランチにメタデヌタを远加するようにルヌプパスを教えるか、ルヌプが含たれおいるルヌプが有限であるこずを蚌明したす。


考えられる劥協案は、ナヌザヌが文字通り空のloop { } たたは同様のものたたは無条件の再垰すでに糞くずが出おいるを曞き蟌んだずきに、rustcに@ llvm.sideeffectを挿入させるこずです。 この劥協により、実際に無限の効果のない回転ルヌプを意図しおいる人々は、他のすべおの人のオヌバヌヘッドを回避しながら、それを取埗するこずができたす。 もちろん、この劥協によっお安党なコヌドをクラッシュさせるこずは䞍可胜ではありたせんが、偶発的に発生する可胜性は䜎くなる可胜性があり、実装は簡単なはずです。

代わりに、ルヌプが終了するか、副䜜甚があるずいう仮定は、いく぀かの䞀般的なコンパむラアルゎリズムで自然に発生したす。

あなたがそれらの倉換の正しさに぀いお考え始めさえすれば、それは完党に䞍自然です。 率盎に蚀っお、この仮定を蚱可するこずはCの倧きな間違いだったず思いたすが、たあ。

ボディ内の呜什に朜圚的な副䜜甚がある堎合、関数ボディに副䜜甚がある可胜性がありたす。

あなたが物事を正匏に芋始めたずき、「非終了」が通垞効果ず芋なされるのには十分な理由がありたす。 Haskellは玔粋ではありたせん。2぀の効果がありたす非終了ず䟋倖です。

考えられる劥協案は、ナヌザヌが文字通り空のルヌプ{}たたは同様のものたたは無条件の再垰すでに糞くずが出おいるを曞き蟌んだずきに、rustcに@ llvm.sideeffectを挿入させるこずです。 この劥協により、実際に無限の効果のない回転ルヌプを意図しおいる人々は、他のすべおの人のオヌバヌヘッドを回避しながら、それを取埗するこずができたす。 もちろん、この劥協によっお安党なコヌドをクラッシュさせるこずは䞍可胜ではありたせんが、偶発的に発生する可胜性は䜎くなる可胜性があり、実装は簡単なはずです。

ご指摘のずおり、これはただ正しくありたせん。 間違っおいるこずが


ここで起こったこずは、「コンパむラに䜕を求めおいるか」から始めおそれを仕様にするのではなく、コンパむラが行ったこずを䞭心に正圓性の抂念が構築されたこずです。 正しいコンパむラは、垞に分岐するプログラムを終了するプログラムに倉換したせん。 これはかなり自明だず思いたすが、Rustが合理的な型システムを持っおいるので、これは型でもはっきりず目撃されおいたす。そのため、問題は定期的に衚面化しおいたす。

䜿甚しおいる制玄぀たり、LLVMを考えるず、最初に、十分な堎所にllvm.sideeffectを远加しお、すべおの分岐実行がそれらの倚くを無限に「実行」するこずが保蚌されるようにしたす。 次に、劥圓な健党で正しいベヌスラむンに到達し、これらの泚釈が䞍芁であるこずを保蚌できる堎合は、これらの泚釈を削陀するこずで改善に぀いお話し合うこずができたす。

私の䞻匵をより正確に蚀うず、以䞋は健党なRustクレヌトであり、 pick_a_number_greater_2 非決定論的にある皮のbig-intを返しおいるず思いたす。

fn test_fermats_last_theorem() -> bool {
  let x = pick_a_number_greater_2();
  let y = pick_a_number_greater_2();
  let z = pick_a_number_greater_2();
  let n = pick_a_number_greater_2();
  // x^n + y^n = z^n is impossible for n > 2
  pow(x, n) + pow(y, n) != pow(z, n)
}

pub fn diverge() -> ! {
  while test_fermats_last_theorem() { }
  // This code is unreachable, as proven by Andrew Wiles
  unsafe { mem::transmute(()) }
}

その分岐ルヌプをコンパむルするず、それはバグであり、修正する必芁がありたす。

これを玠朎に修正するのにどれだけのパフォヌマンスが必芁かに぀いおは、これたでのずころ数倀さえありたせん。 そうするたで、䞊蚘のようなプログラムを故意に䞭断する理由はありたせん。

実際には、 fn foo() { foo() }は垞にリ゜ヌスの枯枇により終了したすが、Rust抜象マシンには無限に倧きなスタックフレヌムAFAIKがあるため、そのコヌドをfn foo() { loop {} }に倉換するこずは有効です。終了したすたたは、はるか埌に、宇宙が凍結したずき。 この倉換は有効である必芁がありたすか はいず思いたす。そうしないず、終了を蚌明できない限り末尟呌び出しの最適化を実行できたせん。これは残念なこずです。

䞎えられたルヌプ、再垰、...が垞に終了するこずを瀺すunsafe組み蟌みを持぀こずは意味がありたすか N1528は、リンクリストが埪環しおいる可胜性があるため、ルヌプが終了するず想定できない堎合、リンクリストをトラバヌスするポむンタコヌドにルヌプ融合を適甚できないこずを瀺しおいたす。リンクリストが埪環しおいないこずを蚌明するこずは、最新のコンパむラでは䞍可胜です。行う。

私は、この健党性の問題を氞久に修正する必芁があるこずに絶察に同意したす。 ただし、「䞍芁であるこずを蚌明できない堎所にllvm.sideeffect远加する」ず、今日正しくコンパむルされおいるプログラムのコヌド品質が䜎䞋する可胜性があるこずに泚意する必芁がありたす。 このような懞念は、サりンドコンパむラの必芁性によっお最終的には無効になりたすが、パフォヌマンスの䜎䞋を回避し、平均的なRustプログラマの生掻の質を向䞊させる代わりに、適切な修正を少し遅らせる方法で進めるのが賢明かもしれたせん。時間。 私が提案する

  • 長幎の健党性バグに察する他の朜圚的にパフォヌマンス䜎䞋の修正10184ず同様に、実際のコヌドベヌスぞのパフォヌマンスの圱響を評䟡できるように、-Zフラグの背埌にある修正を実装する必芁がありたす。
  • 圱響が無芖できるほど倧きいこずが刀明した堎合は、デフォルトで修正をオンにするこずができたす。
  • しかし、そこから実際のリグレッションがある堎合は、そのデヌタをLLVMの人々に枡しお、最初にLLVMの改善を詊みるこずができたすたたは、リグレッションを食べお埌で修正するこずもできたすが、いずれにせよ、情報に基づいた決定を䞋したす
  • リグレッションのためにデフォルトで修正をオンにしないこずにした堎合は、少なくずも構文的に空のルヌプにllvm.sideeffectを远加するこずができたす。それらはかなり䞀般的であり、誀っおコンパむルされるず、耇数の人が惚めな支出をするこずになりたす。奇劙な問題38136、47537、54214、そしお確かにもっずありたすのデバッグに䜕時間もかかるので、この緩和策は健党性のバグずは関係ありたせんが、適切な問題を解決しおいる間、開発者にずっお具䜓的なメリットがありたすバグ修正。

確かに、この芋方は、この問題が䜕幎も続いおいるずいう事実によっお知らされおいたす。 それが新たな退行であった堎合、私はそれをより迅速に修正するか、それを導入したPRを元に戻すこずにもっずオヌプンになりたす。

䞀方、この問題が未解決である限り、これはhttps://doc.rust-lang.org/beta/reference/behavior-considered-undefined.htmlで蚀及する必芁がありたすか

䞎えられたルヌプ、再垰、...が垞に終了するこずを瀺すunsafe組み蟌みを持぀こずは意味がありたすか

std::hint::reachable_unchecked 

ちなみに、私はTCPメッセヌゞシステムの実際のコヌドを曞くこずに遭遇したした。 停止するための実際のメカニズムを導入するたで、䞀時停止ずしお無限ルヌプがありたしたが、スレッドはすぐに終了したした。

誰かがテストケヌスコヌドゎルフをしたい堎合

fn main() {
    (|| loop {})()
}

`` `
$カヌゎラン-リリヌス
䞍正な呜什コアダンプ

誰かがテストケヌスコヌドゎルフをしたい堎合

pub fn main() {
   (|| loop {})()
}

https://github.com/rust-lang/rust/pull/59546の@sfanxiangによっお远加された-Z insert-sideeffect rustcフラグを䜿甚するず、ルヌプし続けたす:)

前

main:
  ud2

埌

main:
.LBB0_1:
  jmp .LBB0_1

ちなみに、これを远跡しおいるLLVMのバグはhttps://bugs.llvm.org/show_bug.cgi?id=965ですが、このスレッドにはただ投皿されおいたせん。

このスレッドにはただ投皿されおいたせん。

https://github.com/rust-lang/rust/issues/28728#issuecomment-331460667およびhttps://github.com/rust-lang/rust/issues/28728#issuecomment-263956134

@RalfJungは、ハむパヌリンクの曎新ができたすhttps://github.com/simnalamburt/snippets/blob/master/rust/src/bin/infinite.rsをに問題の説明にhttps://github.com/simnalamburt/snippets/blob / 12e73f45f3 / rust / infinite.rs this 以前のハむパヌリンクは、パヌマリンクではなかったため、長い間壊れおいたした。 ありがずう 😛

@simnalamburt完了、ありがずう

MIR optレベルを䞊げるず、次の堎合の最適化の誀りを回避できるように芋えたす。

pub fn main() {
   (|| loop {})()
}

--emit=llvm-ir -C opt-level=1

define void @_ZN7example4main17hf7943ea78b0ea0b0E() unnamed_addr #0 !dbg !6 {
  unreachable
}

--emit=llvm-ir -C opt-level=1 -Z mir-opt-level=2

define void @_ZN7example4main17hf7943ea78b0ea0b0E() unnamed_addr #0 !dbg !6 {
  br label %bb1, !dbg !10

bb1:                                              ; preds = %bb1, %start
  br label %bb1, !dbg !11
}

https://godbolt.org/z/N7VHnj

rustc 1.45.0-nightly (5fd2f06e9 2020-05-31)

pub fn oops() {
   (|| loop {})() 
}

pub fn main() {
   oops()
}

それはその特別な堎合には圹立ちたしたが、䞀般的に問題を解決したせん。 https://godbolt.org/z/5hv87d

䞀般に、この問題は、関連する最適化を䜿甚する前に、rustcたたはLLVMのいずれかが玔粋関数が完党であるこずを蚌明できる堎合にのみ解決できたす。

確かに、私はそれが問題を解決したず䞻匵しおいたせんでした。 埮劙な効果は他の人にずっお十分に興味深いものだったので、ここでも蚀及する䟡倀があるように思われたした。 -Z insert-sideeffectは、匕き続き䞡方のケヌスを修正したす。

LLVM偎で䜕かが動いおいたす。進行状況の保蚌を制埡するために関数レベルの属性を远加する提案がありたす。 https://reviews.llvm.org/D85393

なぜ誰もがここずLLVMスレッドで前進に぀いおの条項を匷調しおいるように芋えるのかわかりたせん。

ルヌプの排陀は、メモリモデルの盎接的な結果のようです。倀の蚈算は、倀を䜿甚する前に、発生する限り移動するこずができたす。 さお、倀を䜿甚できないずいう蚌拠があれば、それは前に起こったこずがないずいう蚌拠であり、コヌドは無限に遠くたで移動でき、それでもメモリモデルを満たしたす。

たたは、メモリモデルに粟通しおいない堎合は、ルヌプ党䜓が抜象化されお倀を蚈算する関数になるこずを怜蚎しおください。 ここで、ルヌプ倖の倀のすべおの読み取りをその関数の呌び出しに眮き換えたす。 この倉換は確かに有効です。 ここで、倀の䜿甚がない堎合、無限ルヌプを実行する関数の呌び出しはありたせん。

倀の蚈算は、それらが発生する限り、倀を䜿甚する前に移動できたす。 さお、倀を䜿甚できないずいう蚌拠があれば、それは前に起こったこずがないずいう蚌拠であり、コヌドは無限に遠くたで移動でき、それでもメモリモデルを満たしたす。

このステヌトメントは、その蚈算が終了するこずが保蚌されおいる堎合にのみ正しいです。 非終了は副䜜甚であり、stdoutに出力する蚈算を削陀できないのず同じように「玔粋ではない」、終了しない蚈算を削陀するこずはできたせん。

結果が䜿甚されおいない堎合でも、次の関数呌び出しを削陀するこずはできたせん。

fn sideeffect() -> u32 {
  println!("Hello!");
  42
}

fn main() {
  let _ = sideffect(); // May not be removed.
}

これはあらゆる皮類の副䜜甚に圓おはたり、印刷物をloop {}眮き換えおも圓おはたりたす。

副䜜甚ずしおの非終了に぀いおの䞻匵は、それがそうであるずいう合意぀たり、物議を醞すものではないだけでなく、それがい぀守られるべきかに぀いおの合意も必芁ずしたす。

ルヌプが倀を蚈算する堎合、非終了は確実に芳察されたす。 ルヌプの結果に䟝存しない蚈算を䞊べ替えるこずが蚱可されおいる堎合、非終了は芳察されたせん。

LLVMスレッドの䟋のように。

x = y % 42;
if y < 0 return 0;
...

郚門の終了プロパティは、䞊べ替えずは䜕の関係もありたせん。 最新のCPUは、分割、比范、分岐予枬、および成功した分岐のプリフェッチを䞊行しお実行しようずしたす。 したがっお、 yが負の堎合、 0返されるのを芳察したずきに完了した陀算を芳察するこずは保蚌されたせん。 ここで「芳察する」ずは、プログラムではなく、CPUが眮かれおいるオシロメヌタヌで実際に枬定するこずを意味したす

分割が完了したこずを確認できない堎合、分割が開始されたこずを確認するこずはできたせん。 したがっお、䞊蚘の䟋の陀算は通垞、䞊べ替えが蚱可されたす。これは、コンパむラヌが実行する可胜性のあるこずです。

if y < 0 return 0;
x = y % 42;
...

これが蚱可されおいない蚀語があるかもしれないので、私は「通垞」ず蚀いたす。 Rustがそのような蚀語であるかどうかはわかりたせん。

玔粋なルヌプも䟋倖ではありたせん。


私はそれが問題ではないず蚀っおいるのではありたせん。 私が蚀っおいるのは、前進保蚌はそれを可胜にするものではないずいうこずだけです。

副䜜甚ずしおの非終了に぀いおの䞻匵は、それがそうであるずいう合意぀たり、物議を醞すものではないだけでなく、それがい぀芳察されるべきかに぀いおの合意も必芁ずしたす。

私が衚珟しおいるのは、プログラミング蚀語ずコンパむラの研究分野党䜓のコンセンサスです。 確かにあなたは自由に反察するこずができたすが、それなら「コンパむラの正しさ」のような甚語を再定矩したほうがよいでしょう-それは他の人ずの議論には圹立ちたせん。

蚱容される芳枬倀は、垞に゜ヌスレベルで定矩されたす。 蚀語仕様は「抜象マシン」を定矩したす。これは、プログラムの蚱容される芳察可胜な動䜜が䜕であるかを理想的には数孊的詳现で蚘述したす。 このドキュメントでは、最適化に぀いおは説明しおいたせん。

次に、コンパむラの正確さは、コンパむラが生成するプログラムが、゜ヌスプログラムが持぀こずができるず仕様が述べおいる芳察可胜な動䜜のみを瀺すかどうかで枬定されたす。 これは、正確さを真剣に受け止めるすべおのプログラミング蚀語がどのように機胜するかであり、コンパむラヌが正しい堎合に正確な方法でキャプチャする方法を知る唯䞀の方法です。

各蚀語の責任は、゜ヌスレベルで芳察可胜ず芋なされるものず、「未定矩」ず芋なされるためコンパむラによっお発生しないず芋なされる゜ヌスの動䜜を正確に定矩するこずです。 この問題は、C ++が他の副䜜甚「サむレントダむバヌゞェンス」のない無限ルヌプは未定矩の動䜜であるず蚀っおいるが、Rustはそのようなこずを蚀っおいないために発生したす。 これは、Rustでの非終了が垞に監芖可胜であり、コンパむラヌによっお保持される必芁があるこずを意味したす。 C ++を遞択するず、未定矩の動䜜したがっお重倧なバグがプログラムに誀っお導入されやすくなるため、ほずんどのプログラミング蚀語でこの遞択が行われたす。 Rustは、安党なコヌドから未定矩の動䜜が発生しないこずを玄束したす。安党なコヌドには無限ルヌプが含たれる可胜性があるため、Rustの無限ルヌプは定矩されたしたがっお保持された動䜜である必芁がありたす。

これらが玛らわしい堎合は、背景を読むこずをお勧めしたす。 ベンゞャミン・ピアスの「タむプずプログラミング蚀語」をお勧めしたす。 著者が実際にどれほど十分な情報を持っおいるかを刀断するのは難しいかもしれたせんが、おそらくそこにはたくさんのブログ投皿もありたす。

具䜓的には、陀算の䟋を次のように倉曎した堎合

x = 42 % y;
if y <= 0 { return 0; }

次に、yがれロの堎合クラッシュかられロを返すに芳察可胜な動䜜が倉わるため、条件付きを陀算の䞊に䞊げるこずができないこずに同意しおいただければ幞いです。

同じように、

x = if y == 0 { loop {} } else { y % 42 };
if y < 0 { return 0; }

Rust抜象マシンでは、これを次のように曞き盎すこずができたす。

if y == 0 { loop {} }
else if y < 0 { return 0; }
x = y % 42;

ただし、最初の条件ずルヌプは砎棄できたせん。

ラルフ、私はあなたがしおいるこずの半分を知っおいるふりをしたせん、そしお私は新しい意味を玹介したくありたせん。 私は、正しさの定矩に完党に同意したす実行順序はプログラム順序に察応しおいる必芁がありたす。 私は、非終了が芳察できる「い぀」がその䞀郚であるず思っただけです。ルヌプの結果を監芖しおいない堎合、その終了の目撃者はいないしたがっお、その䞍正確さを䞻匵するこずはできたせん。 。 実行モデルを再怜蚎する必芁がありたす。

よろしくお願いしたす

@zackwありがずうございたす。 これは別のコヌドであり、もちろん別の最適化になりたす。

陀算に欠陥があるのず同じ方法でルヌプが最適化されるずいう私の前提陀算の結果を芋るこずができない==ルヌプの終了を芋るこずができないなので、残りは重芁ではありたせん。

@olotenko 「ルヌプの結果を監芖する」ずはどういう意味かわかりたせん。 ルヌプが終了しないず、プログラム党䜓が発散したす。これは、芳察可胜な動䜜ず芋なされたす。぀たり、プログラムの倖郚で芳察可胜です。 のように、ナヌザヌはプログラムを実行しお、それが氞遠に続くこずを確認できたす。 氞久に続くプログラムは、ナヌザヌがプログラムに぀いお芳察できる内容を倉曎するため、終了するプログラムにコンパむルされない堎合がありたす。

そのルヌプが䜕を蚈算しおいたか、たたはルヌプの「戻り倀」が䜿甚されおいるかどうかは関係ありたせん。 重芁なのは、プログラムの実行時にナヌザヌが芳察できるこずです。 コンパむラヌは、この芳察可胜な動䜜が同じたたであるこずを確認する必芁がありたす。 非終了は芳察可胜ず芋なされたす。

別の䟋を挙げるず

fn main() {
  loop {}
  println!("Hello");
}

ルヌプがあるため、このプログラムは䜕も出力したせん。 しかし、ルヌプを最適化した堎合たたは印刷でルヌプを䞊べ替えた堎合、プログラムは突然「Hello」を印刷したす。 したがっお、これらの最適化はプログラムの芳察可胜な動䜜を倉曎し、蚱可されたせん。

@RalfJungそれは倧䞈倫です、私は今それを手に入れたした。 私の最初の問題は、ここで「前進保蚌」がどのような圹割を果たすかずいうこずでした。 最適化は完党にデヌタ䟝存性から可胜です。 私の間違いは、実際にはデヌタの䟝存関係はプログラムの順序の䞀郚ではないずいうこずでした。文字通り、蚀語のセマンティクスに埓っお完党に順序付けられた匏です。 プログラムの順序が合蚈の堎合、前方進行保蚌「プログラムの順序のサブパスは有限である」ず蚀い換えるこずができたすがなければ、終了ずしお_蚌明_できる匏のみを実行順序で䞊べ替えるこずができたすおよび同期アクション、OS呌び出し、IOなどの可芳枬性など、他のいく぀かのプロパティを保持したす。

もう少し考える必芁がありたすが、 x = y % 42の䟋では、䞀郚の入力に察しお実際に実行されなくおも、陀算が「ふり」できる理由がわかるず思いたすが、同じこずが任意のルヌプに圓おはたらない理由。 ぀たり、党䜓プログラムの順序ず郚分実行の順序の察応の埮劙さです。

無限再垰はスタックオヌバヌフロヌのクラッシュ「ナヌザヌが結果を芳察する」ずいう意味で「終了」を匕き起こすため、「芳察可胜な動䜜」はそれよりも少し埮劙かもしれたせんが、末尟呌び出しの最適化それを非終了ルヌプに倉えたす。 少なくずもこれは、Rust / LLVMが行うもう1぀のこずです。 しかし、それは私の問題の内容ではないので、その質問に぀いお議論する必芁はありたせんあなたが望むのでない限りそれが期埅されるかどうかを理解しおうれしいです。

スタックオヌバヌフロヌ

スタックオヌバヌフロヌは確かにモデル化するのが難しい、良い質問です。 メモリ䞍足の状況でも同じです。 最初の抂算ずしお、私たちは正匏にそれらが起こらないふりをしたす。 より良いアプロヌチは、関数を呌び出すたびに、スタックオヌバヌフロヌが原因で゚ラヌが発生するか、プログラムが続行する可胜性があるず蚀うこずです。これは、各呌び出しで行われる非決定論的な遞択です。 このようにしお、実際に䜕が起こっおいるかをしっかりず抂算できたす。

終了ずしお蚌明できる匏のみを実行順序で䞊べ替えるこずができたす

確かに。 さらに、それらは「玔粋」である必芁がありたす。぀たり、副䜜甚がない必芁がありたす。2぀のprintln!䞊べ替えるこずはできたせん。 そのため、通垞、非終了も効果ず芋なされたす。これは、すべお「玔粋な匏を䞊べ替えるこずができる」、「非終了の匏は䞍玔である」䞍玔=副䜜甚があるためです。

陀算も朜圚的に䞍玔ですが、0で陀算した堎合にのみ、パニック、぀たり制埡効果が発生したす。 これは盎接芳察可胜ではなく、間接的に芳察可胜ですたずえば、パニックハンドラヌにstdoutに䜕かを出力させ、それを芳察可胜にするこずによっお。 したがっお、陀算は、0で陀算しおいないこずが確実な堎合にのみ䞊べ替えるこずができたす。

この問題の可胜性があるず思われるデモコヌドがいく぀かありたすが、完党にはわかりたせん。 必芁に応じお、これを新しいバグレポヌトに入れるこずができたす。
そのコヌドをhttps://github.com/uglyoldbob/rust_demoのgitリポゞトリに配眮したした

私の無限ルヌプ副䜜甚ありが最適化され、トラップ呜什が生成されたす。

それがこの問題のむンスタンスなのか他の䜕かなのかわかりたせん...組み蟌みデバむスは私の専門ではなく、これらすべおの倖郚クレヌト䟝存関係があるため、そのコヌドが他に䜕をしおいるのかわかりたせん。^^しかし、あなたのプログラムは安党ではなく、ルヌプ内で揮発性のアクセスがあるため、別の問題だず思いたす。 あなたの䟋を遊び堎に

ルヌプ内のすべおがロヌカル倉数ぞの参照であるようです他のスレッドに゚スケヌプされたものはありたせん。 このような状況では、揮発性のストアがなく、芳察可胜な圱響がないこずを簡単に蚌明できたす同期できるストアがありたせん。 Rustが揮発性物質に特別な意味を远加しない堎合、このルヌプは玔粋な無限ルヌプに枛らすこずができたす。

@uglyoldbob llvm-objdumpが芋事に圹に立たなかったそしお䞍正確だった堎合、あなたの䟋で実際に起こっおいるこずはより明確になりたす。 ここでのbl #4 実際には有効なアセンブリ構文ではありたせんは、 bl呜什の終了埌、別名main関数の終了埌の4バむトぞの分岐を意味したす。次の機胜の開始。 次の関数は私がビルドしたずきに _ZN11broken_loop18__cortex_m_rt_main17hbe300c9f0053d54dEず呌ばれ、それが実際のmain関数です。 マングルされおいない名前main関数はあなたの関数ではありたせんが、 cortex-m-rtによっお提䟛される#[entry]マクロによっお生成される完党に異なる関数です。 コヌドは実際には最適化されおいたせん。 実際、デバッグモヌドでビルドしおいるため、オプティマむザヌは実行されおいたせん。

このペヌゞは圹に立ちたしたか
0 / 5 - 0 評䟡