add alloca as an expression

This commit is contained in:
Folkert 2023-07-29 20:10:52 +02:00
parent 750234f2de
commit cdd2aab217
No known key found for this signature in database
GPG key ID: 1F17F6FFD112B97C
15 changed files with 171 additions and 14 deletions

View file

@ -1584,6 +1584,16 @@ fn expr_spec<'a>(
builder.add_make_tuple(block, &[]) builder.add_make_tuple(block, &[])
} }
Alloca { initializer, .. } => {
let initializer = &initializer.as_ref().map(|s| env.symbols[s]);
let values = match initializer {
Some(initializer) => std::slice::from_ref(initializer),
None => &[],
};
let type_id = layout_spec(env, builder, interner, interner.get_repr(layout))?;
builder.add_unknown_with(block, values, type_id)
}
} }
} }

View file

@ -95,6 +95,10 @@ macro_rules! map_symbol_to_lowlevel_and_arity {
LowLevel::RefCountDecDataPtr=> unimplemented!(), LowLevel::RefCountDecDataPtr=> unimplemented!(),
LowLevel::RefCountIsUnique => unimplemented!(), LowLevel::RefCountIsUnique => unimplemented!(),
LowLevel::SetJmp => unimplemented!(),
LowLevel::LongJmp => unimplemented!(),
LowLevel::SetLongJmpBuffer => unimplemented!(),
// these are not implemented, not sure why // these are not implemented, not sure why
LowLevel::StrFromInt => unimplemented!(), LowLevel::StrFromInt => unimplemented!(),
LowLevel::StrFromFloat => unimplemented!(), LowLevel::StrFromFloat => unimplemented!(),

View file

@ -3007,11 +3007,17 @@ impl<
ASM::and_reg64_reg64_reg64(buf, sym_reg, sym_reg, ptr_reg); ASM::and_reg64_reg64_reg64(buf, sym_reg, sym_reg, ptr_reg);
} }
fn build_alloca(&mut self, sym: Symbol, value: Symbol, element_layout: InLayout<'a>) { fn build_alloca(&mut self, sym: Symbol, value: Option<Symbol>, element_layout: InLayout<'a>) {
// 1. acquire some stack space // 1. acquire some stack space
let element_width = self.interner().stack_size(element_layout); let element_width = self.interner().stack_size(element_layout);
let allocation = self.debug_symbol("stack_allocation"); let allocation = self.debug_symbol("stack_allocation");
let ptr = self.debug_symbol("ptr"); let ptr = self.debug_symbol("ptr");
if element_width == 0 {
self.storage_manager.claim_pointer_stack_area(sym);
return;
}
let base_offset = self let base_offset = self
.storage_manager .storage_manager
.claim_stack_area(&allocation, element_width); .claim_stack_area(&allocation, element_width);
@ -3021,7 +3027,13 @@ impl<
ASM::mov_reg64_reg64(&mut self.buf, ptr_reg, CC::BASE_PTR_REG); ASM::mov_reg64_reg64(&mut self.buf, ptr_reg, CC::BASE_PTR_REG);
ASM::add_reg64_reg64_imm32(&mut self.buf, ptr_reg, ptr_reg, base_offset); ASM::add_reg64_reg64_imm32(&mut self.buf, ptr_reg, ptr_reg, base_offset);
self.build_ptr_store(sym, ptr, value, element_layout); if let Some(value) = value {
self.build_ptr_store(sym, ptr, value, element_layout);
} else {
// this is now a pointer to uninitialized memory!
let r = self.storage_manager.claim_general_reg(&mut self.buf, &sym);
ASM::mov_reg64_reg64(&mut self.buf, r, ptr_reg);
}
} }
fn expr_box( fn expr_box(

View file

@ -183,9 +183,14 @@ impl<'a> LastSeenMap<'a> {
Expr::Reset { symbol, .. } | Expr::ResetRef { symbol, .. } => { Expr::Reset { symbol, .. } | Expr::ResetRef { symbol, .. } => {
self.set_last_seen(*symbol, stmt); self.set_last_seen(*symbol, stmt);
} }
Expr::Alloca { initializer, .. } => {
if let Some(initializer) = initializer {
self.set_last_seen(*initializer, stmt);
}
}
Expr::RuntimeErrorFunction(_) => {}
Expr::FunctionPointer { .. } => todo_lambda_erasure!(), Expr::FunctionPointer { .. } => todo_lambda_erasure!(),
Expr::EmptyArray => {} Expr::EmptyArray => {}
Expr::RuntimeErrorFunction(_) => {}
} }
self.scan_ast_help(following); self.scan_ast_help(following);
} }
@ -895,6 +900,12 @@ trait Backend<'a> {
self.build_expr(sym, &new_expr, &Layout::BOOL) self.build_expr(sym, &new_expr, &Layout::BOOL)
} }
Expr::Alloca {
initializer,
element_layout,
} => {
self.build_alloca(*sym, *initializer, *element_layout);
}
Expr::RuntimeErrorFunction(_) => todo!(), Expr::RuntimeErrorFunction(_) => todo!(),
} }
} }
@ -1616,7 +1627,7 @@ trait Backend<'a> {
} }
LowLevel::Alloca => { LowLevel::Alloca => {
self.build_alloca(*sym, args[0], arg_layouts[0]); self.build_alloca(*sym, Some(args[0]), arg_layouts[0]);
} }
LowLevel::RefCountDecRcPtr => self.build_fn_call( LowLevel::RefCountDecRcPtr => self.build_fn_call(
@ -2259,7 +2270,7 @@ trait Backend<'a> {
fn build_ptr_clear_tag_id(&mut self, sym: Symbol, ptr: Symbol); fn build_ptr_clear_tag_id(&mut self, sym: Symbol, ptr: Symbol);
fn build_alloca(&mut self, sym: Symbol, value: Symbol, element_layout: InLayout<'a>); fn build_alloca(&mut self, sym: Symbol, value: Option<Symbol>, element_layout: InLayout<'a>);
/// literal_map gets the map from symbol to literal and layout, used for lazy loading and literal folding. /// literal_map gets the map from symbol to literal and layout, used for lazy loading and literal folding.
fn literal_map(&mut self) -> &mut MutMap<Symbol, (*const Literal<'a>, *const InLayout<'a>)>; fn literal_map(&mut self) -> &mut MutMap<Symbol, (*const Literal<'a>, *const InLayout<'a>)>;

View file

@ -1600,6 +1600,24 @@ pub(crate) fn build_exp_expr<'a, 'ctx>(
get_tag_id(env, layout_interner, parent, union_layout, argument).into() get_tag_id(env, layout_interner, parent, union_layout, argument).into()
} }
Alloca {
initializer,
element_layout,
} => {
let element_type = basic_type_from_layout(
env,
layout_interner,
layout_interner.get_repr(*element_layout),
);
let ptr = entry_block_alloca_zerofill(env, element_type, "stack_value");
if let Some(initializer) = initializer {
env.builder.build_store(ptr, scope.load_symbol(initializer));
}
ptr.into()
}
} }
} }

View file

@ -1396,6 +1396,8 @@ pub(crate) fn run_low_level<'a, 'ctx>(
call_bitcode_fn(env, &[], bitcode::UTILS_DICT_PSEUDO_SEED) call_bitcode_fn(env, &[], bitcode::UTILS_DICT_PSEUDO_SEED)
} }
SetJmp | LongJmp | SetLongJmpBuffer => unreachable!("only inserted in dev backend codegen"),
} }
} }

View file

@ -1144,6 +1144,11 @@ impl<'a, 'r> WasmBackend<'a, 'r> {
Expr::ResetRef { symbol: arg, .. } => self.expr_resetref(*arg, sym, storage), Expr::ResetRef { symbol: arg, .. } => self.expr_resetref(*arg, sym, storage),
Expr::Alloca {
initializer,
element_layout,
} => self.expr_alloca(*initializer, *element_layout, sym, storage),
Expr::RuntimeErrorFunction(_) => { Expr::RuntimeErrorFunction(_) => {
todo!("Expression `{}`", expr.to_pretty(100, false)) todo!("Expression `{}`", expr.to_pretty(100, false))
} }
@ -2140,6 +2145,48 @@ impl<'a, 'r> WasmBackend<'a, 'r> {
); );
} }
fn expr_alloca(
&mut self,
initializer: Option<Symbol>,
element_layout: InLayout<'a>,
ret_symbol: Symbol,
ret_storage: &StoredValue,
) {
// Alloca : a -> Ptr a
let (size, alignment_bytes) = self
.layout_interner
.stack_size_and_alignment(element_layout);
let (frame_ptr, offset) = self
.storage
.allocate_anonymous_stack_memory(size, alignment_bytes);
// write the default value into the stack memory
if let Some(initializer) = initializer {
self.storage.copy_value_to_memory(
&mut self.code_builder,
frame_ptr,
offset,
initializer,
);
}
// create a local variable for the pointer
let ptr_local_id = match self.storage.ensure_value_has_local(
&mut self.code_builder,
ret_symbol,
ret_storage.clone(),
) {
StoredValue::Local { local_id, .. } => local_id,
_ => internal_error!("A pointer will always be an i32"),
};
self.code_builder.get_local(frame_ptr);
self.code_builder.i32_const(offset as i32);
self.code_builder.i32_add();
self.code_builder.set_local(ptr_local_id);
}
/// Generate a refcount helper procedure and return a pointer (table index) to it /// Generate a refcount helper procedure and return a pointer (table index) to it
/// This allows it to be indirectly called from Zig code /// This allows it to be indirectly called from Zig code
pub fn get_refcount_fn_index(&mut self, layout: InLayout<'a>, op: HelperOp) -> u32 { pub fn get_refcount_fn_index(&mut self, layout: InLayout<'a>, op: HelperOp) -> u32 {

View file

@ -2048,6 +2048,10 @@ impl<'a> LowLevelCall<'a> {
StoredValue::StackMemory { .. } => { /* do nothing */ } StoredValue::StackMemory { .. } => { /* do nothing */ }
}, },
DictPseudoSeed => self.load_args_and_call_zig(backend, bitcode::UTILS_DICT_PSEUDO_SEED), DictPseudoSeed => self.load_args_and_call_zig(backend, bitcode::UTILS_DICT_PSEUDO_SEED),
SetJmp | LongJmp | SetLongJmpBuffer => {
unreachable!("only inserted in dev backend codegen")
}
} }
} }

View file

@ -131,6 +131,9 @@ pub enum LowLevel {
UnboxExpr, UnboxExpr,
Unreachable, Unreachable,
DictPseudoSeed, DictPseudoSeed,
SetJmp,
LongJmp,
SetLongJmpBuffer,
} }
macro_rules! higher_order { macro_rules! higher_order {
@ -241,6 +244,10 @@ macro_rules! map_symbol_to_lowlevel {
LowLevel::RefCountDecDataPtr=> unimplemented!(), LowLevel::RefCountDecDataPtr=> unimplemented!(),
LowLevel::RefCountIsUnique => unimplemented!(), LowLevel::RefCountIsUnique => unimplemented!(),
LowLevel::SetJmp => unimplemented!(),
LowLevel::LongJmp => unimplemented!(),
LowLevel::SetLongJmpBuffer => unimplemented!(),
// these are not implemented, not sure why // these are not implemented, not sure why
LowLevel::StrFromInt => unimplemented!(), LowLevel::StrFromInt => unimplemented!(),
LowLevel::StrFromFloat => unimplemented!(), LowLevel::StrFromFloat => unimplemented!(),

View file

@ -30,6 +30,7 @@ pub enum UseKind {
ErasedMake(ErasedField), ErasedMake(ErasedField),
Erased, Erased,
FunctionPointer, FunctionPointer,
Alloca,
} }
pub enum ProblemKind<'a> { pub enum ProblemKind<'a> {
@ -524,6 +525,17 @@ impl<'a, 'r> Ctx<'a, 'r> {
self.check_sym_exists(symbol); self.check_sym_exists(symbol);
None None
} }
Expr::Alloca {
initializer,
element_layout,
} => {
if let Some(initializer) = initializer {
self.check_sym_exists(*initializer);
self.check_sym_layout(*initializer, *element_layout, UseKind::Alloca);
}
None
}
Expr::RuntimeErrorFunction(_) => None, Expr::RuntimeErrorFunction(_) => None,
} }
} }

View file

@ -513,6 +513,7 @@ fn format_use_kind(use_kind: UseKind) -> &'static str {
}, },
UseKind::Erased => "erasure", UseKind::Erased => "erasure",
UseKind::FunctionPointer => "function pointer", UseKind::FunctionPointer => "function pointer",
UseKind::Alloca => "alloca initializer",
} }
} }

View file

@ -247,6 +247,7 @@ fn specialize_drops_stmt<'a, 'i>(
RuntimeErrorFunction(_) RuntimeErrorFunction(_)
| FunctionPointer { .. } | FunctionPointer { .. }
| GetTagId { .. } | GetTagId { .. }
| Alloca { .. }
| EmptyArray | EmptyArray
| NullPointer => { /* do nothing */ } | NullPointer => { /* do nothing */ }
} }
@ -1620,6 +1621,8 @@ fn low_level_no_rc(lowlevel: &LowLevel) -> RC {
| RefCountDecDataPtr | RefCountIsUnique => { | RefCountDecDataPtr | RefCountIsUnique => {
unreachable!("Only inserted *after* borrow checking: {:?}", lowlevel); unreachable!("Only inserted *after* borrow checking: {:?}", lowlevel);
} }
SetJmp | LongJmp | SetLongJmpBuffer => unreachable!("only inserted in dev backend codegen"),
} }
} }

View file

@ -1138,6 +1138,9 @@ fn insert_refcount_operations_binding<'a>(
Expr::Reset { .. } | Expr::ResetRef { .. } => { Expr::Reset { .. } | Expr::ResetRef { .. } => {
unreachable!("Reset(ref) should not exist at this point") unreachable!("Reset(ref) should not exist at this point")
} }
Expr::Alloca { .. } => {
unreachable!("Alloca should not exist at this point")
}
} }
} }
@ -1366,6 +1369,10 @@ fn lowlevel_borrow_signature(arena: &Bump, op: LowLevel) -> &[Ownership] {
PtrCast => arena.alloc_slice_copy(&[owned]), PtrCast => arena.alloc_slice_copy(&[owned]),
Alloca => arena.alloc_slice_copy(&[owned]), Alloca => arena.alloc_slice_copy(&[owned]),
SetJmp | LongJmp | SetLongJmpBuffer => {
unreachable!("only inserted in dev backend codegen")
}
PtrClearTagId | RefCountIncRcPtr | RefCountDecRcPtr | RefCountIncDataPtr PtrClearTagId | RefCountIncRcPtr | RefCountDecRcPtr | RefCountIncDataPtr
| RefCountDecDataPtr | RefCountIsUnique => { | RefCountDecDataPtr | RefCountIsUnique => {
unreachable!("Only inserted *after* borrow checking: {:?}", op); unreachable!("Only inserted *after* borrow checking: {:?}", op);

View file

@ -329,7 +329,7 @@ pub enum HostExposedLayouts<'a> {
}, },
} }
#[derive(Clone, Debug, PartialEq, Eq)] #[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum SelfRecursive { pub enum SelfRecursive {
NotSelfRecursive, NotSelfRecursive,
SelfRecursive(JoinPointId), SelfRecursive(JoinPointId),
@ -1924,6 +1924,11 @@ pub enum Expr<'a> {
lambda_name: LambdaName<'a>, lambda_name: LambdaName<'a>,
}, },
Alloca {
element_layout: InLayout<'a>,
initializer: Option<Symbol>,
},
Reset { Reset {
symbol: Symbol, symbol: Symbol,
update_mode: UpdateModeId, update_mode: UpdateModeId,
@ -2142,7 +2147,7 @@ impl<'a> Expr<'a> {
structure, structure,
index, index,
.. ..
} => text!(alloc, "UnionAtIndex (Id {}) (Index {}) ", tag_id, index) } => text!(alloc, "UnionAtIndex (Id {tag_id}) (Index {index}) ")
.append(symbol_to_doc(alloc, *structure, pretty)), .append(symbol_to_doc(alloc, *structure, pretty)),
UnionFieldPtrAtIndex { UnionFieldPtrAtIndex {
@ -2150,13 +2155,15 @@ impl<'a> Expr<'a> {
structure, structure,
index, index,
.. ..
} => text!( } => text!(alloc, "UnionFieldPtrAtIndex (Id {tag_id}) (Index {index}) ",)
alloc, .append(symbol_to_doc(alloc, *structure, pretty)),
"UnionFieldPtrAtIndex (Id {}) (Index {}) ",
tag_id, Alloca { initializer, .. } => match initializer {
index Some(initializer) => {
) text!(alloc, "Alloca ").append(symbol_to_doc(alloc, *initializer, pretty))
.append(symbol_to_doc(alloc, *structure, pretty)), }
None => text!(alloc, "Alloca <uninitialized>"),
},
} }
} }
@ -7935,6 +7942,17 @@ fn substitute_in_expr<'a>(
}), }),
None => None, None => None,
}, },
Alloca {
element_layout,
initializer,
} => match substitute(subs, (*initializer)?) {
Some(initializer) => Some(Alloca {
element_layout: *element_layout,
initializer: Some(initializer),
}),
None => None,
},
} }
} }

View file

@ -1109,6 +1109,7 @@ fn expr_contains_symbol(expr: &Expr, needle: Symbol) -> bool {
value.map(|v| v == needle).unwrap_or(false) || needle == *callee value.map(|v| v == needle).unwrap_or(false) || needle == *callee
} }
Expr::ErasedLoad { symbol, field: _ } => needle == *symbol, Expr::ErasedLoad { symbol, field: _ } => needle == *symbol,
Expr::Alloca { initializer, .. } => &Some(needle) == initializer,
} }
} }