Conditionals: if, else และ select
if construct ใน WebAssembly ทำหน้าที่ทั้ง control-flow branch และ expression จะ pop ค่า i32 condition จาก stack แล้วรัน arm ใด arm หนึ่ง — และถ้ามี result type จะฝากค่าไว้บน stack ทำให้ if รู้สึกเหมือน ternary operator มากกว่า if statement แบบดั้งเดิม
If construct
หัวข้อที่มีชื่อว่า “If construct”if จะ pop i32 บนสุด ถ้าค่าไม่ใช่ศูนย์จะรัน then arm ถ้าเป็นศูนย์จะรัน else arm ที่เป็น optional construct สิ้นสุดด้วย end
(module (func (export "sign") (param $x i32) (result i32) local.get $x i32.const 0 i32.gt_s ;; x > 0? if (result i32) i32.const 1 ;; then: return 1 else i32.const -1 ;; else: return -1 end))Annotation (result i32) ระบุว่า if expression จะฝากค่า i32 หนึ่งตัวบน stack ไม่ว่า arm ใดจะรัน ทั้งสอง arm ต้องฝากค่าจำนวนและชนิดเท่ากัน
if ที่ไม่มี result
หัวข้อที่มีชื่อว่า “if ที่ไม่มี result”เมื่อ condition ใช้เพื่อ side effect เท่านั้น — เช่น การเรียก imported function — สามารถละ result type ได้:
(module (func (export "clampNeg") (param $x i32) (result i32) local.get $x i32.const 0 i32.lt_s if i32.const 0 local.set $x ;; clamp $x to 0 if negative end local.get $x))ในกรณีนี้ if ไม่มี result แค่ conditionally update local
select: branchless ternary
หัวข้อที่มีชื่อว่า “select: branchless ternary”select เป็นทางเลือกที่กระชับกว่า if สำหรับการเลือกค่าง่ายๆ จะ pop สามค่า: condition, val_if_false, val_if_true (ตามลำดับ push: push true-val, push false-val, push condition) แล้ว push ค่าที่ถูกเลือก ไม่มีการ branch — CPU ประเมินทั้งสองค่าก่อน select
(module (func (export "minVal") (param $a i32) (param $b i32) (result i32) local.get $a ;; val if condition is true ($a <= $b) local.get $b ;; val if condition is false local.get $a local.get $b i32.le_s ;; condition: a <= b? select)) ;; choose $a if true, $b if falseทดลองรัน
หัวข้อที่มีชื่อว่า “ทดลองรัน”ข้อแลกเปลี่ยน
หัวข้อที่มีชื่อว่า “ข้อแลกเปลี่ยน”| ตัวเลือก | Benefit | Cost |
|---|---|---|
if/else แบบ structured expression | validate ได้ว่าทั้งสอง arm ฝากค่าชนิดและจำนวนเท่ากันตั้งแต่ compile time ปลอดภัยกว่า arbitrary goto | เขียน conditional ที่ซับซ้อนมากๆ (เช่นหลาย branch ไขว้กัน) ต้องซ้อน if หรือใช้ block ช่วย แทนการกระโดดตรงจุด |
select (branchless) | ไม่มี branch เลย เหมาะกับ hot path ที่ predict ยาก | ต้องคำนวณทั้งสองค่าล่วงหน้าเสมอ แม้บาง arm จะไม่ได้ใช้จริง สิ้นเปลืองกว่าถ้าค่าใดค่าหนึ่งคำนวณแพง |
ข้อผิดพลาดที่พบบ่อย
หัวข้อที่มีชื่อว่า “ข้อผิดพลาดที่พบบ่อย”- ลืมให้
thenและelsearm ฝากค่าชนิดและจำนวนเท่ากันตาม(result i32)ที่ประกาศไว้ ทำให้ module validate ไม่ผ่าน - ใช้
selectแล้วคาดหวัง short-circuit เหมือน ternary ใน JavaScript ทั้งที่จริง engine ประเมิน (หรืออย่างน้อย push) ทั้งสองค่าเสมอก่อนเลือก - สับสน
br_ifกับif—br_ifไม่มี else arm ในตัวเอง แค่ conditionally branch ออกจาก block เดียว ไม่ใช่ two-way conditional แบบif/else
💡 ตัวอย่างจากของจริง
เวลา Emscripten คอมไพล์ C/C++ ที่มี ternary หรือ if-else ซับซ้อน relooper algorithm (อัลกอริทึมที่ Emscripten พัฒนาขึ้นเอง) จะ reconstruct โครงสร้าง
if/elseที่ validate ได้ให้ตรงกับ arbitrary control-flow graph ของโค้ดต้นฉบับ