Skip to content

Commit ac3cb7f

Browse files
Document is_complex and is_memory_allocated type attributes (#1444)
* Document is_complex * Document is_memory_allocated * Add rationale for is_memory_allocated
1 parent 1852943 commit ac3cb7f

File tree

2 files changed

+35
-3
lines changed

2 files changed

+35
-3
lines changed

src/executor.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,13 +109,17 @@ fn invoke_dynamic(
109109
.peekable();
110110

111111
let num_return_args = ret_types_iter.clone().count();
112+
// If there is more than one return value, or the return value is _complex_,
113+
// as defined by the architecture ABI, then we pass a return pointer as
114+
// the first argument to the program entrypoint.
112115
let mut return_ptr = if num_return_args > 1
113116
|| ret_types_iter
114117
.peek()
115118
.map(|id| registry.get_type(id)?.is_complex(registry))
116119
.transpose()?
117120
== Some(true)
118121
{
122+
// The return pointer should be able to hold all the return values.
119123
let layout = ret_types_iter.try_fold(Layout::new::<()>(), |layout, id| {
120124
let type_info = registry.get_type(id)?;
121125
Result::<_, Error>::Ok(layout.extend(type_info.layout(registry)?)?.0)

src/types.rs

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -85,11 +85,26 @@ pub trait TypeBuilder {
8585

8686
/// Return whether the type is a builtin.
8787
fn is_builtin(&self) -> bool;
88-
/// Return whether the type requires a return pointer when returning.
88+
/// Return whether the type requires a return pointer when returning,
89+
/// instead of using the CPU registers.
90+
///
91+
/// This attribute does not modify the compilation, and it only reflects
92+
/// what the ABI of the target architecture already specifies.
93+
/// - For x86-64: https://gitlab.com/x86-psABIs/x86-64-ABI.
94+
/// - For AArch64: https://github.com/ARM-software/abi-aa.
95+
///
96+
/// We can validate this empirically, by building a Cairo program that
97+
/// returns a particular type, and seeing how it is lowered to machine code.
98+
///
99+
/// ```bash
100+
/// llc a.llvmir -o - --mtriple "aarch64"
101+
/// llc a.llvmir -o - --mtriple "x86_64"
102+
/// ```
89103
fn is_complex(
90104
&self,
91105
registry: &ProgramRegistry<CoreType, CoreLibfunc>,
92106
) -> Result<bool, Self::Error>;
107+
93108
/// Return whether the Sierra type resolves to a zero-sized type.
94109
fn is_zst(
95110
&self,
@@ -104,8 +119,21 @@ pub trait TypeBuilder {
104119
registry: &ProgramRegistry<CoreType, CoreLibfunc>,
105120
) -> Result<Layout, Self::Error>;
106121

107-
/// Whether the layout should be allocated in memory (either the stack or the heap) when used as
108-
/// a function invocation argument or return value.
122+
/// Whether the layout should be allocated in memory (either the stack or
123+
/// the heap) when used as a function invocation argument or return value.
124+
///
125+
/// Unlike `is_complex`, this attribute alters the compilation:
126+
///
127+
/// - When passing a memory allocated value to a function, we allocate that
128+
/// value on the stack, and pass a pointer to it.
129+
///
130+
/// - If a function returns a memory allocated value, we receive a return
131+
/// pointer as its first argument, and write the return value there
132+
/// instead.
133+
///
134+
/// The rationale behind allocating a value in memory, rather than
135+
/// registers, is to avoid putting too much pressure on the register
136+
/// allocation pass for really complex types, like enums.
109137
fn is_memory_allocated(
110138
&self,
111139
registry: &ProgramRegistry<CoreType, CoreLibfunc>,

0 commit comments

Comments
 (0)