This commit is contained in:
Janis 2024-09-01 14:28:48 +02:00
parent dd6ce88ad6
commit b0f52da586
3 changed files with 268 additions and 26 deletions

View file

@ -211,6 +211,13 @@ pub enum Inst {
IsGe(bool), IsGe(bool),
/// lhs /// lhs
IsLe(bool), IsLe(bool),
// jrcxz for now
/// lhs, rhs
Branch(u32),
/// lhs
Jump,
/// lhs, rhs
Phi2(Type),
} }
impl Inst { impl Inst {
@ -229,8 +236,11 @@ impl Inst {
| Inst::ConstantSinglePrecision | Inst::ConstantSinglePrecision
| Inst::ConstantDoublePrecision | Inst::ConstantDoublePrecision
| Inst::Cmp(_) | Inst::Cmp(_)
| Inst::Branch(_)
| Inst::Jump
| Inst::Return => None, | Inst::Return => None,
Inst::GetElementPtr(ty) Inst::Phi2(ty)
| Inst::GetElementPtr(ty)
| Inst::Load(ty) | Inst::Load(ty)
| Inst::LoadRegister(ty) | Inst::LoadRegister(ty)
| Inst::Parameter(ty) | Inst::Parameter(ty)
@ -270,6 +280,8 @@ impl Inst {
// TODO: need to account for spilled values eventually; probably move this to `Mir`. // TODO: need to account for spilled values eventually; probably move this to `Mir`.
match self { match self {
Inst::Label Inst::Label
| Inst::Branch(_)
| Inst::Jump
| Inst::ConstantBytes | Inst::ConstantBytes
| Inst::ConstantByte | Inst::ConstantByte
| Inst::ConstantWord | Inst::ConstantWord
@ -312,6 +324,7 @@ impl Inst {
| Inst::IsLt(_) | Inst::IsLt(_)
| Inst::IsGe(_) | Inst::IsGe(_)
| Inst::IsLe(_) | Inst::IsLe(_)
| Inst::Phi2(_)
| Inst::ShiftLeft(_) | Inst::ShiftLeft(_)
| Inst::ShiftRightSigned(_) | Inst::ShiftRightSigned(_)
| Inst::ShiftRightUnsigned(_) => true, | Inst::ShiftRightUnsigned(_) => true,
@ -374,6 +387,11 @@ impl Data {
pub fn binary(lhs: u32, rhs: u32) -> Data { pub fn binary(lhs: u32, rhs: u32) -> Data {
Self { binary: (lhs, rhs) } Self { binary: (lhs, rhs) }
} }
pub fn binary_noderefs(lhs: NodeRef, rhs: NodeRef) -> Data {
Self {
binary: (lhs.0, rhs.0),
}
}
pub fn none() -> Data { pub fn none() -> Data {
Self { none: () } Self { none: () }
} }
@ -731,8 +749,8 @@ impl NodeRef {
} }
/// invalid pseudo-handle to the past-the-end node. /// invalid pseudo-handle to the past-the-end node.
const MAX: Self = NodeRef(u32::MAX); pub const MAX: Self = NodeRef(u32::MAX);
const MIN: Self = NodeRef(0); pub const MIN: Self = NodeRef(0);
} }
impl Display for NodeRef { impl Display for NodeRef {
@ -742,14 +760,18 @@ impl Display for NodeRef {
} }
impl Mir { impl Mir {
fn get_node(&self, node: NodeRef) -> (Inst, Data) { pub fn get_node(&self, node: NodeRef) -> (Inst, Data) {
(self.nodes[node.index()], self.data[node.index()]) (self.nodes[node.index()], self.data[node.index()])
} }
#[allow(dead_code)] #[allow(dead_code)]
fn get_node_mut(&mut self, node: NodeRef) -> (&mut Inst, &mut Data) { pub fn get_node_mut(&mut self, node: NodeRef) -> (&mut Inst, &mut Data) {
(&mut self.nodes[node.index()], &mut self.data[node.index()]) (&mut self.nodes[node.index()], &mut self.data[node.index()])
} }
pub fn set_node_data(&mut self, node: NodeRef, data: Data) {
self.data[node.index()] = data;
}
fn indices(&self) -> impl Iterator<Item = NodeRef> { fn indices(&self) -> impl Iterator<Item = NodeRef> {
(0..self.nodes.len() as u32).map(|n| NodeRef::from(n)) (0..self.nodes.len() as u32).map(|n| NodeRef::from(n))
} }
@ -845,6 +867,15 @@ impl Mir {
}; };
self.push(Inst::Cmp(ty), Data::binary(lhs, rhs)) self.push(Inst::Cmp(ty), Data::binary(lhs, rhs))
} }
pub fn gen_jmp(&mut self, to: u32) -> u32 {
self.push(Inst::Jump, Data::node(to))
}
pub fn gen_branch(&mut self, on: u32, lhs: u32, rhs: u32) -> u32 {
self.push(Inst::Branch(on), Data::binary(lhs, rhs))
}
pub fn gen_phi2(&mut self, ty: Type, lhs: u32, rhs: u32) -> u32 {
self.push(Inst::Phi2(ty), Data::binary(lhs, rhs))
}
pub fn gen_cmp_byte( pub fn gen_cmp_byte(
&mut self, &mut self,
@ -1300,6 +1331,18 @@ impl Mir {
Inst::Return => { Inst::Return => {
writeln!(w, "%{node} = return") writeln!(w, "%{node} = return")
} }
Inst::Jump => {
let lhs = data.as_node();
writeln!(w, "%{node} = jmp %{lhs}")
}
Inst::Branch(condition) => {
let (lhs, rhs) = data.as_binary();
writeln!(w, "%{node} = br bool %{condition}, [%{lhs}, %{rhs}]")
}
Inst::Phi2(ty) => {
let (lhs, rhs) = data.as_binary();
writeln!(w, "%{node} = phi2 [{ty} %{lhs}, {ty} %{rhs}]")
}
} }
} }
@ -1555,6 +1598,9 @@ pub mod liveness {
| Inst::LoadRegister(_) => { | Inst::LoadRegister(_) => {
references.insert((data.as_noderef(), node)); references.insert((data.as_noderef(), node));
} }
Inst::Branch(condition) => {
references.insert((NodeRef(condition), node));
}
Inst::Cmp(_) Inst::Cmp(_)
| Inst::Store(_) | Inst::Store(_)
| Inst::Add(_) | Inst::Add(_)
@ -1571,6 +1617,7 @@ pub mod liveness {
| Inst::BitAnd(_) | Inst::BitAnd(_)
| Inst::BitOr(_) | Inst::BitOr(_)
| Inst::BitXOr(_) | Inst::BitXOr(_)
| Inst::Phi2(_)
| Inst::ShiftLeft(_) | Inst::ShiftLeft(_)
| Inst::ShiftRightSigned(_) | Inst::ShiftRightSigned(_)
| Inst::ShiftRightUnsigned(_) => { | Inst::ShiftRightUnsigned(_) => {
@ -1586,6 +1633,7 @@ pub mod liveness {
// don't want a wildcard match here to make sure new instructions // don't want a wildcard match here to make sure new instructions
// are handled here when they are added. // are handled here when they are added.
Inst::Return Inst::Return
| Inst::Jump
| Inst::Parameter(_) | Inst::Parameter(_)
| Inst::Label | Inst::Label
| Inst::ConstantBytes | Inst::ConstantBytes
@ -1705,6 +1753,15 @@ pub mod liveness {
if let Some(dst) = self.mir.dst_node(noderef) { if let Some(dst) = self.mir.dst_node(noderef) {
_ = self.colors.try_insert(dst, Color::Tentative(color)); _ = self.colors.try_insert(dst, Color::Tentative(color));
} }
// for any Phi(y_1,y_2, y_n) give y_i the color of Phi
// reasonably certain that this will never fail to color all phi nodes the same color.
if let Some(inputs) = self.mir.get_phi_inputs(noderef) {
eprintln!("coloring {inputs:?} {color}");
for node in inputs {
_ = self.colors.insert(node, Color::Tentative(color));
}
}
} }
fn colorise(&mut self) -> BTreeMap<NodeRef, Register> { fn colorise(&mut self) -> BTreeMap<NodeRef, Register> {
@ -1726,6 +1783,16 @@ pub mod liveness {
} }
impl Mir { impl Mir {
fn get_phi_inputs(&self, node: NodeRef) -> Option<Vec<NodeRef>> {
let (inst, data) = self.get_node(node);
match inst {
Inst::Phi2(_) => {
let (lhs, rhs) = data.as_binary_noderefs();
Some([lhs, rhs].to_vec())
}
_ => None,
}
}
/// returns the in/out operand, if it exists: example would be (%node = add rax, rcx) -> rax /// returns the in/out operand, if it exists: example would be (%node = add rax, rcx) -> rax
fn dst_node(&self, node: NodeRef) -> Option<NodeRef> { fn dst_node(&self, node: NodeRef) -> Option<NodeRef> {
// for each node, look at the dst node and see if it has preferred // for each node, look at the dst node and see if it has preferred
@ -1765,9 +1832,10 @@ impl Mir {
| Inst::ConstantDoublePrecision | Inst::ConstantDoublePrecision
| Inst::ExternRef | Inst::ExternRef
| Inst::Alloca | Inst::Alloca
| Inst::Jump
| Inst::Return
| Inst::Store(_) | Inst::Store(_)
| Inst::ReturnValue(_) | Inst::ReturnValue(_)
| Inst::Return
| Inst::SignExtend(_) | Inst::SignExtend(_)
| Inst::ZeroExtend(_) | Inst::ZeroExtend(_)
| Inst::Mul(_) | Inst::Mul(_)
@ -1777,6 +1845,8 @@ impl Mir {
| Inst::Rem(_) | Inst::Rem(_)
| Inst::RemSigned(_) | Inst::RemSigned(_)
| Inst::Cmp(_) | Inst::Cmp(_)
| Inst::Branch(_)
| Inst::Phi2(_)
| Inst::IsEq(_) | Inst::IsEq(_)
| Inst::IsNeq(_) | Inst::IsNeq(_)
| Inst::IsGt(_) | Inst::IsGt(_)
@ -1906,8 +1976,8 @@ impl core::fmt::Display for RipRelative {
pub struct Function { pub struct Function {
name: StringsIndex, name: StringsIndex,
constants: BTreeMap<usize, String>, constants: BTreeMap<usize, String>,
branches: HashMap<StringsIndex, String>, branches: BTreeMap<NodeRef, String>,
current_branch: StringsIndex, current_branch: NodeRef,
stack_offset: u32, stack_offset: u32,
dirty_registers: BTreeSet<Register>, dirty_registers: BTreeSet<Register>,
} }
@ -1916,8 +1986,8 @@ pub struct Function {
impl Function { impl Function {
fn new(name: StringsIndex) -> Self { fn new(name: StringsIndex) -> Self {
let current_branch = StringsIndex::none(); let current_branch = NodeRef::MIN;
let branches = HashMap::from([(current_branch, String::new())]); let branches = BTreeMap::from([(current_branch, String::new())]);
Self { Self {
name, name,
constants: BTreeMap::new(), constants: BTreeMap::new(),
@ -1932,9 +2002,9 @@ impl Function {
fn dirty_register(&mut self, reg: Register) { fn dirty_register(&mut self, reg: Register) {
self.dirty_registers.insert(reg); self.dirty_registers.insert(reg);
} }
fn create_new_branch(&mut self, index: StringsIndex) { fn create_new_branch(&mut self, node: NodeRef) {
self.current_branch = index; self.current_branch = node;
self.branches.insert(index, String::new()); self.branches.insert(node, String::new());
} }
fn get_constant_label(&self, i: usize) -> String { fn get_constant_label(&self, i: usize) -> String {
@ -2009,15 +2079,11 @@ impl Function {
writeln!(w, "mov rbp, rsp")?; writeln!(w, "mov rbp, rsp")?;
writeln!(w, "sub rsp, {}", self.stack_offset)?; writeln!(w, "sub rsp, {}", self.stack_offset)?;
write!( write!(w, "{}", self.branches.remove(&NodeRef::MIN).unwrap())?;
w,
"{}",
self.branches.remove(&StringsIndex::none()).unwrap()
)?;
for (branch, content) in &self.branches { for (branch, content) in &self.branches {
if name != "main" { if name != "main" {
writeln!(w, "{name}_{}:", strings.get_str(*branch))?; writeln!(w, "{name}_L{}:", branch.0)?;
write!(w, "{content}")?; write!(w, "{content}")?;
} }
} }
@ -2076,6 +2142,7 @@ impl Mir {
} }
Inst::GetElementPtr(ty) => liveness.get_register(node.into()).unwrap().into(), Inst::GetElementPtr(ty) => liveness.get_register(node.into()).unwrap().into(),
Inst::Parameter(ty) Inst::Parameter(ty)
| Inst::Phi2(ty)
| Inst::Add(ty) | Inst::Add(ty)
| Inst::Sub(ty) | Inst::Sub(ty)
| Inst::Mul(ty) | Inst::Mul(ty)
@ -2144,7 +2211,7 @@ impl Mir {
match inst { match inst {
Inst::Label => { Inst::Label => {
func.create_new_branch(data.as_index()); func.create_new_branch(NodeRef(node));
} }
Inst::ConstantBytes Inst::ConstantBytes
| Inst::ConstantByte | Inst::ConstantByte
@ -3098,6 +3165,34 @@ impl Mir {
Inst::Return => { Inst::Return => {
writeln!(func.current_branch(), "jmp {name}__epilogue")?; writeln!(func.current_branch(), "jmp {name}__epilogue")?;
} }
Inst::Jump => {
let lhs = data.as_node();
if lhs != node + 1 {
writeln!(func.current_branch(), "jmp {name}__L{lhs}")?;
}
}
Inst::Branch(condition) => {
let cond =
self.node_as_operand(&liveness, &mapping, &mut func, strings, condition);
let (lhs, rhs) = data.as_binary();
writeln!(func.current_branch(), "test {cond}, {cond}")?;
match (lhs, rhs) {
_ if lhs == node + 1 => {
writeln!(func.current_branch(), "jz {name}__L{rhs}")?;
}
_ if rhs == node + 1 => {
writeln!(func.current_branch(), "jnz {name}__L{lhs}")?;
}
_ => {
writeln!(func.current_branch(), "jnz {name}__L{lhs}")?;
writeln!(func.current_branch(), "jz {name}__L{rhs}")?;
}
}
}
Inst::Phi2(ty) => {
// noop, need to ensure that input nodes are merged within their branch
}
} }
} }

View file

@ -1674,6 +1674,14 @@ impl Tree {
Ok(()) Ok(())
} }
pub fn peer_type_of_nodes_unwrap(&self, lhs: Node, rhs: Node) -> Type {
self.peer_type_of_nodes(lhs, rhs).expect({
let at = self.type_of_node(lhs);
let bt = self.type_of_node(rhs);
&format!("incompatible types for %{lhs}({at}) and %{rhs}({bt})")
})
}
pub fn peer_type_of_nodes(&self, lhs: Node, rhs: Node) -> Option<Type> { pub fn peer_type_of_nodes(&self, lhs: Node, rhs: Node) -> Option<Type> {
let lty = self.type_of_node(lhs); let lty = self.type_of_node(lhs);
let rty = self.type_of_node(rhs); let rty = self.type_of_node(rhs);

View file

@ -73,6 +73,22 @@ impl Type2 {
fn align(&self) -> u32 { fn align(&self) -> u32 {
self.size() self.size()
} }
fn try_from_ast_type(ty: &Type) -> Option<Self> {
match ty {
Type::Bool => Some(Type2::Bool),
Type::Integer(i) => Some(Type2::Integral(i.signed, i.bits)),
Type::Floating(f) => match f {
crate::ast::FloatingType::Binary32 => Some(Type2::Binary32),
crate::ast::FloatingType::Binary64 => Some(Type2::Binary64),
},
Type::Pointer { .. } => Some(Type2::Pointer),
_ => {
None
//unimplemented!("conversion from {value:?} to triples type not implemented")
}
}
}
} }
impl core::fmt::Display for Type2 { impl core::fmt::Display for Type2 {
@ -182,6 +198,8 @@ pub enum Inst {
Branch(Node), Branch(Node),
/// lhs: Label /// lhs: Label
Jump, Jump,
/// lhs, rhs
Phi2(Type2),
} }
impl Inst { impl Inst {
@ -555,11 +573,12 @@ impl<'tree, 'ir> IRBuilder<'tree, 'ir> {
let condition = self.visit(*condition); let condition = self.visit(*condition);
let br = self.ir.push(Inst::Branch(condition), None); let br = self.ir.push(Inst::Branch(condition), None);
let lhs = self.visit(*body); let label_lhs = self.ir.push(Inst::Label, Some(StringsIndex::none().into()));
let _ = self.visit(*body);
let jmp = self.ir.push(Inst::Jump, None); let jmp = self.ir.push(Inst::Jump, None);
let nojump = self.ir.push(Inst::Label, Some(StringsIndex::none().into())); let nojump = self.ir.push(Inst::Label, Some(StringsIndex::none().into()));
self.ir.data[br as usize] = Some(Data::new(lhs, nojump)); self.ir.data[br as usize] = Some(Data::new(label_lhs, nojump));
self.ir.data[jmp as usize] = Some(Data::lhs(nojump)); self.ir.data[jmp as usize] = Some(Data::lhs(nojump));
br br
} }
@ -569,21 +588,29 @@ impl<'tree, 'ir> IRBuilder<'tree, 'ir> {
else_expr, else_expr,
} => { } => {
assert_eq!(self.tree.type_of_node(*condition), Type::Bool); assert_eq!(self.tree.type_of_node(*condition), Type::Bool);
let ty = self.tree.peer_type_of_nodes_unwrap(*body, *else_expr);
let condition = self.visit(*condition); let condition = self.visit(*condition);
let br = self.ir.push(Inst::Branch(condition), None); let br = self.ir.push(Inst::Branch(condition), None);
let label_lhs = self.ir.push(Inst::Label, Some(StringsIndex::none().into()));
let lhs = self.visit(*body); let lhs = self.visit(*body);
let ljmp = self.ir.push(Inst::Jump, None); let ljmp = self.ir.push(Inst::Jump, None);
let label_rhs = self.ir.push(Inst::Label, Some(StringsIndex::none().into()));
let rhs = self.visit(*else_expr); let rhs = self.visit(*else_expr);
let rjmp = self.ir.push(Inst::Jump, None); let rjmp = self.ir.push(Inst::Jump, None);
let nojump = self.ir.push(Inst::Label, Some(StringsIndex::none().into())); let nojump = self.ir.push(Inst::Label, Some(StringsIndex::none().into()));
let phi = if let Some(ty) = Type2::try_from_ast_type(&ty) {
self.ir.push(Inst::Phi2(ty), Some(Data::new(lhs, rhs)))
} else {
br
};
self.ir.data[br as usize] = Some(Data::new(lhs, rhs)); self.ir.data[br as usize] = Some(Data::new(label_lhs, label_rhs));
self.ir.data[ljmp as usize] = Some(Data::lhs(nojump)); self.ir.data[ljmp as usize] = Some(Data::lhs(nojump));
self.ir.data[rjmp as usize] = Some(Data::lhs(nojump)); self.ir.data[rjmp as usize] = Some(Data::lhs(nojump));
br phi
} }
_ => { _ => {
dbg!(&self.tree.nodes[node]); dbg!(&self.tree.nodes[node]);
@ -764,12 +791,20 @@ impl<'tree, 'ir> IRBuilder<'tree, 'ir> {
} }
Inst::Branch(condition) => { Inst::Branch(condition) => {
let (lhs, rhs) = data.as_lhs_rhs(); let (lhs, rhs) = data.as_lhs_rhs();
writeln_indented!(indent, w, "%{node} = br bool %{condition} [%{lhs}, %{rhs}]")?; writeln_indented!(
indent,
w,
"%{node} = br bool %{condition}, [%{lhs}, %{rhs}]"
)?;
} }
Inst::Jump => { Inst::Jump => {
let lhs = data.lhs; let lhs = data.lhs;
writeln_indented!(indent, w, "%{node} = jmp %{lhs}")?; writeln_indented!(indent, w, "%{node} = jmp %{lhs}")?;
} }
Inst::Phi2(ty) => {
let (lhs, rhs) = data.as_lhs_rhs();
writeln_indented!(indent, w, "%{node} = phi [{ty} %{lhs}, {ty} %{rhs}]")?;
}
_ => { _ => {
unimplemented!("{inst:?} rendering unimplemented") unimplemented!("{inst:?} rendering unimplemented")
} }
@ -1085,6 +1120,22 @@ impl<'a> MirBuilder<'a> {
fn build_function(&mut self, name: StringsIndex) { fn build_function(&mut self, name: StringsIndex) {
let mut mir = mir::Mir::new(name); let mut mir = mir::Mir::new(name);
let mut mapping = BTreeMap::<u32, u32>::new(); let mut mapping = BTreeMap::<u32, u32>::new();
// map of label -> unresolved mir jump or branch instruction
// stored as a tree of (label, unresolved)
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
enum LeftRight {
Left(mir::NodeRef),
Right(mir::NodeRef),
}
impl LeftRight {
fn noderef(self) -> mir::NodeRef {
match self {
LeftRight::Left(noderef) => noderef,
LeftRight::Right(noderef) => noderef,
}
}
}
let mut unresolved_jumps_branches = BTreeSet::<(Node, LeftRight)>::new();
loop { loop {
let ir_node = self.ir.node(); let ir_node = self.ir.node();
@ -1096,7 +1147,43 @@ impl<'a> MirBuilder<'a> {
self.ir.offset -= 1; self.ir.offset -= 1;
break; break;
} }
Inst::Label => mir.gen_label(data.unwrap().as_index()), Inst::Label => {
let label = mir.gen_label(data.unwrap().as_index());
let range = unresolved_jumps_branches
.range(
(ir_node, LeftRight::Left(mir::NodeRef::MIN))
..=(ir_node, LeftRight::Right(mir::NodeRef::MAX)),
)
.map(|(_, n)| n)
.cloned()
.collect::<Vec<_>>();
for unresolved in range {
unresolved_jumps_branches.remove(&(ir_node, unresolved));
let mir_node = unresolved.noderef();
let (inst, data) = mir.get_node_mut(mir_node);
match inst {
mir::Inst::Jump => {
*data = mir::Data::node(label);
}
mir::Inst::Branch(_) => {
let (lhs, rhs) = data.as_binary_noderefs();
*data = match unresolved {
LeftRight::Left(_) => mir::Data::binary(label, rhs.0),
LeftRight::Right(_) => mir::Data::binary(lhs.0, label),
};
}
_ => {
unreachable!()
}
}
}
label
}
Inst::ConstantU32 => mir.push( Inst::ConstantU32 => mir.push(
mir::Inst::ConstantDWord, mir::Inst::ConstantDWord,
mir::Data::imm32(data.unwrap().as_u32()), mir::Data::imm32(data.unwrap().as_u32()),
@ -1464,6 +1551,58 @@ impl<'a> MirBuilder<'a> {
mir.gen_ret_val(ty.mir_type(), src) mir.gen_ret_val(ty.mir_type(), src)
} }
Inst::Return => mir.gen_ret(), Inst::Return => mir.gen_ret(),
Inst::Jump => {
let label = data.unwrap().as_u32();
let jmp = mir.gen_jmp(label);
let label = match mapping.get(&label) {
Some(&label) => label,
None => {
unresolved_jumps_branches
.insert((label, LeftRight::Left(mir::NodeRef(jmp))));
0
}
};
mir.set_node_data(mir::NodeRef(jmp), mir::Data::node(label));
jmp
}
Inst::Branch(condition) => {
let condition = *mapping.get(&condition).unwrap();
let (lhs, rhs) = data.unwrap().as_lhs_rhs();
let br = mir.gen_branch(condition, lhs, rhs);
let lhs = match mapping.get(&lhs) {
Some(&n) => n,
None => {
unresolved_jumps_branches
.insert((lhs, LeftRight::Left(mir::NodeRef(br))));
0
}
};
let rhs = match mapping.get(&rhs) {
Some(&n) => n,
None => {
unresolved_jumps_branches
.insert((rhs, LeftRight::Right(mir::NodeRef(br))));
0
}
};
mir.set_node_data(mir::NodeRef(br), mir::Data::binary(lhs, rhs));
br
}
Inst::Phi2(ty) => {
let (src, dst) = data.unwrap().as_lhs_rhs();
let lhs = *mapping.get(&src).unwrap();
let rhs = *mapping.get(&dst).unwrap();
mir.gen_phi2(ty.mir_type(), lhs, rhs)
}
#[allow(unreachable_patterns)] #[allow(unreachable_patterns)]
_ => { _ => {
unimplemented!() unimplemented!()