mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-29 14:54:47 +00:00
Update dev backend to allow reusing stack space and using less memory
This commit is contained in:
parent
05bea50730
commit
0696044a5c
4 changed files with 395 additions and 131 deletions
10
Cargo.lock
generated
10
Cargo.lock
generated
|
@ -1829,13 +1829,13 @@ dependencies = [
|
||||||
name = "inkwell"
|
name = "inkwell"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
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]]
|
[[package]]
|
||||||
name = "inkwell"
|
name = "inkwell"
|
||||||
version = "0.1.0"
|
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 = [
|
dependencies = [
|
||||||
"either",
|
"either",
|
||||||
"inkwell_internals",
|
"inkwell_internals",
|
||||||
|
@ -1849,7 +1849,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "inkwell_internals"
|
name = "inkwell_internals"
|
||||||
version = "0.3.0"
|
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 = [
|
dependencies = [
|
||||||
"proc-macro2 1.0.27",
|
"proc-macro2 1.0.27",
|
||||||
"quote 1.0.9",
|
"quote 1.0.9",
|
||||||
|
@ -2034,9 +2034,9 @@ checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "llvm-sys"
|
name = "llvm-sys"
|
||||||
version = "120.2.0"
|
version = "120.2.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7cd0739fb23e5d801f6ff11a9d587448bdd86dea2459ba2b57fbed90a9ae1b5a"
|
checksum = "b4a810627ac62b396f5fd2214ba9bbd8748d4d6efdc4d2c1c1303ea7a75763ce"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cc",
|
"cc",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
|
|
|
@ -175,13 +175,26 @@ pub trait Assembler<GeneralReg: RegTrait, FloatReg: RegTrait> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
#[allow(dead_code)]
|
|
||||||
pub enum SymbolStorage<GeneralReg: RegTrait, FloatReg: RegTrait> {
|
pub enum SymbolStorage<GeneralReg: RegTrait, FloatReg: RegTrait> {
|
||||||
GeneralReg(GeneralReg),
|
GeneralReg(GeneralReg),
|
||||||
FloatReg(FloatReg),
|
FloatReg(FloatReg),
|
||||||
Base(i32),
|
Base {
|
||||||
BaseAndGeneralReg(GeneralReg, i32),
|
offset: i32,
|
||||||
BaseAndFloatReg(FloatReg, 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 {}
|
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<GeneralReg>,
|
general_used_callee_saved_regs: MutSet<GeneralReg>,
|
||||||
float_used_callee_saved_regs: MutSet<FloatReg>,
|
float_used_callee_saved_regs: MutSet<FloatReg>,
|
||||||
|
|
||||||
|
free_stack_chunks: Vec<'a, (i32, u32)>,
|
||||||
stack_size: u32,
|
stack_size: u32,
|
||||||
// The amount of stack space needed to pass args for function calling.
|
// The amount of stack space needed to pass args for function calling.
|
||||||
fn_call_stack_size: u32,
|
fn_call_stack_size: u32,
|
||||||
|
@ -251,6 +265,7 @@ impl<
|
||||||
float_free_regs: bumpalo::vec![in env.arena],
|
float_free_regs: bumpalo::vec![in env.arena],
|
||||||
float_used_regs: bumpalo::vec![in env.arena],
|
float_used_regs: bumpalo::vec![in env.arena],
|
||||||
float_used_callee_saved_regs: MutSet::default(),
|
float_used_callee_saved_regs: MutSet::default(),
|
||||||
|
free_stack_chunks: bumpalo::vec![in env.arena],
|
||||||
stack_size: 0,
|
stack_size: 0,
|
||||||
fn_call_stack_size: 0,
|
fn_call_stack_size: 0,
|
||||||
})
|
})
|
||||||
|
@ -262,6 +277,7 @@ impl<
|
||||||
|
|
||||||
fn reset(&mut self) {
|
fn reset(&mut self) {
|
||||||
self.stack_size = 0;
|
self.stack_size = 0;
|
||||||
|
self.free_stack_chunks.clear();
|
||||||
self.fn_call_stack_size = 0;
|
self.fn_call_stack_size = 0;
|
||||||
self.last_seen_map.clear();
|
self.last_seen_map.clear();
|
||||||
self.layout_map.clear();
|
self.layout_map.clear();
|
||||||
|
@ -355,15 +371,15 @@ impl<
|
||||||
// Update used and free regs.
|
// Update used and free regs.
|
||||||
for (sym, storage) in &self.symbol_storage_map {
|
for (sym, storage) in &self.symbol_storage_map {
|
||||||
match storage {
|
match storage {
|
||||||
SymbolStorage::GeneralReg(reg) | SymbolStorage::BaseAndGeneralReg(reg, _) => {
|
SymbolStorage::GeneralReg(reg) | SymbolStorage::BaseAndGeneralReg { reg, .. } => {
|
||||||
self.general_free_regs.retain(|r| *r != *reg);
|
self.general_free_regs.retain(|r| *r != *reg);
|
||||||
self.general_used_regs.push((*reg, *sym));
|
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_free_regs.retain(|r| *r != *reg);
|
||||||
self.float_used_regs.push((*reg, *sym));
|
self.float_used_regs.push((*reg, *sym));
|
||||||
}
|
}
|
||||||
SymbolStorage::Base(_) => {}
|
SymbolStorage::Base { .. } => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -625,9 +641,15 @@ impl<
|
||||||
if let Layout::Struct(field_layouts) = layout {
|
if let Layout::Struct(field_layouts) = layout {
|
||||||
let struct_size = layout.stack_size(PTR_SIZE);
|
let struct_size = layout.stack_size(PTR_SIZE);
|
||||||
if struct_size > 0 {
|
if struct_size > 0 {
|
||||||
let offset = self.increase_stack_size(struct_size)?;
|
let offset = self.claim_stack_size(struct_size)?;
|
||||||
self.symbol_storage_map
|
self.symbol_storage_map.insert(
|
||||||
.insert(*sym, SymbolStorage::Base(offset));
|
*sym,
|
||||||
|
SymbolStorage::Base {
|
||||||
|
offset,
|
||||||
|
size: struct_size,
|
||||||
|
owned: true,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
let mut current_offset = offset;
|
let mut current_offset = offset;
|
||||||
for (field, field_layout) in fields.iter().zip(field_layouts.iter()) {
|
for (field, field_layout) in fields.iter().zip(field_layouts.iter()) {
|
||||||
|
@ -636,15 +658,28 @@ impl<
|
||||||
current_offset += field_size as i32;
|
current_offset += field_size as i32;
|
||||||
}
|
}
|
||||||
} else {
|
} 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(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
// This is a single element struct. Just copy the single field to the stack.
|
// This is a single element struct. Just copy the single field to the stack.
|
||||||
let struct_size = layout.stack_size(PTR_SIZE);
|
let struct_size = layout.stack_size(PTR_SIZE);
|
||||||
let offset = self.increase_stack_size(struct_size)?;
|
let offset = self.claim_stack_size(struct_size)?;
|
||||||
self.symbol_storage_map
|
self.symbol_storage_map.insert(
|
||||||
.insert(*sym, SymbolStorage::Base(offset));
|
*sym,
|
||||||
|
SymbolStorage::Base {
|
||||||
|
offset,
|
||||||
|
size: struct_size,
|
||||||
|
owned: true,
|
||||||
|
},
|
||||||
|
);
|
||||||
self.copy_symbol_to_stack_offset(offset, &fields[0], layout)?;
|
self.copy_symbol_to_stack_offset(offset, &fields[0], layout)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -657,14 +692,20 @@ impl<
|
||||||
index: u64,
|
index: u64,
|
||||||
field_layouts: &'a [Layout<'a>],
|
field_layouts: &'a [Layout<'a>],
|
||||||
) -> Result<(), String> {
|
) -> Result<(), String> {
|
||||||
if let Some(SymbolStorage::Base(struct_offset)) = self.symbol_storage_map.get(structure) {
|
if let Some(SymbolStorage::Base { offset, .. }) = self.symbol_storage_map.get(structure) {
|
||||||
let mut data_offset = *struct_offset;
|
let mut data_offset = *offset;
|
||||||
for i in 0..index {
|
for i in 0..index {
|
||||||
let field_size = field_layouts[i as usize].stack_size(PTR_SIZE);
|
let field_size = field_layouts[i as usize].stack_size(PTR_SIZE);
|
||||||
data_offset += field_size as i32;
|
data_offset += field_size as i32;
|
||||||
}
|
}
|
||||||
self.symbol_storage_map
|
self.symbol_storage_map.insert(
|
||||||
.insert(*sym, SymbolStorage::Base(data_offset));
|
*sym,
|
||||||
|
SymbolStorage::Base {
|
||||||
|
offset: data_offset,
|
||||||
|
size: field_layouts[index as usize].stack_size(PTR_SIZE),
|
||||||
|
owned: false,
|
||||||
|
},
|
||||||
|
);
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
Err(format!("unknown struct: {:?}", structure))
|
Err(format!("unknown struct: {:?}", structure))
|
||||||
|
@ -689,8 +730,82 @@ impl<
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn free_symbol(&mut self, sym: &Symbol) {
|
fn free_symbol(&mut self, sym: &Symbol) -> Result<(), String> {
|
||||||
self.symbol_storage_map.remove(sym);
|
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() {
|
for i in 0..self.general_used_regs.len() {
|
||||||
let (reg, saved_sym) = self.general_used_regs[i];
|
let (reg, saved_sym) = self.general_used_regs[i];
|
||||||
if saved_sym == *sym {
|
if saved_sym == *sym {
|
||||||
|
@ -707,6 +822,7 @@ impl<
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn return_symbol(&mut self, sym: &Symbol, layout: &Layout<'a>) -> Result<(), String> {
|
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);
|
ASM::mov_freg64_freg64(&mut self.buf, CC::FLOAT_RETURN_REGS[0], *reg);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
Some(SymbolStorage::Base(offset)) => match layout {
|
Some(SymbolStorage::Base { offset, size, .. }) => match layout {
|
||||||
Layout::Builtin(Builtin::Int64) => {
|
Layout::Builtin(Builtin::Int64) => {
|
||||||
ASM::mov_reg64_base32(&mut self.buf, CC::GENERAL_RETURN_REGS[0], *offset);
|
ASM::mov_reg64_base32(&mut self.buf, CC::GENERAL_RETURN_REGS[0], *offset);
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -734,28 +850,15 @@ impl<
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
Layout::Struct(field_layouts) => {
|
Layout::Struct(field_layouts) => {
|
||||||
let struct_size = layout.stack_size(PTR_SIZE);
|
let (offset, size) = (*offset, *size);
|
||||||
if struct_size > 0 {
|
if 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 ret_reg = if self.symbol_storage_map.contains_key(&Symbol::RET_POINTER)
|
let ret_reg = if self.symbol_storage_map.contains_key(&Symbol::RET_POINTER)
|
||||||
{
|
{
|
||||||
Some(self.load_to_general_reg(&Symbol::RET_POINTER)?)
|
Some(self.load_to_general_reg(&Symbol::RET_POINTER)?)
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
CC::return_struct(
|
CC::return_struct(&mut self.buf, offset, size, field_layouts, ret_reg)
|
||||||
&mut self.buf,
|
|
||||||
struct_offset,
|
|
||||||
struct_size,
|
|
||||||
field_layouts,
|
|
||||||
ret_reg,
|
|
||||||
)
|
|
||||||
} else {
|
} else {
|
||||||
// Nothing to do for empty struct
|
// Nothing to do for empty struct
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -854,19 +957,42 @@ impl<
|
||||||
.insert(*sym, SymbolStorage::GeneralReg(reg));
|
.insert(*sym, SymbolStorage::GeneralReg(reg));
|
||||||
Ok(reg)
|
Ok(reg)
|
||||||
}
|
}
|
||||||
Some(SymbolStorage::Base(offset)) => {
|
Some(SymbolStorage::Base {
|
||||||
|
offset,
|
||||||
|
size,
|
||||||
|
owned,
|
||||||
|
}) => {
|
||||||
let reg = self.claim_general_reg(sym)?;
|
let reg = self.claim_general_reg(sym)?;
|
||||||
self.symbol_storage_map
|
self.symbol_storage_map.insert(
|
||||||
.insert(*sym, SymbolStorage::BaseAndGeneralReg(reg, offset));
|
*sym,
|
||||||
|
SymbolStorage::BaseAndGeneralReg {
|
||||||
|
reg,
|
||||||
|
offset,
|
||||||
|
size,
|
||||||
|
owned,
|
||||||
|
},
|
||||||
|
);
|
||||||
ASM::mov_reg64_base32(&mut self.buf, reg, offset as i32);
|
ASM::mov_reg64_base32(&mut self.buf, reg, offset as i32);
|
||||||
Ok(reg)
|
Ok(reg)
|
||||||
}
|
}
|
||||||
Some(SymbolStorage::BaseAndGeneralReg(reg, offset)) => {
|
Some(SymbolStorage::BaseAndGeneralReg {
|
||||||
self.symbol_storage_map
|
reg,
|
||||||
.insert(*sym, SymbolStorage::BaseAndGeneralReg(reg, offset));
|
offset,
|
||||||
|
size,
|
||||||
|
owned,
|
||||||
|
}) => {
|
||||||
|
self.symbol_storage_map.insert(
|
||||||
|
*sym,
|
||||||
|
SymbolStorage::BaseAndGeneralReg {
|
||||||
|
reg,
|
||||||
|
offset,
|
||||||
|
size,
|
||||||
|
owned,
|
||||||
|
},
|
||||||
|
);
|
||||||
Ok(reg)
|
Ok(reg)
|
||||||
}
|
}
|
||||||
Some(SymbolStorage::FloatReg(_)) | Some(SymbolStorage::BaseAndFloatReg(_, _)) => {
|
Some(SymbolStorage::FloatReg(_)) | Some(SymbolStorage::BaseAndFloatReg { .. }) => {
|
||||||
Err("Cannot load floating point symbol into GeneralReg".to_string())
|
Err("Cannot load floating point symbol into GeneralReg".to_string())
|
||||||
}
|
}
|
||||||
None => Err(format!("Unknown symbol: {}", sym)),
|
None => Err(format!("Unknown symbol: {}", sym)),
|
||||||
|
@ -881,19 +1007,42 @@ impl<
|
||||||
.insert(*sym, SymbolStorage::FloatReg(reg));
|
.insert(*sym, SymbolStorage::FloatReg(reg));
|
||||||
Ok(reg)
|
Ok(reg)
|
||||||
}
|
}
|
||||||
Some(SymbolStorage::Base(offset)) => {
|
Some(SymbolStorage::Base {
|
||||||
|
offset,
|
||||||
|
size,
|
||||||
|
owned,
|
||||||
|
}) => {
|
||||||
let reg = self.claim_float_reg(sym)?;
|
let reg = self.claim_float_reg(sym)?;
|
||||||
self.symbol_storage_map
|
self.symbol_storage_map.insert(
|
||||||
.insert(*sym, SymbolStorage::BaseAndFloatReg(reg, offset));
|
*sym,
|
||||||
|
SymbolStorage::BaseAndFloatReg {
|
||||||
|
reg,
|
||||||
|
offset,
|
||||||
|
size,
|
||||||
|
owned,
|
||||||
|
},
|
||||||
|
);
|
||||||
ASM::mov_freg64_base32(&mut self.buf, reg, offset as i32);
|
ASM::mov_freg64_base32(&mut self.buf, reg, offset as i32);
|
||||||
Ok(reg)
|
Ok(reg)
|
||||||
}
|
}
|
||||||
Some(SymbolStorage::BaseAndFloatReg(reg, offset)) => {
|
Some(SymbolStorage::BaseAndFloatReg {
|
||||||
self.symbol_storage_map
|
reg,
|
||||||
.insert(*sym, SymbolStorage::BaseAndFloatReg(reg, offset));
|
offset,
|
||||||
|
size,
|
||||||
|
owned,
|
||||||
|
}) => {
|
||||||
|
self.symbol_storage_map.insert(
|
||||||
|
*sym,
|
||||||
|
SymbolStorage::BaseAndFloatReg {
|
||||||
|
reg,
|
||||||
|
offset,
|
||||||
|
size,
|
||||||
|
owned,
|
||||||
|
},
|
||||||
|
);
|
||||||
Ok(reg)
|
Ok(reg)
|
||||||
}
|
}
|
||||||
Some(SymbolStorage::GeneralReg(_)) | Some(SymbolStorage::BaseAndGeneralReg(_, _)) => {
|
Some(SymbolStorage::GeneralReg(_)) | Some(SymbolStorage::BaseAndGeneralReg { .. }) => {
|
||||||
Err("Cannot load integer symbol into FloatReg".to_string())
|
Err("Cannot load integer symbol into FloatReg".to_string())
|
||||||
}
|
}
|
||||||
None => Err(format!("Unknown symbol: {}", sym)),
|
None => Err(format!("Unknown symbol: {}", sym)),
|
||||||
|
@ -904,45 +1053,107 @@ impl<
|
||||||
let val = self.symbol_storage_map.remove(sym);
|
let val = self.symbol_storage_map.remove(sym);
|
||||||
match val {
|
match val {
|
||||||
Some(SymbolStorage::GeneralReg(reg)) => {
|
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.
|
// For base addresssing, use the negative offset - 8.
|
||||||
ASM::mov_base32_reg64(&mut self.buf, offset, reg);
|
ASM::mov_base32_reg64(&mut self.buf, offset, reg);
|
||||||
self.symbol_storage_map
|
self.symbol_storage_map.insert(
|
||||||
.insert(*sym, SymbolStorage::Base(offset));
|
*sym,
|
||||||
|
SymbolStorage::Base {
|
||||||
|
offset,
|
||||||
|
size: 8,
|
||||||
|
owned: true,
|
||||||
|
},
|
||||||
|
);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
Some(SymbolStorage::FloatReg(reg)) => {
|
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.
|
// For base addresssing, use the negative offset.
|
||||||
ASM::mov_base32_freg64(&mut self.buf, offset, reg);
|
ASM::mov_base32_freg64(&mut self.buf, offset, reg);
|
||||||
self.symbol_storage_map
|
self.symbol_storage_map.insert(
|
||||||
.insert(*sym, SymbolStorage::Base(offset));
|
*sym,
|
||||||
|
SymbolStorage::Base {
|
||||||
|
offset,
|
||||||
|
size: 8,
|
||||||
|
owned: true,
|
||||||
|
},
|
||||||
|
);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
Some(SymbolStorage::Base(offset)) => {
|
Some(SymbolStorage::Base {
|
||||||
self.symbol_storage_map
|
offset,
|
||||||
.insert(*sym, SymbolStorage::Base(offset));
|
size,
|
||||||
|
owned,
|
||||||
|
}) => {
|
||||||
|
self.symbol_storage_map.insert(
|
||||||
|
*sym,
|
||||||
|
SymbolStorage::Base {
|
||||||
|
offset,
|
||||||
|
size,
|
||||||
|
owned,
|
||||||
|
},
|
||||||
|
);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
Some(SymbolStorage::BaseAndGeneralReg(_, offset)) => {
|
Some(SymbolStorage::BaseAndGeneralReg {
|
||||||
self.symbol_storage_map
|
offset,
|
||||||
.insert(*sym, SymbolStorage::Base(offset));
|
size,
|
||||||
|
owned,
|
||||||
|
..
|
||||||
|
}) => {
|
||||||
|
self.symbol_storage_map.insert(
|
||||||
|
*sym,
|
||||||
|
SymbolStorage::Base {
|
||||||
|
offset,
|
||||||
|
size,
|
||||||
|
owned,
|
||||||
|
},
|
||||||
|
);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
Some(SymbolStorage::BaseAndFloatReg(_, offset)) => {
|
Some(SymbolStorage::BaseAndFloatReg {
|
||||||
self.symbol_storage_map
|
offset,
|
||||||
.insert(*sym, SymbolStorage::Base(offset));
|
size,
|
||||||
|
owned,
|
||||||
|
..
|
||||||
|
}) => {
|
||||||
|
self.symbol_storage_map.insert(
|
||||||
|
*sym,
|
||||||
|
SymbolStorage::Base {
|
||||||
|
offset,
|
||||||
|
size,
|
||||||
|
owned,
|
||||||
|
},
|
||||||
|
);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
None => Err(format!("Unknown symbol: {}", sym)),
|
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.
|
/// It returns base pointer relative offset of the new data.
|
||||||
fn increase_stack_size(&mut self, amount: u32) -> Result<i32, String> {
|
fn claim_stack_size(&mut self, amount: u32) -> Result<i32, String> {
|
||||||
debug_assert!(amount > 0);
|
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.
|
// 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 {
|
if new_size > i32::MAX as u32 {
|
||||||
Err("Ran out of stack space".to_string())
|
Err("Ran out of stack space".to_string())
|
||||||
|
@ -975,7 +1186,17 @@ impl<
|
||||||
}
|
}
|
||||||
Layout::Struct(_) if layout.safe_to_memcpy() => {
|
Layout::Struct(_) if layout.safe_to_memcpy() => {
|
||||||
let tmp_reg = self.get_tmp_general_reg()?;
|
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 {
|
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_reg64_base32(&mut self.buf, tmp_reg, from_offset + i);
|
||||||
ASM::mov_base32_reg64(&mut self.buf, to_offset + i, tmp_reg);
|
ASM::mov_base32_reg64(&mut self.buf, to_offset + i, tmp_reg);
|
||||||
|
|
|
@ -200,7 +200,14 @@ impl CallConv<X86_64GeneralReg, X86_64FloatReg> for X86_64SystemV {
|
||||||
general_i += 1;
|
general_i += 1;
|
||||||
} else {
|
} else {
|
||||||
base_offset += 8;
|
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) => {
|
Layout::Builtin(Builtin::Float64) => {
|
||||||
|
@ -212,7 +219,14 @@ impl CallConv<X86_64GeneralReg, X86_64FloatReg> for X86_64SystemV {
|
||||||
float_i += 1;
|
float_i += 1;
|
||||||
} else {
|
} else {
|
||||||
base_offset += 8;
|
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 => {
|
x => {
|
||||||
|
@ -260,13 +274,13 @@ impl CallConv<X86_64GeneralReg, X86_64FloatReg> for X86_64SystemV {
|
||||||
.ok_or("function argument does not reference any symbol")?
|
.ok_or("function argument does not reference any symbol")?
|
||||||
{
|
{
|
||||||
SymbolStorage::GeneralReg(reg)
|
SymbolStorage::GeneralReg(reg)
|
||||||
| SymbolStorage::BaseAndGeneralReg(reg, _) => {
|
| SymbolStorage::BaseAndGeneralReg { reg, .. } => {
|
||||||
X86_64Assembler::mov_reg64_reg64(buf, dst, *reg);
|
X86_64Assembler::mov_reg64_reg64(buf, dst, *reg);
|
||||||
}
|
}
|
||||||
SymbolStorage::Base(offset) => {
|
SymbolStorage::Base { offset, .. } => {
|
||||||
X86_64Assembler::mov_reg64_base32(buf, dst, *offset);
|
X86_64Assembler::mov_reg64_base32(buf, dst, *offset);
|
||||||
}
|
}
|
||||||
SymbolStorage::FloatReg(_) | SymbolStorage::BaseAndFloatReg(_, _) => {
|
SymbolStorage::FloatReg(_) | SymbolStorage::BaseAndFloatReg { .. } => {
|
||||||
return Err(
|
return Err(
|
||||||
"Cannot load floating point symbol into GeneralReg".to_string()
|
"Cannot load floating point symbol into GeneralReg".to_string()
|
||||||
)
|
)
|
||||||
|
@ -280,10 +294,10 @@ impl CallConv<X86_64GeneralReg, X86_64FloatReg> for X86_64SystemV {
|
||||||
.ok_or("function argument does not reference any symbol")?
|
.ok_or("function argument does not reference any symbol")?
|
||||||
{
|
{
|
||||||
SymbolStorage::GeneralReg(reg)
|
SymbolStorage::GeneralReg(reg)
|
||||||
| SymbolStorage::BaseAndGeneralReg(reg, _) => {
|
| SymbolStorage::BaseAndGeneralReg { reg, .. } => {
|
||||||
X86_64Assembler::mov_stack32_reg64(buf, stack_offset, *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.
|
// Use RAX as a tmp reg because it will be free before function calls.
|
||||||
X86_64Assembler::mov_reg64_base32(
|
X86_64Assembler::mov_reg64_base32(
|
||||||
buf,
|
buf,
|
||||||
|
@ -296,7 +310,7 @@ impl CallConv<X86_64GeneralReg, X86_64FloatReg> for X86_64SystemV {
|
||||||
X86_64GeneralReg::RAX,
|
X86_64GeneralReg::RAX,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
SymbolStorage::FloatReg(_) | SymbolStorage::BaseAndFloatReg(_, _) => {
|
SymbolStorage::FloatReg(_) | SymbolStorage::BaseAndFloatReg { .. } => {
|
||||||
return Err(
|
return Err(
|
||||||
"Cannot load floating point symbol into GeneralReg".to_string()
|
"Cannot load floating point symbol into GeneralReg".to_string()
|
||||||
)
|
)
|
||||||
|
@ -314,14 +328,14 @@ impl CallConv<X86_64GeneralReg, X86_64FloatReg> for X86_64SystemV {
|
||||||
.ok_or("function argument does not reference any symbol")?
|
.ok_or("function argument does not reference any symbol")?
|
||||||
{
|
{
|
||||||
SymbolStorage::FloatReg(reg)
|
SymbolStorage::FloatReg(reg)
|
||||||
| SymbolStorage::BaseAndFloatReg(reg, _) => {
|
| SymbolStorage::BaseAndFloatReg { reg, .. } => {
|
||||||
X86_64Assembler::mov_freg64_freg64(buf, dst, *reg);
|
X86_64Assembler::mov_freg64_freg64(buf, dst, *reg);
|
||||||
}
|
}
|
||||||
SymbolStorage::Base(offset) => {
|
SymbolStorage::Base { offset, .. } => {
|
||||||
X86_64Assembler::mov_freg64_base32(buf, dst, *offset);
|
X86_64Assembler::mov_freg64_base32(buf, dst, *offset);
|
||||||
}
|
}
|
||||||
SymbolStorage::GeneralReg(_)
|
SymbolStorage::GeneralReg(_)
|
||||||
| SymbolStorage::BaseAndGeneralReg(_, _) => {
|
| SymbolStorage::BaseAndGeneralReg { .. } => {
|
||||||
return Err("Cannot load general symbol into FloatReg".to_string())
|
return Err("Cannot load general symbol into FloatReg".to_string())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -333,10 +347,10 @@ impl CallConv<X86_64GeneralReg, X86_64FloatReg> for X86_64SystemV {
|
||||||
.ok_or("function argument does not reference any symbol")?
|
.ok_or("function argument does not reference any symbol")?
|
||||||
{
|
{
|
||||||
SymbolStorage::FloatReg(reg)
|
SymbolStorage::FloatReg(reg)
|
||||||
| SymbolStorage::BaseAndFloatReg(reg, _) => {
|
| SymbolStorage::BaseAndFloatReg { reg, .. } => {
|
||||||
X86_64Assembler::mov_stack32_freg64(buf, stack_offset, *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.
|
// Use XMM0 as a tmp reg because it will be free before function calls.
|
||||||
X86_64Assembler::mov_freg64_base32(
|
X86_64Assembler::mov_freg64_base32(
|
||||||
buf,
|
buf,
|
||||||
|
@ -350,7 +364,7 @@ impl CallConv<X86_64GeneralReg, X86_64FloatReg> for X86_64SystemV {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
SymbolStorage::GeneralReg(_)
|
SymbolStorage::GeneralReg(_)
|
||||||
| SymbolStorage::BaseAndGeneralReg(_, _) => {
|
| SymbolStorage::BaseAndGeneralReg { .. } => {
|
||||||
return Err("Cannot load general symbol into FloatReg".to_string())
|
return Err("Cannot load general symbol into FloatReg".to_string())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -541,7 +555,14 @@ impl CallConv<X86_64GeneralReg, X86_64FloatReg> 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(())
|
Ok(())
|
||||||
|
@ -580,13 +601,13 @@ impl CallConv<X86_64GeneralReg, X86_64FloatReg> for X86_64WindowsFastcall {
|
||||||
.ok_or("function argument does not reference any symbol")?
|
.ok_or("function argument does not reference any symbol")?
|
||||||
{
|
{
|
||||||
SymbolStorage::GeneralReg(reg)
|
SymbolStorage::GeneralReg(reg)
|
||||||
| SymbolStorage::BaseAndGeneralReg(reg, _) => {
|
| SymbolStorage::BaseAndGeneralReg { reg, .. } => {
|
||||||
X86_64Assembler::mov_reg64_reg64(buf, dst, *reg);
|
X86_64Assembler::mov_reg64_reg64(buf, dst, *reg);
|
||||||
}
|
}
|
||||||
SymbolStorage::Base(offset) => {
|
SymbolStorage::Base { offset, .. } => {
|
||||||
X86_64Assembler::mov_reg64_base32(buf, dst, *offset);
|
X86_64Assembler::mov_reg64_base32(buf, dst, *offset);
|
||||||
}
|
}
|
||||||
SymbolStorage::FloatReg(_) | SymbolStorage::BaseAndFloatReg(_, _) => {
|
SymbolStorage::FloatReg(_) | SymbolStorage::BaseAndFloatReg { .. } => {
|
||||||
return Err(
|
return Err(
|
||||||
"Cannot load floating point symbol into GeneralReg".to_string()
|
"Cannot load floating point symbol into GeneralReg".to_string()
|
||||||
)
|
)
|
||||||
|
@ -600,10 +621,10 @@ impl CallConv<X86_64GeneralReg, X86_64FloatReg> for X86_64WindowsFastcall {
|
||||||
.ok_or("function argument does not reference any symbol")?
|
.ok_or("function argument does not reference any symbol")?
|
||||||
{
|
{
|
||||||
SymbolStorage::GeneralReg(reg)
|
SymbolStorage::GeneralReg(reg)
|
||||||
| SymbolStorage::BaseAndGeneralReg(reg, _) => {
|
| SymbolStorage::BaseAndGeneralReg { reg, .. } => {
|
||||||
X86_64Assembler::mov_stack32_reg64(buf, stack_offset, *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.
|
// Use RAX as a tmp reg because it will be free before function calls.
|
||||||
X86_64Assembler::mov_reg64_base32(
|
X86_64Assembler::mov_reg64_base32(
|
||||||
buf,
|
buf,
|
||||||
|
@ -616,7 +637,7 @@ impl CallConv<X86_64GeneralReg, X86_64FloatReg> for X86_64WindowsFastcall {
|
||||||
X86_64GeneralReg::RAX,
|
X86_64GeneralReg::RAX,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
SymbolStorage::FloatReg(_) | SymbolStorage::BaseAndFloatReg(_, _) => {
|
SymbolStorage::FloatReg(_) | SymbolStorage::BaseAndFloatReg { .. } => {
|
||||||
return Err(
|
return Err(
|
||||||
"Cannot load floating point symbol into GeneralReg".to_string()
|
"Cannot load floating point symbol into GeneralReg".to_string()
|
||||||
)
|
)
|
||||||
|
@ -634,14 +655,14 @@ impl CallConv<X86_64GeneralReg, X86_64FloatReg> for X86_64WindowsFastcall {
|
||||||
.ok_or("function argument does not reference any symbol")?
|
.ok_or("function argument does not reference any symbol")?
|
||||||
{
|
{
|
||||||
SymbolStorage::FloatReg(reg)
|
SymbolStorage::FloatReg(reg)
|
||||||
| SymbolStorage::BaseAndFloatReg(reg, _) => {
|
| SymbolStorage::BaseAndFloatReg { reg, .. } => {
|
||||||
X86_64Assembler::mov_freg64_freg64(buf, dst, *reg);
|
X86_64Assembler::mov_freg64_freg64(buf, dst, *reg);
|
||||||
}
|
}
|
||||||
SymbolStorage::Base(offset) => {
|
SymbolStorage::Base { offset, .. } => {
|
||||||
X86_64Assembler::mov_freg64_base32(buf, dst, *offset);
|
X86_64Assembler::mov_freg64_base32(buf, dst, *offset);
|
||||||
}
|
}
|
||||||
SymbolStorage::GeneralReg(_)
|
SymbolStorage::GeneralReg(_)
|
||||||
| SymbolStorage::BaseAndGeneralReg(_, _) => {
|
| SymbolStorage::BaseAndGeneralReg { .. } => {
|
||||||
return Err("Cannot load general symbol into FloatReg".to_string())
|
return Err("Cannot load general symbol into FloatReg".to_string())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -653,10 +674,10 @@ impl CallConv<X86_64GeneralReg, X86_64FloatReg> for X86_64WindowsFastcall {
|
||||||
.ok_or("function argument does not reference any symbol")?
|
.ok_or("function argument does not reference any symbol")?
|
||||||
{
|
{
|
||||||
SymbolStorage::FloatReg(reg)
|
SymbolStorage::FloatReg(reg)
|
||||||
| SymbolStorage::BaseAndFloatReg(reg, _) => {
|
| SymbolStorage::BaseAndFloatReg { reg, .. } => {
|
||||||
X86_64Assembler::mov_stack32_freg64(buf, stack_offset, *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.
|
// Use XMM0 as a tmp reg because it will be free before function calls.
|
||||||
X86_64Assembler::mov_freg64_base32(
|
X86_64Assembler::mov_freg64_base32(
|
||||||
buf,
|
buf,
|
||||||
|
@ -670,7 +691,7 @@ impl CallConv<X86_64GeneralReg, X86_64FloatReg> for X86_64WindowsFastcall {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
SymbolStorage::GeneralReg(_)
|
SymbolStorage::GeneralReg(_)
|
||||||
| SymbolStorage::BaseAndGeneralReg(_, _) => {
|
| SymbolStorage::BaseAndGeneralReg { .. } => {
|
||||||
return Err("Cannot load general symbol into FloatReg".to_string())
|
return Err("Cannot load general symbol into FloatReg".to_string())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -98,14 +98,14 @@ where
|
||||||
Stmt::Let(sym, expr, layout, following) => {
|
Stmt::Let(sym, expr, layout, following) => {
|
||||||
self.build_expr(sym, expr, layout)?;
|
self.build_expr(sym, expr, layout)?;
|
||||||
self.set_layout_map(*sym, layout)?;
|
self.set_layout_map(*sym, layout)?;
|
||||||
self.free_symbols(stmt);
|
self.free_symbols(stmt)?;
|
||||||
self.build_stmt(following, ret_layout)?;
|
self.build_stmt(following, ret_layout)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
Stmt::Ret(sym) => {
|
Stmt::Ret(sym) => {
|
||||||
self.load_literal_symbols(&[*sym])?;
|
self.load_literal_symbols(&[*sym])?;
|
||||||
self.return_symbol(sym, ret_layout)?;
|
self.return_symbol(sym, ret_layout)?;
|
||||||
self.free_symbols(stmt);
|
self.free_symbols(stmt)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
Stmt::Switch {
|
Stmt::Switch {
|
||||||
|
@ -123,7 +123,7 @@ where
|
||||||
default_branch,
|
default_branch,
|
||||||
ret_layout,
|
ret_layout,
|
||||||
)?;
|
)?;
|
||||||
self.free_symbols(stmt);
|
self.free_symbols(stmt)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
x => Err(format!("the statement, {:?}, is not yet implemented", x)),
|
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>;
|
fn return_symbol(&mut self, sym: &Symbol, layout: &Layout<'a>) -> Result<(), String>;
|
||||||
|
|
||||||
/// free_symbols will free all symbols for the given statement.
|
/// 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>)) {
|
if let Some(syms) = self.free_map().remove(&(stmt as *const Stmt<'a>)) {
|
||||||
for sym in syms {
|
for sym in syms {
|
||||||
//println!("Freeing symbol: {:?}", sym);
|
// println!("Freeing symbol: {:?}", sym);
|
||||||
self.free_symbol(&sym);
|
self.free_symbol(&sym)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// free_symbol frees any registers or stack space used to hold a symbol.
|
/// 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.
|
/// 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<Symbol, Symbol>,
|
||||||
|
) {
|
||||||
self.last_seen_map().insert(sym, stmt);
|
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.
|
/// 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.
|
/// 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.
|
/// 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>) {
|
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<Symbol, Symbol> = MutMap::default();
|
||||||
match stmt {
|
match stmt {
|
||||||
Stmt::Let(sym, expr, _, following) => {
|
Stmt::Let(sym, expr, _, following) => {
|
||||||
self.set_last_seen(*sym, stmt);
|
self.set_last_seen(*sym, stmt, &owning_symbol);
|
||||||
match expr {
|
match expr {
|
||||||
Expr::Literal(_) => {}
|
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, .. } => {
|
Expr::Tag { arguments, .. } => {
|
||||||
for sym in *arguments {
|
for sym in *arguments {
|
||||||
self.set_last_seen(*sym, stmt);
|
self.set_last_seen(*sym, stmt, &owning_symbol);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Expr::Struct(syms) => {
|
Expr::Struct(syms) => {
|
||||||
for sym in *syms {
|
for sym in *syms {
|
||||||
self.set_last_seen(*sym, stmt);
|
self.set_last_seen(*sym, stmt, &owning_symbol);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Expr::StructAtIndex { structure, .. } => {
|
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, .. } => {
|
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, .. } => {
|
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, .. } => {
|
Expr::Array { elems, .. } => {
|
||||||
for sym in *elems {
|
for sym in *elems {
|
||||||
self.set_last_seen(*sym, stmt);
|
self.set_last_seen(*sym, stmt, &owning_symbol);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Expr::Reuse {
|
Expr::Reuse {
|
||||||
|
@ -611,22 +628,22 @@ where
|
||||||
tag_name,
|
tag_name,
|
||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
self.set_last_seen(*symbol, stmt);
|
self.set_last_seen(*symbol, stmt, &owning_symbol);
|
||||||
match tag_name {
|
match tag_name {
|
||||||
TagName::Closure(sym) => {
|
TagName::Closure(sym) => {
|
||||||
self.set_last_seen(*sym, stmt);
|
self.set_last_seen(*sym, stmt, &owning_symbol);
|
||||||
}
|
}
|
||||||
TagName::Private(sym) => {
|
TagName::Private(sym) => {
|
||||||
self.set_last_seen(*sym, stmt);
|
self.set_last_seen(*sym, stmt, &owning_symbol);
|
||||||
}
|
}
|
||||||
TagName::Global(_) => {}
|
TagName::Global(_) => {}
|
||||||
}
|
}
|
||||||
for sym in *arguments {
|
for sym in *arguments {
|
||||||
self.set_last_seen(*sym, stmt);
|
self.set_last_seen(*sym, stmt, &owning_symbol);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Expr::Reset(sym) => {
|
Expr::Reset(sym) => {
|
||||||
self.set_last_seen(*sym, stmt);
|
self.set_last_seen(*sym, stmt, &owning_symbol);
|
||||||
}
|
}
|
||||||
Expr::EmptyArray => {}
|
Expr::EmptyArray => {}
|
||||||
Expr::RuntimeErrorFunction(_) => {}
|
Expr::RuntimeErrorFunction(_) => {}
|
||||||
|
@ -640,19 +657,19 @@ where
|
||||||
default_branch,
|
default_branch,
|
||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
self.set_last_seen(*cond_symbol, stmt);
|
self.set_last_seen(*cond_symbol, stmt, &owning_symbol);
|
||||||
for (_, _, branch) in *branches {
|
for (_, _, branch) in *branches {
|
||||||
self.scan_ast(branch);
|
self.scan_ast(branch);
|
||||||
}
|
}
|
||||||
self.scan_ast(default_branch.1);
|
self.scan_ast(default_branch.1);
|
||||||
}
|
}
|
||||||
Stmt::Ret(sym) => {
|
Stmt::Ret(sym) => {
|
||||||
self.set_last_seen(*sym, stmt);
|
self.set_last_seen(*sym, stmt, &owning_symbol);
|
||||||
}
|
}
|
||||||
Stmt::Refcounting(modify, following) => {
|
Stmt::Refcounting(modify, following) => {
|
||||||
let sym = modify.get_symbol();
|
let sym = modify.get_symbol();
|
||||||
|
|
||||||
self.set_last_seen(sym, stmt);
|
self.set_last_seen(sym, stmt, &owning_symbol);
|
||||||
self.scan_ast(following);
|
self.scan_ast(following);
|
||||||
}
|
}
|
||||||
Stmt::Join {
|
Stmt::Join {
|
||||||
|
@ -662,29 +679,34 @@ where
|
||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
for param in *parameters {
|
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(continuation);
|
||||||
self.scan_ast(remainder);
|
self.scan_ast(remainder);
|
||||||
}
|
}
|
||||||
Stmt::Jump(JoinPointId(sym), symbols) => {
|
Stmt::Jump(JoinPointId(sym), symbols) => {
|
||||||
self.set_last_seen(*sym, stmt);
|
self.set_last_seen(*sym, stmt, &owning_symbol);
|
||||||
for sym in *symbols {
|
for sym in *symbols {
|
||||||
self.set_last_seen(*sym, stmt);
|
self.set_last_seen(*sym, stmt, &owning_symbol);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Stmt::RuntimeError(_) => {}
|
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<Symbol, Symbol>,
|
||||||
|
) {
|
||||||
let roc_mono::ir::Call {
|
let roc_mono::ir::Call {
|
||||||
call_type,
|
call_type,
|
||||||
arguments,
|
arguments,
|
||||||
} = call;
|
} = call;
|
||||||
|
|
||||||
for sym in *arguments {
|
for sym in *arguments {
|
||||||
self.set_last_seen(*sym, stmt);
|
self.set_last_seen(*sym, stmt, &owning_symbol);
|
||||||
}
|
}
|
||||||
|
|
||||||
match call_type {
|
match call_type {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue