Branches: block, br, br_if, br_table
การ branch ใน WebAssembly เกิดขึ้นเทียบกับ labeled construct เสมอ — ไม่ใช่กับ raw memory address กลุ่มคำสั่ง br จะชี้ไปที่ nesting level ไม่ใช่หมายเลขบรรทัด ดังนั้นพฤติกรรมของ br จึงขึ้นอยู่กับชนิด construct ที่ชี้ถึงอย่างสิ้นเชิง
block และ br
หัวข้อที่มีชื่อว่า “block และ br”block คือกลุ่มคำสั่งที่เดินไปข้างหน้าเท่านั้น เมื่อ br $label ชี้ที่ block execution จะกระโดดไปยังคำสั่งที่อยู่ หลัง end ของ block ทันที ให้คิดว่าเป็น labeled break
(module (func (export "earlyExit") (result i32) (block $b (result i32) i32.const 42 br $b ;; exits the block; 42 is the block result i32.const 99) ;; never reached ))Annotation (result i32) บน block ระบุว่า block จะฝากค่า i32 หนึ่งตัวบน stack เมื่อออก — ไม่ว่าจะ fall through ถึง end หรือกระโดดออกด้วย br
ทิศทางของ br
หัวข้อที่มีชื่อว่า “ทิศทางของ br”คำสั่ง br เดียวกันจะทำงานต่างกันขึ้นอยู่กับสิ่งที่ชี้:
flowchart LR A["block $b\n ...body...\n br $b"] --> B["jumps to END\nof block (forward)"] C["loop $l\n ...body...\n br $l"] --> D["jumps to START\nof loop (backward)"]
br_if: conditional branch
หัวข้อที่มีชื่อว่า “br_if: conditional branch”br_if $label pop ค่า i32 condition จาก stack ถ้าไม่ใช่ศูนย์จะ branch ถ้าเป็นศูนย์จะ execute คำสั่งถัดไปต่อไป นี่คือวิธีมาตรฐานในการเขียน conditional early exit
(module (func (export "relu") (param $x i32) (result i32) (block $done (result i32) i32.const 0 ;; block result if we exit early local.get $x i32.const 0 i32.lt_s ;; x < 0? br_if $done ;; if yes, exit block — 0 is already on stack drop ;; discard the 0; x >= 0 local.get $x))) ;; return xbr_table: jump table
หัวข้อที่มีชื่อว่า “br_table: jump table”br_table รับรายการ label และค่า default จะ pop ค่า i32 index แล้วกระโดดไปที่ label ที่ตำแหน่ง index นั้น หรือไปที่ default ถ้า index เกินขอบเขต เทียบได้กับ switch statement ใน WAT
(module (func (export "describe") (param $n i32) (result i32) (block $two (block $one (block $zero local.get $n br_table $zero $one $two $two) ;; index 0→$zero, 1→$one, ≥2→$two i32.const 0) ;; $zero case: return 0 i32.const 1) ;; $one case: return 1 i32.const 2)) ;; $two case: return 2ทดลองรัน
หัวข้อที่มีชื่อว่า “ทดลองรัน”ข้อแลกเปลี่ยน
หัวข้อที่มีชื่อว่า “ข้อแลกเปลี่ยน”| ตัวเลือก | Benefit | Cost |
|---|---|---|
Structured control flow (block/loop/br) | validate ได้แบบ static และปลอดภัยกว่า เพราะไม่มี arbitrary goto ที่กระโดดไปที่ไหนก็ได้ | เขียน control flow ที่ซับซ้อนยากขึ้น ต้องซ้อน block หลายชั้นแทนการ jump ตรงจุด |
| ไม่มี raw jump แบบ assembly-style | engine ตรวจสอบ label ทุกตัวได้ตั้งแต่ compile time ลด security risk | CFG ที่มาจาก native code (เช่นมี goto เยอะๆ) ต้องแปลงผ่าน algorithm อย่าง relooper ก่อนถึงจะ map เข้า Wasm ได้ |
ข้อผิดพลาดที่พบบ่อย
หัวข้อที่มีชื่อว่า “ข้อผิดพลาดที่พบบ่อย”- นับ nesting depth ผิดเวลามี
blockซ้อนกันหลายชั้น ทำให้brหรือbr_tableชี้ผิด label (off-by-one ใน depth) - ลืมว่า
br_tableต้องมี default label เสมอ ดัชนีที่เกินขอบเขตของรายการทั้งหมดจะตกไปที่ default โดยอัตโนมัติ - สับสนทิศทางของ
brเมื่อเปลี่ยนจากblockไปloop—brตัวเดียวกัน forward ใน block แต่ backward ใน loop ถ้าไม่ระวัง label ก็จะพา flow ไปผิดทาง
💡 ตัวอย่างจากของจริง
Emscripten ใช้ relooper algorithm (อัลกอริทึมที่ Emscripten พัฒนาขึ้นเอง) แปลง arbitrary control-flow graph จากโค้ด C/C++ ให้กลายเป็น structured
block/loop/brที่ Wasm ต้องการ เพราะ native code เต็มไปด้วยgotoที่ Wasm ไม่รองรับตรงๆ