1 //! Binary machine code emission.
2 //!
3 //! The `binemit` module contains code for translating Cranelift's intermediate representation into
4 //! binary machine code.
5 
6 mod memorysink;
7 mod relaxation;
8 mod shrink;
9 mod stack_map;
10 
11 pub use self::memorysink::{
12     MemoryCodeSink, NullRelocSink, NullStackMapSink, NullTrapSink, RelocSink, StackMapSink,
13     TrapSink,
14 };
15 pub use self::relaxation::relax_branches;
16 pub use self::shrink::shrink_instructions;
17 pub use self::stack_map::StackMap;
18 use crate::ir::entities::Value;
19 use crate::ir::{
20     ConstantOffset, ExternalName, Function, Inst, JumpTable, Opcode, SourceLoc, TrapCode,
21 };
22 use crate::isa::TargetIsa;
23 pub use crate::regalloc::RegDiversions;
24 use core::fmt;
25 #[cfg(feature = "enable-serde")]
26 use serde::{Deserialize, Serialize};
27 
28 /// Offset in bytes from the beginning of the function.
29 ///
30 /// Cranelift can be used as a cross compiler, so we don't want to use a type like `usize` which
31 /// depends on the *host* platform, not the *target* platform.
32 pub type CodeOffset = u32;
33 
34 /// Addend to add to the symbol value.
35 pub type Addend = i64;
36 
37 /// Relocation kinds for every ISA
38 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
39 #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
40 pub enum Reloc {
41     /// absolute 4-byte
42     Abs4,
43     /// absolute 8-byte
44     Abs8,
45     /// x86 PC-relative 4-byte
46     X86PCRel4,
47     /// x86 PC-relative 4-byte offset to trailing rodata
48     X86PCRelRodata4,
49     /// x86 call to PC-relative 4-byte
50     X86CallPCRel4,
51     /// x86 call to PLT-relative 4-byte
52     X86CallPLTRel4,
53     /// x86 GOT PC-relative 4-byte
54     X86GOTPCRel4,
55     /// Arm32 call target
56     Arm32Call,
57     /// Arm64 call target. Encoded as bottom 26 bits of instruction. This
58     /// value is sign-extended, multiplied by 4, and added to the PC of
59     /// the call instruction to form the destination address.
60     Arm64Call,
61     /// RISC-V call target
62     RiscvCall,
63     /// s390x PC-relative 4-byte offset
64     S390xPCRel32Dbl,
65 
66     /// Elf x86_64 32 bit signed PC relative offset to two GOT entries for GD symbol.
67     ElfX86_64TlsGd,
68 
69     /// Mach-O x86_64 32 bit signed PC relative offset to a `__thread_vars` entry.
70     MachOX86_64Tlv,
71 }
72 
73 impl fmt::Display for Reloc {
74     /// Display trait implementation drops the arch, since its used in contexts where the arch is
75     /// already unambiguous, e.g. clif syntax with isa specified. In other contexts, use Debug.
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result76     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
77         match *self {
78             Self::Abs4 => write!(f, "Abs4"),
79             Self::Abs8 => write!(f, "Abs8"),
80             Self::S390xPCRel32Dbl => write!(f, "PCRel32Dbl"),
81             Self::X86PCRel4 => write!(f, "PCRel4"),
82             Self::X86PCRelRodata4 => write!(f, "PCRelRodata4"),
83             Self::X86CallPCRel4 => write!(f, "CallPCRel4"),
84             Self::X86CallPLTRel4 => write!(f, "CallPLTRel4"),
85             Self::X86GOTPCRel4 => write!(f, "GOTPCRel4"),
86             Self::Arm32Call | Self::Arm64Call | Self::RiscvCall => write!(f, "Call"),
87 
88             Self::ElfX86_64TlsGd => write!(f, "ElfX86_64TlsGd"),
89             Self::MachOX86_64Tlv => write!(f, "MachOX86_64Tlv"),
90         }
91     }
92 }
93 
94 /// Container for information about a vector of compiled code and its supporting read-only data.
95 ///
96 /// The code starts at offset 0 and is followed optionally by relocatable jump tables and copyable
97 /// (raw binary) read-only data.  Any padding between sections is always part of the section that
98 /// precedes the boundary between the sections.
99 #[derive(PartialEq)]
100 pub struct CodeInfo {
101     /// Number of bytes of machine code (the code starts at offset 0).
102     pub code_size: CodeOffset,
103 
104     /// Number of bytes of jumptables.
105     pub jumptables_size: CodeOffset,
106 
107     /// Number of bytes of rodata.
108     pub rodata_size: CodeOffset,
109 
110     /// Number of bytes in total.
111     pub total_size: CodeOffset,
112 }
113 
114 impl CodeInfo {
115     /// Offset of any relocatable jump tables, or equal to rodata if there are no jump tables.
jumptables(&self) -> CodeOffset116     pub fn jumptables(&self) -> CodeOffset {
117         self.code_size
118     }
119 
120     /// Offset of any copyable read-only data, or equal to total_size if there are no rodata.
rodata(&self) -> CodeOffset121     pub fn rodata(&self) -> CodeOffset {
122         self.code_size + self.jumptables_size
123     }
124 }
125 
126 /// Abstract interface for adding bytes to the code segment.
127 ///
128 /// A `CodeSink` will receive all of the machine code for a function. It also accepts relocations
129 /// which are locations in the code section that need to be fixed up when linking.
130 pub trait CodeSink {
131     /// Get the current position.
offset(&self) -> CodeOffset132     fn offset(&self) -> CodeOffset;
133 
134     /// Add 1 byte to the code section.
put1(&mut self, _: u8)135     fn put1(&mut self, _: u8);
136 
137     /// Add 2 bytes to the code section.
put2(&mut self, _: u16)138     fn put2(&mut self, _: u16);
139 
140     /// Add 4 bytes to the code section.
put4(&mut self, _: u32)141     fn put4(&mut self, _: u32);
142 
143     /// Add 8 bytes to the code section.
put8(&mut self, _: u64)144     fn put8(&mut self, _: u64);
145 
146     /// Add a relocation referencing an external symbol plus the addend at the current offset.
reloc_external(&mut self, _: SourceLoc, _: Reloc, _: &ExternalName, _: Addend)147     fn reloc_external(&mut self, _: SourceLoc, _: Reloc, _: &ExternalName, _: Addend);
148 
149     /// Add a relocation referencing a constant.
reloc_constant(&mut self, _: Reloc, _: ConstantOffset)150     fn reloc_constant(&mut self, _: Reloc, _: ConstantOffset);
151 
152     /// Add a relocation referencing a jump table.
reloc_jt(&mut self, _: Reloc, _: JumpTable)153     fn reloc_jt(&mut self, _: Reloc, _: JumpTable);
154 
155     /// Add trap information for the current offset.
trap(&mut self, _: TrapCode, _: SourceLoc)156     fn trap(&mut self, _: TrapCode, _: SourceLoc);
157 
158     /// Machine code output is complete, jump table data may follow.
begin_jumptables(&mut self)159     fn begin_jumptables(&mut self);
160 
161     /// Jump table output is complete, raw read-only data may follow.
begin_rodata(&mut self)162     fn begin_rodata(&mut self);
163 
164     /// Read-only data output is complete, we're done.
end_codegen(&mut self)165     fn end_codegen(&mut self);
166 
167     /// Add a stack map at the current code offset.
add_stack_map(&mut self, _: &[Value], _: &Function, _: &dyn TargetIsa)168     fn add_stack_map(&mut self, _: &[Value], _: &Function, _: &dyn TargetIsa);
169 
170     /// Add a call site for a call with the given opcode, returning at the current offset.
add_call_site(&mut self, _: Opcode, _: SourceLoc)171     fn add_call_site(&mut self, _: Opcode, _: SourceLoc) {
172         // Default implementation doesn't need to do anything.
173     }
174 }
175 
176 /// Report a bad encoding error.
177 #[cold]
bad_encoding(func: &Function, inst: Inst) -> !178 pub fn bad_encoding(func: &Function, inst: Inst) -> ! {
179     panic!(
180         "Bad encoding {} for {}",
181         func.encodings[inst],
182         func.dfg.display_inst(inst, None)
183     );
184 }
185 
186 /// Emit a function to `sink`, given an instruction emitter function.
187 ///
188 /// This function is called from the `TargetIsa::emit_function()` implementations with the
189 /// appropriate instruction emitter.
emit_function<CS, EI>(func: &Function, emit_inst: EI, sink: &mut CS, isa: &dyn TargetIsa) where CS: CodeSink, EI: Fn(&Function, Inst, &mut RegDiversions, &mut CS, &dyn TargetIsa),190 pub fn emit_function<CS, EI>(func: &Function, emit_inst: EI, sink: &mut CS, isa: &dyn TargetIsa)
191 where
192     CS: CodeSink,
193     EI: Fn(&Function, Inst, &mut RegDiversions, &mut CS, &dyn TargetIsa),
194 {
195     let mut divert = RegDiversions::new();
196     for block in func.layout.blocks() {
197         divert.at_block(&func.entry_diversions, block);
198         debug_assert_eq!(func.offsets[block], sink.offset());
199         for inst in func.layout.block_insts(block) {
200             emit_inst(func, inst, &mut divert, sink, isa);
201         }
202     }
203 
204     sink.begin_jumptables();
205 
206     // Output jump tables.
207     for (jt, jt_data) in func.jump_tables.iter() {
208         let jt_offset = func.jt_offsets[jt];
209         for block in jt_data.iter() {
210             let rel_offset: i32 = func.offsets[*block] as i32 - jt_offset as i32;
211             sink.put4(rel_offset as u32)
212         }
213     }
214 
215     sink.begin_rodata();
216 
217     // Output constants.
218     for (_, constant_data) in func.dfg.constants.iter() {
219         for byte in constant_data.iter() {
220             sink.put1(*byte)
221         }
222     }
223 
224     sink.end_codegen();
225 }
226