Skip to content

Commit 29a94e3

Browse files
committed
LowerNamedToMemory for MoonBit
1 parent af97439 commit 29a94e3

7 files changed

Lines changed: 270 additions & 4 deletions

File tree

crates/c/src/lib.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3999,6 +3999,10 @@ impl Bindgen for FunctionBindgen<'_, '_> {
39993999
"LiftNamedFromMemory is only emitted by generators that implement \
40004000
Bindgen::lift_helper_name, which this generator does not"
40014001
),
4002+
Instruction::LowerNamedToMemory { .. } => unreachable!(
4003+
"LowerNamedToMemory is only emitted by generators that implement \
4004+
Bindgen::lower_helper_name, which this generator does not"
4005+
),
40024006
Instruction::I32Store { offset } => self.store("int32_t", *offset, operands),
40034007
Instruction::I64Store { offset } => self.store("int64_t", *offset, operands),
40044008
Instruction::F32Store { offset } => self.store("float", *offset, operands),

crates/core/src/abi.rs

Lines changed: 79 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,18 @@ def_instruction! {
134134
/// of a single huge function.
135135
LiftNamedFromMemory { ty: TypeId, offset: ArchitectureSize } : [1] => [1],
136136

137+
/// Pops a value to lower and then a base pointer from the stack, calls a
138+
/// pre-generated, shared per-type lower helper function to write the
139+
/// named aggregate type `ty` at the constant `offset` from that
140+
/// pointer, producing no result.
141+
///
142+
/// This is the lower-side counterpart of `LiftNamedFromMemory`: it
143+
/// outlines the canonical-ABI "write to memory" of large named
144+
/// record/variant types into shared helper functions instead of
145+
/// inlining the full recursive lower at every use site. The value
146+
/// operand is `operands[0]` and the base pointer is `operands[1]`.
147+
LowerNamedToMemory { ty: TypeId, offset: ArchitectureSize } : [2] => [0],
148+
137149
/// Pops a pointer from the stack and then an `i32` value.
138150
/// Stores the value in little-endian at the pointer specified plus the
139151
/// constant `offset`.
@@ -816,6 +828,19 @@ pub trait Bindgen {
816828
let _ = (resolve, id);
817829
None
818830
}
831+
832+
/// Returns the name of a pre-generated, shared lower helper function for
833+
/// the named aggregate type `id`, if one exists.
834+
///
835+
/// When this returns `Some`, the canonical-ABI lower of that type (in
836+
/// `write_to_memory`) is "outlined" into a call to the named helper
837+
/// (emitted as `Instruction::LowerNamedToMemory`) instead of being inlined
838+
/// recursively. Generators that do not implement helper outlining should
839+
/// return `None` (the default).
840+
fn lower_helper_name(&self, resolve: &Resolve, id: TypeId) -> Option<String> {
841+
let _ = (resolve, id);
842+
None
843+
}
819844
}
820845

821846
/// Generates an abstract sequence of instructions which represents this
@@ -914,6 +939,38 @@ pub fn lift_from_memory_root<B: Bindgen>(
914939
generator.stack.pop().unwrap()
915940
}
916941

942+
/// Like [`lower_to_memory`], but used to generate the *body* of an outlined
943+
/// lower helper for the named type `skip_outline_root`.
944+
///
945+
/// The root type itself is lowered inline (one level deep) while nested named
946+
/// aggregate types are outlined into calls to their own shared helpers (see
947+
/// [`Bindgen::lower_helper_name`] and [`Instruction::LowerNamedToMemory`]).
948+
///
949+
/// `skip_outline_root` must be the id of the named aggregate (record/variant)
950+
/// being lowered, and `ty` must be exactly `Type::Id(skip_outline_root)` (not
951+
/// an alias to it) so that the single-shot inline of the root in
952+
/// `write_to_memory` lands on the intended type.
953+
pub fn lower_to_memory_root<B: Bindgen>(
954+
resolve: &Resolve,
955+
bindgen: &mut B,
956+
address: B::Operand,
957+
value: B::Operand,
958+
ty: &Type,
959+
skip_outline_root: TypeId,
960+
) {
961+
assert!(
962+
matches!(ty, Type::Id(id) if *id == skip_outline_root),
963+
"lower_to_memory_root requires `ty` to be `Type::Id(skip_outline_root)`, \
964+
not an alias or other type"
965+
);
966+
let mut generator = Generator::new(resolve, bindgen);
967+
generator.realloc = Some(Realloc::Export("cabi_realloc"));
968+
generator.lower_outline_root = Some(skip_outline_root);
969+
generator.stack.push(value);
970+
generator.write_to_memory(ty, address, Default::default());
971+
debug_assert!(generator.stack.is_empty());
972+
}
973+
917974
/// Used in a similar manner as the `Interface::call` function except is
918975
/// used to generate the `post-return` callback for `func`.
919976
///
@@ -1060,6 +1117,11 @@ struct Generator<'a, B: Bindgen> {
10601117
/// level) while nested named aggregates are still outlined into their own
10611118
/// helpers. `None` everywhere else (so all eligible named types outline).
10621119
lift_outline_root: Option<TypeId>,
1120+
/// Like `lift_outline_root`, but for the lower side: when generating the
1121+
/// body of an outlined lower helper for a named type, this holds that
1122+
/// type's id so its own top-level lower is inlined (one level) while
1123+
/// nested named aggregates are still outlined. `None` everywhere else.
1124+
lower_outline_root: Option<TypeId>,
10631125
}
10641126

10651127
const MAX_FLAT_PARAMS: usize = 16;
@@ -1076,6 +1138,7 @@ impl<'a, B: Bindgen> Generator<'a, B> {
10761138
return_pointer: None,
10771139
realloc: None,
10781140
lift_outline_root: None,
1141+
lower_outline_root: None,
10791142
}
10801143
}
10811144

@@ -2029,7 +2092,21 @@ impl<'a, B: Bindgen> Generator<'a, B> {
20292092
Type::String => self.write_list_to_memory(ty, addr, offset),
20302093
Type::ErrorContext => self.lower_and_emit(ty, addr, &I32Store { offset }),
20312094

2032-
Type::Id(id) => match &self.resolve.types[id].kind {
2095+
Type::Id(id) => {
2096+
// Outline the lower of large named aggregate types into shared
2097+
// helper functions instead of inlining the full recursive
2098+
// lower here. `lower_outline_root` is `Some(id)` only while
2099+
// generating that type's own helper body, in which case its
2100+
// top level is inlined (one level) and nested types still
2101+
// outline.
2102+
let is_outline_root = self.lower_outline_root == Some(id);
2103+
self.lower_outline_root = None;
2104+
if !is_outline_root && self.bindgen.lower_helper_name(self.resolve, id).is_some() {
2105+
self.stack.push(addr);
2106+
self.emit(&Instruction::LowerNamedToMemory { ty: id, offset });
2107+
return;
2108+
}
2109+
match &self.resolve.types[id].kind {
20332110
TypeDefKind::Type(t) => self.write_to_memory(t, addr, offset),
20342111
TypeDefKind::List(_) => self.write_list_to_memory(ty, addr, offset),
20352112
// Maps have the same linear memory layout as list<tuple<K, V>>.
@@ -2143,6 +2220,7 @@ impl<'a, B: Bindgen> Generator<'a, B> {
21432220
id,
21442221
});
21452222
}
2223+
}
21462224
},
21472225
}
21482226
}

crates/cpp/src/lib.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3524,6 +3524,10 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> {
35243524
"LiftNamedFromMemory is only emitted by generators that implement \
35253525
Bindgen::lift_helper_name, which this generator does not"
35263526
),
3527+
abi::Instruction::LowerNamedToMemory { .. } => unreachable!(
3528+
"LowerNamedToMemory is only emitted by generators that implement \
3529+
Bindgen::lower_helper_name, which this generator does not"
3530+
),
35273531
abi::Instruction::PointerStore { offset } => {
35283532
let ptr_type = self.r#gen.r#gen.opts.ptr_type();
35293533
self.store(ptr_type, *offset, operands)

crates/csharp/src/function.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -468,6 +468,10 @@ impl Bindgen for FunctionBindgen<'_, '_> {
468468
"LiftNamedFromMemory is only emitted by generators that implement \
469469
Bindgen::lift_helper_name, which this generator does not"
470470
),
471+
Instruction::LowerNamedToMemory { .. } => unreachable!(
472+
"LowerNamedToMemory is only emitted by generators that implement \
473+
Bindgen::lower_helper_name, which this generator does not"
474+
),
471475
Instruction::PointerLoad { offset } => results.push(format!(
472476
"new global::System.Span<nint>((void*)((byte*){} + {offset}), 1)[0]",
473477
operands[0],

crates/go/src/lib.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1996,6 +1996,10 @@ return {results}"
19961996
"LiftNamedFromMemory is only emitted by generators that implement \
19971997
Bindgen::lift_helper_name, which this generator does not"
19981998
),
1999+
Instruction::LowerNamedToMemory { .. } => unreachable!(
2000+
"LowerNamedToMemory is only emitted by generators that implement \
2001+
Bindgen::lower_helper_name, which this generator does not"
2002+
),
19992003
Instruction::PointerLoad { offset } => {
20002004
load(self, results, &operands[0], offset, "uint32", &|v| {
20012005
format!("uintptr({v})")

0 commit comments

Comments
 (0)