juicebox_asm/
label.rs

1// SPDX-License-Identifier: MIT
2//
3// Copyright (c) 2023, Johannes Stoelp <dev@memzero.de>
4
5//! Definition of the lable type which can be used as jump target and can be bound to a location in
6//! the emitted code.
7
8use std::collections::HashSet;
9
10/// A label which is used as target for jump instructions.
11///
12/// ```rust
13/// use juicebox_asm::{Asm, Label, Reg64};
14/// use juicebox_asm::insn::{Mov, Jmp};
15///
16/// let mut lbl = Label::new();
17/// let mut asm = Asm::new();
18///
19/// // Skip the mov instruction.
20/// asm.jmp(&mut lbl);
21/// asm.mov(Reg64::rax, Reg64::rax);
22/// asm.bind(&mut lbl);
23/// ```
24///
25/// # Panics
26///
27/// Panics if the label is dropped while not yet bound, or having unresolved relocations.
28/// This is mainly a safety-guard to detect wrong usage.
29pub struct Label {
30    /// Location of the label. Will be set after the label is bound, else None.
31    location: Option<usize>,
32
33    /// Offsets that must be patched with the label location.
34    offsets: HashSet<usize>,
35}
36
37impl Label {
38    /// Create a new `unbound` [Label].
39    pub fn new() -> Label {
40        Label {
41            location: None,
42            offsets: HashSet::new(),
43        }
44    }
45
46    /// Bind the label to the `location`, can only be bound once.
47    ///
48    /// # Panics
49    ///
50    /// Panics if the lable is already bound.
51    pub(crate) fn bind(&mut self, loc: usize) {
52        // A label can only be bound once!
53        assert!(!self.is_bound());
54
55        self.location = Some(loc);
56    }
57
58    /// Record an offset that must be patched with the label location.
59    pub(crate) fn record_offset(&mut self, off: usize) {
60        self.offsets.insert(off);
61    }
62
63    /// Get the location of the lable if already bound, `None` else.
64    pub(crate) fn location(&self) -> Option<usize> {
65        self.location
66    }
67
68    /// Get the offsets which refer to the label. These are used to patch the jump instructions to
69    /// the label location.
70    pub(crate) fn offsets_mut(&mut self) -> &mut HashSet<usize> {
71        &mut self.offsets
72    }
73
74    /// Check whether the label is bound to a location.
75    const fn is_bound(&self) -> bool {
76        self.location.is_some()
77    }
78}
79
80impl Drop for Label {
81    fn drop(&mut self) {
82        // Ensure the label was bound when it is dropped.
83        assert!(self.is_bound());
84        // Ensure all offsets have been patched when the label is dropped.
85        assert!(self.offsets.is_empty());
86    }
87}