Skip to main content

rucc_target/
branch.rs

1//! The instructions a laid out branch is made of.
2//!
3//! Design: `spec/10-backend.md` sections 10.6 and 10.8.
4//!
5//! A lowering rule for a conditional branch says one thing, which is what the branch is on. Where
6//! its two arms go is on the block rather than in the instruction, and which of them the block
7//! falls through to is not knowable until every block of the function has been put in an order.
8//! So the instructions that actually branch are chosen by the block layout, after allocation, and
9//! they are named here for the same reason [`crate::FrameInsts`] names a push: the crate that
10//! writes them is a pipeline crate and `spec/10-backend.md` section 10.8 says a pipeline crate
11//! holds no target-specific code.
12//!
13//! # What each one has to be
14//!
15//! The shapes are fixed, because the code that writes them writes one shape each. The test reads
16//! one register and sets whatever the machine's condition state is. The three jumps read nothing
17//! and write nothing, and where each goes is the first successor of the block it ends, which is
18//! how every other arm is already carried.
19//!
20//! Two conditional jumps rather than one, because which one a block ends with depends on which
21//! arm the layout put next. A block that falls into the arm taken when the condition does not
22//! hold ends with the jump that is taken when it does, and a block that falls into the other arm
23//! ends with the other jump. Neither is more natural than the other and a target that could only
24//! name one would force the layout to lay every second branch out backwards.
25//!
26//! After the layout has run, a block that ends in a conditional jump has exactly two successors:
27//! the first is where the jump goes, and the second is the block laid out next, which is where it
28//! goes when the jump is not taken. There is never a second jump in the same block, because the
29//! layout makes a block for one rather than writing it.
30//!
31//! # The condition state is not an operand
32//!
33//! Nothing here mentions the flags, on a machine that has them or on one that does not. What
34//! makes that sound is that the test and the jump that reads it are written next to each other,
35//! by one pass, after the allocator has finished, so there is nothing left in the compiler that
36//! could put an instruction between them.
37
38/// Every instruction a laid out branch is made of.
39#[derive(Debug, Clone, Copy, PartialEq, Eq)]
40pub struct BranchInsts {
41    /// What a rule file and the machine IR put in front of this target's opcodes, such as `x64.`,
42    /// which says which target a term belongs to and is not part of the opcode.
43    pub prefix: &'static str,
44    /// What a lowering rule selects for a conditional branch, which is what the layout replaces.
45    ///
46    /// It reads the condition and does nothing, which is as much of a branch as a rule can say.
47    /// Naming it here is what lets the layout find one and be sure it has found one, rather than
48    /// assuming that whatever a two-armed block ends with must be the branch.
49    pub cond: &'static str,
50    /// Reads the register the branch is on and sets the condition state from whether it is zero.
51    pub test: &'static str,
52    /// Goes to the block's first successor when the condition held.
53    pub if_true: &'static str,
54    /// Goes to the block's first successor when the condition did not hold.
55    pub if_false: &'static str,
56    /// Goes to the block's first successor.
57    pub jump: &'static str,
58}