Skip to main content

llvm_bitcode/
writer.rs

1//! Bitcode writer: serializes an IR `Module` to the LRIR binary format.
2//!
3//! # Format
4//!
5//! The LRIR ("LLVM-in-Rust IR") binary format is a custom format designed for
6//! compact, faithful round-trip serialization of a `(Context, Module)` pair.
7//!
8//! Layout:
9//! ```text
10//! [4B]  magic  = 0x4C 0x52 0x49 0x52  ("LRIR")
11//! [4B]  version (u32 LE) = 1
12//! [4B]  type_count (u32 LE)
13//! [...]  type_count × TypeRecord
14//! [4B]  const_count (u32 LE)
15//! [...]  const_count × ConstRecord
16//! [str]  module_name (u32 len + bytes)
17//! [4B]  func_count (u32 LE)
18//! [...]  func_count × FunctionRecord
19//! ```
20//!
21//! Each string is encoded as `u32 length` + UTF-8 bytes (no null terminator).
22//! Optional strings use a 0 length to mean "absent".
23
24use llvm_ir::{
25    BasicBlock, ConstantData, Context, FloatKind, Function, InstrKind, Instruction, Module,
26    TypeData, ValueRef,
27};
28
29/// Serialize `(ctx, module)` into the LRIR binary format.
30///
31/// Returns the encoded bytes.  The encoding is always valid; no `Result` is
32/// needed because we never encounter unrepresentable values in the IR model.
33pub fn write_bitcode(ctx: &Context, module: &Module) -> Vec<u8> {
34    let mut w = Writer::default();
35
36    // ── header ────────────────────────────────────────────────────────────
37    w.raw(b"LRIR");
38    w.u32(1); // version
39
40    // ── type table ────────────────────────────────────────────────────────
41    let type_count = ctx.num_types() as u32;
42    w.u32(type_count);
43    for (_, td) in ctx.types() {
44        encode_type(&mut w, td);
45    }
46
47    // ── constant table ─────────────────────────────────────────────────────
48    let const_count = ctx.constants.len() as u32;
49    w.u32(const_count);
50    for cd in &ctx.constants {
51        encode_const(&mut w, cd);
52    }
53
54    // ── module header ──────────────────────────────────────────────────────
55    w.string(&module.name);
56
57    // ── functions ──────────────────────────────────────────────────────────
58    w.u32(module.functions.len() as u32);
59    for func in &module.functions {
60        encode_function(&mut w, func);
61    }
62
63    w.buf
64}
65
66// ── type encoding ──────────────────────────────────────────────────────────
67
68/// Tag bytes for type records.
69mod type_tag {
70    /// Public API for `VOID`.
71    pub const VOID: u8 = 0;
72    /// Public API for `INTEGER`.
73    pub const INTEGER: u8 = 1;
74    /// Public API for `FLOAT`.
75    pub const FLOAT: u8 = 2;
76    /// Public API for `POINTER`.
77    pub const POINTER: u8 = 3;
78    /// Public API for `ARRAY`.
79    pub const ARRAY: u8 = 4;
80    /// Public API for `VECTOR`.
81    pub const VECTOR: u8 = 5;
82    /// Public API for `STRUCT`.
83    pub const STRUCT: u8 = 6;
84    /// Public API for `FUNCTION`.
85    pub const FUNCTION: u8 = 7;
86    /// Public API for `LABEL`.
87    pub const LABEL: u8 = 8;
88    /// Public API for `METADATA`.
89    pub const METADATA: u8 = 9;
90}
91
92mod float_tag {
93    /// Public API for `HALF`.
94    pub const HALF: u8 = 0;
95    /// Public API for `BFLOAT`.
96    pub const BFLOAT: u8 = 1;
97    /// Public API for `SINGLE`.
98    pub const SINGLE: u8 = 2;
99    /// Public API for `DOUBLE`.
100    pub const DOUBLE: u8 = 3;
101    /// Public API for `FP128`.
102    pub const FP128: u8 = 4;
103    /// Public API for `X86FP80`.
104    pub const X86FP80: u8 = 5;
105}
106
107fn encode_type(w: &mut Writer, td: &TypeData) {
108    match td {
109        TypeData::Void => {
110            w.u8(type_tag::VOID);
111        }
112        TypeData::Integer(bits) => {
113            w.u8(type_tag::INTEGER);
114            w.u32(*bits);
115        }
116        TypeData::Float(kind) => {
117            w.u8(type_tag::FLOAT);
118            let tag = match kind {
119                FloatKind::Half => float_tag::HALF,
120                FloatKind::BFloat => float_tag::BFLOAT,
121                FloatKind::Single => float_tag::SINGLE,
122                FloatKind::Double => float_tag::DOUBLE,
123                FloatKind::Fp128 => float_tag::FP128,
124                FloatKind::X86Fp80 => float_tag::X86FP80,
125            };
126            w.u8(tag);
127        }
128        TypeData::Pointer => {
129            w.u8(type_tag::POINTER);
130        }
131        TypeData::Array { element, len } => {
132            w.u8(type_tag::ARRAY);
133            w.u32(element.0);
134            w.u64(*len);
135        }
136        TypeData::Vector {
137            element,
138            len,
139            scalable,
140        } => {
141            w.u8(type_tag::VECTOR);
142            w.u32(element.0);
143            w.u32(*len);
144            w.u8(if *scalable { 1 } else { 0 });
145        }
146        TypeData::Struct(st) => {
147            w.u8(type_tag::STRUCT);
148            // Optional name.
149            match &st.name {
150                Some(n) => w.string(n),
151                None => w.u32(0),
152            }
153            w.u8(if st.packed { 1 } else { 0 });
154            w.u32(st.fields.len() as u32);
155            for &f in &st.fields {
156                w.u32(f.0);
157            }
158        }
159        TypeData::Function(ft) => {
160            w.u8(type_tag::FUNCTION);
161            w.u32(ft.ret.0);
162            w.u8(if ft.variadic { 1 } else { 0 });
163            w.u32(ft.params.len() as u32);
164            for &p in &ft.params {
165                w.u32(p.0);
166            }
167        }
168        TypeData::Label => {
169            w.u8(type_tag::LABEL);
170        }
171        TypeData::Metadata => {
172            w.u8(type_tag::METADATA);
173        }
174    }
175}
176
177// ── constant encoding ─────────────────────────────────────────────────────
178
179mod const_tag {
180    /// Public API for `INT`.
181    pub const INT: u8 = 0;
182    /// Public API for `INT_WIDE`.
183    pub const INT_WIDE: u8 = 1;
184    /// Public API for `FLOAT`.
185    pub const FLOAT: u8 = 2;
186    /// Public API for `NULL`.
187    pub const NULL: u8 = 3;
188    /// Public API for `UNDEF`.
189    pub const UNDEF: u8 = 4;
190    /// Public API for `POISON`.
191    pub const POISON: u8 = 5;
192    /// Public API for `ZERO_INIT`.
193    pub const ZERO_INIT: u8 = 6;
194    /// Public API for `ARRAY`.
195    pub const ARRAY: u8 = 7;
196    /// Public API for `STRUCT`.
197    pub const STRUCT: u8 = 8;
198    /// Public API for `VECTOR`.
199    pub const VECTOR: u8 = 9;
200    /// Public API for `GLOBAL_REF`.
201    pub const GLOBAL_REF: u8 = 10;
202}
203
204fn encode_const(w: &mut Writer, cd: &ConstantData) {
205    match cd {
206        ConstantData::Int { ty, val } => {
207            w.u8(const_tag::INT);
208            w.u32(ty.0);
209            w.u64(*val);
210        }
211        ConstantData::IntWide { ty, words } => {
212            w.u8(const_tag::INT_WIDE);
213            w.u32(ty.0);
214            w.u32(words.len() as u32);
215            for &word in words {
216                w.u64(word);
217            }
218        }
219        ConstantData::Float { ty, bits } => {
220            w.u8(const_tag::FLOAT);
221            w.u32(ty.0);
222            w.u64(*bits);
223        }
224        ConstantData::Null(ty) => {
225            w.u8(const_tag::NULL);
226            w.u32(ty.0);
227        }
228        ConstantData::Undef(ty) => {
229            w.u8(const_tag::UNDEF);
230            w.u32(ty.0);
231        }
232        ConstantData::Poison(ty) => {
233            w.u8(const_tag::POISON);
234            w.u32(ty.0);
235        }
236        ConstantData::ZeroInitializer(ty) => {
237            w.u8(const_tag::ZERO_INIT);
238            w.u32(ty.0);
239        }
240        ConstantData::Array { ty, elements } => {
241            w.u8(const_tag::ARRAY);
242            w.u32(ty.0);
243            w.u32(elements.len() as u32);
244            for &e in elements {
245                w.u32(e.0);
246            }
247        }
248        ConstantData::Struct { ty, fields } => {
249            w.u8(const_tag::STRUCT);
250            w.u32(ty.0);
251            w.u32(fields.len() as u32);
252            for &f in fields {
253                w.u32(f.0);
254            }
255        }
256        ConstantData::Vector { ty, elements } => {
257            w.u8(const_tag::VECTOR);
258            w.u32(ty.0);
259            w.u32(elements.len() as u32);
260            for &e in elements {
261                w.u32(e.0);
262            }
263        }
264        ConstantData::GlobalRef { ty, id, name } => {
265            w.u8(const_tag::GLOBAL_REF);
266            w.u32(ty.0);
267            w.u32(id.0);
268            w.string(name);
269        }
270    }
271}
272
273// ── function encoding ─────────────────────────────────────────────────────
274
275mod linkage_tag {
276    /// Public API for `PRIVATE`.
277    pub const PRIVATE: u8 = 0;
278    /// Public API for `INTERNAL`.
279    pub const INTERNAL: u8 = 1;
280    /// Public API for `EXTERNAL`.
281    pub const EXTERNAL: u8 = 2;
282    /// Public API for `WEAK`.
283    pub const WEAK: u8 = 3;
284    /// Public API for `WEAK_ODR`.
285    pub const WEAK_ODR: u8 = 4;
286    /// Public API for `LINK_ONCE`.
287    pub const LINK_ONCE: u8 = 5;
288    /// Public API for `LINK_ONCE_ODR`.
289    pub const LINK_ONCE_ODR: u8 = 6;
290    /// Public API for `COMMON`.
291    pub const COMMON: u8 = 7;
292    /// Public API for `AVAILABLE_EXTERNALLY`.
293    pub const AVAILABLE_EXTERNALLY: u8 = 8;
294}
295
296fn encode_function(w: &mut Writer, func: &Function) {
297    w.string(&func.name);
298    w.u32(func.ty.0);
299    // Linkage.
300    use llvm_ir::Linkage;
301    let ltag = match func.linkage {
302        Linkage::Private => linkage_tag::PRIVATE,
303        Linkage::Internal => linkage_tag::INTERNAL,
304        Linkage::External => linkage_tag::EXTERNAL,
305        Linkage::Weak => linkage_tag::WEAK,
306        Linkage::WeakOdr => linkage_tag::WEAK_ODR,
307        Linkage::LinkOnce => linkage_tag::LINK_ONCE,
308        Linkage::LinkOnceOdr => linkage_tag::LINK_ONCE_ODR,
309        Linkage::Common => linkage_tag::COMMON,
310        Linkage::AvailableExternally => linkage_tag::AVAILABLE_EXTERNALLY,
311    };
312    w.u8(ltag);
313    w.u8(if func.is_declaration { 1 } else { 0 });
314
315    // Arguments.
316    w.u32(func.args.len() as u32);
317    for arg in &func.args {
318        w.string(&arg.name);
319        w.u32(arg.ty.0);
320        w.u32(arg.index);
321    }
322
323    // Basic blocks.
324    w.u32(func.blocks.len() as u32);
325    for bb in &func.blocks {
326        encode_block(w, bb, func);
327    }
328
329    // Flat instruction pool (used for round-trip; type info is embedded here).
330    w.u32(func.instructions.len() as u32);
331    for instr in &func.instructions {
332        encode_instr(w, instr);
333    }
334}
335
336fn encode_block(w: &mut Writer, bb: &BasicBlock, func: &Function) {
337    w.string(&bb.name);
338    w.u32(bb.body.len() as u32);
339    for &iid in &bb.body {
340        w.u32(iid.0);
341    }
342    // Terminator: 0xFFFFFFFF means None.
343    match bb.terminator {
344        Some(tid) => w.u32(tid.0),
345        None => w.u32(0xFFFF_FFFF),
346    }
347    let _ = func;
348}
349
350// ── instruction encoding ──────────────────────────────────────────────────
351//
352// We encode InstrKind as a tag + operands.
353// For the full round-trip, we need the instruction name, type, and kind.
354
355mod instr_tag {
356    /// Public API for `ADD`.
357    pub const ADD: u32 = 0;
358    /// Public API for `SUB`.
359    pub const SUB: u32 = 1;
360    /// Public API for `MUL`.
361    pub const MUL: u32 = 2;
362    /// Public API for `UDIV`.
363    pub const UDIV: u32 = 3;
364    /// Public API for `SDIV`.
365    pub const SDIV: u32 = 4;
366    /// Public API for `UREM`.
367    pub const UREM: u32 = 5;
368    /// Public API for `SREM`.
369    pub const SREM: u32 = 6;
370    /// Public API for `AND`.
371    pub const AND: u32 = 10;
372    /// Public API for `OR`.
373    pub const OR: u32 = 11;
374    /// Public API for `XOR`.
375    pub const XOR: u32 = 12;
376    /// Public API for `SHL`.
377    pub const SHL: u32 = 13;
378    /// Public API for `LSHR`.
379    pub const LSHR: u32 = 14;
380    /// Public API for `ASHR`.
381    pub const ASHR: u32 = 15;
382    /// Public API for `FADD`.
383    pub const FADD: u32 = 20;
384    /// Public API for `FSUB`.
385    pub const FSUB: u32 = 21;
386    /// Public API for `FMUL`.
387    pub const FMUL: u32 = 22;
388    /// Public API for `FDIV`.
389    pub const FDIV: u32 = 23;
390    /// Public API for `FREM`.
391    pub const FREM: u32 = 24;
392    /// Public API for `FNEG`.
393    pub const FNEG: u32 = 25;
394    /// Public API for `ICMP`.
395    pub const ICMP: u32 = 30;
396    /// Public API for `FCMP`.
397    pub const FCMP: u32 = 31;
398    /// Public API for `ALLOCA`.
399    pub const ALLOCA: u32 = 40;
400    /// Public API for `LOAD`.
401    pub const LOAD: u32 = 41;
402    /// Public API for `STORE`.
403    pub const STORE: u32 = 42;
404    /// Public API for `GEP`.
405    pub const GEP: u32 = 43;
406    /// Public API for `TRUNC`.
407    pub const TRUNC: u32 = 50;
408    /// Public API for `ZEXT`.
409    pub const ZEXT: u32 = 51;
410    /// Public API for `SEXT`.
411    pub const SEXT: u32 = 52;
412    /// Public API for `FPTRUNC`.
413    pub const FPTRUNC: u32 = 53;
414    /// Public API for `FPEXT`.
415    pub const FPEXT: u32 = 54;
416    /// Public API for `FPTOUI`.
417    pub const FPTOUI: u32 = 55;
418    /// Public API for `FPTOSI`.
419    pub const FPTOSI: u32 = 56;
420    /// Public API for `UITOFP`.
421    pub const UITOFP: u32 = 57;
422    /// Public API for `SITOFP`.
423    pub const SITOFP: u32 = 58;
424    /// Public API for `PTRTOINT`.
425    pub const PTRTOINT: u32 = 59;
426    /// Public API for `INTTOPTR`.
427    pub const INTTOPTR: u32 = 60;
428    /// Public API for `BITCAST`.
429    pub const BITCAST: u32 = 61;
430    /// Public API for `ADDRSPACECAST`.
431    pub const ADDRSPACECAST: u32 = 62;
432    /// Public API for `FREEZE`.
433    pub const FREEZE: u32 = 63;
434    /// Public API for `SELECT`.
435    pub const SELECT: u32 = 70;
436    /// Public API for `PHI`.
437    pub const PHI: u32 = 71;
438    /// Public API for `EXTRACTVALUE`.
439    pub const EXTRACTVALUE: u32 = 72;
440    /// Public API for `INSERTVALUE`.
441    pub const INSERTVALUE: u32 = 73;
442    /// Public API for `EXTRACTELEM`.
443    pub const EXTRACTELEM: u32 = 74;
444    /// Public API for `INSERTELEM`.
445    pub const INSERTELEM: u32 = 75;
446    /// Public API for `SHUFFLEVEC`.
447    pub const SHUFFLEVEC: u32 = 76;
448    /// Public API for `CALL`.
449    pub const CALL: u32 = 80;
450    /// Public API for `RET`.
451    pub const RET: u32 = 90;
452    /// Public API for `BR`.
453    pub const BR: u32 = 91;
454    /// Public API for `CONDBR`.
455    pub const CONDBR: u32 = 92;
456    /// Public API for `SWITCH`.
457    pub const SWITCH: u32 = 93;
458    /// Public API for `UNREACHABLE`.
459    pub const UNREACHABLE: u32 = 94;
460}
461
462fn encode_vref(w: &mut Writer, vr: &ValueRef) {
463    match vr {
464        ValueRef::Instruction(id) => {
465            w.u8(0);
466            w.u32(id.0);
467        }
468        ValueRef::Argument(id) => {
469            w.u8(1);
470            w.u32(id.0);
471        }
472        ValueRef::Constant(id) => {
473            w.u8(2);
474            w.u32(id.0);
475        }
476        ValueRef::Global(id) => {
477            w.u8(3);
478            w.u32(id.0);
479        }
480    }
481}
482
483fn encode_opt_vref(w: &mut Writer, ovr: &Option<ValueRef>) {
484    match ovr {
485        Some(vr) => {
486            w.u8(1);
487            encode_vref(w, vr);
488        }
489        None => {
490            w.u8(0);
491        }
492    }
493}
494
495fn encode_instr(w: &mut Writer, instr: &Instruction) {
496    // Name: empty string → unnamed.
497    match &instr.name {
498        Some(n) => w.string(n),
499        None => w.u32(0),
500    }
501    // Result type.
502    w.u32(instr.ty.0);
503
504    // Kind tag + operands.
505    use InstrKind::*;
506    match &instr.kind {
507        Add {
508            flags, lhs, rhs, ..
509        } => {
510            w.u32(instr_tag::ADD);
511            w.u8(if flags.nuw { 1 } else { 0 });
512            w.u8(if flags.nsw { 1 } else { 0 });
513            encode_vref(w, lhs);
514            encode_vref(w, rhs);
515        }
516        Sub {
517            flags, lhs, rhs, ..
518        } => {
519            w.u32(instr_tag::SUB);
520            w.u8(if flags.nuw { 1 } else { 0 });
521            w.u8(if flags.nsw { 1 } else { 0 });
522            encode_vref(w, lhs);
523            encode_vref(w, rhs);
524        }
525        Mul {
526            flags, lhs, rhs, ..
527        } => {
528            w.u32(instr_tag::MUL);
529            w.u8(if flags.nuw { 1 } else { 0 });
530            w.u8(if flags.nsw { 1 } else { 0 });
531            encode_vref(w, lhs);
532            encode_vref(w, rhs);
533        }
534        UDiv { exact, lhs, rhs } => {
535            w.u32(instr_tag::UDIV);
536            w.u8(if *exact { 1 } else { 0 });
537            encode_vref(w, lhs);
538            encode_vref(w, rhs);
539        }
540        SDiv { exact, lhs, rhs } => {
541            w.u32(instr_tag::SDIV);
542            w.u8(if *exact { 1 } else { 0 });
543            encode_vref(w, lhs);
544            encode_vref(w, rhs);
545        }
546        URem { lhs, rhs } => {
547            w.u32(instr_tag::UREM);
548            encode_vref(w, lhs);
549            encode_vref(w, rhs);
550        }
551        SRem { lhs, rhs } => {
552            w.u32(instr_tag::SREM);
553            encode_vref(w, lhs);
554            encode_vref(w, rhs);
555        }
556        And { lhs, rhs } => {
557            w.u32(instr_tag::AND);
558            encode_vref(w, lhs);
559            encode_vref(w, rhs);
560        }
561        Or { lhs, rhs } => {
562            w.u32(instr_tag::OR);
563            encode_vref(w, lhs);
564            encode_vref(w, rhs);
565        }
566        Xor { lhs, rhs } => {
567            w.u32(instr_tag::XOR);
568            encode_vref(w, lhs);
569            encode_vref(w, rhs);
570        }
571        Shl {
572            flags, lhs, rhs, ..
573        } => {
574            w.u32(instr_tag::SHL);
575            w.u8(if flags.nuw { 1 } else { 0 });
576            w.u8(if flags.nsw { 1 } else { 0 });
577            encode_vref(w, lhs);
578            encode_vref(w, rhs);
579        }
580        LShr {
581            exact, lhs, rhs, ..
582        } => {
583            w.u32(instr_tag::LSHR);
584            w.u8(if *exact { 1 } else { 0 });
585            encode_vref(w, lhs);
586            encode_vref(w, rhs);
587        }
588        AShr {
589            exact, lhs, rhs, ..
590        } => {
591            w.u32(instr_tag::ASHR);
592            w.u8(if *exact { 1 } else { 0 });
593            encode_vref(w, lhs);
594            encode_vref(w, rhs);
595        }
596        FAdd { lhs, rhs, .. } => {
597            w.u32(instr_tag::FADD);
598            encode_vref(w, lhs);
599            encode_vref(w, rhs);
600        }
601        FSub { lhs, rhs, .. } => {
602            w.u32(instr_tag::FSUB);
603            encode_vref(w, lhs);
604            encode_vref(w, rhs);
605        }
606        FMul { lhs, rhs, .. } => {
607            w.u32(instr_tag::FMUL);
608            encode_vref(w, lhs);
609            encode_vref(w, rhs);
610        }
611        FDiv { lhs, rhs, .. } => {
612            w.u32(instr_tag::FDIV);
613            encode_vref(w, lhs);
614            encode_vref(w, rhs);
615        }
616        FRem { lhs, rhs, .. } => {
617            w.u32(instr_tag::FREM);
618            encode_vref(w, lhs);
619            encode_vref(w, rhs);
620        }
621        FNeg { operand, .. } => {
622            w.u32(instr_tag::FNEG);
623            encode_vref(w, operand);
624        }
625        ICmp { pred, lhs, rhs } => {
626            w.u32(instr_tag::ICMP);
627            w.u8(encode_int_pred(*pred));
628            encode_vref(w, lhs);
629            encode_vref(w, rhs);
630        }
631        FCmp { pred, lhs, rhs, .. } => {
632            w.u32(instr_tag::FCMP);
633            w.u8(encode_float_pred(*pred));
634            encode_vref(w, lhs);
635            encode_vref(w, rhs);
636        }
637        Alloca {
638            alloc_ty,
639            num_elements,
640            align,
641        } => {
642            w.u32(instr_tag::ALLOCA);
643            w.u32(alloc_ty.0);
644            encode_opt_vref(w, num_elements);
645            encode_opt_u32(w, *align);
646        }
647        Load {
648            ty,
649            ptr,
650            align,
651            volatile,
652        } => {
653            w.u32(instr_tag::LOAD);
654            w.u32(ty.0);
655            encode_vref(w, ptr);
656            encode_opt_u32(w, *align);
657            w.u8(if *volatile { 1 } else { 0 });
658        }
659        Store {
660            val,
661            ptr,
662            align,
663            volatile,
664        } => {
665            w.u32(instr_tag::STORE);
666            encode_vref(w, val);
667            encode_vref(w, ptr);
668            encode_opt_u32(w, *align);
669            w.u8(if *volatile { 1 } else { 0 });
670        }
671        GetElementPtr {
672            inbounds,
673            base_ty,
674            ptr,
675            indices,
676        } => {
677            w.u32(instr_tag::GEP);
678            w.u8(if *inbounds { 1 } else { 0 });
679            w.u32(base_ty.0);
680            encode_vref(w, ptr);
681            w.u32(indices.len() as u32);
682            for idx in indices {
683                encode_vref(w, idx);
684            }
685        }
686        Trunc { val, to } => {
687            w.u32(instr_tag::TRUNC);
688            encode_vref(w, val);
689            w.u32(to.0);
690        }
691        ZExt { val, to } => {
692            w.u32(instr_tag::ZEXT);
693            encode_vref(w, val);
694            w.u32(to.0);
695        }
696        SExt { val, to } => {
697            w.u32(instr_tag::SEXT);
698            encode_vref(w, val);
699            w.u32(to.0);
700        }
701        FPTrunc { val, to } => {
702            w.u32(instr_tag::FPTRUNC);
703            encode_vref(w, val);
704            w.u32(to.0);
705        }
706        FPExt { val, to } => {
707            w.u32(instr_tag::FPEXT);
708            encode_vref(w, val);
709            w.u32(to.0);
710        }
711        FPToUI { val, to } => {
712            w.u32(instr_tag::FPTOUI);
713            encode_vref(w, val);
714            w.u32(to.0);
715        }
716        FPToSI { val, to } => {
717            w.u32(instr_tag::FPTOSI);
718            encode_vref(w, val);
719            w.u32(to.0);
720        }
721        UIToFP { val, to } => {
722            w.u32(instr_tag::UITOFP);
723            encode_vref(w, val);
724            w.u32(to.0);
725        }
726        SIToFP { val, to } => {
727            w.u32(instr_tag::SITOFP);
728            encode_vref(w, val);
729            w.u32(to.0);
730        }
731        PtrToInt { val, to } => {
732            w.u32(instr_tag::PTRTOINT);
733            encode_vref(w, val);
734            w.u32(to.0);
735        }
736        IntToPtr { val, to } => {
737            w.u32(instr_tag::INTTOPTR);
738            encode_vref(w, val);
739            w.u32(to.0);
740        }
741        BitCast { val, to } => {
742            w.u32(instr_tag::BITCAST);
743            encode_vref(w, val);
744            w.u32(to.0);
745        }
746        AddrSpaceCast { val, to } => {
747            w.u32(instr_tag::ADDRSPACECAST);
748            encode_vref(w, val);
749            w.u32(to.0);
750        }
751        Freeze { val } => {
752            w.u32(instr_tag::FREEZE);
753            encode_vref(w, val);
754        }
755        Select {
756            cond,
757            then_val,
758            else_val,
759        } => {
760            w.u32(instr_tag::SELECT);
761            encode_vref(w, cond);
762            encode_vref(w, then_val);
763            encode_vref(w, else_val);
764        }
765        Phi { ty, incoming } => {
766            w.u32(instr_tag::PHI);
767            w.u32(ty.0);
768            w.u32(incoming.len() as u32);
769            for (vr, bid) in incoming {
770                encode_vref(w, vr);
771                w.u32(bid.0);
772            }
773        }
774        ExtractValue { aggregate, indices } => {
775            w.u32(instr_tag::EXTRACTVALUE);
776            encode_vref(w, aggregate);
777            w.u32(indices.len() as u32);
778            for &i in indices {
779                w.u32(i);
780            }
781        }
782        InsertValue {
783            aggregate,
784            val,
785            indices,
786        } => {
787            w.u32(instr_tag::INSERTVALUE);
788            encode_vref(w, aggregate);
789            encode_vref(w, val);
790            w.u32(indices.len() as u32);
791            for &i in indices {
792                w.u32(i);
793            }
794        }
795        ExtractElement { vec, idx } => {
796            w.u32(instr_tag::EXTRACTELEM);
797            encode_vref(w, vec);
798            encode_vref(w, idx);
799        }
800        InsertElement { vec, val, idx } => {
801            w.u32(instr_tag::INSERTELEM);
802            encode_vref(w, vec);
803            encode_vref(w, val);
804            encode_vref(w, idx);
805        }
806        ShuffleVector { v1, v2, mask } => {
807            w.u32(instr_tag::SHUFFLEVEC);
808            encode_vref(w, v1);
809            encode_vref(w, v2);
810            w.u32(mask.len() as u32);
811            for &m in mask {
812                w.i32(m);
813            }
814        }
815        Call {
816            tail,
817            callee_ty,
818            callee,
819            args,
820        } => {
821            w.u32(instr_tag::CALL);
822            use llvm_ir::TailCallKind;
823            let tail_tag = match tail {
824                TailCallKind::None => 0u8,
825                TailCallKind::Tail => 1,
826                TailCallKind::MustTail => 2,
827                TailCallKind::NoTail => 3,
828            };
829            w.u8(tail_tag);
830            w.u32(callee_ty.0);
831            encode_vref(w, callee);
832            w.u32(args.len() as u32);
833            for arg in args {
834                encode_vref(w, arg);
835            }
836        }
837        Ret { val } => {
838            w.u32(instr_tag::RET);
839            encode_opt_vref(w, val);
840        }
841        Br { dest } => {
842            w.u32(instr_tag::BR);
843            w.u32(dest.0);
844        }
845        CondBr {
846            cond,
847            then_dest,
848            else_dest,
849        } => {
850            w.u32(instr_tag::CONDBR);
851            encode_vref(w, cond);
852            w.u32(then_dest.0);
853            w.u32(else_dest.0);
854        }
855        Switch {
856            val,
857            default,
858            cases,
859        } => {
860            w.u32(instr_tag::SWITCH);
861            encode_vref(w, val);
862            w.u32(default.0);
863            w.u32(cases.len() as u32);
864            for (cv, bd) in cases {
865                encode_vref(w, cv);
866                w.u32(bd.0);
867            }
868        }
869        Unreachable => {
870            w.u32(instr_tag::UNREACHABLE);
871        }
872    }
873}
874
875fn encode_opt_u32(w: &mut Writer, v: Option<u32>) {
876    match v {
877        Some(x) => {
878            w.u8(1);
879            w.u32(x);
880        }
881        None => {
882            w.u8(0);
883        }
884    }
885}
886
887fn encode_int_pred(pred: llvm_ir::IntPredicate) -> u8 {
888    use llvm_ir::IntPredicate::*;
889    match pred {
890        Eq => 0,
891        Ne => 1,
892        Ugt => 2,
893        Uge => 3,
894        Ult => 4,
895        Ule => 5,
896        Sgt => 6,
897        Sge => 7,
898        Slt => 8,
899        Sle => 9,
900    }
901}
902
903fn encode_float_pred(pred: llvm_ir::FloatPredicate) -> u8 {
904    use llvm_ir::FloatPredicate::*;
905    match pred {
906        False => 0,
907        Oeq => 1,
908        Ogt => 2,
909        Oge => 3,
910        Olt => 4,
911        Ole => 5,
912        One => 6,
913        Ord => 7,
914        Uno => 8,
915        Ueq => 9,
916        Ugt => 10,
917        Uge => 11,
918        Ult => 12,
919        Ule => 13,
920        Une => 14,
921        True => 15,
922    }
923}
924
925// ── writer helper ─────────────────────────────────────────────────────────
926
927#[derive(Default)]
928struct Writer {
929    buf: Vec<u8>,
930}
931
932impl Writer {
933    fn raw(&mut self, b: &[u8]) {
934        self.buf.extend_from_slice(b);
935    }
936    fn u8(&mut self, v: u8) {
937        self.buf.push(v);
938    }
939    fn u32(&mut self, v: u32) {
940        self.buf.extend_from_slice(&v.to_le_bytes());
941    }
942    fn i32(&mut self, v: i32) {
943        self.buf.extend_from_slice(&v.to_le_bytes());
944    }
945    fn u64(&mut self, v: u64) {
946        self.buf.extend_from_slice(&v.to_le_bytes());
947    }
948    /// Length-prefixed UTF-8 string.  A length of 0 means "absent/empty".
949    fn string(&mut self, s: &str) {
950        self.u32(s.len() as u32);
951        self.raw(s.as_bytes());
952    }
953}
954
955// ── public API re-exported from crate root ────────────────────────────────
956
957/// Public API for `re-export`.
958pub use write_bitcode as write;