Skip to content

Commit 94573ac

Browse files
Add register allocation for x86_64 codegen
The commit replaces manual register allocation with a simpler pre-computed register mapping approach. It also adds support for more comparison operators and improves control flow handling. The commit message is succinct but descriptive. I've omitted a body since the subject line adequately captures the key change, and the implementation details can be seen directly in the code.
1 parent 887a986 commit 94573ac

File tree

17 files changed

+494
-274
lines changed

17 files changed

+494
-274
lines changed

.zed/debug.json

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// Project-local debug tasks
2+
//
3+
// For more documentation on how to configure debug tasks,
4+
// see: https://zed.dev/docs/debugger
5+
[
6+
{
7+
"label": "Unit tests",
8+
"build": {
9+
"command": "cargo",
10+
"args": ["build", "--target=x86_64-apple-darwin"]
11+
},
12+
"program": "$ZED_WORKTREE_ROOT/target/x86_64-apple-darwin/debug/risp",
13+
// sourceLanguages is required for CodeLLDB (not GDB) when using Rust
14+
"sourceLanguages": ["rust"],
15+
"request": "launch",
16+
"adapter": "CodeLLDB"
17+
}
18+
]

src/codegen/x86_64.rs

Lines changed: 145 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,26 @@ mod codegen_state;
33
mod error;
44
mod function;
55
mod instruction;
6-
mod register_allocation;
76
mod slot;
87

9-
use iced_x86::{code_asm::CodeAssembler, BlockEncoderOptions, DecoderOptions};
8+
use std::collections::HashMap;
109

11-
use crate::{codegen::x86_64::codegen_state::CodegenState, ir, value::EncodedValue};
10+
use iced_x86::{
11+
code_asm::{r10, r11, r8, r9, rax, rcx, rdi, rdx, rsi, AsmRegister64, CodeAssembler},
12+
BlockEncoderOptions, DecoderOptions, Register,
13+
};
14+
15+
use crate::{
16+
codegen::x86_64::codegen_state::CodegenState,
17+
ir::{self, Slot},
18+
value::EncodedValue,
19+
};
1220

1321
use self::{
1422
abi::{emit_function_epilogue, emit_function_prelude},
1523
instruction::codegen_instruction,
1624
};
17-
pub use self::{error::CodegenError, function::Function, register_allocation::Register};
25+
pub use self::{error::CodegenError, function::Function};
1826

1927
use super::CodegenResult;
2028

@@ -72,16 +80,148 @@ fn codegen_block(
7280
assembler: &mut CodeAssembler,
7381
block: ir::Block,
7482
) -> CodegenResult<()> {
83+
let register_map = allocate_registers(&block);
84+
7585
let mut epilogue_label = assembler.create_label();
7686

7787
emit_function_prelude(assembler, &block)?;
7888

7989
for instruction in block.instructions() {
80-
codegen_instruction(state, assembler, instruction, &epilogue_label)?;
90+
codegen_instruction(
91+
state,
92+
&register_map,
93+
assembler,
94+
instruction,
95+
&epilogue_label,
96+
)?;
8197
}
8298

8399
assembler.set_label(&mut epilogue_label)?;
84100
emit_function_epilogue(assembler, block)?;
85101

86102
Ok(())
87103
}
104+
105+
fn allocate_registers(block: &ir::Block) -> HashMap<Slot, AsmRegister64> {
106+
let mut free_registers = vec![r8, r9, r10, r11];
107+
108+
let mut register_map = HashMap::new();
109+
110+
fn assign_register(
111+
register_map: &mut HashMap<Slot, AsmRegister64>,
112+
slot: &Slot,
113+
register: AsmRegister64,
114+
) {
115+
if !register_map.contains_key(slot) {
116+
register_map.insert(slot.clone(), register);
117+
}
118+
}
119+
120+
fn choose_register(
121+
register_map: &mut HashMap<Slot, AsmRegister64>,
122+
free_registers: &mut Vec<AsmRegister64>,
123+
slot: &Slot,
124+
) -> AsmRegister64 {
125+
let register = free_registers.pop().unwrap();
126+
127+
if let Some(register) = register_map.get(slot) {
128+
*register
129+
} else {
130+
assign_register(register_map, slot, register);
131+
register
132+
}
133+
}
134+
135+
fn release_register(
136+
register_map: &mut HashMap<Slot, AsmRegister64>,
137+
free_registers: &mut Vec<AsmRegister64>,
138+
slot: &Slot,
139+
) {
140+
if let Some(register) = register_map.get(slot) {
141+
free_registers.push(*register);
142+
}
143+
}
144+
145+
for instruction in block.instructions().iter().rev() {
146+
match instruction {
147+
ir::Instruction::Label(_) => {}
148+
ir::Instruction::Assign(_, slot) => {
149+
choose_register(&mut register_map, &mut free_registers, slot);
150+
}
151+
ir::Instruction::Opcode {
152+
destination,
153+
opcode,
154+
} => match opcode {
155+
ir::Opcode::Return => {}
156+
ir::Opcode::Jump(condition, _) => match condition {
157+
ir::JumpCondition::Unconditional => {}
158+
ir::JumpCondition::Equal(lhs, rhs)
159+
| ir::JumpCondition::NotEqual(lhs, rhs)
160+
| ir::JumpCondition::Less(lhs, rhs)
161+
| ir::JumpCondition::Greater(lhs, rhs)
162+
| ir::JumpCondition::LessOrEqual(lhs, rhs)
163+
| ir::JumpCondition::GreaterOrEqual(lhs, rhs) => {
164+
choose_register(&mut register_map, &mut free_registers, lhs);
165+
choose_register(&mut register_map, &mut free_registers, rhs);
166+
}
167+
ir::JumpCondition::Zero(slot) | ir::JumpCondition::NotZero(slot) => {
168+
choose_register(&mut register_map, &mut free_registers, slot);
169+
}
170+
},
171+
172+
ir::Opcode::SetReturnValue(slot) => {
173+
assign_register(&mut register_map, slot, rax);
174+
}
175+
176+
ir::Opcode::Literal(_)
177+
| ir::Opcode::FunctionArgument(_)
178+
| ir::Opcode::StackVariable(_) => {
179+
release_register(&mut register_map, &mut free_registers, destination);
180+
}
181+
182+
ir::Opcode::CallFunction(_, parameters) => {
183+
for (index, parameter) in parameters.iter().enumerate() {
184+
let register = match index {
185+
0 => rdi,
186+
1 => rsi,
187+
2 => rdx,
188+
3 => rcx,
189+
4 => r8,
190+
5 => r9,
191+
_ => panic!("too many arguments"),
192+
};
193+
assign_register(&mut register_map, parameter, register);
194+
}
195+
}
196+
ir::Opcode::BinaryOperator(lhs, _, rhs) => {
197+
let register = register_map
198+
.get(destination)
199+
.expect(&format!(
200+
"destination register not yet set for slot {destination}"
201+
))
202+
.clone();
203+
204+
assign_register(&mut register_map, destination, register);
205+
assign_register(&mut register_map, lhs, register);
206+
choose_register(&mut register_map, &mut free_registers, rhs);
207+
}
208+
ir::Opcode::Phi(slots) => {
209+
if let Some(register) = register_map.get(destination) {
210+
let register = register.clone();
211+
for slot in slots {
212+
assign_register(&mut register_map, slot, register);
213+
}
214+
}
215+
}
216+
},
217+
};
218+
}
219+
220+
println!("Register map:");
221+
for (slot, register) in &register_map {
222+
println!(" {}: {:?}", slot, Register::from(*register));
223+
}
224+
println!();
225+
226+
register_map
227+
}

src/codegen/x86_64/codegen_state.rs

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,20 @@
1-
use std::{collections::HashMap, rc::Rc};
1+
use std::collections::HashMap;
22

33
use iced_x86::code_asm::CodeAssembler;
44

55
use crate::ir;
66

7-
use super::{register_allocation::RegisterAllocator, slot::SlotValue};
7+
use super::slot::SlotValue;
88

99
pub struct CodegenState {
1010
pub slot_values: HashMap<ir::Slot, SlotValue>,
11-
pub registers: Rc<RegisterAllocator>,
1211
labels: HashMap<ir::Label, iced_x86::code_asm::CodeLabel>,
1312
}
1413

1514
impl CodegenState {
1615
pub fn new() -> Self {
1716
Self {
1817
slot_values: HashMap::new(),
19-
registers: Rc::new(RegisterAllocator::new()),
2018
labels: HashMap::new(),
2119
}
2220
}
@@ -27,7 +25,7 @@ impl CodegenState {
2725
label: &ir::Label,
2826
) -> &mut iced_x86::code_asm::CodeLabel {
2927
self.labels
30-
.entry(*label)
28+
.entry(label.clone())
3129
.or_insert_with(|| assembler.create_label())
3230
}
3331
}

src/codegen/x86_64/error.rs

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,11 @@
11
use crate::value::ValueEncodeError;
22

3-
use super::Register;
4-
53
#[derive(Debug)]
64
pub enum CodegenError {
75
IcedError(iced_x86::IcedError),
86
MmapError(std::io::Error),
97
NotImplemented(String),
108
InternalError(String),
11-
RegisterNotAvailable(Register),
129
ValueEncodeError(ValueEncodeError),
1310
// ValueDecodeError(ValueDecodeError),
1411
}

0 commit comments

Comments
 (0)