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