Skip to main content

cranelift_codegen/isa/x64/
mod.rs

1//! X86_64-bit Instruction Set Architecture.
2
3pub use self::inst::{AtomicRmwSeqOp, EmitInfo, EmitState, Inst, args, external};
4
5use super::{OwnedTargetIsa, TargetIsa};
6use crate::dominator_tree::DominatorTree;
7use crate::ir::{self, Function, Type, types};
8#[cfg(feature = "unwind")]
9use crate::isa::unwind::systemv;
10use crate::isa::x64::settings as x64_settings;
11use crate::isa::{Builder as IsaBuilder, FunctionAlignment, IsaFlagsHashKey};
12use crate::machinst::{
13    CompiledCodeStencil, MachInst, MachTextSectionBuilder, Reg, SigSet, TextSectionBuilder, VCode,
14    compile,
15};
16use crate::result::{CodegenError, CodegenResult};
17use crate::settings::{self as shared_settings, Flags};
18use crate::{Final, MachBufferFinalized};
19use alloc::string::String;
20use alloc::{borrow::ToOwned, boxed::Box, vec::Vec};
21use core::fmt;
22use cranelift_control::ControlPlane;
23use target_lexicon::Triple;
24
25mod abi;
26mod inst;
27mod lower;
28mod pcc;
29pub mod settings;
30
31#[cfg(feature = "unwind")]
32pub use inst::unwind::systemv::create_cie;
33
34/// An X64 backend.
35pub(crate) struct X64Backend {
36    triple: Triple,
37    flags: Flags,
38    x64_flags: x64_settings::Flags,
39}
40
41impl 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
76impl 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_x86_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.
215pub 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
245impl 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`.
256pub(crate) fn isa_builder(triple: Triple) -> IsaBuilder {
257    IsaBuilder {
258        triple,
259        setup: x64_settings::builder(),
260        constructor: isa_constructor,
261    }
262}
263
264fn 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}