1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
//! This module exposes the machine-specific backend definition pieces.
//!
//! The MachInst infrastructure is the compiler backend, from CLIF
//! (ir::Function) to machine code. The purpose of this infrastructure is, at a
//! high level, to do instruction selection/lowering (to machine instructions),
//! register allocation, and then perform all the fixups to branches, constant
//! data references, etc., needed to actually generate machine code.
//!
//! The container for machine instructions, at various stages of construction,
//! is the `VCode` struct. We refer to a sequence of machine instructions organized
//! into basic blocks as "vcode". This is short for "virtual-register code", though
//! it's a bit of a misnomer because near the end of the pipeline, vcode has all
//! real registers. Nevertheless, the name is catchy and we like it.
//!
//! The compilation pipeline, from an `ir::Function` (already optimized as much as
//! you like by machine-independent optimization passes) onward, is as follows.
//! (N.B.: though we show the VCode separately at each stage, the passes
//! mutate the VCode in place; these are not separate copies of the code.)
//!
//! ```plain
//!
//!     ir::Function                (SSA IR, machine-independent opcodes)
//!         |
//!         |  [lower]
//!         |
//!     VCode<arch_backend::Inst>   (machine instructions:
//!         |                        - mostly virtual registers.
//!         |                        - cond branches in two-target form.
//!         |                        - branch targets are block indices.
//!         |                        - in-memory constants held by insns,
//!         |                          with unknown offsets.
//!         |                        - critical edges (actually all edges)
//!         |                          are split.)
//!         | [regalloc]
//!         |
//!     VCode<arch_backend::Inst>   (machine instructions:
//!         |                        - all real registers.
//!         |                        - new instruction sequence returned
//!         |                          out-of-band in RegAllocResult.
//!         |                        - instruction sequence has spills,
//!         |                          reloads, and moves inserted.
//!         |                        - other invariants same as above.)
//!         |
//!         | [preamble/postamble]
//!         |
//!     VCode<arch_backend::Inst>   (machine instructions:
//!         |                        - stack-frame size known.
//!         |                        - out-of-band instruction sequence
//!         |                          has preamble prepended to entry
//!         |                          block, and postamble injected before
//!         |                          every return instruction.
//!         |                        - all symbolic stack references to
//!         |                          stackslots and spillslots are resolved
//!         |                          to concrete FP-offset mem addresses.)
//!         | [block/insn ordering]
//!         |
//!     VCode<arch_backend::Inst>   (machine instructions:
//!         |                        - vcode.final_block_order is filled in.
//!         |                        - new insn sequence from regalloc is
//!         |                          placed back into vcode and block
//!         |                          boundaries are updated.)
//!         | [redundant branch/block
//!         |  removal]
//!         |
//!     VCode<arch_backend::Inst>   (machine instructions:
//!         |                        - all blocks that were just an
//!         |                          unconditional branch are removed.)
//!         |
//!         | [branch finalization
//!         |  (fallthroughs)]
//!         |
//!     VCode<arch_backend::Inst>   (machine instructions:
//!         |                        - all branches are in lowered one-
//!         |                          target form, but targets are still
//!         |                          block indices.)
//!         |
//!         | [branch finalization
//!         |  (offsets)]
//!         |
//!     VCode<arch_backend::Inst>   (machine instructions:
//!         |                        - all branch offsets from start of
//!         |                          function are known, and all branches
//!         |                          have resolved-offset targets.)
//!         |
//!         | [MemArg finalization]
//!         |
//!     VCode<arch_backend::Inst>   (machine instructions:
//!         |                        - all MemArg references to the constant
//!         |                          pool are replaced with offsets.
//!         |                        - all constant-pool data is collected
//!         |                          in the VCode.)
//!         |
//!         | [binary emission]
//!         |
//!     Vec<u8>                     (machine code!)
//!
//! ```

use crate::binemit::{CodeInfo, CodeOffset};
use crate::ir::condcodes::IntCC;
use crate::ir::{Function, Type};
use crate::result::CodegenResult;
use crate::settings::Flags;

use alloc::boxed::Box;
use alloc::vec::Vec;
use core::fmt::Debug;
use regalloc::RegUsageCollector;
use regalloc::{
    RealReg, RealRegUniverse, Reg, RegClass, RegUsageMapper, SpillSlot, VirtualReg, Writable,
};
use std::string::String;
use target_lexicon::Triple;

pub mod lower;
pub use lower::*;
pub mod vcode;
pub use vcode::*;
pub mod compile;
pub use compile::*;
pub mod blockorder;
pub use blockorder::*;
pub mod abi;
pub use abi::*;
pub mod pretty_print;
pub use pretty_print::*;
pub mod sections;
pub use sections::*;
pub mod adapter;
pub use adapter::*;

/// A machine instruction.
pub trait MachInst: Clone + Debug {
    /// Return the registers referenced by this machine instruction along with
    /// the modes of reference (use, def, modify).
    fn get_regs(&self, collector: &mut RegUsageCollector);

    /// Map virtual registers to physical registers using the given virt->phys
    /// maps corresponding to the program points prior to, and after, this instruction.
    fn map_regs(&mut self, maps: &RegUsageMapper);

    /// If this is a simple move, return the (source, destination) tuple of registers.
    fn is_move(&self) -> Option<(Writable<Reg>, Reg)>;

    /// Is this a terminator (branch or ret)? If so, return its type
    /// (ret/uncond/cond) and target if applicable.
    fn is_term<'a>(&'a self) -> MachTerminator<'a>;

    /// Returns true if the instruction is an epilogue placeholder.
    fn is_epilogue_placeholder(&self) -> bool;

    /// Generate a move.
    fn gen_move(to_reg: Writable<Reg>, from_reg: Reg, ty: Type) -> Self;

    /// Generate a zero-length no-op.
    fn gen_zero_len_nop() -> Self;

    /// Possibly operate on a value directly in a spill-slot rather than a
    /// register. Useful if the machine has register-memory instruction forms
    /// (e.g., add directly from or directly to memory), like x86.
    fn maybe_direct_reload(&self, reg: VirtualReg, slot: SpillSlot) -> Option<Self>;

    /// Determine a register class to store the given Cranelift type.
    /// May return an error if the type isn't supported by this backend.
    fn rc_for_type(ty: Type) -> CodegenResult<RegClass>;

    /// Generate a jump to another target. Used during lowering of
    /// control flow.
    fn gen_jump(target: BlockIndex) -> Self;

    /// Generate a NOP. The `preferred_size` parameter allows the caller to
    /// request a NOP of that size, or as close to it as possible. The machine
    /// backend may return a NOP whose binary encoding is smaller than the
    /// preferred size, but must not return a NOP that is larger. However,
    /// the instruction must have a nonzero size.
    fn gen_nop(preferred_size: usize) -> Self;

    /// Rewrite block targets using the block-target map.
    fn with_block_rewrites(&mut self, block_target_map: &[BlockIndex]);

    /// Finalize branches once the block order (fallthrough) is known.
    fn with_fallthrough_block(&mut self, fallthrough_block: Option<BlockIndex>);

    /// Update instruction once block offsets are known.  These offsets are
    /// relative to the beginning of the function. `targets` is indexed by
    /// BlockIndex.
    fn with_block_offsets(&mut self, my_offset: CodeOffset, targets: &[CodeOffset]);

    /// Get the register universe for this backend.
    fn reg_universe(flags: &Flags) -> RealRegUniverse;

    /// Align a basic block offset (from start of function).  By default, no
    /// alignment occurs.
    fn align_basic_block(offset: CodeOffset) -> CodeOffset {
        offset
    }
}

/// Describes a block terminator (not call) in the vcode, when its branches
/// have not yet been finalized (so a branch may have two targets).
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum MachTerminator<'a> {
    /// Not a terminator.
    None,
    /// A return instruction.
    Ret,
    /// An unconditional branch to another block.
    Uncond(BlockIndex),
    /// A conditional branch to one of two other blocks.
    Cond(BlockIndex, BlockIndex),
    /// An indirect branch with known possible targets.
    Indirect(&'a [BlockIndex]),
}

/// A trait describing the ability to encode a MachInst into binary machine code.
pub trait MachInstEmit<O: MachSectionOutput> {
    /// Emit the instruction.
    fn emit(&self, code: &mut O, flags: &Flags);
}

/// The result of a `MachBackend::compile_function()` call. Contains machine
/// code (as bytes) and a disassembly, if requested.
pub struct MachCompileResult {
    /// Machine code.
    pub sections: MachSections,
    /// Size of stack frame, in bytes.
    pub frame_size: u32,
    /// Disassembly, if requested.
    pub disasm: Option<String>,
}

impl MachCompileResult {
    /// Get a `CodeInfo` describing section sizes from this compilation result.
    pub fn code_info(&self) -> CodeInfo {
        let code_size = self.sections.total_size();
        CodeInfo {
            code_size,
            jumptables_size: 0,
            rodata_size: 0,
            total_size: code_size,
        }
    }
}

/// Top-level machine backend trait, which wraps all monomorphized code and
/// allows a virtual call from the machine-independent `Function::compile()`.
pub trait MachBackend {
    /// Compile the given function.
    fn compile_function(
        &self,
        func: &Function,
        want_disasm: bool,
    ) -> CodegenResult<MachCompileResult>;

    /// Return flags for this backend.
    fn flags(&self) -> &Flags;

    /// Return triple for this backend.
    fn triple(&self) -> Triple;

    /// Return name for this backend.
    fn name(&self) -> &'static str;

    /// Return the register universe for this backend.
    fn reg_universe(&self) -> RealRegUniverse;

    /// Machine-specific condcode info needed by TargetIsa.
    fn unsigned_add_overflow_condition(&self) -> IntCC {
        // TODO: this is what x86 specifies. Is this right for arm64?
        IntCC::UnsignedLessThan
    }

    /// Machine-specific condcode info needed by TargetIsa.
    fn unsigned_sub_overflow_condition(&self) -> IntCC {
        // TODO: this is what x86 specifies. Is this right for arm64?
        IntCC::UnsignedLessThan
    }
}