1 //! X86_64-bit Instruction Set Architecture.
2 
3 pub use self::inst::{AtomicRmwSeqOp, EmitInfo, EmitState, Inst, args, external};
4 
5 use super::{OwnedTargetIsa, TargetIsa};
6 use crate::dominator_tree::DominatorTree;
7 use crate::ir::{self, Function, Type, types};
8 #[cfg(feature = "unwind")]
9 use crate::isa::unwind::systemv;
10 use crate::isa::x64::settings as x64_settings;
11 use crate::isa::{Builder as IsaBuilder, FunctionAlignment, IsaFlagsHashKey};
12 use crate::machinst::{
13     CompiledCodeStencil, MachInst, MachTextSectionBuilder, Reg, SigSet, TextSectionBuilder, VCode,
14     compile,
15 };
16 use crate::result::{CodegenError, CodegenResult};
17 use crate::settings::{self as shared_settings, Flags};
18 use crate::{Final, MachBufferFinalized};
19 use alloc::string::String;
20 use alloc::{borrow::ToOwned, boxed::Box, vec::Vec};
21 use core::fmt;
22 use cranelift_control::ControlPlane;
23 use target_lexicon::Triple;
24 
25 mod abi;
26 mod inst;
27 mod lower;
28 mod pcc;
29 pub mod settings;
30 
31 #[cfg(feature = "unwind")]
32 pub use inst::unwind::systemv::create_cie;
33 
34 /// An X64 backend.
35 pub(crate) struct X64Backend {
36     triple: Triple,
37     flags: Flags,
38     x64_flags: x64_settings::Flags,
39 }
40 
41 impl X64Backend {
42     /// Create a new X64 backend with the given (shared) flags.
43     fn new_with_flags(
44         triple: Triple,
45         flags: Flags,
46         x64_flags: x64_settings::Flags,
47     ) -> CodegenResult<Self> {
48         if triple.pointer_width().unwrap() != target_lexicon::PointerWidth::U64 {
49             return Err(CodegenError::Unsupported(
50                 "the x32 ABI is not supported".to_owned(),
51             ));
52         }
53 
54         Ok(Self {
55             triple,
56             flags,
57             x64_flags,
58         })
59     }
60 
61     fn compile_vcode(
62         &self,
63         func: &Function,
64         domtree: &DominatorTree,
65         ctrl_plane: &mut ControlPlane,
66     ) -> CodegenResult<(VCode<inst::Inst>, regalloc2::Output)> {
67         // This performs lowering to VCode, register-allocates the code, computes
68         // block layout and finalizes branches. The result is ready for binary emission.
69         let emit_info = EmitInfo::new(self.flags.clone(), self.x64_flags.clone());
70         let sigs = SigSet::new::<abi::X64ABIMachineSpec>(func, &self.flags)?;
71         let abi = abi::X64Callee::new(func, self, &self.x64_flags, &sigs)?;
72         compile::compile::<Self>(func, domtree, self, abi, emit_info, sigs, ctrl_plane)
73     }
74 }
75 
76 impl TargetIsa for X64Backend {
77     fn compile_function(
78         &self,
79         func: &Function,
80         domtree: &DominatorTree,
81         want_disasm: bool,
82         ctrl_plane: &mut ControlPlane,
83     ) -> CodegenResult<CompiledCodeStencil> {
84         let (vcode, regalloc_result) = self.compile_vcode(func, domtree, ctrl_plane)?;
85 
86         let emit_result = vcode.emit(&regalloc_result, want_disasm, &self.flags, ctrl_plane);
87         let value_labels_ranges = emit_result.value_labels_ranges;
88         let buffer = emit_result.buffer;
89 
90         if let Some(disasm) = emit_result.disasm.as_ref() {
91             crate::trace!("disassembly:\n{}", disasm);
92         }
93 
94         Ok(CompiledCodeStencil {
95             buffer,
96             vcode: emit_result.disasm,
97             value_labels_ranges,
98             bb_starts: emit_result.bb_offsets,
99             bb_edges: emit_result.bb_edges,
100         })
101     }
102 
103     fn flags(&self) -> &Flags {
104         &self.flags
105     }
106 
107     fn isa_flags(&self) -> Vec<shared_settings::Value> {
108         self.x64_flags.iter().collect()
109     }
110 
111     fn isa_flags_hash_key(&self) -> IsaFlagsHashKey<'_> {
112         IsaFlagsHashKey(self.x64_flags.hash_key())
113     }
114 
115     fn dynamic_vector_bytes(&self, _dyn_ty: Type) -> u32 {
116         16
117     }
118 
119     fn name(&self) -> &'static str {
120         "x64"
121     }
122 
123     fn triple(&self) -> &Triple {
124         &self.triple
125     }
126 
127     #[cfg(feature = "unwind")]
128     fn emit_unwind_info(
129         &self,
130         result: &crate::machinst::CompiledCode,
131         kind: crate::isa::unwind::UnwindInfoKind,
132     ) -> CodegenResult<Option<crate::isa::unwind::UnwindInfo>> {
133         emit_unwind_info(&result.buffer, kind)
134     }
135 
136     #[cfg(feature = "unwind")]
137     fn create_systemv_cie(&self) -> Option<gimli::write::CommonInformationEntry> {
138         Some(inst::unwind::systemv::create_cie())
139     }
140 
141     #[cfg(feature = "unwind")]
142     fn map_regalloc_reg_to_dwarf(&self, reg: Reg) -> Result<u16, systemv::RegisterMappingError> {
143         inst::unwind::systemv::map_reg(reg).map(|reg| reg.0)
144     }
145 
146     fn text_section_builder(&self, num_funcs: usize) -> Box<dyn TextSectionBuilder> {
147         Box::new(MachTextSectionBuilder::<inst::Inst>::new(num_funcs))
148     }
149 
150     fn function_alignment(&self) -> FunctionAlignment {
151         Inst::function_alignment()
152     }
153 
154     fn page_size_align_log2(&self) -> u8 {
155         debug_assert_eq!(1 << 12, 0x1000);
156         12
157     }
158 
159     #[cfg(feature = "disas")]
160     fn to_capstone(&self) -> Result<capstone::Capstone, capstone::Error> {
161         use capstone::prelude::*;
162         Capstone::new()
163             .x86()
164             .mode(arch::x86::ArchMode::Mode64)
165             .syntax(arch::x86::ArchSyntax::Att)
166             .detail(true)
167             .build()
168     }
169 
170     fn pretty_print_reg(&self, reg: Reg, size: u8) -> String {
171         inst::regs::pretty_print_reg(reg, size)
172     }
173 
174     fn has_native_fma(&self) -> bool {
175         self.x64_flags.has_avx() && self.x64_flags.has_fma()
176     }
177 
178     fn has_round(&self) -> bool {
179         self.x64_flags.has_sse41()
180     }
181 
182     fn has_blendv_lowering(&self, ty: Type) -> bool {
183         // The `blendvpd`, `blendvps`, and `pblendvb` instructions are all only
184         // available from SSE 4.1 and onwards. Otherwise the i16x8 type has no
185         // equivalent instruction which only looks at the top bit for a select
186         // operation, so that always returns `false`
187         self.x64_flags.has_sse41() && ty != types::I16X8
188     }
189 
190     fn has_x86_pshufb_lowering(&self) -> bool {
191         self.x64_flags.has_ssse3()
192     }
193 
194     fn has_x86_pmulhrsw_lowering(&self) -> bool {
195         self.x64_flags.has_ssse3()
196     }
197 
198     fn has_x86_pmaddubsw_lowering(&self) -> bool {
199         self.x64_flags.has_ssse3()
200     }
201 
202     fn default_argument_extension(&self) -> ir::ArgumentExtension {
203         // This is copied/carried over from a historical piece of code in
204         // Wasmtime:
205         //
206         // https://github.com/bytecodealliance/wasmtime/blob/a018a5a9addb77d5998021a0150192aa955c71bf/crates/cranelift/src/lib.rs#L366-L374
207         //
208         // Whether or not it is still applicable here is unsure, but it's left
209         // the same as-is for now to reduce the likelihood of problems arising.
210         ir::ArgumentExtension::Uext
211     }
212 }
213 
214 /// Emit unwind info for an x86 target.
215 pub fn emit_unwind_info(
216     buffer: &MachBufferFinalized<Final>,
217     kind: crate::isa::unwind::UnwindInfoKind,
218 ) -> CodegenResult<Option<crate::isa::unwind::UnwindInfo>> {
219     #[cfg(feature = "unwind")]
220     use crate::isa::unwind::{UnwindInfo, UnwindInfoKind};
221     #[cfg(not(feature = "unwind"))]
222     let _ = buffer;
223     Ok(match kind {
224         #[cfg(feature = "unwind")]
225         UnwindInfoKind::SystemV => {
226             let mapper = self::inst::unwind::systemv::RegisterMapper;
227             Some(UnwindInfo::SystemV(
228                 crate::isa::unwind::systemv::create_unwind_info_from_insts(
229                     &buffer.unwind_info[..],
230                     buffer.data().len(),
231                     &mapper,
232                 )?,
233             ))
234         }
235         #[cfg(feature = "unwind")]
236         UnwindInfoKind::Windows => Some(UnwindInfo::WindowsX64(
237             crate::isa::unwind::winx64::create_unwind_info_from_insts::<
238                 self::inst::unwind::winx64::RegisterMapper,
239             >(&buffer.unwind_info[..])?,
240         )),
241         _ => None,
242     })
243 }
244 
245 impl fmt::Display for X64Backend {
246     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
247         f.debug_struct("MachBackend")
248             .field("name", &self.name())
249             .field("triple", &self.triple())
250             .field("flags", &format!("{}", self.flags()))
251             .finish()
252     }
253 }
254 
255 /// Create a new `isa::Builder`.
256 pub(crate) fn isa_builder(triple: Triple) -> IsaBuilder {
257     IsaBuilder {
258         triple,
259         setup: x64_settings::builder(),
260         constructor: isa_constructor,
261     }
262 }
263 
264 fn isa_constructor(
265     triple: Triple,
266     shared_flags: Flags,
267     builder: &shared_settings::Builder,
268 ) -> CodegenResult<OwnedTargetIsa> {
269     let isa_flags = x64_settings::Flags::new(&shared_flags, builder);
270     let backend = X64Backend::new_with_flags(triple, shared_flags, isa_flags)?;
271     Ok(backend.wrapped())
272 }
273