1 //! The `Encoding` struct.
2 
3 use crate::binemit::CodeOffset;
4 use crate::ir::{Function, Inst};
5 use crate::isa::constraints::{BranchRange, RecipeConstraints};
6 use crate::regalloc::RegDiversions;
7 use core::fmt;
8 
9 #[cfg(feature = "enable-serde")]
10 use serde::{Deserialize, Serialize};
11 
12 /// Bits needed to encode an instruction as binary machine code.
13 ///
14 /// The encoding consists of two parts, both specific to the target ISA: An encoding *recipe*, and
15 /// encoding *bits*. The recipe determines the native instruction format and the mapping of
16 /// operands to encoded bits. The encoding bits provide additional information to the recipe,
17 /// typically parts of the opcode.
18 #[derive(Clone, Copy, Debug, PartialEq, Eq)]
19 #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
20 pub struct Encoding {
21     recipe: u16,
22     bits: u16,
23 }
24 
25 impl Encoding {
26     /// Create a new `Encoding` containing `(recipe, bits)`.
new(recipe: u16, bits: u16) -> Self27     pub fn new(recipe: u16, bits: u16) -> Self {
28         Self { recipe, bits }
29     }
30 
31     /// Get the recipe number in this encoding.
recipe(self) -> usize32     pub fn recipe(self) -> usize {
33         self.recipe as usize
34     }
35 
36     /// Get the recipe-specific encoding bits.
bits(self) -> u1637     pub fn bits(self) -> u16 {
38         self.bits
39     }
40 
41     /// Is this a legal encoding, or the default placeholder?
is_legal(self) -> bool42     pub fn is_legal(self) -> bool {
43         self != Self::default()
44     }
45 }
46 
47 /// The default encoding is the illegal one.
48 impl Default for Encoding {
default() -> Self49     fn default() -> Self {
50         Self::new(0xffff, 0xffff)
51     }
52 }
53 
54 /// ISA-independent display of an encoding.
55 impl fmt::Display for Encoding {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result56     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
57         if self.is_legal() {
58             write!(f, "{}#{:02x}", self.recipe, self.bits)
59         } else {
60             write!(f, "-")
61         }
62     }
63 }
64 
65 /// Temporary object that holds enough context to properly display an encoding.
66 /// This is meant to be created by `EncInfo::display()`.
67 pub struct DisplayEncoding {
68     pub encoding: Encoding,
69     pub recipe_names: &'static [&'static str],
70 }
71 
72 impl fmt::Display for DisplayEncoding {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result73     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
74         if self.encoding.is_legal() {
75             write!(
76                 f,
77                 "{}#{:02x}",
78                 self.recipe_names[self.encoding.recipe()],
79                 self.encoding.bits
80             )
81         } else {
82             write!(f, "-")
83         }
84     }
85 }
86 
87 type SizeCalculatorFn = fn(&RecipeSizing, Encoding, Inst, &RegDiversions, &Function) -> u8;
88 
89 /// Returns the base size of the Recipe, assuming it's fixed. This is the default for most
90 /// encodings; others can be variable and longer than this base size, depending on the registers
91 /// they're using and use a different function, specific per platform.
base_size( sizing: &RecipeSizing, _: Encoding, _: Inst, _: &RegDiversions, _: &Function, ) -> u892 pub fn base_size(
93     sizing: &RecipeSizing,
94     _: Encoding,
95     _: Inst,
96     _: &RegDiversions,
97     _: &Function,
98 ) -> u8 {
99     sizing.base_size
100 }
101 
102 /// Code size information for an encoding recipe.
103 ///
104 /// Encoding recipes may have runtime-determined instruction size.
105 pub struct RecipeSizing {
106     /// Minimum size in bytes of instructions encoded with this recipe.
107     pub base_size: u8,
108 
109     /// Method computing the instruction's real size, given inputs and outputs.
110     pub compute_size: SizeCalculatorFn,
111 
112     /// Allowed branch range in this recipe, if any.
113     ///
114     /// All encoding recipes for branches have exact branch range information.
115     pub branch_range: Option<BranchRange>,
116 }
117 
118 /// Information about all the encodings in this ISA.
119 #[derive(Clone)]
120 pub struct EncInfo {
121     /// Constraints on value operands per recipe.
122     pub constraints: &'static [RecipeConstraints],
123 
124     /// Code size information per recipe.
125     pub sizing: &'static [RecipeSizing],
126 
127     /// Names of encoding recipes.
128     pub names: &'static [&'static str],
129 }
130 
131 impl EncInfo {
132     /// Get the value operand constraints for `enc` if it is a legal encoding.
operand_constraints(&self, enc: Encoding) -> Option<&'static RecipeConstraints>133     pub fn operand_constraints(&self, enc: Encoding) -> Option<&'static RecipeConstraints> {
134         self.constraints.get(enc.recipe())
135     }
136 
137     /// Create an object that can display an ISA-dependent encoding properly.
display(&self, enc: Encoding) -> DisplayEncoding138     pub fn display(&self, enc: Encoding) -> DisplayEncoding {
139         DisplayEncoding {
140             encoding: enc,
141             recipe_names: self.names,
142         }
143     }
144 
145     /// Get the size in bytes of `inst`, if it were encoded with `enc`.
146     ///
147     /// Returns 0 for illegal encodings.
byte_size( &self, enc: Encoding, inst: Inst, divert: &RegDiversions, func: &Function, ) -> CodeOffset148     pub fn byte_size(
149         &self,
150         enc: Encoding,
151         inst: Inst,
152         divert: &RegDiversions,
153         func: &Function,
154     ) -> CodeOffset {
155         self.sizing.get(enc.recipe()).map_or(0, |s| {
156             let compute_size = s.compute_size;
157             CodeOffset::from(compute_size(&s, enc, inst, divert, func))
158         })
159     }
160 
161     /// Get the branch range that is supported by `enc`, if any.
162     ///
163     /// This will never return `None` for a legal branch encoding.
branch_range(&self, enc: Encoding) -> Option<BranchRange>164     pub fn branch_range(&self, enc: Encoding) -> Option<BranchRange> {
165         self.sizing.get(enc.recipe()).and_then(|s| s.branch_range)
166     }
167 }
168