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}