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