Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Tail Recursion Modulo Cons for record payloads #6071

Draft
wants to merge 23 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
a0e0d1f
format and add record trmc test
HajagosNorbert Nov 16, 2023
c182ad5
wip exploring
HajagosNorbert Oct 25, 2023
3a2f974
finds the simplest trmc candidates with records, v1
HajagosNorbert Nov 9, 2023
f7c905d
refactor: made prev changes more idiomatic
HajagosNorbert Nov 9, 2023
c15c7a3
remove some dbg code
HajagosNorbert Nov 16, 2023
032a943
preping to make changes
HajagosNorbert Nov 16, 2023
4514060
store struct index inside trmc_candidate_set
HajagosNorbert Nov 17, 2023
9f69250
indices from candidate_set show in in GetElementPointer when rec val …
HajagosNorbert Nov 18, 2023
b735363
1 deep struct IR seems good
HajagosNorbert Nov 20, 2023
e7ce8c4
refactored to match stmt based on struct or not
HajagosNorbert Nov 21, 2023
3de6775
better indices slice reference creation
HajagosNorbert Nov 21, 2023
607ffc0
changed symbol creation order so the IR has different identifiers
HajagosNorbert Nov 21, 2023
d234adb
delete unused code
HajagosNorbert Nov 22, 2023
ecb3bc9
first time linked_list_with_record_trmc test passes for test-den-dev
HajagosNorbert Nov 23, 2023
307dc13
refactor and replace vec allocation with slice
HajagosNorbert Nov 23, 2023
becd2a1
touches before pr for review
HajagosNorbert Nov 23, 2023
3f828b6
wip, add comments and todoes to code
HajagosNorbert Nov 25, 2023
df6d0e8
..nested_records_trmc test passes
HajagosNorbert Nov 26, 2023
16133b4
before refactoring the concept of Path and locations
HajagosNorbert Nov 27, 2023
7597cee
got rid of RecCallPath
HajagosNorbert Nov 27, 2023
64ff005
add docs and refactor
HajagosNorbert Nov 27, 2023
91714d2
Merge branch 'main' into trmc
HajagosNorbert Nov 27, 2023
48d8c26
rename vars
HajagosNorbert Nov 27, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .cargo/config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,4 @@ ROC_PRINT_RUNTIME_ERROR_GEN = "0"
ROC_DEBUG_ALIAS_ANALYSIS = "0"
ROC_PRINT_LLVM_FN_VERIFICATION = "0"
ROC_PRINT_LOAD_LOG = "0"
ROC_PRINT_IR_AFTER_TRMC = "0"
9 changes: 8 additions & 1 deletion crates/compiler/alias_analysis/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1442,7 +1442,14 @@ fn expr_spec<'a>(
union_layout,
..
} => {
debug_assert!(indices.len() >= 2);
//TODO: indices can have > 2 values. The first 2 is tag_id and index,
//and after that, it depends on the layout of the element at index.
//If it is a struct, the next index
//denotes which field we are targeting. If it is a tag union (only non recursive), than
//there is at least 2 more "indices".
//1st is the tag_id, 2nd is the
//index and so on recursively for arbitrary length.
debug_assert!(indices.len() == 2);
let tag_id = indices[0] as u32;
let index = indices[1];
let tag_value_id = env.symbols[structure];
Expand Down
86 changes: 67 additions & 19 deletions crates/compiler/gen_dev/src/generic64/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3434,25 +3434,27 @@ impl<
&mut self,
sym: &Symbol,
structure: &Symbol,
tag_id: TagIdIntType,
index: u64,
indices: &[u64],
union_layout: &UnionLayout<'a>,
) {
let tag_id = indices[0] as TagIdIntType;
let ptr_reg = self
.storage_manager
.load_to_general_reg(&mut self.buf, structure);

let sym_reg = self.storage_manager.claim_general_reg(&mut self.buf, sym);

let indices = &indices[1..];
match union_layout {
UnionLayout::NonRecursive(_) => {
unreachable!("operation not supported")
}
UnionLayout::NonNullableUnwrapped(field_layouts) => {
let mut offset = 0;
for field in &field_layouts[..index as usize] {
offset += self.layout_interner.stack_size(*field);
}
let offset = load_union_field_ptr_at_index_help(
self.layout_interner,
indices,
field_layouts,
0,
);

ASM::add_reg64_reg64_imm32(&mut self.buf, sym_reg, ptr_reg, offset as i32);
}
Expand All @@ -3462,10 +3464,12 @@ impl<
} => {
debug_assert_ne!(tag_id, *nullable_id as TagIdIntType);

let mut offset = 0;
for field in &other_fields[..index as usize] {
offset += self.layout_interner.stack_size(*field);
}
let offset = load_union_field_ptr_at_index_help(
self.layout_interner,
indices,
other_fields,
0,
);

ASM::add_reg64_reg64_imm32(&mut self.buf, sym_reg, ptr_reg, offset as i32);
}
Expand All @@ -3484,10 +3488,12 @@ impl<

let (mask_symbol, mask_reg) = self.clear_tag_id(ptr_reg);

let mut offset = 0;
for field in &other_fields[..index as usize] {
offset += self.layout_interner.stack_size(*field);
}
let offset = load_union_field_ptr_at_index_help(
self.layout_interner,
indices,
other_fields,
0,
);

ASM::add_reg64_reg64_imm32(&mut self.buf, sym_reg, mask_reg, offset as i32);

Expand All @@ -3509,10 +3515,12 @@ impl<
(Some(mask_symbol), mask_reg)
};

let mut offset = 0;
for field in &other_fields[..index as usize] {
offset += self.layout_interner.stack_size(*field);
}
let offset = load_union_field_ptr_at_index_help(
self.layout_interner,
indices,
other_fields,
0,
);

ASM::add_reg64_reg64_imm32(&mut self.buf, sym_reg, unmasked_reg, offset as i32);

Expand Down Expand Up @@ -5367,6 +5375,46 @@ impl<
}
}

fn load_union_field_ptr_at_index_help<'a>(
layout_interner: &'a STLayoutInterner<'a>,
indices: &[u64],
layouts: &[InLayout<'a>],
mut offset: u32,
) -> u32 {
debug_assert_ne!(indices.len(), 0);

let idx = indices[0];
for field in &layouts[..idx as usize] {
offset += layout_interner.stack_size(*field);
}

if indices.len() <= 1 {
return offset;
}
use LayoutRepr::*;
let enclosing_layout = layouts[idx as usize];
return match layout_interner.get_repr(enclosing_layout) {
Struct(layouts) => {
load_union_field_ptr_at_index_help(layout_interner, &indices[1..], layouts, offset)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doesn't need to happen now, but it would be nice if we could transform this into a loop instead of performing recursive calls. I think it would also simplify the logic. Something like

let mut indices = &indices[..];
let mut offset = 0;
while !indicies.is_empty() {
  let idx = indices[0];
  for field in &layouts[..idx as usize] {
      offset += layout_interner.stack_size(*field);
  }
  let enclosing_layout = layouts[idx as usize];
  match layout_interner.get_repr(enclosing_layout) {
    Struct(layouts) => { indices = &indices[1..]; }
    Union(UnionLayout::NonRecursive(tags)) => {
      // ...
    }
  }
}
offset

},
Union(UnionLayout::NonRecursive(tags)) => {
// apart from the current index, to get information out of a
// non-recursive tag union we need 2 more number: a tag id and an index
debug_assert!(indices.len() >= 3);
let tag_id = indices[1] as usize;
let tag_layout = tags[tag_id];

load_union_field_ptr_at_index_help(layout_interner, &indices[2..], tag_layout, offset)
}
//TODO: can any of these be a valid target for getting a pointer out of their inner layouts?
//(so not them being pointers)
Union(_) | Builtin(_) | Ptr(_) | RecursivePointer(_) | LambdaSet(_) | FunctionPointer(_)
| Erased(_) => unreachable!(
"Following the path of the indices, a layout has been reached where the address of the desired pointer is behind another pointer"
),
Comment on lines +5409 to +5414
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lambda set probably could (it should just recurse on its inner type). Also the error message is not correct for Builtin. The remainder (other union values, ptr, recursiveptr, functionpointer and erased) are all pointer types. I'd say just give builtin its own branch with a custom error message.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For the lambda set case, I believe the access path should be at most 1 right? Since I believe we will never explicitly TR-optimize a lambda set. If so, let's add a debug assertion for the size of the indices in that case.

};
}

#[macro_export]
macro_rules! sign_extended_int_builtins {
() => {
Expand Down
11 changes: 2 additions & 9 deletions crates/compiler/gen_dev/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -856,13 +856,7 @@ trait Backend<'a> {
..
} => {
debug_assert!(indices.len() >= 2);
self.load_union_field_ptr_at_index(
sym,
structure,
indices[0] as u16,
indices[1],
union_layout,
);
self.load_union_field_ptr_at_index(sym, structure, indices, union_layout);
}
Expr::GetTagId {
structure,
Expand Down Expand Up @@ -2437,8 +2431,7 @@ trait Backend<'a> {
&mut self,
sym: &Symbol,
structure: &Symbol,
tag_id: TagIdIntType,
index: u64,
indices: &[u64],
union_layout: &UnionLayout<'a>,
);

Expand Down
2 changes: 2 additions & 0 deletions crates/compiler/mono/src/debug/checker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -618,6 +618,8 @@ impl<'a, 'r> Ctx<'a, 'r> {
})
}

//TODO: make it work for Expr::GetElementPointer
//which has list of indices, not a single index
fn check_union_field_ptr_at_index(
&mut self,
structure: Symbol,
Expand Down
Loading