Hacktoberfest 2026:メンテナが10月に向けて印を付けた、オープンで初心者向けの issue。 Hacktoberfest の issue を見る

O2 regression in optimize-instructions due to coalesceLocals

オープン
#7,458 コメント 2 件 リアクション 0 件 担当者 0 名 GitHub で見る

まだ誰も着手していません。

評価

難易度
4/5
見積もり時間
3〜5日
初心者へのやさしさ
45/100
issue の種類
バグ
明瞭さ
おおむね明確
活発さ
停滞
技術スタック
wasm
領域
compilers

調査の方向性

wasm-opt -all -O1 と -all -O2 で WebAssembly の例を再現し、その後 precompute、coalesce-locals、optimize-instructions の実行後の出力を比較します。local.tee 0 と local.tee 1 の違いが条件の推論にどのように影響するかを追跡します。optimize-instructions が O2 で条件をゼロとして認識し、動作を変更せずに dead な br_if の本体を削除できれば完了です。

索引モデルが issue の本文から書いたものです。

説明

Given the following code:

(module
  (import "External" "external_function" (func $external_function))
  (func $_start
    (local $0 i32) (local $3 i32) (local $5 i32) (local $6 i32) (local $11 i32) (local $1 i32) (local $2 i32) (local $7 i32)
    loop ;; label = @1
      block ;; label = @2
        i32.const 0
        i32.load
        br_if 0 (;@2;)
        i32.const 0
        local.get $11
        i32.store
        i32.const 0
        local.set $5
        i32.const 1
        local.set $0
        i32.const 0
        local.get $0
        i32.store
        i32.const -346457217
        local.set $6
        block ;; label = @3
          local.get $0
          local.get $6
          i32.ne
          br_if 0 (;@3;)
          call $external_function
        end
        local.get $5
        i32.const 0
        i32.load
        i32.lt_s
        i32.const 0
        local.set $1
        local.get $1
        i32.shl
        drop
        local.get $1
        unreachable
      end
      i32.const 0
      i32.load
      local.set $2
      local.get $2
      local.set $3
      local.get $2
      local.get $3
      i32.store
      br 0 (;@1;)
    end)
  (memory $0 1)
  (export "_start" (func $_start)))

wasm-opt (16dbac1) can eliminate the dead br_if body by -all -O1 but cannot by -all -O2.

Analysis

The direct issue is that optimize-instructions cannot deduce the condition to zero in -all -O2 while it can in -all -O1. (further the dead if statement body will be eliminated by --vacuum)

Before optimize-instructions, that is precompute:

`-all -O1`, after `precompute`, going to `optimize-instructions`
(module
  (type (;0;) (func))
  (import "External" "external_functional" (func (;0;) (type 0)))
  (func (;1;) (type 0)
    (local i32 i32)
    loop  ;; label = @1
      i32.const 0
      i32.load
      i32.eqz
      if  ;; label = @2
        i32.const 0
        local.get 1
        i32.store
        i32.const 0
        i32.const 1
        local.tee 0
        i32.store
        local.get 0
        i32.const -346457217
        i32.eq
        if  ;; label = @3
          call 0
        end
        i32.const 0
        i32.load
        drop
        unreachable
      else
        i32.const 0
        i32.load
        local.tee 1 ;; Note this line: using variable 1, not affects condition
        local.get 1
        i32.store
        br 1 (;@1;)
      end
      unreachable
    end
    unreachable)
  (memory (;0;) 1)
  (export "_start" (func 1)))
`-all -O2`, after `precompute`, going to `optimize-instructions`
(module
  (type (;0;) (func))
  (import "External" "external_function" (func (;0;) (type 0)))
  (func (;1;) (type 0)
    (local i32 i32)
    loop  ;; label = @1
      i32.const 0
      i32.load
      i32.eqz
      if  ;; label = @2
        i32.const 0
        local.get 1
        i32.store
        i32.const 0
        i32.const 1
        local.tee 0
        i32.store
        local.get 0
        i32.const -346457217
        i32.eq
        if  ;; label = @3
          call 0
        end
        i32.const 0
        i32.load
        unreachable
      else
        i32.const 0
        i32.load
        local.tee 0 ;; Note this line: using variable 0 will affect condition deduction
        local.get 0
        i32.store
        br 1 (;@1;)
      end
      unreachable
    end
    unreachable)
  (memory (;0;) 1)
  (export "_start" (func 1)))

As you can see, the condition are the same:

(if
 (i32.eq
  (local.get $0) 
  (i32.const -346457217)
 )
 (then
  (call $external_functional)
 )
)

However, the statements in else statements are slightly different, but critical: -all -O1 uses local variable 1, while -all -O2 uses local variable 0 (local.tee 0), which I thinks maybe affect the deduction of the condition in optimize-instructions .

Continue to trace back and analyze in the optimized pipeline, the slightly but critical difference begins in coalesce-locals, which is the optimizations of Key "register allocation" pass. Does a live range analysis and then reuses locals in order to minimize their number, as well as to remove copies between them.

Although the coalesce-locals performs aggressive optimizations, I think there is missed optimization in the optimize-instructions, for it should have deduced the condition to be 0.

主要言語
WebAssembly
スター
8.6k
フォーク
885
平均マージ
2日 4時間
マージ済み PR(30日)
77

コントリビューションガイド

コントリビューションガイドを開く

はじめの一歩

  1. issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
  2. 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
  3. リポジトリをフォークし、ブランチを切って変更します。
  4. issue 番号を参照したプルリクエストを送ります。

WebAssembly/binaryen のほかの issue

WebAssembly/binaryen の issue をすべて見る

似ている issue

Compilers の issue をもっと見る

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。