llvm_ir/
terminator.rs

1use crate::debugloc::{DebugLoc, HasDebugLoc};
2use crate::function::{CallingConvention, FunctionAttribute, ParameterAttribute};
3use crate::instruction::{HasResult, InlineAssembly};
4use crate::types::{Typed, Types};
5use crate::{Constant, ConstantRef, Name, Operand, Type, TypeRef};
6use either::Either;
7use std::convert::TryFrom;
8use std::fmt::{self, Display};
9
10/// Terminator instructions end a basic block.
11/// See [LLVM 14 docs on Terminator Instructions](https://releases.llvm.org/14.0.0/docs/LangRef.html#terminator-instructions)
12#[derive(PartialEq, Clone, Debug, Hash)]
13pub enum Terminator {
14    Ret(Ret),
15    Br(Br),
16    CondBr(CondBr),
17    Switch(Switch),
18    IndirectBr(IndirectBr),
19    Invoke(Invoke),
20    Resume(Resume),
21    Unreachable(Unreachable),
22    CleanupRet(CleanupRet),
23    CatchRet(CatchRet),
24    CatchSwitch(CatchSwitch),
25    CallBr(CallBr),
26}
27
28/// The [`Type`](../enum.Type.html) of a `Terminator` is its result type.
29/// For most terminators, this is `VoidType`.
30/// For instance, a [`Ret`](struct.Ret.html) instruction has void type even if
31/// the function returns a non-void value; we do not store the result of a `Ret`
32/// instruction using something like `%3 = ret i32 %2`.
33/// See [LLVM 14 docs on Terminator Instructions](https://releases.llvm.org/14.0.0/docs/LangRef.html#terminator-instructions)
34impl Typed for Terminator {
35    fn get_type(&self, types: &Types) -> TypeRef {
36        match self {
37            Terminator::Ret(t) => types.type_of(t),
38            Terminator::Br(t) => types.type_of(t),
39            Terminator::CondBr(t) => types.type_of(t),
40            Terminator::Switch(t) => types.type_of(t),
41            Terminator::IndirectBr(t) => types.type_of(t),
42            Terminator::Invoke(t) => types.type_of(t),
43            Terminator::Resume(t) => types.type_of(t),
44            Terminator::Unreachable(t) => types.type_of(t),
45            Terminator::CleanupRet(t) => types.type_of(t),
46            Terminator::CatchRet(t) => types.type_of(t),
47            Terminator::CatchSwitch(t) => types.type_of(t),
48            Terminator::CallBr(t) => types.type_of(t),
49        }
50    }
51}
52
53impl HasDebugLoc for Terminator {
54    fn get_debug_loc(&self) -> &Option<DebugLoc> {
55        match self {
56            Terminator::Ret(t) => t.get_debug_loc(),
57            Terminator::Br(t) => t.get_debug_loc(),
58            Terminator::CondBr(t) => t.get_debug_loc(),
59            Terminator::Switch(t) => t.get_debug_loc(),
60            Terminator::IndirectBr(t) => t.get_debug_loc(),
61            Terminator::Invoke(t) => t.get_debug_loc(),
62            Terminator::Resume(t) => t.get_debug_loc(),
63            Terminator::Unreachable(t) => t.get_debug_loc(),
64            Terminator::CleanupRet(t) => t.get_debug_loc(),
65            Terminator::CatchRet(t) => t.get_debug_loc(),
66            Terminator::CatchSwitch(t) => t.get_debug_loc(),
67            Terminator::CallBr(t) => t.get_debug_loc(),
68        }
69    }
70}
71
72impl Display for Terminator {
73    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
74        match self {
75            Terminator::Ret(t) => write!(f, "{}", t),
76            Terminator::Br(t) => write!(f, "{}", t),
77            Terminator::CondBr(t) => write!(f, "{}", t),
78            Terminator::Switch(t) => write!(f, "{}", t),
79            Terminator::IndirectBr(t) => write!(f, "{}", t),
80            Terminator::Invoke(t) => write!(f, "{}", t),
81            Terminator::Resume(t) => write!(f, "{}", t),
82            Terminator::Unreachable(t) => write!(f, "{}", t),
83            Terminator::CleanupRet(t) => write!(f, "{}", t),
84            Terminator::CatchRet(t) => write!(f, "{}", t),
85            Terminator::CatchSwitch(t) => write!(f, "{}", t),
86            Terminator::CallBr(t) => write!(f, "{}", t),
87        }
88    }
89}
90
91/* --TODO not yet implemented: metadata
92impl Terminator {
93    pub fn get_metadata(&self) -> &InstructionMetadata {
94        match self {
95            Terminator::Ret(t) => &t.metadata,
96            Terminator::Br(t) => &t.metadata,
97            Terminator::CondBr(t) => &t.metadata,
98            Terminator::Switch(t) => &t.metadata,
99            Terminator::IndirectBr(t) => &t.metadata,
100            Terminator::Invoke(t) => &t.metadata,
101            Terminator::Resume(t) => &t.metadata,
102            Terminator::Unreachable(t) => &t.metadata,
103            Terminator::CleanupRet(t) => &t.metadata,
104            Terminator::CatchRet(t) => &t.metadata,
105            Terminator::CatchSwitch(t) => &t.metadata,
106            Terminator::CallBr(t) => &t.metadata,
107        }
108    }
109}
110*/
111
112impl Terminator {
113    /// Get the result (destination) of the `Terminator`, or `None` if the
114    /// `Terminator` doesn't have a result (has void type).
115    pub fn try_get_result(&self) -> Option<&Name> {
116        match self {
117            Terminator::Ret(_) => None,
118            Terminator::Br(_) => None,
119            Terminator::CondBr(_) => None,
120            Terminator::Switch(_) => None,
121            Terminator::IndirectBr(_) => None,
122            Terminator::Invoke(t) => Some(&t.result),
123            Terminator::Resume(_) => None,
124            Terminator::Unreachable(_) => None,
125            Terminator::CleanupRet(_) => None,
126            Terminator::CatchRet(_) => None,
127            Terminator::CatchSwitch(t) => Some(&t.result),
128            Terminator::CallBr(t) => Some(&t.result),
129        }
130    }
131}
132
133macro_rules! impl_term {
134    ($term:ty, $id:ident) => {
135        impl From<$term> for Terminator {
136            fn from(term: $term) -> Terminator {
137                Terminator::$id(term)
138            }
139        }
140
141        impl TryFrom<Terminator> for $term {
142            type Error = &'static str;
143            fn try_from(term: Terminator) -> Result<Self, Self::Error> {
144                match term {
145                    Terminator::$id(term) => Ok(term),
146                    _ => Err("Terminator is not of requested type"),
147                }
148            }
149        }
150
151        impl HasDebugLoc for $term {
152            fn get_debug_loc(&self) -> &Option<DebugLoc> {
153                &self.debugloc
154            }
155        }
156
157        /* --TODO not yet implemented: metadata
158        impl HasMetadata for $term {
159            fn get_metadata(&self) -> &InstructionMetadata {
160                &self.metadata
161            }
162        }
163        */
164    };
165}
166
167macro_rules! impl_hasresult {
168    ($term:ty) => {
169        impl HasResult for $term {
170            fn get_result(&self) -> &Name {
171                &self.result
172            }
173        }
174    };
175}
176
177macro_rules! void_typed {
178    ($term:ty) => {
179        impl Typed for $term {
180            fn get_type(&self, types: &Types) -> TypeRef {
181                types.void()
182            }
183        }
184    };
185}
186
187/// See [LLVM 14 docs on the 'ret' instruction](https://releases.llvm.org/14.0.0/docs/LangRef.html#ret-instruction)
188#[derive(PartialEq, Clone, Debug, Hash)]
189pub struct Ret {
190    /// The value being returned, or `None` if returning void.
191    pub return_operand: Option<Operand>,
192    pub debugloc: Option<DebugLoc>,
193    // --TODO not yet implemented-- pub metadata: InstructionMetadata,
194}
195
196impl_term!(Ret, Ret);
197void_typed!(Ret); // technically the instruction has void type, even though the function may not
198
199impl Display for Ret {
200    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
201        write!(
202            f,
203            "ret {}",
204            match &self.return_operand {
205                None => "void".into(),
206                Some(op) => format!("{}", op),
207            },
208        )?;
209        if self.debugloc.is_some() {
210            write!(f, " (with debugloc)")?;
211        }
212        Ok(())
213    }
214}
215
216/// See [LLVM 14 docs on the 'br' instruction](https://releases.llvm.org/14.0.0/docs/LangRef.html#br-instruction).
217/// The LLVM 'br' instruction has both conditional and unconditional variants, which we separate -- this is
218/// the unconditional variant, while the conditional variant is [`CondBr`](struct.CondBr.html).
219#[derive(PartialEq, Clone, Debug, Hash)]
220pub struct Br {
221    /// The [`Name`](../enum.Name.html) of the [`BasicBlock`](../struct.BasicBlock.html) destination.
222    pub dest: Name,
223    pub debugloc: Option<DebugLoc>,
224    // --TODO not yet implemented-- pub metadata: InstructionMetadata,
225}
226
227impl_term!(Br, Br);
228void_typed!(Br);
229
230impl Display for Br {
231    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
232        write!(f, "br label {}", &self.dest)?;
233        if self.debugloc.is_some() {
234            write!(f, " (with debugloc)")?;
235        }
236        Ok(())
237    }
238}
239
240/// See [LLVM 14 docs on the 'br' instruction](https://releases.llvm.org/14.0.0/docs/LangRef.html#br-instruction).
241/// The LLVM 'br' instruction has both conditional and unconditional variants, which we separate -- this is
242/// the conditional variant, while the unconditional variant is [`Br`](struct.Br.html).
243#[derive(PartialEq, Clone, Debug, Hash)]
244pub struct CondBr {
245    /// The branch condition.
246    pub condition: Operand,
247    /// The [`Name`](../enum.Name.html) of the [`BasicBlock`](../struct.BasicBlock.html) destination if the `condition` is true.
248    pub true_dest: Name,
249    /// The [`Name`](../enum.Name.html) of the [`BasicBlock`](../struct.BasicBlock.html) destination if the `condition` is false.
250    pub false_dest: Name,
251    pub debugloc: Option<DebugLoc>,
252    // --TODO not yet implemented-- pub metadata: InstructionMetadata,
253}
254
255impl_term!(CondBr, CondBr);
256void_typed!(CondBr);
257
258impl Display for CondBr {
259    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
260        write!(
261            f,
262            "br {}, label {}, label {}",
263            &self.condition, &self.true_dest, &self.false_dest,
264        )?;
265        if self.debugloc.is_some() {
266            write!(f, " (with debugloc)")?;
267        }
268        Ok(())
269    }
270}
271
272/// See [LLVM 14 docs on the 'switch' instruction](https://releases.llvm.org/14.0.0/docs/LangRef.html#switch-instruction)
273#[derive(PartialEq, Clone, Debug, Hash)]
274pub struct Switch {
275    pub operand: Operand,
276    pub dests: Vec<(ConstantRef, Name)>,
277    pub default_dest: Name,
278    pub debugloc: Option<DebugLoc>,
279    // --TODO not yet implemented-- pub metadata: InstructionMetadata,
280}
281
282impl_term!(Switch, Switch);
283void_typed!(Switch);
284
285impl Display for Switch {
286    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
287        write!(
288            f,
289            "switch {}, label {} [ ",
290            &self.operand, &self.default_dest,
291        )?;
292        for (val, label) in &self.dests {
293            write!(f, "{}, label {}; ", val, label)?;
294        }
295        write!(f, "]")?;
296        if self.debugloc.is_some() {
297            write!(f, " (with debugloc)")?;
298        }
299        Ok(())
300    }
301}
302
303/// See [LLVM 14 docs on the 'indirectbr' instruction](https://releases.llvm.org/14.0.0/docs/LangRef.html#indirectbr-instruction)
304#[derive(PartialEq, Clone, Debug, Hash)]
305pub struct IndirectBr {
306    /// Address to jump to (must be derived from a [`Constant::BlockAddress`](../enum.Constant.html))
307    pub operand: Operand,
308    /// The "full set of possible destinations" which the `IndirectBr` could jump to.
309    /// These are [`Name`](../enum.Name.html)s of
310    /// [`BasicBlock`](../struct.BasicBlock.html)s in the current function;
311    /// `IndirectBr` cannot be used to jump between functions.
312    pub possible_dests: Vec<Name>,
313    pub debugloc: Option<DebugLoc>,
314    // --TODO not yet implemented-- pub metadata: InstructionMetadata,
315}
316
317impl_term!(IndirectBr, IndirectBr);
318void_typed!(IndirectBr);
319
320impl Display for IndirectBr {
321    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
322        write!(
323            f,
324            "indirectbr {}, [ label {}",
325            &self.operand,
326            &self
327                .possible_dests
328                .get(0)
329                .expect("IndirectBr with no possible dests"),
330        )?;
331        for dest in &self.possible_dests[1 ..] {
332            write!(f, ", label {}", dest)?;
333        }
334        write!(f, " ]")?;
335        if self.debugloc.is_some() {
336            write!(f, " (with debugloc)")?;
337        }
338        Ok(())
339    }
340}
341
342/// See [LLVM 14 docs on the 'invoke' instruction](https://releases.llvm.org/14.0.0/docs/LangRef.html#invoke-instruction)
343#[derive(PartialEq, Clone, Debug, Hash)]
344pub struct Invoke {
345    pub function: Either<InlineAssembly, Operand>,
346    #[cfg(feature = "llvm-15-or-greater")]
347    pub function_ty: TypeRef,
348    pub arguments: Vec<(Operand, Vec<ParameterAttribute>)>,
349    pub return_attributes: Vec<ParameterAttribute>,
350    pub result: Name, // The name of the variable that will get the result of the call (if the callee returns with 'ret')
351    pub return_label: Name, // Should be the name of a basic block. If the callee returns normally (i.e., with 'ret'), control flow resumes here.
352    pub exception_label: Name, // Should be the name of a basic block. If the callee returns with 'resume' or another exception-handling mechanism, control flow resumes here.
353    pub function_attributes: Vec<FunctionAttribute>, // llvm-hs has the equivalent of Vec<Either<GroupID, FunctionAttribute>>, but I'm not sure how the GroupID option comes up
354    pub calling_convention: CallingConvention,
355    pub debugloc: Option<DebugLoc>,
356    // --TODO not yet implemented-- pub metadata: InstructionMetadata,
357}
358
359impl_term!(Invoke, Invoke);
360impl_hasresult!(Invoke);
361
362#[cfg(feature = "llvm-14-or-lower")]
363impl Typed for Invoke {
364    fn get_type(&self, types: &Types) -> TypeRef {
365        match types.type_of(&self.function).as_ref() {
366            Type::PointerType { pointee_type, .. } => match pointee_type.as_ref() {
367                Type::FuncType { result_type, .. } => result_type.clone(),
368                ty => panic!("Expected Invoke's function argument to be of type pointer-to-function, got pointer-to-{:?}", ty),
369            },
370            ty => panic!("Expected Invoke's function argument to be of type pointer-to-function, got {:?}", ty),
371        }
372    }
373}
374#[cfg(feature = "llvm-15-or-greater")]
375impl Typed for Invoke {
376    fn get_type(&self, _types: &Types) -> TypeRef {
377        match self.function_ty.as_ref() {
378            Type::FuncType { result_type, .. } => result_type.clone(),
379            ty => panic!("Expected Invoke.function_ty to be a FuncType, got {:?}", ty),
380        }
381    }
382}
383
384impl Display for Invoke {
385    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
386        // Like with `Call`, we choose not to include all the detailed
387        // information available in the `Invoke` struct in this `Display` impl
388        write!(
389            f,
390            "{} = invoke {}(",
391            &self.result,
392            match &self.function {
393                Either::Left(_) => "<inline assembly>".into(),
394                Either::Right(op) => format!("{}", op),
395            }
396        )?;
397        for (i, (arg, _)) in self.arguments.iter().enumerate() {
398            if i == self.arguments.len() - 1 {
399                write!(f, "{}", arg)?;
400            } else {
401                write!(f, "{}, ", arg)?;
402            }
403        }
404        write!(
405            f,
406            ") to label {} unwind label {}",
407            &self.return_label, &self.exception_label,
408        )?;
409        if self.debugloc.is_some() {
410            write!(f, " (with debugloc)")?;
411        }
412        Ok(())
413    }
414}
415
416/// See [LLVM 14 docs on the 'resume' instruction](https://releases.llvm.org/14.0.0/docs/LangRef.html#resume-instruction)
417#[derive(PartialEq, Clone, Debug, Hash)]
418pub struct Resume {
419    pub operand: Operand,
420    pub debugloc: Option<DebugLoc>,
421    // --TODO not yet implemented-- pub metadata: InstructionMetadata,
422}
423
424impl_term!(Resume, Resume);
425void_typed!(Resume);
426
427impl Display for Resume {
428    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
429        write!(f, "resume {}", &self.operand)?;
430        if self.debugloc.is_some() {
431            write!(f, " (with debugloc)")?;
432        }
433        Ok(())
434    }
435}
436
437/// See [LLVM 14 docs on the 'unreachable' instruction](https://releases.llvm.org/14.0.0/docs/LangRef.html#unreachable-instruction)
438#[derive(PartialEq, Clone, Debug, Hash)]
439pub struct Unreachable {
440    pub debugloc: Option<DebugLoc>,
441    // --TODO not yet implemented-- pub metadata: InstructionMetadata,
442}
443
444impl_term!(Unreachable, Unreachable);
445void_typed!(Unreachable);
446
447impl Display for Unreachable {
448    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
449        write!(f, "unreachable")?;
450        if self.debugloc.is_some() {
451            write!(f, " (with debugloc)")?;
452        }
453        Ok(())
454    }
455}
456
457/// See [LLVM 14 docs on the 'cleanupret' instruction](https://releases.llvm.org/14.0.0/docs/LangRef.html#cleanupret-instruction)
458#[derive(PartialEq, Clone, Debug, Hash)]
459pub struct CleanupRet {
460    pub cleanup_pad: Operand,
461    /// `None` here indicates 'unwind to caller'
462    pub unwind_dest: Option<Name>,
463    pub debugloc: Option<DebugLoc>,
464    // --TODO not yet implemented-- pub metadata: InstructionMetadata,
465}
466
467impl_term!(CleanupRet, CleanupRet);
468void_typed!(CleanupRet);
469
470impl Display for CleanupRet {
471    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
472        write!(
473            f,
474            "cleanupret from {} unwind {}",
475            &self.cleanup_pad,
476            match &self.unwind_dest {
477                None => "to caller".into(),
478                Some(dest) => format!("label {}", dest),
479            },
480        )?;
481        if self.debugloc.is_some() {
482            write!(f, " (with debugloc)")?;
483        }
484        Ok(())
485    }
486}
487
488/// See [LLVM 14 docs on the 'catchret' instruction](https://releases.llvm.org/14.0.0/docs/LangRef.html#catchret-instruction)
489#[derive(PartialEq, Clone, Debug, Hash)]
490pub struct CatchRet {
491    pub catch_pad: Operand,
492    pub successor: Name,
493    pub debugloc: Option<DebugLoc>,
494    // --TODO not yet implemented-- pub metadata: InstructionMetadata,
495}
496
497impl_term!(CatchRet, CatchRet);
498void_typed!(CatchRet);
499
500impl Display for CatchRet {
501    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
502        write!(
503            f,
504            "catchret from {} to label {}",
505            &self.catch_pad, &self.successor,
506        )?;
507        if self.debugloc.is_some() {
508            write!(f, " (with debugloc)")?;
509        }
510        Ok(())
511    }
512}
513
514/// See [LLVM 14 docs on the 'catchswitch' instruction](https://releases.llvm.org/14.0.0/docs/LangRef.html#catchswitch-instruction)
515#[derive(PartialEq, Clone, Debug, Hash)]
516pub struct CatchSwitch {
517    pub parent_pad: Operand,
518    /// Cannot be empty
519    pub catch_handlers: Vec<Name>,
520    /// `None` here indicates 'unwind to caller'
521    pub default_unwind_dest: Option<Name>,
522    pub result: Name,
523    pub debugloc: Option<DebugLoc>,
524    // --TODO not yet implemented-- pub metadata: InstructionMetadata,
525}
526
527impl_term!(CatchSwitch, CatchSwitch);
528impl_hasresult!(CatchSwitch);
529
530impl Typed for CatchSwitch {
531    fn get_type(&self, _types: &Types) -> TypeRef {
532        unimplemented!("Typed for CatchSwitch")
533        // It's clear that there is a result of this instruction, but the documentation doesn't appear to clearly describe what its type is
534    }
535}
536
537impl Display for CatchSwitch {
538    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
539        write!(
540            f,
541            "{} = catchswitch within {} [ label {}",
542            &self.result,
543            &self.parent_pad,
544            &self
545                .catch_handlers
546                .get(0)
547                .expect("CatchSwitch with no handlers"),
548        )?;
549        for handler in &self.catch_handlers[1 ..] {
550            write!(f, ", label {}", handler)?;
551        }
552        write!(
553            f,
554            " ] unwind {}",
555            match &self.default_unwind_dest {
556                None => "to caller".into(),
557                Some(dest) => format!("label {}", dest),
558            },
559        )?;
560        if self.debugloc.is_some() {
561            write!(f, " (with debugloc)")?;
562        }
563        Ok(())
564    }
565}
566
567/// See [LLVM 14 docs on the 'callbr' instruction](https://releases.llvm.org/14.0.0/docs/LangRef.html#callbr-instruction)
568#[derive(PartialEq, Clone, Debug, Hash)]
569pub struct CallBr {
570    pub function: Either<InlineAssembly, Operand>,
571    pub arguments: Vec<(Operand, Vec<ParameterAttribute>)>,
572    pub return_attributes: Vec<ParameterAttribute>,
573    pub result: Name, // The name of the variable that will get the result of the call (if the callee returns with 'ret')
574    pub return_label: Name, // Should be the name of a basic block. If the callee returns normally (i.e., with 'ret'), control flow resumes here.
575    /// `other_labels` should be `Vec<Name>`, but it appears there is no way to get this information with the LLVM C API (as opposed to the C++ API)
576    pub other_labels: (), //Vec<Name>, // Should be names of basic blocks. The callee may use an inline-asm 'goto' to resume control flow at one of these places.
577    pub function_attributes: Vec<FunctionAttribute>,
578    pub calling_convention: CallingConvention,
579    pub debugloc: Option<DebugLoc>,
580    // --TODO not yet implemented-- pub metadata: InstructionMetadata,
581}
582
583impl_term!(CallBr, CallBr);
584impl_hasresult!(CallBr);
585
586impl Typed for CallBr {
587    fn get_type(&self, types: &Types) -> TypeRef {
588        match types.type_of(&self.function).as_ref() {
589            Type::FuncType { result_type, .. } => result_type.clone(),
590            ty => panic!(
591                "Expected the function argument of a CallBr to have type FuncType; got {:?}",
592                ty
593            ),
594        }
595    }
596}
597
598impl Display for CallBr {
599    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
600        // Like with `Call` and `Invoke, we choose not to include all the
601        // detailed information available in the `CallBr` struct in this
602        // `Display` impl
603        write!(
604            f,
605            "{} = callbr {}(",
606            &self.result,
607            match &self.function {
608                Either::Left(_) => "<inline assembly>".into(),
609                Either::Right(op) => format!("{}", op),
610            }
611        )?;
612        for (i, (arg, _)) in self.arguments.iter().enumerate() {
613            if i == self.arguments.len() - 1 {
614                write!(f, "{}", arg)?;
615            } else {
616                write!(f, "{}, ", arg)?;
617            }
618        }
619        write!(f, ") to label {}", &self.return_label)?;
620        if self.debugloc.is_some() {
621            write!(f, " (with debugloc)")?;
622        }
623        Ok(())
624    }
625}
626
627// ********* //
628// from_llvm //
629// ********* //
630
631use crate::from_llvm::*;
632use crate::function::FunctionContext;
633use crate::llvm_sys::*;
634use crate::module::ModuleContext;
635use llvm_sys::LLVMOpcode;
636
637impl Terminator {
638    #[rustfmt::skip] // so we can keep all of the match arms consistent
639    pub(crate) fn from_llvm_ref(
640        term: LLVMValueRef,
641        ctx: &mut ModuleContext,
642        func_ctx: &mut FunctionContext,
643    ) -> Self {
644        debug!("Processing terminator {:?}", unsafe {
645            print_to_string(term)
646        });
647        match unsafe { LLVMGetInstructionOpcode(term) } {
648            LLVMOpcode::LLVMRet => {
649                Terminator::Ret(Ret::from_llvm_ref(term, ctx, func_ctx))
650            },
651            LLVMOpcode::LLVMBr => match unsafe { LLVMGetNumOperands(term) } {
652                1 => Terminator::Br(Br::from_llvm_ref(term, func_ctx)),
653                3 => Terminator::CondBr(CondBr::from_llvm_ref(term, ctx, func_ctx)),
654                n => panic!("LLVMBr with {} operands, expected 1 or 3", n),
655            },
656            LLVMOpcode::LLVMSwitch => {
657                Terminator::Switch(Switch::from_llvm_ref(term, ctx, func_ctx))
658            },
659            LLVMOpcode::LLVMIndirectBr => {
660                Terminator::IndirectBr(IndirectBr::from_llvm_ref(term, ctx, func_ctx))
661            },
662            LLVMOpcode::LLVMInvoke => {
663                Terminator::Invoke(Invoke::from_llvm_ref(term, ctx, func_ctx))
664            },
665            LLVMOpcode::LLVMResume => {
666                Terminator::Resume(Resume::from_llvm_ref(term, ctx, func_ctx))
667            },
668            LLVMOpcode::LLVMUnreachable => {
669                Terminator::Unreachable(Unreachable::from_llvm_ref(term))
670            },
671            LLVMOpcode::LLVMCleanupRet => {
672                Terminator::CleanupRet(CleanupRet::from_llvm_ref(term, ctx, func_ctx))
673            },
674            LLVMOpcode::LLVMCatchRet => {
675                Terminator::CatchRet(CatchRet::from_llvm_ref(term, ctx, func_ctx))
676            },
677            LLVMOpcode::LLVMCatchSwitch => {
678                Terminator::CatchSwitch(CatchSwitch::from_llvm_ref(term, ctx, func_ctx))
679            },
680            LLVMOpcode::LLVMCallBr => {
681                Terminator::CallBr(CallBr::from_llvm_ref(term, ctx, func_ctx))
682            },
683            opcode => panic!(
684                "Terminator::from_llvm_ref called with a non-terminator instruction (opcode {:?})",
685                opcode
686            ),
687        }
688    }
689}
690
691impl Ret {
692    pub(crate) fn from_llvm_ref(
693        term: LLVMValueRef,
694        ctx: &mut ModuleContext,
695        func_ctx: &mut FunctionContext,
696    ) -> Self {
697        Self {
698            return_operand: match unsafe { LLVMGetNumOperands(term) } {
699                0 => None,
700                1 => Some(Operand::from_llvm_ref(
701                    unsafe { LLVMGetOperand(term, 0) },
702                    ctx,
703                    func_ctx,
704                )),
705                n => panic!("Ret instruction with {} operands", n),
706            },
707            debugloc: DebugLoc::from_llvm_with_col(term),
708            // metadata: InstructionMetadata::from_llvm_inst(term),
709        }
710    }
711}
712
713impl Br {
714    pub(crate) fn from_llvm_ref(term: LLVMValueRef, func_ctx: &mut FunctionContext) -> Self {
715        assert_eq!(unsafe { LLVMGetNumOperands(term) }, 1);
716        Self {
717            dest: func_ctx
718                .bb_names
719                .get(unsafe { &op_to_bb(LLVMGetOperand(term, 0)) })
720                .expect("Failed to find destination bb in map")
721                .clone(),
722            debugloc: DebugLoc::from_llvm_with_col(term),
723            // metadata: InstructionMetadata::from_llvm_inst(term),
724        }
725    }
726}
727
728impl CondBr {
729    pub(crate) fn from_llvm_ref(
730        term: LLVMValueRef,
731        ctx: &mut ModuleContext,
732        func_ctx: &mut FunctionContext,
733    ) -> Self {
734        assert_eq!(unsafe { LLVMGetNumOperands(term) }, 3);
735        Self {
736            condition: Operand::from_llvm_ref(unsafe { LLVMGetOperand(term, 0) }, ctx, func_ctx),
737            true_dest: func_ctx
738                .bb_names
739                .get(unsafe { &op_to_bb(LLVMGetOperand(term, 2)) })
740                .expect("Failed to find true-destination bb in map")
741                .clone(),
742            false_dest: func_ctx
743                .bb_names
744                .get(unsafe { &op_to_bb(LLVMGetOperand(term, 1)) })
745                .expect("Failed to find false-destination in bb map")
746                .clone(),
747            debugloc: DebugLoc::from_llvm_with_col(term),
748            // metadata: InstructionMetadata::from_llvm_inst(term),
749        }
750    }
751}
752
753impl Switch {
754    pub(crate) fn from_llvm_ref(
755        term: LLVMValueRef,
756        ctx: &mut ModuleContext,
757        func_ctx: &mut FunctionContext,
758    ) -> Self {
759        Self {
760            operand: Operand::from_llvm_ref(unsafe { LLVMGetOperand(term, 0) }, ctx, func_ctx),
761            dests: {
762                let num_dests = unsafe { LLVMGetNumSuccessors(term) };
763                let dest_bbs = (1 ..= num_dests) // LLVMGetSuccessor(0) apparently gives the default dest
764                    .map(|i| {
765                        func_ctx
766                            .bb_names
767                            .get(unsafe { &LLVMGetSuccessor(term, i) })
768                            .expect("Failed to find switch destination in map")
769                            .clone()
770                    });
771                let dest_vals = (1 .. num_dests).map(|i| {
772                    Constant::from_llvm_ref(unsafe { LLVMGetOperand(term, 2 * i) }, ctx)
773                    // 2*i because empirically, operand 1 is the default dest, and operands 3/5/7/etc are the successor blocks
774                });
775                Iterator::zip(dest_vals, dest_bbs).collect()
776            },
777            default_dest: func_ctx
778                .bb_names
779                .get(unsafe { &LLVMGetSwitchDefaultDest(term) })
780                .expect("Failed to find switch default destination in map")
781                .clone(),
782            debugloc: DebugLoc::from_llvm_with_col(term),
783            // metadata: InstructionMetadata::from_llvm_inst(term),
784        }
785    }
786}
787
788impl IndirectBr {
789    pub(crate) fn from_llvm_ref(
790        term: LLVMValueRef,
791        ctx: &mut ModuleContext,
792        func_ctx: &mut FunctionContext,
793    ) -> Self {
794        Self {
795            operand: Operand::from_llvm_ref(unsafe { LLVMGetOperand(term, 0) }, ctx, func_ctx),
796            possible_dests: {
797                let num_dests = unsafe { LLVMGetNumSuccessors(term) };
798                (0 .. num_dests)
799                    .map(|i| {
800                        func_ctx
801                            .bb_names
802                            .get(unsafe { &LLVMGetSuccessor(term, i) })
803                            .expect("Failed to find indirect branch destination in map")
804                            .clone()
805                    })
806                    .collect()
807            },
808            debugloc: DebugLoc::from_llvm_with_col(term),
809            // metadata: InstructionMetadata::from_llvm_inst(term),
810        }
811    }
812}
813
814impl Invoke {
815    pub(crate) fn from_llvm_ref(
816        term: LLVMValueRef,
817        ctx: &mut ModuleContext,
818        func_ctx: &mut FunctionContext,
819    ) -> Self {
820        use crate::instruction::CallInfo;
821        let callinfo = CallInfo::from_llvm_ref(term, ctx, func_ctx);
822        Self {
823            function: callinfo.function,
824            #[cfg(feature = "llvm-15-or-greater")]
825            function_ty: callinfo.function_ty,
826            arguments: callinfo.arguments,
827            return_attributes: callinfo.return_attributes,
828            result: Name::name_or_num(unsafe { get_value_name(term) }, &mut func_ctx.ctr),
829            return_label: func_ctx
830                .bb_names
831                .get(unsafe { &LLVMGetNormalDest(term) })
832                .expect("Failed to find invoke return destination in map")
833                .clone(),
834            exception_label: func_ctx
835                .bb_names
836                .get(unsafe { &LLVMGetUnwindDest(term) })
837                .expect("Failed to find invoke exception destination in map")
838                .clone(),
839            function_attributes: callinfo.function_attributes,
840            calling_convention: callinfo.calling_convention,
841            debugloc: DebugLoc::from_llvm_with_col(term),
842            // metadata: InstructionMetadata::from_llvm_inst(term),
843        }
844    }
845}
846
847impl Resume {
848    pub(crate) fn from_llvm_ref(
849        term: LLVMValueRef,
850        ctx: &mut ModuleContext,
851        func_ctx: &mut FunctionContext,
852    ) -> Self {
853        assert_eq!(unsafe { LLVMGetNumOperands(term) }, 1);
854        Self {
855            operand: Operand::from_llvm_ref(unsafe { LLVMGetOperand(term, 0) }, ctx, func_ctx),
856            debugloc: DebugLoc::from_llvm_with_col(term),
857            // metadata: InstructionMetadata::from_llvm_inst(term),
858        }
859    }
860}
861
862impl Unreachable {
863    pub(crate) fn from_llvm_ref(term: LLVMValueRef) -> Self {
864        assert_eq!(unsafe { LLVMGetNumOperands(term) }, 0);
865        Self {
866            debugloc: DebugLoc::from_llvm_with_col(term),
867            // metadata: InstructionMetadata::from_llvm_inst(term),
868        }
869    }
870}
871
872impl CleanupRet {
873    pub(crate) fn from_llvm_ref(
874        term: LLVMValueRef,
875        ctx: &mut ModuleContext,
876        func_ctx: &mut FunctionContext,
877    ) -> Self {
878        assert_eq!(unsafe { LLVMGetNumOperands(term) }, 1);
879        Self {
880            cleanup_pad: Operand::from_llvm_ref(unsafe { LLVMGetOperand(term, 0) }, ctx, func_ctx),
881            unwind_dest: {
882                let dest = unsafe { LLVMGetUnwindDest(term) };
883                if dest.is_null() {
884                    None
885                } else {
886                    Some(
887                        func_ctx
888                            .bb_names
889                            .get(&dest)
890                            .unwrap_or_else(|| {
891                                let names: Vec<_> = func_ctx.bb_names.values().collect();
892                                panic!(
893                                    "Failed to find unwind destination in map; have names {:?}",
894                                    names
895                                )
896                            })
897                            .clone(),
898                    )
899                }
900            },
901            debugloc: DebugLoc::from_llvm_with_col(term),
902            // metadata: InstructionMetadata::from_llvm_inst(term),
903        }
904    }
905}
906
907impl CatchRet {
908    pub(crate) fn from_llvm_ref(
909        term: LLVMValueRef,
910        ctx: &mut ModuleContext,
911        func_ctx: &mut FunctionContext,
912    ) -> Self {
913        Self {
914            catch_pad: Operand::from_llvm_ref(unsafe { LLVMGetOperand(term, 0) }, ctx, func_ctx),
915            successor: func_ctx
916                .bb_names
917                .get(unsafe { &LLVMGetSuccessor(term, 0) })
918                .expect("Failed to find CatchRet successor in map")
919                .clone(),
920            debugloc: DebugLoc::from_llvm_with_col(term),
921            // metadata: InstructionMetadata::from_llvm_inst(term),
922        }
923    }
924}
925
926impl CatchSwitch {
927    pub(crate) fn from_llvm_ref(
928        term: LLVMValueRef,
929        ctx: &mut ModuleContext,
930        func_ctx: &mut FunctionContext,
931    ) -> Self {
932        Self {
933            parent_pad: Operand::from_llvm_ref(unsafe { LLVMGetOperand(term, 0) }, ctx, func_ctx),
934            catch_handlers: {
935                let num_handlers = unsafe { LLVMGetNumHandlers(term) };
936                let mut handlers: Vec<LLVMBasicBlockRef> =
937                    Vec::with_capacity(num_handlers as usize);
938                unsafe {
939                    LLVMGetHandlers(term, handlers.as_mut_ptr());
940                    handlers.set_len(num_handlers as usize);
941                };
942                handlers
943                    .into_iter()
944                    .map(|h| {
945                        func_ctx
946                            .bb_names
947                            .get(&h)
948                            .expect("Failed to find catch handler in map")
949                            .clone()
950                    })
951                    .collect()
952            },
953            default_unwind_dest: {
954                let dest = unsafe { LLVMGetUnwindDest(term) };
955                if dest.is_null() {
956                    None
957                } else {
958                    Some(func_ctx.bb_names.get(&dest)
959                        .unwrap_or_else(|| { let names: Vec<_> = func_ctx.bb_names.values().collect(); panic!("Failed to find CatchSwitch default unwind destination in map; have names {:?}", names) })
960                        .clone()
961                    )
962                }
963            },
964            result: Name::name_or_num(unsafe { get_value_name(term) }, &mut func_ctx.ctr),
965            debugloc: DebugLoc::from_llvm_with_col(term),
966            // metadata: InstructionMetadata::from_llvm_inst(term),
967        }
968    }
969}
970
971impl CallBr {
972    pub(crate) fn from_llvm_ref(
973        term: LLVMValueRef,
974        ctx: &mut ModuleContext,
975        func_ctx: &mut FunctionContext,
976    ) -> Self {
977        use crate::instruction::CallInfo;
978        let callinfo = CallInfo::from_llvm_ref(term, ctx, func_ctx);
979        Self {
980            function: callinfo.function,
981            arguments: callinfo.arguments,
982            return_attributes: callinfo.return_attributes,
983            result: Name::name_or_num(unsafe { get_value_name(term) }, &mut func_ctx.ctr),
984            return_label: func_ctx
985                .bb_names
986                .get(unsafe { &LLVMGetNormalDest(term) })
987                .expect("Failed to find invoke return destination in map")
988                .clone(),
989            other_labels: (),
990            function_attributes: callinfo.function_attributes,
991            calling_convention: callinfo.calling_convention,
992            debugloc: DebugLoc::from_llvm_with_col(term),
993            // metadata: InstructionMetadata::from_llvm_inst(term),
994        }
995    }
996}