Skip to content

Commit 1069c02

Browse files
committed
Finished implementing ability to call normal functions at compile-time
1 parent 1a79b43 commit 1069c02

File tree

11 files changed

+215
-117
lines changed

11 files changed

+215
-117
lines changed

src/components/job/src/cfg/mod.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,12 @@ impl<'env> Cfg<'env> {
116116
})
117117
}
118118

119+
pub fn iter_instrs_unordered(&self) -> impl Iterator<Item = &Instr<'env>> {
120+
self.basicblocks
121+
.iter()
122+
.flat_map(|(_bb_id, bb)| bb.instrs.iter())
123+
}
124+
119125
pub fn get_typed(
120126
&self,
121127
cfg_value: CfgValue,

src/components/job/src/conform/to_default.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use primitives::{CIntegerAssumptions, IntegerSign};
1111
use source_files::Source;
1212
use target::Target;
1313

14-
#[derive(Debug)]
14+
#[derive(Clone, Debug)]
1515
pub struct Conform<'env> {
1616
pub ty: UnaliasedType<'env>,
1717
pub cast: Option<UnaryCast<'env>>,
@@ -42,15 +42,16 @@ impl<'env> Conform<'env> {
4242
panic!("cannot implicitly dereference non deref'$T type");
4343
};
4444

45+
from_ty = UnaliasedType(after_deref);
46+
4547
result = Self::new(
4648
from_ty,
4749
UnaryCast::Dereference {
48-
after_deref: UnaliasedType(*after_deref),
50+
after_deref: from_ty,
4951
then: std::mem::take(&mut result.cast).map(|cast| &*ctx.alloc(cast)),
5052
},
5153
);
5254

53-
from_ty = UnaliasedType(after_deref);
5455
count -= 1;
5556
}
5657

src/components/job/src/execution/lower/function_body/mod.rs

Lines changed: 31 additions & 42 deletions
Large diffs are not rendered by default.

src/components/job/src/execution/lower/function_head.rs

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use crate::{
22
Continuation, Executable, ExecutionCtx, Executor, SuspendMany, execution::lower::LowerType, ir,
3-
module_graph::ModuleView, repr::FuncHead,
3+
repr::FuncHead,
44
};
55
use by_address::ByAddress;
66
use derivative::Derivative;
@@ -9,7 +9,6 @@ use std::sync::OnceLock;
99
#[derive(Clone, Derivative)]
1010
#[derivative(Debug, PartialEq, Eq, Hash)]
1111
pub struct LowerFunctionHead<'env> {
12-
view: &'env ModuleView<'env>,
1312
head: ByAddress<&'env FuncHead<'env>>,
1413

1514
#[derivative(Hash = "ignore")]
@@ -19,9 +18,8 @@ pub struct LowerFunctionHead<'env> {
1918
}
2019

2120
impl<'env> LowerFunctionHead<'env> {
22-
pub fn new(view: &'env ModuleView<'env>, head: &'env FuncHead<'env>) -> Self {
21+
pub fn new(head: &'env FuncHead<'env>) -> Self {
2322
Self {
24-
view,
2523
head: ByAddress(head),
2624
inner_types: None,
2725
}
@@ -36,7 +34,8 @@ impl<'env> Executable<'env> for LowerFunctionHead<'env> {
3634
executor: &Executor<'env>,
3735
ctx: &mut ExecutionCtx<'env>,
3836
) -> Result<Self::Output, Continuation<'env>> {
39-
let ir = self.view.web.graph(self.view.graph, |graph| graph.ir);
37+
let view = self.head.view;
38+
let ir = view.web.graph(view.graph, |graph| graph.ir);
4039

4140
let Some(inner_types) = executor.demand_many(&self.inner_types) else {
4241
let suspend_on_types = self
@@ -50,7 +49,7 @@ impl<'env> Executable<'env> for LowerFunctionHead<'env> {
5049
return suspend_many!(
5150
self.inner_types,
5251
suspend_on_types
53-
.map(|ty| executor.request(LowerType::new(self.view, ty.0)))
52+
.map(|ty| executor.request(LowerType::new(view, ty.0)))
5453
.collect(),
5554
ctx
5655
);

src/components/job/src/execution/process/function.rs

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ impl<'env> Executable<'env> for ProcessFunction<'env> {
7979
let Some(resolved_head) = executor.demand(self.resolving_head) else {
8080
return suspend!(
8181
self.resolving_head,
82-
executor.request(ResolveFunctionHead::new(self.view, &self.func.head)),
82+
executor.request(ResolveFunctionHead::new(self.view, &self.func)),
8383
ctx
8484
);
8585
};
@@ -105,11 +105,7 @@ impl<'env> Executable<'env> for ProcessFunction<'env> {
105105
let Some(resolved_body) = executor.demand(self.resolving_body) else {
106106
return suspend!(
107107
self.resolving_body,
108-
executor.request(ResolveFunctionBody::new(
109-
self.view,
110-
&self.func,
111-
resolved_head
112-
)),
108+
executor.request(ResolveFunctionBody::new(self.view, resolved_head)),
113109
ctx
114110
);
115111
};
@@ -126,7 +122,7 @@ impl<'env> Executable<'env> for ProcessFunction<'env> {
126122
let Some(lowered_head) = executor.demand(self.lowering_head) else {
127123
return suspend!(
128124
self.lowering_head,
129-
executor.request(LowerFunctionHead::new(self.view, resolved_head)),
125+
executor.request(LowerFunctionHead::new(resolved_head)),
130126
ctx
131127
);
132128
};
@@ -135,7 +131,6 @@ impl<'env> Executable<'env> for ProcessFunction<'env> {
135131
return suspend!(
136132
self.lowering_body,
137133
executor.request(LowerFunctionBody::new(
138-
self.view,
139134
lowered_head,
140135
resolved_head,
141136
resolved_body

src/components/job/src/execution/resolve/evaluate.rs

Lines changed: 125 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use crate::{
2-
Continuation, Executable, ExecutionCtx, Executor, Suspend,
2+
Continuation, Executable, ExecutionCtx, Executor, InstrKind, Pending, Suspend,
33
execution::{
44
lower::{LowerFunctionBody, LowerFunctionHead},
55
resolve::{ResolveFunctionBody, ResolveFunctionHead},
@@ -13,6 +13,8 @@ use attributes::{Exposure, Privacy, SymbolOwnership, Tag};
1313
use by_address::ByAddress;
1414
use derivative::Derivative;
1515
use diagnostics::ErrorDiagnostic;
16+
use itertools::Itertools;
17+
use std::collections::HashMap;
1618

1719
#[derive(Clone, Derivative)]
1820
#[derivative(Debug, PartialEq, Eq, Hash)]
@@ -44,6 +46,30 @@ pub struct ResolveEvaluation<'env> {
4446
#[derivative(Debug = "ignore")]
4547
#[derivative(PartialEq = "ignore")]
4648
lowered_func_body: Suspend<'env, ()>,
49+
50+
#[derivative(Hash = "ignore")]
51+
#[derivative(Debug = "ignore")]
52+
#[derivative(PartialEq = "ignore")]
53+
transitive_func_state: TransitiveFuncState<'env>,
54+
}
55+
56+
/// State machine for completing transitive function dependencies
57+
#[derive(Clone)]
58+
enum TransitiveFuncState<'env> {
59+
Initialize,
60+
Step {
61+
scan_next: Vec<&'env FuncBody<'env>>,
62+
resolved_bodies: HashMap<ByAddress<&'env FuncHead<'env>>, &'env FuncBody<'env>>,
63+
requests: HashMap<ByAddress<&'env FuncHead<'env>>, Pending<'env, &'env FuncBody<'env>>>,
64+
},
65+
LowerBodies(
66+
Vec<(
67+
&'env FuncHead<'env>,
68+
&'env FuncBody<'env>,
69+
Pending<'env, ir::FuncRef<'env>>,
70+
)>,
71+
),
72+
Finished,
4773
}
4874

4975
impl<'env> ResolveEvaluation<'env> {
@@ -56,6 +82,7 @@ impl<'env> ResolveEvaluation<'env> {
5682
resolved_func_body: None,
5783
lowered_func_head: None,
5884
lowered_func_body: None,
85+
transitive_func_state: TransitiveFuncState::Initialize,
5986
}
6087
}
6188
}
@@ -96,7 +123,7 @@ impl<'env> Executable<'env> for ResolveEvaluation<'env> {
96123
let Some(resolved_func_head) = executor.demand(self.resolved_func_head) else {
97124
return suspend!(
98125
self.resolved_func_head,
99-
executor.request(ResolveFunctionHead::new(self.comptime_view, &ast_func.head)),
126+
executor.request(ResolveFunctionHead::new(self.comptime_view, &ast_func)),
100127
ctx
101128
);
102129
};
@@ -106,30 +133,114 @@ impl<'env> Executable<'env> for ResolveEvaluation<'env> {
106133
self.resolved_func_body,
107134
executor.request(ResolveFunctionBody::new(
108135
self.comptime_view,
109-
ast_func,
110136
resolved_func_head
111137
)),
112138
ctx
113139
);
114140
};
115141

116-
// 3) Lower the function
142+
// 3) Resolve transitive function dependencies
143+
if let TransitiveFuncState::Initialize = &self.transitive_func_state {
144+
self.transitive_func_state = TransitiveFuncState::Step {
145+
scan_next: vec![resolved_func_body],
146+
resolved_bodies: HashMap::new(),
147+
requests: HashMap::new(),
148+
};
149+
}
150+
151+
match &mut self.transitive_func_state {
152+
TransitiveFuncState::Initialize => unreachable!(),
153+
TransitiveFuncState::Step {
154+
scan_next,
155+
resolved_bodies,
156+
requests,
157+
} => {
158+
// Receive completed resolved function bodies
159+
{
160+
let truth = executor.truth.read().unwrap();
161+
for (head, pending) in std::mem::take(requests) {
162+
let body = truth.demand(pending);
163+
resolved_bodies.insert(head, body);
164+
scan_next.push(body);
165+
}
166+
}
167+
168+
// Search newly resolved function bodies
169+
for body in scan_next.drain(..) {
170+
for instr in body.cfg.iter_instrs_unordered() {
171+
// For any function calls made
172+
if let InstrKind::Call(_, call_target) = &instr.kind {
173+
let call_target = call_target.as_ref().unwrap();
174+
let key = ByAddress(call_target.callee);
175+
176+
// If we didn't already resolve the callee and aren't already planning
177+
// to request the callee to be resolved
178+
if !resolved_bodies.contains_key(&key) && !requests.contains_key(&key) {
179+
// Then add the callee to the set of functions to resolve in the next
180+
// suspend.
181+
requests.insert(
182+
key,
183+
executor.request(ResolveFunctionBody::new(key.view, key.0)),
184+
);
185+
}
186+
}
187+
}
188+
}
189+
190+
// Suspend and continue stepping if any requests need to be made
191+
if !requests.is_empty() {
192+
ctx.suspend_on(requests.iter().map(|(_, pending)| pending));
193+
return Err(Continuation::suspend(self));
194+
}
195+
196+
// Otherwise, all function CFG bodies have been resolved,
197+
// and now we have to lower all of the function heads used...
198+
let mut pending_heads = Vec::new();
199+
for (head, body) in resolved_bodies {
200+
let request = executor.request(LowerFunctionHead::new(head));
201+
pending_heads.push((**head, *body, request));
202+
}
203+
204+
// Suspend on all function heads being lowered that we need
205+
ctx.suspend_on(pending_heads.iter().map(|(_, _, pending)| pending));
206+
self.transitive_func_state = TransitiveFuncState::LowerBodies(pending_heads);
207+
return Err(Continuation::suspend(self));
208+
}
209+
TransitiveFuncState::LowerBodies(pending_heads) => {
210+
// Extract lowered function heads that we waited on
211+
let pending = {
212+
let truth = executor.truth.read().unwrap();
213+
pending_heads
214+
.into_iter()
215+
.map(|(head, body, request)| {
216+
let ir_func_ref = truth.demand(*request);
217+
LowerFunctionBody::new(*ir_func_ref, head, body)
218+
})
219+
.collect_vec()
220+
};
221+
222+
// Suspend on all function bodies being lowered that we need
223+
ctx.suspend_on(executor.request_many(pending.into_iter()));
224+
self.transitive_func_state = TransitiveFuncState::Finished;
225+
return Err(Continuation::suspend(self));
226+
}
227+
TransitiveFuncState::Finished => (),
228+
}
229+
230+
// 4) Lower the entry point function head
117231
let Some(lowered_func_head) = executor.demand(self.lowered_func_head) else {
118232
return suspend!(
119233
self.lowered_func_head,
120-
executor.request(LowerFunctionHead::new(
121-
self.comptime_view,
122-
resolved_func_head
123-
)),
234+
executor.request(LowerFunctionHead::new(resolved_func_head)),
124235
ctx
125236
);
126237
};
127238

239+
// 5) Lower the entry point function body
128240
let Some(_lowered_func_body) = executor.demand(self.lowered_func_body) else {
129241
return suspend!(
130242
self.lowered_func_body,
131243
executor.request(LowerFunctionBody::new(
132-
self.comptime_view,
133244
lowered_func_head,
134245
resolved_func_head,
135246
resolved_func_body,
@@ -138,10 +249,10 @@ impl<'env> Executable<'env> for ResolveEvaluation<'env> {
138249
);
139250
};
140251

141-
// 4) Obtain the intermediate representation for comptime so far
252+
// 6) Obtain the intermediate representation for comptime so far
142253
let ir = self.comptime_view.graph(|graph| graph.ir);
143254

144-
// 5) Interpret the function and raise any interpretation errors
255+
// 7) Interpret the function and raise any interpretation errors
145256
let mut interpreter =
146257
Interpreter::new(ComptimeSystemSyscallHandler::default(), ir, Some(1_000_000));
147258

@@ -152,10 +263,10 @@ impl<'env> Executable<'env> for ResolveEvaluation<'env> {
152263
// The actual entry point result should be void
153264
entry_point_result.kind.unwrap_literal().unwrap_void();
154265

155-
// 6) Examine the result value that was baked by the function
266+
// 8) Examine the result value that was baked by the function
156267
let exit_value = interpreter.exit_value();
157268

158-
// 7) Expect that the exit value is transferrable
269+
// 9) Expect that the exit value is transferrable
159270
let Some(exit_value) = exit_value else {
160271
return Err(ErrorDiagnostic::new(
161272
"Compile-time evaluation must evaluate to transferable value",
@@ -164,7 +275,7 @@ impl<'env> Executable<'env> for ResolveEvaluation<'env> {
164275
.into());
165276
};
166277

167-
// 8) Translate the constant value into a literal value
278+
// 10) Translate the constant value into a literal value
168279
// and/or static data that can be used as a literal.
169280
Ok(ctx.alloc(Evaluated::new_unsigned(exit_value)))
170281

src/components/job/src/execution/resolve/function_body/mod.rs

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,6 @@ use std::collections::HashSet;
3636
#[derivative(Debug, PartialEq, Eq, Hash)]
3737
pub struct ResolveFunctionBody<'env> {
3838
view: &'env ModuleView<'env>,
39-
func: ByAddress<&'env ast::Func>,
4039
resolved_head: ByAddress<&'env FuncHead<'env>>,
4140

4241
#[derivative(Hash = "ignore")]
@@ -86,14 +85,9 @@ pub struct ResolveFunctionBody<'env> {
8685
}
8786

8887
impl<'env> ResolveFunctionBody<'env> {
89-
pub fn new(
90-
view: &'env ModuleView<'env>,
91-
func: &'env ast::Func,
92-
resolved_head: &'env FuncHead<'env>,
93-
) -> Self {
88+
pub fn new(view: &'env ModuleView<'env>, resolved_head: &'env FuncHead<'env>) -> Self {
9489
Self {
9590
view,
96-
func: ByAddress(func),
9791
resolved_head: ByAddress(resolved_head),
9892
inner_types: None,
9993
rev_post_order: None,
@@ -136,7 +130,7 @@ impl<'env> Executable<'env> for ResolveFunctionBody<'env> {
136130
executor: &Executor<'env>,
137131
ctx: &mut ExecutionCtx<'env>,
138132
) -> Result<Self::Output, Continuation<'env>> {
139-
let def = self.func;
133+
let def = self.resolved_head.ast_func;
140134
let builtin_types = self.view.compiler().builtin_types;
141135

142136
// 0) Foreign functions do not have bodies
@@ -1168,9 +1162,14 @@ impl<'env> Executable<'env> for ResolveFunctionBody<'env> {
11681162
)?;
11691163

11701164
if !conformed.ty.0.kind.is_boolean() {
1171-
return Err(
1172-
ErrorDiagnostic::new("Expected 'bool' value", instr.source).into()
1173-
);
1165+
return Err(ErrorDiagnostic::new(
1166+
format!(
1167+
"Expected `bool` value, got `{}`",
1168+
conformed.ty.display_one(self.view)
1169+
),
1170+
instr.source,
1171+
)
1172+
.into());
11741173
}
11751174

11761175
cfg.set_typed(instr_ref, conformed.ty);

0 commit comments

Comments
 (0)