ข้ามไปยังเนื้อหา

Loops: loop และ br_if

loop ใน WebAssembly ไม่วนซ้ำอัตโนมัติ เป็นเพียง labeled construct ที่จุดเริ่มต้นเป็น branch target การวนซ้ำจะเกิดขึ้นเมื่อวาง br_if ไว้ที่ท้าย body เพื่อกระโดดกลับไปที่ loop label เมื่อ condition ยังเป็นจริง

(loop $label ...) กำหนดจุดเริ่มต้นของ loop br $label ภายใน body จะกระโดดกลับไปที่จุดเริ่มต้น — ตรงข้ามกับทิศทางของ br ใน block การออกจาก loop ทำได้โดยให้ execution ไปถึง end (fall-through) หรือกระโดดไปที่ outer label

(module
(func (export "countDown") (param $n i32)
(loop $again
;; body here
local.get $n
i32.const 1
i32.sub
local.tee $n ;; decrement $n and keep value on stack
i32.const 0
i32.gt_s
br_if $again))) ;; repeat while $n > 0
flowchart LR
  A["init locals"] --> B["loop $again (start)"]
  B --> C["body: update acc, increment i"]
  C --> D["i <= n?"]
  D -->|yes| B
  D -->|no| E["exit: return acc"]
Counted loop flow: br_if สร้าง back-edge

ฟังก์ชันด้านล่างรวมจำนวนเต็มตั้งแต่ 1 ถึง n โดยใช้ loop local $i เป็น counter และ $acc สะสมผลรวม

(module
(func (export "sumTo") (param $n i32) (result i32)
(local $i i32)
(local $acc i32)
i32.const 1
local.set $i ;; i = 1
i32.const 0
local.set $acc ;; acc = 0
(loop $again
local.get $i
local.get $acc
i32.add
local.set $acc ;; acc += i
local.get $i
i32.const 1
i32.add
local.set $i ;; i++
local.get $i
local.get $n
i32.le_s
br_if $again) ;; repeat while i <= n
local.get $acc)) ;; return acc
WebAssembly
ตัวเลือกBenefitCost
loop + br_if (structured back-edge)validate ได้ตั้งแต่ compile time และปลอดภัยกว่า arbitrary backward gotoต้องเขียน exit condition เองทุกครั้ง ไม่มี built-in for/while ให้ engine จัดการให้
ไม่มี arbitrary jump ระหว่าง loopengine ทำ loop compilation/JIT ได้เร็วเพราะรู้ boundary ของ loop ชัดเจนportability ของ CFG ที่ซับซ้อนจาก native code ต้องแปลงผ่าน relooper ก่อนจะ map เข้า loop/br ได้
  • เข้าใจผิดว่า br_if ใน loop ทำงานเหมือน break แต่จริง ๆ แล้วกระโดดกลับไปที่จุดเริ่มต้นของ loop (ทำงานคล้าย continue มากกว่า) ไม่ใช่ทางออกจาก loop
  • ลืมใส่ exit condition ที่ถูกต้อง ทำให้ br_if $again เป็นจริงตลอดไปและ loop วนไม่รู้จบ (Wasm ไม่มี infinite-loop guard ให้อัตโนมัติ)
  • Off-by-one ใน exit condition เช่นใช้ i <= n ผิดเป็น i < n ทำให้ loop วนขาดหรือเกินไปหนึ่งรอบ อย่างในตัวอย่าง sumTo

💡 ตัวอย่างจากของจริง

Wasmtime ใช้ประโยชน์จาก structured loop construct ของ Wasm ในการ compile loop เป็น native machine code ที่มี back-edge ชัดเจน ทำให้ JIT ทำ loop optimization ได้เร็วและปลอดภัยกว่าการวิเคราะห์ arbitrary jump graph

ใน WAT `br $l` ทำอะไรเมื่อ `$l` เป็น `loop` label?
วิธีออกจาก WAT loop คืออะไร?
ค่าเริ่มต้นของ local variable ที่ประกาศด้วย `(local $x i32)` คืออะไร?