cranelift_codegen/isa/x64/
mod.rs

1//! X86_64-bit Instruction Set Architecture.
2
3pub use self::inst::{args, CallInfo, EmitInfo, EmitState, Inst};
4
5use super::{OwnedTargetIsa, TargetIsa};
6use crate::dominator_tree::DominatorTree;
7use crate::ir::{condcodes::IntCC, Function, Type};
8#[cfg(feature = "unwind")]
9use crate::isa::unwind::systemv;
10use crate::isa::x64::{inst::regs::create_reg_env_systemv, settings as x64_settings};
11use crate::isa::Builder as IsaBuilder;
12use crate::machinst::{
13    compile, CompiledCode, CompiledCodeStencil, MachTextSectionBuilder, Reg, SigSet,
14    TextSectionBuilder, VCode,
15};
16use crate::result::{CodegenError, CodegenResult};
17use crate::settings::{self as shared_settings, Flags};
18use alloc::{boxed::Box, vec::Vec};
19use core::fmt;
20use regalloc2::MachineEnv;
21use target_lexicon::Triple;
22
23mod abi;
24pub mod encoding;
25mod inst;
26mod lower;
27pub mod settings;
28
29/// An X64 backend.
30pub(crate) struct X64Backend {
31    triple: Triple,
32    flags: Flags,
33    x64_flags: x64_settings::Flags,
34    reg_env: MachineEnv,
35}
36
37impl X64Backend {
38    /// Create a new X64 backend with the given (shared) flags.
39    fn new_with_flags(triple: Triple, flags: Flags, x64_flags: x64_settings::Flags) -> Self {
40        let reg_env = create_reg_env_systemv(&flags);
41        Self {
42            triple,
43            flags,
44            x64_flags,
45            reg_env,
46        }
47    }
48
49    fn compile_vcode(
50        &self,
51        func: &Function,
52        domtree: &DominatorTree,
53    ) -> CodegenResult<(VCode<inst::Inst>, regalloc2::Output)> {
54        // This performs lowering to VCode, register-allocates the code, computes
55        // block layout and finalizes branches. The result is ready for binary emission.
56        let emit_info = EmitInfo::new(self.flags.clone(), self.x64_flags.clone());
57        let sigs = SigSet::new::<abi::X64ABIMachineSpec>(func, &self.flags)?;
58        let abi = abi::X64Callee::new(func, self, &self.x64_flags, &sigs)?;
59        compile::compile::<Self>(func, domtree, self, abi, emit_info, sigs)
60    }
61}
62
63impl TargetIsa for X64Backend {
64    fn compile_function(
65        &self,
66        func: &Function,
67        domtree: &DominatorTree,
68        want_disasm: bool,
69    ) -> CodegenResult<CompiledCodeStencil> {
70        let (vcode, regalloc_result) = self.compile_vcode(func, domtree)?;
71
72        let emit_result = vcode.emit(
73            &regalloc_result,
74            want_disasm,
75            self.flags.machine_code_cfg_info(),
76        );
77        let frame_size = emit_result.frame_size;
78        let value_labels_ranges = emit_result.value_labels_ranges;
79        let buffer = emit_result.buffer.finish();
80        let sized_stackslot_offsets = emit_result.sized_stackslot_offsets;
81        let dynamic_stackslot_offsets = emit_result.dynamic_stackslot_offsets;
82
83        if let Some(disasm) = emit_result.disasm.as_ref() {
84            log::trace!("disassembly:\n{}", disasm);
85        }
86
87        Ok(CompiledCodeStencil {
88            buffer,
89            frame_size,
90            vcode: emit_result.disasm,
91            value_labels_ranges,
92            sized_stackslot_offsets,
93            dynamic_stackslot_offsets,
94            bb_starts: emit_result.bb_offsets,
95            bb_edges: emit_result.bb_edges,
96            alignment: emit_result.alignment,
97        })
98    }
99
100    fn flags(&self) -> &Flags {
101        &self.flags
102    }
103
104    fn machine_env(&self) -> &MachineEnv {
105        &self.reg_env
106    }
107
108    fn isa_flags(&self) -> Vec<shared_settings::Value> {
109        self.x64_flags.iter().collect()
110    }
111
112    fn dynamic_vector_bytes(&self, _dyn_ty: Type) -> u32 {
113        16
114    }
115
116    fn name(&self) -> &'static str {
117        "x64"
118    }
119
120    fn triple(&self) -> &Triple {
121        &self.triple
122    }
123
124    fn unsigned_add_overflow_condition(&self) -> IntCC {
125        // Unsigned `<`; this corresponds to the carry flag set on x86, which
126        // indicates an add has overflowed.
127        IntCC::UnsignedLessThan
128    }
129
130    #[cfg(feature = "unwind")]
131    fn emit_unwind_info(
132        &self,
133        result: &CompiledCode,
134        kind: crate::machinst::UnwindInfoKind,
135    ) -> CodegenResult<Option<crate::isa::unwind::UnwindInfo>> {
136        use crate::isa::unwind::UnwindInfo;
137        use crate::machinst::UnwindInfoKind;
138        Ok(match kind {
139            UnwindInfoKind::SystemV => {
140                let mapper = self::inst::unwind::systemv::RegisterMapper;
141                Some(UnwindInfo::SystemV(
142                    crate::isa::unwind::systemv::create_unwind_info_from_insts(
143                        &result.buffer.unwind_info[..],
144                        result.buffer.data().len(),
145                        &mapper,
146                    )?,
147                ))
148            }
149            UnwindInfoKind::Windows => Some(UnwindInfo::WindowsX64(
150                crate::isa::unwind::winx64::create_unwind_info_from_insts::<
151                    self::inst::unwind::winx64::RegisterMapper,
152                >(&result.buffer.unwind_info[..])?,
153            )),
154            _ => None,
155        })
156    }
157
158    #[cfg(feature = "unwind")]
159    fn create_systemv_cie(&self) -> Option<gimli::write::CommonInformationEntry> {
160        Some(inst::unwind::systemv::create_cie())
161    }
162
163    #[cfg(feature = "unwind")]
164    fn map_regalloc_reg_to_dwarf(&self, reg: Reg) -> Result<u16, systemv::RegisterMappingError> {
165        inst::unwind::systemv::map_reg(reg).map(|reg| reg.0)
166    }
167
168    fn text_section_builder(&self, num_funcs: usize) -> Box<dyn TextSectionBuilder> {
169        Box::new(MachTextSectionBuilder::<inst::Inst>::new(num_funcs))
170    }
171
172    /// Align functions on x86 to 16 bytes, ensuring that rip-relative loads to SSE registers are
173    /// always from aligned memory.
174    fn function_alignment(&self) -> u32 {
175        16
176    }
177
178    #[cfg(feature = "disas")]
179    fn to_capstone(&self) -> Result<capstone::Capstone, capstone::Error> {
180        use capstone::prelude::*;
181        Capstone::new()
182            .x86()
183            .mode(arch::x86::ArchMode::Mode64)
184            .syntax(arch::x86::ArchSyntax::Att)
185            .build()
186    }
187
188    fn has_native_fma(&self) -> bool {
189        self.x64_flags.use_fma()
190    }
191}
192
193impl fmt::Display for X64Backend {
194    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
195        f.debug_struct("MachBackend")
196            .field("name", &self.name())
197            .field("triple", &self.triple())
198            .field("flags", &format!("{}", self.flags()))
199            .finish()
200    }
201}
202
203/// Create a new `isa::Builder`.
204pub(crate) fn isa_builder(triple: Triple) -> IsaBuilder {
205    IsaBuilder {
206        triple,
207        setup: x64_settings::builder(),
208        constructor: isa_constructor,
209    }
210}
211
212fn isa_constructor(
213    triple: Triple,
214    shared_flags: Flags,
215    builder: &shared_settings::Builder,
216) -> CodegenResult<OwnedTargetIsa> {
217    let isa_flags = x64_settings::Flags::new(&shared_flags, builder);
218
219    // Check for compatibility between flags and ISA level
220    // requested. In particular, SIMD support requires SSE4.2.
221    if shared_flags.enable_simd() {
222        if !isa_flags.has_sse3()
223            || !isa_flags.has_ssse3()
224            || !isa_flags.has_sse41()
225            || !isa_flags.has_sse42()
226        {
227            return Err(CodegenError::Unsupported(
228                "SIMD support requires SSE3, SSSE3, SSE4.1, and SSE4.2 on x86_64.".into(),
229            ));
230        }
231    }
232
233    let backend = X64Backend::new_with_flags(triple, shared_flags, isa_flags);
234    Ok(backend.wrapped())
235}
236
237#[cfg(test)]
238mod test {
239    use super::*;
240    use crate::settings;
241    use crate::settings::Configurable;
242
243    // Check that feature tests for SIMD work correctly.
244    #[test]
245    fn simd_required_features() {
246        let mut shared_flags_builder = settings::builder();
247        shared_flags_builder.set("enable_simd", "true").unwrap();
248        let shared_flags = settings::Flags::new(shared_flags_builder);
249        let mut isa_builder = crate::isa::lookup_by_name("x86_64").unwrap();
250        isa_builder.set("has_sse3", "false").unwrap();
251        isa_builder.set("has_ssse3", "false").unwrap();
252        isa_builder.set("has_sse41", "false").unwrap();
253        isa_builder.set("has_sse42", "false").unwrap();
254        assert!(matches!(
255            isa_builder.finish(shared_flags),
256            Err(CodegenError::Unsupported(_)),
257        ));
258    }
259}