diff --git a/Cargo.lock b/Cargo.lock index bfbcf5b91f..76b426922a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1829,13 +1829,13 @@ dependencies = [ name = "inkwell" version = "0.1.0" dependencies = [ - "inkwell 0.1.0 (git+https://github.com/rtfeldman/inkwell?tag=llvm12-0.release6)", + "inkwell 0.1.0 (git+https://github.com/rtfeldman/inkwell?tag=llvm12-0.release8)", ] [[package]] name = "inkwell" version = "0.1.0" -source = "git+https://github.com/rtfeldman/inkwell?tag=llvm12-0.release6#b1b464aabaf259b42a88d8c7506619b9d9b56510" +source = "git+https://github.com/rtfeldman/inkwell?tag=llvm12-0.release8#14b78d96d2dbc95694e181be66e4cd53df3fc02f" dependencies = [ "either", "inkwell_internals", @@ -1849,7 +1849,7 @@ dependencies = [ [[package]] name = "inkwell_internals" version = "0.3.0" -source = "git+https://github.com/rtfeldman/inkwell?tag=llvm12-0.release6#b1b464aabaf259b42a88d8c7506619b9d9b56510" +source = "git+https://github.com/rtfeldman/inkwell?tag=llvm12-0.release8#14b78d96d2dbc95694e181be66e4cd53df3fc02f" dependencies = [ "proc-macro2 1.0.27", "quote 1.0.9", @@ -2034,9 +2034,9 @@ checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3" [[package]] name = "llvm-sys" -version = "120.2.0" +version = "120.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cd0739fb23e5d801f6ff11a9d587448bdd86dea2459ba2b57fbed90a9ae1b5a" +checksum = "b4a810627ac62b396f5fd2214ba9bbd8748d4d6efdc4d2c1c1303ea7a75763ce" dependencies = [ "cc", "lazy_static", diff --git a/compiler/gen_dev/src/generic64/mod.rs b/compiler/gen_dev/src/generic64/mod.rs index 545a05cac0..171a92ca9a 100644 --- a/compiler/gen_dev/src/generic64/mod.rs +++ b/compiler/gen_dev/src/generic64/mod.rs @@ -175,13 +175,26 @@ pub trait Assembler { } #[derive(Clone, Debug, PartialEq)] -#[allow(dead_code)] pub enum SymbolStorage { GeneralReg(GeneralReg), FloatReg(FloatReg), - Base(i32), - BaseAndGeneralReg(GeneralReg, i32), - BaseAndFloatReg(FloatReg, i32), + Base { + offset: i32, + size: u32, + owned: bool, + }, + BaseAndGeneralReg { + reg: GeneralReg, + offset: i32, + size: u32, + owned: bool, + }, + BaseAndFloatReg { + reg: FloatReg, + offset: i32, + size: u32, + owned: bool, + }, } pub trait RegTrait: Copy + Eq + std::hash::Hash + std::fmt::Debug + 'static {} @@ -220,6 +233,7 @@ pub struct Backend64Bit< general_used_callee_saved_regs: MutSet, float_used_callee_saved_regs: MutSet, + free_stack_chunks: Vec<'a, (i32, u32)>, stack_size: u32, // The amount of stack space needed to pass args for function calling. fn_call_stack_size: u32, @@ -251,6 +265,7 @@ impl< float_free_regs: bumpalo::vec![in env.arena], float_used_regs: bumpalo::vec![in env.arena], float_used_callee_saved_regs: MutSet::default(), + free_stack_chunks: bumpalo::vec![in env.arena], stack_size: 0, fn_call_stack_size: 0, }) @@ -262,6 +277,7 @@ impl< fn reset(&mut self) { self.stack_size = 0; + self.free_stack_chunks.clear(); self.fn_call_stack_size = 0; self.last_seen_map.clear(); self.layout_map.clear(); @@ -355,15 +371,15 @@ impl< // Update used and free regs. for (sym, storage) in &self.symbol_storage_map { match storage { - SymbolStorage::GeneralReg(reg) | SymbolStorage::BaseAndGeneralReg(reg, _) => { + SymbolStorage::GeneralReg(reg) | SymbolStorage::BaseAndGeneralReg { reg, .. } => { self.general_free_regs.retain(|r| *r != *reg); self.general_used_regs.push((*reg, *sym)); } - SymbolStorage::FloatReg(reg) | SymbolStorage::BaseAndFloatReg(reg, _) => { + SymbolStorage::FloatReg(reg) | SymbolStorage::BaseAndFloatReg { reg, .. } => { self.float_free_regs.retain(|r| *r != *reg); self.float_used_regs.push((*reg, *sym)); } - SymbolStorage::Base(_) => {} + SymbolStorage::Base { .. } => {} } } Ok(()) @@ -625,9 +641,15 @@ impl< if let Layout::Struct(field_layouts) = layout { let struct_size = layout.stack_size(PTR_SIZE); if struct_size > 0 { - let offset = self.increase_stack_size(struct_size)?; - self.symbol_storage_map - .insert(*sym, SymbolStorage::Base(offset)); + let offset = self.claim_stack_size(struct_size)?; + self.symbol_storage_map.insert( + *sym, + SymbolStorage::Base { + offset, + size: struct_size, + owned: true, + }, + ); let mut current_offset = offset; for (field, field_layout) in fields.iter().zip(field_layouts.iter()) { @@ -636,15 +658,28 @@ impl< current_offset += field_size as i32; } } else { - self.symbol_storage_map.insert(*sym, SymbolStorage::Base(0)); + self.symbol_storage_map.insert( + *sym, + SymbolStorage::Base { + offset: 0, + size: 0, + owned: false, + }, + ); } Ok(()) } else { // This is a single element struct. Just copy the single field to the stack. let struct_size = layout.stack_size(PTR_SIZE); - let offset = self.increase_stack_size(struct_size)?; - self.symbol_storage_map - .insert(*sym, SymbolStorage::Base(offset)); + let offset = self.claim_stack_size(struct_size)?; + self.symbol_storage_map.insert( + *sym, + SymbolStorage::Base { + offset, + size: struct_size, + owned: true, + }, + ); self.copy_symbol_to_stack_offset(offset, &fields[0], layout)?; Ok(()) } @@ -657,14 +692,20 @@ impl< index: u64, field_layouts: &'a [Layout<'a>], ) -> Result<(), String> { - if let Some(SymbolStorage::Base(struct_offset)) = self.symbol_storage_map.get(structure) { - let mut data_offset = *struct_offset; + if let Some(SymbolStorage::Base { offset, .. }) = self.symbol_storage_map.get(structure) { + let mut data_offset = *offset; for i in 0..index { let field_size = field_layouts[i as usize].stack_size(PTR_SIZE); data_offset += field_size as i32; } - self.symbol_storage_map - .insert(*sym, SymbolStorage::Base(data_offset)); + self.symbol_storage_map.insert( + *sym, + SymbolStorage::Base { + offset: data_offset, + size: field_layouts[index as usize].stack_size(PTR_SIZE), + owned: false, + }, + ); Ok(()) } else { Err(format!("unknown struct: {:?}", structure)) @@ -689,8 +730,82 @@ impl< } } - fn free_symbol(&mut self, sym: &Symbol) { - self.symbol_storage_map.remove(sym); + fn free_symbol(&mut self, sym: &Symbol) -> Result<(), String> { + match self.symbol_storage_map.remove(sym) { + Some( + SymbolStorage::Base { + offset, + size, + owned: true, + } + | SymbolStorage::BaseAndGeneralReg { + offset, + size, + owned: true, + .. + } + | SymbolStorage::BaseAndFloatReg { + offset, + size, + owned: true, + .. + }, + ) => { + let loc = (offset, size); + // Note: this position current points to the offset following the specified location. + // If loc was inserted at this position, it would shift the data at this position over by 1. + let pos = self + .free_stack_chunks + .binary_search(&loc) + .unwrap_or_else(|e| e); + + // Check for overlap with previous and next free chunk. + let merge_with_prev = if pos > 0 { + if let Some((prev_offset, prev_size)) = self.free_stack_chunks.get(pos - 1) { + let prev_end = *prev_offset + *prev_size as i32; + if prev_end > offset { + return Err("Double free? A previously freed stack location overlaps with the currently freed stack location.".to_string()); + } + prev_end == offset + } else { + false + } + } else { + false + }; + let merge_with_next = if let Some((next_offset, _)) = + self.free_stack_chunks.get(pos) + { + let current_end = offset + size as i32; + if current_end > *next_offset { + return Err("Double free? A previously freed stack location overlaps with the currently freed stack location.".to_string()); + } + current_end == *next_offset + } else { + false + }; + + match (merge_with_prev, merge_with_next) { + (true, true) => { + let (prev_offset, prev_size) = self.free_stack_chunks[pos - 1]; + let (_, next_size) = self.free_stack_chunks[pos]; + self.free_stack_chunks[pos - 1] = + (prev_offset, prev_size + size + next_size); + self.free_stack_chunks.remove(pos); + } + (true, false) => { + let (prev_offset, prev_size) = self.free_stack_chunks[pos - 1]; + self.free_stack_chunks[pos - 1] = (prev_offset, prev_size + size); + } + (false, true) => { + let (_, next_size) = self.free_stack_chunks[pos]; + self.free_stack_chunks[pos] = (offset, next_size + size); + } + (false, false) => self.free_stack_chunks.insert(pos, loc), + } + } + Some(_) | None => {} + } for i in 0..self.general_used_regs.len() { let (reg, saved_sym) = self.general_used_regs[i]; if saved_sym == *sym { @@ -707,6 +822,7 @@ impl< break; } } + Ok(()) } fn return_symbol(&mut self, sym: &Symbol, layout: &Layout<'a>) -> Result<(), String> { @@ -724,7 +840,7 @@ impl< ASM::mov_freg64_freg64(&mut self.buf, CC::FLOAT_RETURN_REGS[0], *reg); Ok(()) } - Some(SymbolStorage::Base(offset)) => match layout { + Some(SymbolStorage::Base { offset, size, .. }) => match layout { Layout::Builtin(Builtin::Int64) => { ASM::mov_reg64_base32(&mut self.buf, CC::GENERAL_RETURN_REGS[0], *offset); Ok(()) @@ -734,28 +850,15 @@ impl< Ok(()) } Layout::Struct(field_layouts) => { - let struct_size = layout.stack_size(PTR_SIZE); - if struct_size > 0 { - let struct_offset = if let Some(SymbolStorage::Base(offset)) = - self.symbol_storage_map.get(sym) - { - Ok(*offset) - } else { - Err(format!("unknown struct: {:?}", sym)) - }?; + let (offset, size) = (*offset, *size); + if size > 0 { let ret_reg = if self.symbol_storage_map.contains_key(&Symbol::RET_POINTER) { Some(self.load_to_general_reg(&Symbol::RET_POINTER)?) } else { None }; - CC::return_struct( - &mut self.buf, - struct_offset, - struct_size, - field_layouts, - ret_reg, - ) + CC::return_struct(&mut self.buf, offset, size, field_layouts, ret_reg) } else { // Nothing to do for empty struct Ok(()) @@ -854,19 +957,42 @@ impl< .insert(*sym, SymbolStorage::GeneralReg(reg)); Ok(reg) } - Some(SymbolStorage::Base(offset)) => { + Some(SymbolStorage::Base { + offset, + size, + owned, + }) => { let reg = self.claim_general_reg(sym)?; - self.symbol_storage_map - .insert(*sym, SymbolStorage::BaseAndGeneralReg(reg, offset)); + self.symbol_storage_map.insert( + *sym, + SymbolStorage::BaseAndGeneralReg { + reg, + offset, + size, + owned, + }, + ); ASM::mov_reg64_base32(&mut self.buf, reg, offset as i32); Ok(reg) } - Some(SymbolStorage::BaseAndGeneralReg(reg, offset)) => { - self.symbol_storage_map - .insert(*sym, SymbolStorage::BaseAndGeneralReg(reg, offset)); + Some(SymbolStorage::BaseAndGeneralReg { + reg, + offset, + size, + owned, + }) => { + self.symbol_storage_map.insert( + *sym, + SymbolStorage::BaseAndGeneralReg { + reg, + offset, + size, + owned, + }, + ); Ok(reg) } - Some(SymbolStorage::FloatReg(_)) | Some(SymbolStorage::BaseAndFloatReg(_, _)) => { + Some(SymbolStorage::FloatReg(_)) | Some(SymbolStorage::BaseAndFloatReg { .. }) => { Err("Cannot load floating point symbol into GeneralReg".to_string()) } None => Err(format!("Unknown symbol: {}", sym)), @@ -881,19 +1007,42 @@ impl< .insert(*sym, SymbolStorage::FloatReg(reg)); Ok(reg) } - Some(SymbolStorage::Base(offset)) => { + Some(SymbolStorage::Base { + offset, + size, + owned, + }) => { let reg = self.claim_float_reg(sym)?; - self.symbol_storage_map - .insert(*sym, SymbolStorage::BaseAndFloatReg(reg, offset)); + self.symbol_storage_map.insert( + *sym, + SymbolStorage::BaseAndFloatReg { + reg, + offset, + size, + owned, + }, + ); ASM::mov_freg64_base32(&mut self.buf, reg, offset as i32); Ok(reg) } - Some(SymbolStorage::BaseAndFloatReg(reg, offset)) => { - self.symbol_storage_map - .insert(*sym, SymbolStorage::BaseAndFloatReg(reg, offset)); + Some(SymbolStorage::BaseAndFloatReg { + reg, + offset, + size, + owned, + }) => { + self.symbol_storage_map.insert( + *sym, + SymbolStorage::BaseAndFloatReg { + reg, + offset, + size, + owned, + }, + ); Ok(reg) } - Some(SymbolStorage::GeneralReg(_)) | Some(SymbolStorage::BaseAndGeneralReg(_, _)) => { + Some(SymbolStorage::GeneralReg(_)) | Some(SymbolStorage::BaseAndGeneralReg { .. }) => { Err("Cannot load integer symbol into FloatReg".to_string()) } None => Err(format!("Unknown symbol: {}", sym)), @@ -904,45 +1053,107 @@ impl< let val = self.symbol_storage_map.remove(sym); match val { Some(SymbolStorage::GeneralReg(reg)) => { - let offset = self.increase_stack_size(8)?; + let offset = self.claim_stack_size(8)?; // For base addresssing, use the negative offset - 8. ASM::mov_base32_reg64(&mut self.buf, offset, reg); - self.symbol_storage_map - .insert(*sym, SymbolStorage::Base(offset)); + self.symbol_storage_map.insert( + *sym, + SymbolStorage::Base { + offset, + size: 8, + owned: true, + }, + ); Ok(()) } Some(SymbolStorage::FloatReg(reg)) => { - let offset = self.increase_stack_size(8)?; + let offset = self.claim_stack_size(8)?; // For base addresssing, use the negative offset. ASM::mov_base32_freg64(&mut self.buf, offset, reg); - self.symbol_storage_map - .insert(*sym, SymbolStorage::Base(offset)); + self.symbol_storage_map.insert( + *sym, + SymbolStorage::Base { + offset, + size: 8, + owned: true, + }, + ); Ok(()) } - Some(SymbolStorage::Base(offset)) => { - self.symbol_storage_map - .insert(*sym, SymbolStorage::Base(offset)); + Some(SymbolStorage::Base { + offset, + size, + owned, + }) => { + self.symbol_storage_map.insert( + *sym, + SymbolStorage::Base { + offset, + size, + owned, + }, + ); Ok(()) } - Some(SymbolStorage::BaseAndGeneralReg(_, offset)) => { - self.symbol_storage_map - .insert(*sym, SymbolStorage::Base(offset)); + Some(SymbolStorage::BaseAndGeneralReg { + offset, + size, + owned, + .. + }) => { + self.symbol_storage_map.insert( + *sym, + SymbolStorage::Base { + offset, + size, + owned, + }, + ); Ok(()) } - Some(SymbolStorage::BaseAndFloatReg(_, offset)) => { - self.symbol_storage_map - .insert(*sym, SymbolStorage::Base(offset)); + Some(SymbolStorage::BaseAndFloatReg { + offset, + size, + owned, + .. + }) => { + self.symbol_storage_map.insert( + *sym, + SymbolStorage::Base { + offset, + size, + owned, + }, + ); Ok(()) } None => Err(format!("Unknown symbol: {}", sym)), } } - /// increase_stack_size increase the current stack size `amount` bytes. + /// claim_stack_size claims `amount` bytes from the stack. + /// This may be free space in the stack or result in increasing the stack size. /// It returns base pointer relative offset of the new data. - fn increase_stack_size(&mut self, amount: u32) -> Result { + fn claim_stack_size(&mut self, amount: u32) -> Result { debug_assert!(amount > 0); - if let Some(new_size) = self.stack_size.checked_add(amount) { + if let Some(fitting_chunk) = self + .free_stack_chunks + .iter() + .enumerate() + .filter(|(_, (_, size))| *size >= amount) + .min_by_key(|(_, (_, size))| size) + { + let (pos, (offset, size)) = fitting_chunk; + let (offset, size) = (*offset, *size); + if size == amount { + self.free_stack_chunks.remove(pos); + Ok(offset) + } else { + let (prev_offset, prev_size) = self.free_stack_chunks[pos]; + self.free_stack_chunks[pos] = (prev_offset + amount as i32, prev_size - amount); + Ok(prev_offset) + } + } else if let Some(new_size) = self.stack_size.checked_add(amount) { // Since stack size is u32, but the max offset is i32, if we pass i32 max, we have overflowed. if new_size > i32::MAX as u32 { Err("Ran out of stack space".to_string()) @@ -975,7 +1186,17 @@ impl< } Layout::Struct(_) if layout.safe_to_memcpy() => { let tmp_reg = self.get_tmp_general_reg()?; - if let Some(SymbolStorage::Base(from_offset)) = self.symbol_storage_map.get(sym) { + if let Some(SymbolStorage::Base { + offset: from_offset, + size, + .. + }) = self.symbol_storage_map.get(sym) + { + debug_assert_eq!( + *size, + layout.stack_size(PTR_SIZE), + "expected struct to have same size as data being stored in it" + ); for i in 0..layout.stack_size(PTR_SIZE) as i32 { ASM::mov_reg64_base32(&mut self.buf, tmp_reg, from_offset + i); ASM::mov_base32_reg64(&mut self.buf, to_offset + i, tmp_reg); diff --git a/compiler/gen_dev/src/generic64/x86_64.rs b/compiler/gen_dev/src/generic64/x86_64.rs index a1b2f27887..d052c44e43 100644 --- a/compiler/gen_dev/src/generic64/x86_64.rs +++ b/compiler/gen_dev/src/generic64/x86_64.rs @@ -200,7 +200,14 @@ impl CallConv for X86_64SystemV { general_i += 1; } else { base_offset += 8; - symbol_map.insert(*sym, SymbolStorage::Base(base_offset)); + symbol_map.insert( + *sym, + SymbolStorage::Base { + offset: base_offset, + size: 8, + owned: true, + }, + ); } } Layout::Builtin(Builtin::Float64) => { @@ -212,7 +219,14 @@ impl CallConv for X86_64SystemV { float_i += 1; } else { base_offset += 8; - symbol_map.insert(*sym, SymbolStorage::Base(base_offset)); + symbol_map.insert( + *sym, + SymbolStorage::Base { + offset: base_offset, + size: 8, + owned: true, + }, + ); } } x => { @@ -260,13 +274,13 @@ impl CallConv for X86_64SystemV { .ok_or("function argument does not reference any symbol")? { SymbolStorage::GeneralReg(reg) - | SymbolStorage::BaseAndGeneralReg(reg, _) => { + | SymbolStorage::BaseAndGeneralReg { reg, .. } => { X86_64Assembler::mov_reg64_reg64(buf, dst, *reg); } - SymbolStorage::Base(offset) => { + SymbolStorage::Base { offset, .. } => { X86_64Assembler::mov_reg64_base32(buf, dst, *offset); } - SymbolStorage::FloatReg(_) | SymbolStorage::BaseAndFloatReg(_, _) => { + SymbolStorage::FloatReg(_) | SymbolStorage::BaseAndFloatReg { .. } => { return Err( "Cannot load floating point symbol into GeneralReg".to_string() ) @@ -280,10 +294,10 @@ impl CallConv for X86_64SystemV { .ok_or("function argument does not reference any symbol")? { SymbolStorage::GeneralReg(reg) - | SymbolStorage::BaseAndGeneralReg(reg, _) => { + | SymbolStorage::BaseAndGeneralReg { reg, .. } => { X86_64Assembler::mov_stack32_reg64(buf, stack_offset, *reg); } - SymbolStorage::Base(offset) => { + SymbolStorage::Base { offset, .. } => { // Use RAX as a tmp reg because it will be free before function calls. X86_64Assembler::mov_reg64_base32( buf, @@ -296,7 +310,7 @@ impl CallConv for X86_64SystemV { X86_64GeneralReg::RAX, ); } - SymbolStorage::FloatReg(_) | SymbolStorage::BaseAndFloatReg(_, _) => { + SymbolStorage::FloatReg(_) | SymbolStorage::BaseAndFloatReg { .. } => { return Err( "Cannot load floating point symbol into GeneralReg".to_string() ) @@ -314,14 +328,14 @@ impl CallConv for X86_64SystemV { .ok_or("function argument does not reference any symbol")? { SymbolStorage::FloatReg(reg) - | SymbolStorage::BaseAndFloatReg(reg, _) => { + | SymbolStorage::BaseAndFloatReg { reg, .. } => { X86_64Assembler::mov_freg64_freg64(buf, dst, *reg); } - SymbolStorage::Base(offset) => { + SymbolStorage::Base { offset, .. } => { X86_64Assembler::mov_freg64_base32(buf, dst, *offset); } SymbolStorage::GeneralReg(_) - | SymbolStorage::BaseAndGeneralReg(_, _) => { + | SymbolStorage::BaseAndGeneralReg { .. } => { return Err("Cannot load general symbol into FloatReg".to_string()) } } @@ -333,10 +347,10 @@ impl CallConv for X86_64SystemV { .ok_or("function argument does not reference any symbol")? { SymbolStorage::FloatReg(reg) - | SymbolStorage::BaseAndFloatReg(reg, _) => { + | SymbolStorage::BaseAndFloatReg { reg, .. } => { X86_64Assembler::mov_stack32_freg64(buf, stack_offset, *reg); } - SymbolStorage::Base(offset) => { + SymbolStorage::Base { offset, .. } => { // Use XMM0 as a tmp reg because it will be free before function calls. X86_64Assembler::mov_freg64_base32( buf, @@ -350,7 +364,7 @@ impl CallConv for X86_64SystemV { ); } SymbolStorage::GeneralReg(_) - | SymbolStorage::BaseAndGeneralReg(_, _) => { + | SymbolStorage::BaseAndGeneralReg { .. } => { return Err("Cannot load general symbol into FloatReg".to_string()) } } @@ -541,7 +555,14 @@ impl CallConv for X86_64WindowsFastcall { )); } }; - symbol_map.insert(*sym, SymbolStorage::Base(base_offset)); + symbol_map.insert( + *sym, + SymbolStorage::Base { + offset: base_offset, + size: 8, + owned: true, + }, + ); } } Ok(()) @@ -580,13 +601,13 @@ impl CallConv for X86_64WindowsFastcall { .ok_or("function argument does not reference any symbol")? { SymbolStorage::GeneralReg(reg) - | SymbolStorage::BaseAndGeneralReg(reg, _) => { + | SymbolStorage::BaseAndGeneralReg { reg, .. } => { X86_64Assembler::mov_reg64_reg64(buf, dst, *reg); } - SymbolStorage::Base(offset) => { + SymbolStorage::Base { offset, .. } => { X86_64Assembler::mov_reg64_base32(buf, dst, *offset); } - SymbolStorage::FloatReg(_) | SymbolStorage::BaseAndFloatReg(_, _) => { + SymbolStorage::FloatReg(_) | SymbolStorage::BaseAndFloatReg { .. } => { return Err( "Cannot load floating point symbol into GeneralReg".to_string() ) @@ -600,10 +621,10 @@ impl CallConv for X86_64WindowsFastcall { .ok_or("function argument does not reference any symbol")? { SymbolStorage::GeneralReg(reg) - | SymbolStorage::BaseAndGeneralReg(reg, _) => { + | SymbolStorage::BaseAndGeneralReg { reg, .. } => { X86_64Assembler::mov_stack32_reg64(buf, stack_offset, *reg); } - SymbolStorage::Base(offset) => { + SymbolStorage::Base { offset, .. } => { // Use RAX as a tmp reg because it will be free before function calls. X86_64Assembler::mov_reg64_base32( buf, @@ -616,7 +637,7 @@ impl CallConv for X86_64WindowsFastcall { X86_64GeneralReg::RAX, ); } - SymbolStorage::FloatReg(_) | SymbolStorage::BaseAndFloatReg(_, _) => { + SymbolStorage::FloatReg(_) | SymbolStorage::BaseAndFloatReg { .. } => { return Err( "Cannot load floating point symbol into GeneralReg".to_string() ) @@ -634,14 +655,14 @@ impl CallConv for X86_64WindowsFastcall { .ok_or("function argument does not reference any symbol")? { SymbolStorage::FloatReg(reg) - | SymbolStorage::BaseAndFloatReg(reg, _) => { + | SymbolStorage::BaseAndFloatReg { reg, .. } => { X86_64Assembler::mov_freg64_freg64(buf, dst, *reg); } - SymbolStorage::Base(offset) => { + SymbolStorage::Base { offset, .. } => { X86_64Assembler::mov_freg64_base32(buf, dst, *offset); } SymbolStorage::GeneralReg(_) - | SymbolStorage::BaseAndGeneralReg(_, _) => { + | SymbolStorage::BaseAndGeneralReg { .. } => { return Err("Cannot load general symbol into FloatReg".to_string()) } } @@ -653,10 +674,10 @@ impl CallConv for X86_64WindowsFastcall { .ok_or("function argument does not reference any symbol")? { SymbolStorage::FloatReg(reg) - | SymbolStorage::BaseAndFloatReg(reg, _) => { + | SymbolStorage::BaseAndFloatReg { reg, .. } => { X86_64Assembler::mov_stack32_freg64(buf, stack_offset, *reg); } - SymbolStorage::Base(offset) => { + SymbolStorage::Base { offset, .. } => { // Use XMM0 as a tmp reg because it will be free before function calls. X86_64Assembler::mov_freg64_base32( buf, @@ -670,7 +691,7 @@ impl CallConv for X86_64WindowsFastcall { ); } SymbolStorage::GeneralReg(_) - | SymbolStorage::BaseAndGeneralReg(_, _) => { + | SymbolStorage::BaseAndGeneralReg { .. } => { return Err("Cannot load general symbol into FloatReg".to_string()) } } diff --git a/compiler/gen_dev/src/lib.rs b/compiler/gen_dev/src/lib.rs index ea6aac119d..d008440d67 100644 --- a/compiler/gen_dev/src/lib.rs +++ b/compiler/gen_dev/src/lib.rs @@ -98,14 +98,14 @@ where Stmt::Let(sym, expr, layout, following) => { self.build_expr(sym, expr, layout)?; self.set_layout_map(*sym, layout)?; - self.free_symbols(stmt); + self.free_symbols(stmt)?; self.build_stmt(following, ret_layout)?; Ok(()) } Stmt::Ret(sym) => { self.load_literal_symbols(&[*sym])?; self.return_symbol(sym, ret_layout)?; - self.free_symbols(stmt); + self.free_symbols(stmt)?; Ok(()) } Stmt::Switch { @@ -123,7 +123,7 @@ where default_branch, ret_layout, )?; - self.free_symbols(stmt); + self.free_symbols(stmt)?; Ok(()) } x => Err(format!("the statement, {:?}, is not yet implemented", x)), @@ -509,21 +509,30 @@ where fn return_symbol(&mut self, sym: &Symbol, layout: &Layout<'a>) -> Result<(), String>; /// free_symbols will free all symbols for the given statement. - fn free_symbols(&mut self, stmt: &Stmt<'a>) { + fn free_symbols(&mut self, stmt: &Stmt<'a>) -> Result<(), String> { if let Some(syms) = self.free_map().remove(&(stmt as *const Stmt<'a>)) { for sym in syms { - //println!("Freeing symbol: {:?}", sym); - self.free_symbol(&sym); + // println!("Freeing symbol: {:?}", sym); + self.free_symbol(&sym)?; } } + Ok(()) } /// free_symbol frees any registers or stack space used to hold a symbol. - fn free_symbol(&mut self, sym: &Symbol); + fn free_symbol(&mut self, sym: &Symbol) -> Result<(), String>; /// set_last_seen sets the statement a symbol was last seen in. - fn set_last_seen(&mut self, sym: Symbol, stmt: &Stmt<'a>) { + fn set_last_seen( + &mut self, + sym: Symbol, + stmt: &Stmt<'a>, + owning_symbol: &MutMap, + ) { self.last_seen_map().insert(sym, stmt); + if let Some(parent) = owning_symbol.get(&sym) { + self.last_seen_map().insert(*parent, stmt); + } } /// last_seen_map gets the map from symbol to when it is last seen in the function. @@ -573,36 +582,44 @@ where /// scan_ast runs through the ast and fill the last seen map. /// This must iterate through the ast in the same way that build_stmt does. i.e. then before else. fn scan_ast(&mut self, stmt: &Stmt<'a>) { + // This keeps track of symbols that depend on other symbols. + // The main case of this is data in structures and tagged unions. + // This data must extend the lifetime of the original structure or tagged union. + // For arrays the loading is always done through low levels and does not depend on the underlying array's lifetime. + let mut owning_symbol: MutMap = MutMap::default(); match stmt { Stmt::Let(sym, expr, _, following) => { - self.set_last_seen(*sym, stmt); + self.set_last_seen(*sym, stmt, &owning_symbol); match expr { Expr::Literal(_) => {} - Expr::Call(call) => self.scan_ast_call(call, stmt), + Expr::Call(call) => self.scan_ast_call(call, stmt, &owning_symbol), Expr::Tag { arguments, .. } => { for sym in *arguments { - self.set_last_seen(*sym, stmt); + self.set_last_seen(*sym, stmt, &owning_symbol); } } Expr::Struct(syms) => { for sym in *syms { - self.set_last_seen(*sym, stmt); + self.set_last_seen(*sym, stmt, &owning_symbol); } } Expr::StructAtIndex { structure, .. } => { - self.set_last_seen(*structure, stmt); + self.set_last_seen(*structure, stmt, &owning_symbol); + owning_symbol.insert(*sym, *structure); } Expr::GetTagId { structure, .. } => { - self.set_last_seen(*structure, stmt); + self.set_last_seen(*structure, stmt, &owning_symbol); + owning_symbol.insert(*sym, *structure); } Expr::UnionAtIndex { structure, .. } => { - self.set_last_seen(*structure, stmt); + self.set_last_seen(*structure, stmt, &owning_symbol); + owning_symbol.insert(*sym, *structure); } Expr::Array { elems, .. } => { for sym in *elems { - self.set_last_seen(*sym, stmt); + self.set_last_seen(*sym, stmt, &owning_symbol); } } Expr::Reuse { @@ -611,22 +628,22 @@ where tag_name, .. } => { - self.set_last_seen(*symbol, stmt); + self.set_last_seen(*symbol, stmt, &owning_symbol); match tag_name { TagName::Closure(sym) => { - self.set_last_seen(*sym, stmt); + self.set_last_seen(*sym, stmt, &owning_symbol); } TagName::Private(sym) => { - self.set_last_seen(*sym, stmt); + self.set_last_seen(*sym, stmt, &owning_symbol); } TagName::Global(_) => {} } for sym in *arguments { - self.set_last_seen(*sym, stmt); + self.set_last_seen(*sym, stmt, &owning_symbol); } } Expr::Reset(sym) => { - self.set_last_seen(*sym, stmt); + self.set_last_seen(*sym, stmt, &owning_symbol); } Expr::EmptyArray => {} Expr::RuntimeErrorFunction(_) => {} @@ -640,19 +657,19 @@ where default_branch, .. } => { - self.set_last_seen(*cond_symbol, stmt); + self.set_last_seen(*cond_symbol, stmt, &owning_symbol); for (_, _, branch) in *branches { self.scan_ast(branch); } self.scan_ast(default_branch.1); } Stmt::Ret(sym) => { - self.set_last_seen(*sym, stmt); + self.set_last_seen(*sym, stmt, &owning_symbol); } Stmt::Refcounting(modify, following) => { let sym = modify.get_symbol(); - self.set_last_seen(sym, stmt); + self.set_last_seen(sym, stmt, &owning_symbol); self.scan_ast(following); } Stmt::Join { @@ -662,29 +679,34 @@ where .. } => { for param in *parameters { - self.set_last_seen(param.symbol, stmt); + self.set_last_seen(param.symbol, stmt, &owning_symbol); } self.scan_ast(continuation); self.scan_ast(remainder); } Stmt::Jump(JoinPointId(sym), symbols) => { - self.set_last_seen(*sym, stmt); + self.set_last_seen(*sym, stmt, &owning_symbol); for sym in *symbols { - self.set_last_seen(*sym, stmt); + self.set_last_seen(*sym, stmt, &owning_symbol); } } Stmt::RuntimeError(_) => {} } } - fn scan_ast_call(&mut self, call: &roc_mono::ir::Call, stmt: &roc_mono::ir::Stmt<'a>) { + fn scan_ast_call( + &mut self, + call: &roc_mono::ir::Call, + stmt: &roc_mono::ir::Stmt<'a>, + owning_symbol: &MutMap, + ) { let roc_mono::ir::Call { call_type, arguments, } = call; for sym in *arguments { - self.set_last_seen(*sym, stmt); + self.set_last_seen(*sym, stmt, &owning_symbol); } match call_type {