Coverage Report

Created: 2024-10-16 07:58

/rust/registry/src/index.crates.io-6f17d22bba15001f/cranelift-codegen-0.91.1/src/isa/aarch64/mod.rs
Line
Count
Source (jump to first uncovered line)
1
//! ARM 64-bit Instruction Set Architecture.
2
3
use crate::ir::condcodes::IntCC;
4
use crate::ir::{Function, Type};
5
use crate::isa::aarch64::settings as aarch64_settings;
6
#[cfg(feature = "unwind")]
7
use crate::isa::unwind::systemv;
8
use crate::isa::{Builder as IsaBuilder, TargetIsa};
9
use crate::machinst::{
10
    compile, CompiledCode, CompiledCodeStencil, MachTextSectionBuilder, Reg, SigSet,
11
    TextSectionBuilder, VCode,
12
};
13
use crate::result::CodegenResult;
14
use crate::settings as shared_settings;
15
use alloc::{boxed::Box, vec::Vec};
16
use core::fmt;
17
use regalloc2::MachineEnv;
18
use target_lexicon::{Aarch64Architecture, Architecture, OperatingSystem, Triple};
19
20
// New backend:
21
mod abi;
22
pub(crate) mod inst;
23
mod lower;
24
mod lower_inst;
25
mod settings;
26
27
use inst::create_reg_env;
28
29
use self::inst::EmitInfo;
30
31
/// An AArch64 backend.
32
pub struct AArch64Backend {
33
    triple: Triple,
34
    flags: shared_settings::Flags,
35
    isa_flags: aarch64_settings::Flags,
36
    machine_env: MachineEnv,
37
}
38
39
impl AArch64Backend {
40
    /// Create a new AArch64 backend with the given (shared) flags.
41
0
    pub fn new_with_flags(
42
0
        triple: Triple,
43
0
        flags: shared_settings::Flags,
44
0
        isa_flags: aarch64_settings::Flags,
45
0
    ) -> AArch64Backend {
46
0
        let machine_env = create_reg_env(&flags);
47
0
        AArch64Backend {
48
0
            triple,
49
0
            flags,
50
0
            isa_flags,
51
0
            machine_env,
52
0
        }
53
0
    }
54
55
    /// This performs lowering to VCode, register-allocates the code, computes block layout and
56
    /// finalizes branches. The result is ready for binary emission.
57
0
    fn compile_vcode(
58
0
        &self,
59
0
        func: &Function,
60
0
    ) -> CodegenResult<(VCode<inst::Inst>, regalloc2::Output)> {
61
0
        let emit_info = EmitInfo::new(self.flags.clone());
62
0
        let sigs = SigSet::new::<abi::AArch64MachineDeps>(func, &self.flags)?;
63
0
        let abi = abi::AArch64Callee::new(func, self, &self.isa_flags, &sigs)?;
64
0
        compile::compile::<AArch64Backend>(func, self, abi, emit_info, sigs)
65
0
    }
66
}
67
68
impl TargetIsa for AArch64Backend {
69
0
    fn compile_function(
70
0
        &self,
71
0
        func: &Function,
72
0
        want_disasm: bool,
73
0
    ) -> CodegenResult<CompiledCodeStencil> {
74
0
        let (vcode, regalloc_result) = self.compile_vcode(func)?;
75
76
0
        let emit_result = vcode.emit(
77
0
            &regalloc_result,
78
0
            want_disasm,
79
0
            self.flags.machine_code_cfg_info(),
80
0
        );
81
0
        let frame_size = emit_result.frame_size;
82
0
        let value_labels_ranges = emit_result.value_labels_ranges;
83
0
        let buffer = emit_result.buffer.finish();
84
0
        let sized_stackslot_offsets = emit_result.sized_stackslot_offsets;
85
0
        let dynamic_stackslot_offsets = emit_result.dynamic_stackslot_offsets;
86
87
0
        if let Some(disasm) = emit_result.disasm.as_ref() {
88
0
            log::debug!("disassembly:\n{}", disasm);
89
0
        }
90
91
0
        Ok(CompiledCodeStencil {
92
0
            buffer,
93
0
            frame_size,
94
0
            disasm: emit_result.disasm,
95
0
            value_labels_ranges,
96
0
            sized_stackslot_offsets,
97
0
            dynamic_stackslot_offsets,
98
0
            bb_starts: emit_result.bb_offsets,
99
0
            bb_edges: emit_result.bb_edges,
100
0
            alignment: emit_result.alignment,
101
0
        })
102
0
    }
103
104
0
    fn name(&self) -> &'static str {
105
0
        "aarch64"
106
0
    }
107
108
0
    fn triple(&self) -> &Triple {
109
0
        &self.triple
110
0
    }
111
112
0
    fn flags(&self) -> &shared_settings::Flags {
113
0
        &self.flags
114
0
    }
115
116
0
    fn machine_env(&self) -> &MachineEnv {
117
0
        &self.machine_env
118
0
    }
119
120
0
    fn isa_flags(&self) -> Vec<shared_settings::Value> {
121
0
        self.isa_flags.iter().collect()
122
0
    }
123
124
0
    fn is_branch_protection_enabled(&self) -> bool {
125
0
        self.isa_flags.use_bti()
126
0
    }
127
128
0
    fn dynamic_vector_bytes(&self, _dyn_ty: Type) -> u32 {
129
0
        16
130
0
    }
131
132
0
    fn unsigned_add_overflow_condition(&self) -> IntCC {
133
0
        // Unsigned `>=`; this corresponds to the carry flag set on aarch64, which happens on
134
0
        // overflow of an add.
135
0
        IntCC::UnsignedGreaterThanOrEqual
136
0
    }
137
138
    #[cfg(feature = "unwind")]
139
0
    fn emit_unwind_info(
140
0
        &self,
141
0
        result: &CompiledCode,
142
0
        kind: crate::machinst::UnwindInfoKind,
143
0
    ) -> CodegenResult<Option<crate::isa::unwind::UnwindInfo>> {
144
0
        use crate::isa::unwind::UnwindInfo;
145
0
        use crate::machinst::UnwindInfoKind;
146
0
        Ok(match kind {
147
            UnwindInfoKind::SystemV => {
148
0
                let mapper = self::inst::unwind::systemv::RegisterMapper;
149
0
                Some(UnwindInfo::SystemV(
150
0
                    crate::isa::unwind::systemv::create_unwind_info_from_insts(
151
0
                        &result.buffer.unwind_info[..],
152
0
                        result.buffer.data().len(),
153
0
                        &mapper,
154
0
                    )?,
155
                ))
156
            }
157
            UnwindInfoKind::Windows => {
158
                // TODO: support Windows unwind info on AArch64
159
0
                None
160
            }
161
0
            _ => None,
162
        })
163
0
    }
164
165
    #[cfg(feature = "unwind")]
166
0
    fn create_systemv_cie(&self) -> Option<gimli::write::CommonInformationEntry> {
167
0
        let is_apple_os = match self.triple.operating_system {
168
            OperatingSystem::Darwin
169
            | OperatingSystem::Ios
170
            | OperatingSystem::MacOSX { .. }
171
0
            | OperatingSystem::Tvos => true,
172
0
            _ => false,
173
        };
174
175
0
        if self.isa_flags.sign_return_address()
176
0
            && self.isa_flags.sign_return_address_with_bkey()
177
0
            && !is_apple_os
178
        {
179
0
            unimplemented!("Specifying that the B key is used with pointer authentication instructions in the CIE is not implemented.");
180
0
        }
181
0
182
0
        Some(inst::unwind::systemv::create_cie())
183
0
    }
184
185
0
    fn text_section_builder(&self, num_funcs: usize) -> Box<dyn TextSectionBuilder> {
186
0
        Box::new(MachTextSectionBuilder::<inst::Inst>::new(num_funcs))
187
0
    }
188
189
    #[cfg(feature = "unwind")]
190
0
    fn map_regalloc_reg_to_dwarf(&self, reg: Reg) -> Result<u16, systemv::RegisterMappingError> {
191
0
        inst::unwind::systemv::map_reg(reg).map(|reg| reg.0)
192
0
    }
193
194
0
    fn function_alignment(&self) -> u32 {
195
0
        // We use 32-byte alignment for performance reasons, but for correctness we would only need
196
0
        // 4-byte alignment.
197
0
        32
198
0
    }
199
}
200
201
impl fmt::Display for AArch64Backend {
202
0
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
203
0
        f.debug_struct("MachBackend")
204
0
            .field("name", &self.name())
205
0
            .field("triple", &self.triple())
206
0
            .field("flags", &format!("{}", self.flags()))
207
0
            .finish()
208
0
    }
209
}
210
211
/// Create a new `isa::Builder`.
212
0
pub fn isa_builder(triple: Triple) -> IsaBuilder {
213
0
    assert!(triple.architecture == Architecture::Aarch64(Aarch64Architecture::Aarch64));
214
0
    IsaBuilder {
215
0
        triple,
216
0
        setup: aarch64_settings::builder(),
217
0
        constructor: |triple, shared_flags, builder| {
218
0
            let isa_flags = aarch64_settings::Flags::new(&shared_flags, builder);
219
0
            let backend = AArch64Backend::new_with_flags(triple, shared_flags, isa_flags);
220
0
            Ok(Box::new(backend))
221
0
        },
222
0
    }
223
0
}
224
225
#[cfg(test)]
226
mod test {
227
    use super::*;
228
    use crate::cursor::{Cursor, FuncCursor};
229
    use crate::ir::types::*;
230
    use crate::ir::{AbiParam, Function, InstBuilder, JumpTableData, Signature, UserFuncName};
231
    use crate::isa::CallConv;
232
    use crate::settings;
233
    use crate::settings::Configurable;
234
    use core::str::FromStr;
235
    use target_lexicon::Triple;
236
237
    #[test]
238
    fn test_compile_function() {
239
        let name = UserFuncName::testcase("test0");
240
        let mut sig = Signature::new(CallConv::SystemV);
241
        sig.params.push(AbiParam::new(I32));
242
        sig.returns.push(AbiParam::new(I32));
243
        let mut func = Function::with_name_signature(name, sig);
244
245
        let bb0 = func.dfg.make_block();
246
        let arg0 = func.dfg.append_block_param(bb0, I32);
247
248
        let mut pos = FuncCursor::new(&mut func);
249
        pos.insert_block(bb0);
250
        let v0 = pos.ins().iconst(I32, 0x1234);
251
        let v1 = pos.ins().iadd(arg0, v0);
252
        pos.ins().return_(&[v1]);
253
254
        let mut shared_flags_builder = settings::builder();
255
        shared_flags_builder.set("opt_level", "none").unwrap();
256
        let shared_flags = settings::Flags::new(shared_flags_builder);
257
        let isa_flags = aarch64_settings::Flags::new(&shared_flags, aarch64_settings::builder());
258
        let backend = AArch64Backend::new_with_flags(
259
            Triple::from_str("aarch64").unwrap(),
260
            shared_flags,
261
            isa_flags,
262
        );
263
        let buffer = backend.compile_function(&mut func, false).unwrap().buffer;
264
        let code = buffer.data();
265
266
        // To update this comment, write the golden bytes to a file, and run the following command
267
        // on it to update:
268
        // > aarch64-linux-gnu-objdump -b binary -D <file> -m aarch64
269
        //
270
        // 0:   d2824682        mov     x2, #0x1234                     // #4660
271
        // 4:   0b020000        add     w0, w0, w2
272
        // 8:   d65f03c0        ret
273
274
        let golden = vec![130, 70, 130, 210, 0, 0, 2, 11, 192, 3, 95, 214];
275
276
        assert_eq!(code, &golden[..]);
277
    }
278
279
    #[test]
280
    fn test_branch_lowering() {
281
        let name = UserFuncName::testcase("test0");
282
        let mut sig = Signature::new(CallConv::SystemV);
283
        sig.params.push(AbiParam::new(I32));
284
        sig.returns.push(AbiParam::new(I32));
285
        let mut func = Function::with_name_signature(name, sig);
286
287
        let bb0 = func.dfg.make_block();
288
        let arg0 = func.dfg.append_block_param(bb0, I32);
289
        let bb1 = func.dfg.make_block();
290
        let bb2 = func.dfg.make_block();
291
        let bb3 = func.dfg.make_block();
292
293
        let mut pos = FuncCursor::new(&mut func);
294
        pos.insert_block(bb0);
295
        let v0 = pos.ins().iconst(I32, 0x1234);
296
        let v1 = pos.ins().iadd(arg0, v0);
297
        pos.ins().brnz(v1, bb1, &[]);
298
        pos.ins().jump(bb2, &[]);
299
        pos.insert_block(bb1);
300
        pos.ins().brnz(v1, bb2, &[]);
301
        pos.ins().jump(bb3, &[]);
302
        pos.insert_block(bb2);
303
        let v2 = pos.ins().iadd(v1, v0);
304
        pos.ins().brnz(v2, bb2, &[]);
305
        pos.ins().jump(bb1, &[]);
306
        pos.insert_block(bb3);
307
        let v3 = pos.ins().isub(v1, v0);
308
        pos.ins().return_(&[v3]);
309
310
        let mut shared_flags_builder = settings::builder();
311
        shared_flags_builder.set("opt_level", "none").unwrap();
312
        let shared_flags = settings::Flags::new(shared_flags_builder);
313
        let isa_flags = aarch64_settings::Flags::new(&shared_flags, aarch64_settings::builder());
314
        let backend = AArch64Backend::new_with_flags(
315
            Triple::from_str("aarch64").unwrap(),
316
            shared_flags,
317
            isa_flags,
318
        );
319
        let result = backend
320
            .compile_function(&mut func, /* want_disasm = */ false)
321
            .unwrap();
322
        let code = result.buffer.data();
323
324
        // To update this comment, write the golden bytes to a file, and run the following command
325
        // on it to update:
326
        // > aarch64-linux-gnu-objdump -b binary -D <file> -m aarch64
327
        //
328
        //   0:   d2824689        mov     x9, #0x1234                     // #4660
329
        //   4:   0b09000b        add     w11, w0, w9
330
        //   8:   2a0b03ea        mov     w10, w11
331
        //   c:   b50000aa        cbnz    x10, 0x20
332
        //  10:   d282468c        mov     x12, #0x1234                    // #4660
333
        //  14:   0b0c016e        add     w14, w11, w12
334
        //  18:   2a0e03ed        mov     w13, w14
335
        //  1c:   b5ffffad        cbnz    x13, 0x10
336
        //  20:   2a0b03e0        mov     w0, w11
337
        //  24:   b5ffff60        cbnz    x0, 0x10
338
        //  28:   d2824681        mov     x1, #0x1234                     // #4660
339
        //  2c:   4b010160        sub     w0, w11, w1
340
        //  30:   d65f03c0        ret
341
342
        let golden = vec![
343
            137, 70, 130, 210, 11, 0, 9, 11, 234, 3, 11, 42, 170, 0, 0, 181, 140, 70, 130, 210,
344
            110, 1, 12, 11, 237, 3, 14, 42, 173, 255, 255, 181, 224, 3, 11, 42, 96, 255, 255, 181,
345
            129, 70, 130, 210, 96, 1, 1, 75, 192, 3, 95, 214,
346
        ];
347
348
        assert_eq!(code, &golden[..]);
349
    }
350
351
    #[test]
352
    fn test_br_table() {
353
        let name = UserFuncName::testcase("test0");
354
        let mut sig = Signature::new(CallConv::SystemV);
355
        sig.params.push(AbiParam::new(I32));
356
        sig.returns.push(AbiParam::new(I32));
357
        let mut func = Function::with_name_signature(name, sig);
358
359
        let bb0 = func.dfg.make_block();
360
        let arg0 = func.dfg.append_block_param(bb0, I32);
361
        let bb1 = func.dfg.make_block();
362
        let bb2 = func.dfg.make_block();
363
        let bb3 = func.dfg.make_block();
364
365
        let mut pos = FuncCursor::new(&mut func);
366
367
        pos.insert_block(bb0);
368
        let mut jt_data = JumpTableData::new();
369
        jt_data.push_entry(bb1);
370
        jt_data.push_entry(bb2);
371
        let jt = pos.func.create_jump_table(jt_data);
372
        pos.ins().br_table(arg0, bb3, jt);
373
374
        pos.insert_block(bb1);
375
        let v1 = pos.ins().iconst(I32, 1);
376
        pos.ins().return_(&[v1]);
377
378
        pos.insert_block(bb2);
379
        let v2 = pos.ins().iconst(I32, 2);
380
        pos.ins().return_(&[v2]);
381
382
        pos.insert_block(bb3);
383
        let v3 = pos.ins().iconst(I32, 3);
384
        pos.ins().return_(&[v3]);
385
386
        let mut shared_flags_builder = settings::builder();
387
        shared_flags_builder.set("opt_level", "none").unwrap();
388
        shared_flags_builder.set("enable_verifier", "true").unwrap();
389
        let shared_flags = settings::Flags::new(shared_flags_builder);
390
        let isa_flags = aarch64_settings::Flags::new(&shared_flags, aarch64_settings::builder());
391
        let backend = AArch64Backend::new_with_flags(
392
            Triple::from_str("aarch64").unwrap(),
393
            shared_flags,
394
            isa_flags,
395
        );
396
        let result = backend
397
            .compile_function(&mut func, /* want_disasm = */ false)
398
            .unwrap();
399
        let code = result.buffer.data();
400
401
        // To update this comment, write the golden bytes to a file, and run the following command
402
        // on it to update:
403
        // > aarch64-linux-gnu-objdump -b binary -D <file> -m aarch64
404
        //
405
        //   0:   7100081f        cmp     w0, #0x2
406
        //   4:   54000122        b.cs    0x28  // b.hs, b.nlast
407
        //   8:   9a8023e8        csel    x8, xzr, x0, cs  // cs = hs, nlast
408
        //   c:   d503229f        csdb
409
        //  10:   10000087        adr     x7, 0x20
410
        //  14:   b8a858e8        ldrsw   x8, [x7, w8, uxtw #2]
411
        //  18:   8b0800e7        add     x7, x7, x8
412
        //  1c:   d61f00e0        br      x7
413
        //  20:   00000010        udf     #16
414
        //  24:   00000018        udf     #24
415
        //  28:   d2800060        mov     x0, #0x3                        // #3
416
        //  2c:   d65f03c0        ret
417
        //  30:   d2800020        mov     x0, #0x1                        // #1
418
        //  34:   d65f03c0        ret
419
        //  38:   d2800040        mov     x0, #0x2                        // #2
420
        //  3c:   d65f03c0        ret
421
422
        let golden = vec![
423
            31, 8, 0, 113, 34, 1, 0, 84, 232, 35, 128, 154, 159, 34, 3, 213, 135, 0, 0, 16, 232,
424
            88, 168, 184, 231, 0, 8, 139, 224, 0, 31, 214, 16, 0, 0, 0, 24, 0, 0, 0, 96, 0, 128,
425
            210, 192, 3, 95, 214, 32, 0, 128, 210, 192, 3, 95, 214, 64, 0, 128, 210, 192, 3, 95,
426
            214,
427
        ];
428
429
        assert_eq!(code, &golden[..]);
430
    }
431
}