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

Channels, fibers, oh my! #241

Merged
merged 64 commits into from
Oct 24, 2022
Merged
Show file tree
Hide file tree
Changes from 63 commits
Commits
Show all changes
64 commits
Select commit Hold shift + click to select a range
86d21e8
Add channels
MarcelGarus Aug 18, 2022
5062d56
Add ports
MarcelGarus Aug 18, 2022
d5d921a
Refactor Heap::create_list
MarcelGarus Aug 18, 2022
54a0429
Start implementing builtins
MarcelGarus Aug 18, 2022
ba2475e
Implement channels
MarcelGarus Aug 18, 2022
ea50a6e
Start implementing fibers
MarcelGarus Aug 19, 2022
b85327d
Continue implementing fibers
MarcelGarus Aug 19, 2022
47b06b5
Update docs
MarcelGarus Aug 19, 2022
37fb3cb
Fix Int
MarcelGarus Aug 21, 2022
cb0c211
Use Context instead of UseProvider when running code
MarcelGarus Aug 21, 2022
7925556
Update todos
MarcelGarus Aug 26, 2022
4ab75e0
Create better benchmark
MarcelGarus Aug 26, 2022
832f7ae
Continue implementing the pushing of operations
MarcelGarus Aug 26, 2022
3d8dd67
Merged 'better-logging' into channels
MarcelGarus Aug 26, 2022
12f8381
Continue implementing channel operations
MarcelGarus Aug 27, 2022
4620266
Merge branch 'main' into channels
MarcelGarus Sep 3, 2022
01b5ca3
Make channel more efficient
MarcelGarus Sep 3, 2022
6b41d82
Specify Rust version
MarcelGarus Sep 4, 2022
6bc1bae
Improve Packet's Debug implementation
MarcelGarus Sep 4, 2022
fa02344
Continue implementing channel (WIP)
MarcelGarus Sep 4, 2022
2814e69
Comment out Fibonacci code
MarcelGarus Sep 4, 2022
33d7a28
Improve debug output
MarcelGarus Sep 4, 2022
3ac34ec
Better todos
MarcelGarus Sep 5, 2022
e2f0a30
Restructure code
MarcelGarus Sep 5, 2022
af9ff38
Completely refactor VM architecture
MarcelGarus Sep 6, 2022
8c9ef93
Implement spawning of fibers
MarcelGarus Sep 7, 2022
a762c53
Continue implementing channels
MarcelGarus Sep 7, 2022
0602c32
Make channels work
MarcelGarus Sep 7, 2022
b09a84e
Improve code quality
MarcelGarus Sep 7, 2022
11cbc29
Improve debug output
MarcelGarus Sep 7, 2022
3b8d791
Restructure code
MarcelGarus Sep 8, 2022
0e01979
Propagate panics happening in parallel scope
MarcelGarus Sep 8, 2022
d5f95e9
Remove canceled fibers
MarcelGarus Sep 8, 2022
48d5dff
Clean up code
MarcelGarus Sep 8, 2022
14230e8
Implement try
MarcelGarus Sep 10, 2022
8f89e96
Call main function
MarcelGarus Sep 10, 2022
e0d59ce
Merge branch 'channels' of https://github.com/candy-lang/candy into c…
MarcelGarus Sep 11, 2022
8391286
Clean up cargo lints
MarcelGarus Sep 11, 2022
f3f7d49
Add main function to benchmark
MarcelGarus Sep 11, 2022
a22f716
Implement parallel builtin without channels
MarcelGarus Sep 11, 2022
d5b5f6e
Improve code quality
MarcelGarus Sep 11, 2022
da2400a
Improve code quality
MarcelGarus Sep 11, 2022
2d15b86
Implement external channels
MarcelGarus Sep 12, 2022
030f805
Don't panic when sending to a dead nursery
MarcelGarus Sep 12, 2022
26d6daa
Make it an error to sent to a dead nursery
MarcelGarus Sep 22, 2022
0db9b4d
Clean up tracer
MarcelGarus Sep 24, 2022
5670716
Fix tracer
MarcelGarus Sep 24, 2022
b370621
Make code more readable
MarcelGarus Sep 24, 2022
9e5a4d8
Refactor context
MarcelGarus Oct 3, 2022
0acb7ad
Use tear down when appropriate
MarcelGarus Oct 3, 2022
989541e
Ignore some lints
MarcelGarus Oct 3, 2022
433cea7
Update todos
MarcelGarus Oct 3, 2022
56d8b57
Ignore more lints
MarcelGarus Oct 3, 2022
7d169c4
Merge branch 'main' into channels
MarcelGarus Oct 10, 2022
c8267ca
Apply minor suggestions from code review
MarcelGarus Oct 10, 2022
8b0ebb8
Update todos
MarcelGarus Oct 10, 2022
c817906
Update Core Candy code
MarcelGarus Oct 10, 2022
3546a2f
Apply more suggestions
MarcelGarus Oct 12, 2022
375fd78
Restructure how channels to the outside work
MarcelGarus Oct 13, 2022
275f00f
Free channels
MarcelGarus Oct 13, 2022
70060b9
Merge branch 'main' into channels
MarcelGarus Oct 17, 2022
f9d8f21
Log less things during fuzzing
MarcelGarus Oct 17, 2022
d57c061
Remove let_else feature flag
MarcelGarus Oct 17, 2022
f95e722
Apply suggestions
MarcelGarus Oct 23, 2022
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
13 changes: 8 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -136,20 +136,23 @@ We already have a language server that provides some tooling.

## Short-term TODOs

- implement fibers, channels, and nurseries
- remove builtinPrint
- fix fault attribution
- rearchitect tracing
- new name?
- add caching while compile-time evaluating code
- tags?
- pattern matching
- pipe operator
- text interpolation
- eliminate common subtrees
- inline functions
- minimize inputs found through fuzzing
- make condition whether to keep running more granular
- fuzz parser
- support recursion
- remove builtinPrint
- tail call optimization
- new name?
- parse function declaration with doc comment but no code
- tracing visualization
- distinguish packages from normal modules
- complain about comment lines with too much indentation
- develop guidelines about how to format reasons

Expand Down
1 change: 1 addition & 0 deletions compiler/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
name = "candy"
version = "0.1.0"
edition = "2021"
rust-version = "1.56"

[profile.release]
debug = true
Expand Down
5 changes: 5 additions & 0 deletions compiler/src/builtin_functions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ use strum_macros::EnumIter;

#[derive(Debug, EnumIter, PartialEq, Eq, Clone, Hash, Copy)]
pub enum BuiltinFunction {
ChannelCreate, // capacity -> [sendPort, receivePort]
ChannelSend, // channel any -> Nothing
ChannelReceive, // channel -> any
Equals, // any any -> booleanSymbol
FunctionRun, // (lambdaWith0Arguments) -> (returnValue: any)
GetArgumentCount, // closure -> argumentCount
Expand All @@ -22,6 +25,7 @@ pub enum BuiltinFunction {
IntShiftLeft, // (value: int) (amount: int) -> (shifted: int)
IntShiftRight, // (value: int) (amount: int) -> (shifted: int)
IntSubtract, // (minuend: int) (subtrahend: int) -> (difference: int)
Parallel, // body: Closure -> returnValueOfClosure
Print, // message -> Nothing
StructGet, // struct key -> value
StructGetKeys, // struct -> listOfKeys
Expand All @@ -36,6 +40,7 @@ pub enum BuiltinFunction {
TextStartsWith, // text (pattern: text) -> booleanSymbol
TextTrimEnd, // text -> text
TextTrimStart, // text -> text
Try, // closure -> okWithClosureResultOrErrorWithPanicReason
TypeOf, // any -> typeSymbol
}
lazy_static! {
Expand Down
99 changes: 46 additions & 53 deletions compiler/src/fuzzer/fuzzer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,12 @@ use super::{generator::generate_n_values, utils::did_need_in_closure_cause_panic
use crate::{
compiler::hir,
database::Database,
vm::{self, tracer::Tracer, use_provider::DbUseProvider, Closure, Heap, Pointer, Vm},
vm::{
self,
context::{ExecutionController, UseProvider},
tracer::Tracer,
Closure, Heap, Pointer, TearDownResult, Vm,
},
};
use std::mem;

Expand Down Expand Up @@ -32,7 +37,7 @@ pub enum Status {
}

impl Status {
fn new_fuzzing_attempt(db: &Database, closure_heap: &Heap, closure: Pointer) -> Status {
fn new_fuzzing_attempt(closure_heap: &Heap, closure: Pointer) -> Status {
let num_args = {
let closure: Closure = closure_heap.get(closure).data.clone().try_into().unwrap();
closure.num_args
Expand All @@ -42,19 +47,19 @@ impl Status {
let closure = closure_heap.clone_single_to_other_heap(&mut vm_heap, closure);
let arguments = generate_n_values(&mut vm_heap, num_args);

let use_provider = DbUseProvider { db };
let vm = Vm::new_for_running_closure(vm_heap, &use_provider, closure, &arguments);
let mut vm = Vm::new();
vm.set_up_for_running_closure(vm_heap, closure, &arguments);

Status::StillFuzzing { vm, arguments }
}
}
impl Fuzzer {
pub fn new(db: &Database, closure_heap: &Heap, closure: Pointer, closure_id: hir::Id) -> Self {
pub fn new(closure_heap: &Heap, closure: Pointer, closure_id: hir::Id) -> Self {
// The given `closure_heap` may contain other fuzzable closures.
let mut heap = Heap::default();
let closure = closure_heap.clone_single_to_other_heap(&mut heap, closure);

let status = Status::new_fuzzing_attempt(db, &heap, closure);
let status = Status::new_fuzzing_attempt(&heap, closure);
Self {
closure_heap: heap,
closure,
Expand All @@ -67,62 +72,53 @@ impl Fuzzer {
self.status.as_ref().unwrap()
}

pub fn run(&mut self, db: &Database, mut num_instructions: usize) {
pub fn run<U: UseProvider, E: ExecutionController>(
&mut self,
db: &Database,
use_provider: &mut U,
execution_controller: &mut E,
) {
let mut status = mem::replace(&mut self.status, None).unwrap();
while matches!(status, Status::StillFuzzing { .. }) {
let (new_status, num_instructions_executed) =
self.map_status(db, status, num_instructions);
status = new_status;

if num_instructions_executed >= num_instructions {
break;
} else {
num_instructions -= num_instructions_executed;
}
while matches!(status, Status::StillFuzzing { .. })
&& execution_controller.should_continue_running()
{
status = self.map_status(status, db, use_provider, execution_controller);
}
self.status = Some(status);
}
fn map_status(
fn map_status<U: UseProvider, E: ExecutionController>(
&self,
db: &Database,
status: Status,
num_instructions: usize,
) -> (Status, usize) {
db: &Database,
use_provider: &mut U,
execution_controller: &mut E,
) -> Status {
match status {
Status::StillFuzzing { mut vm, arguments } => match &vm.status {
vm::Status::Running => {
let use_provider = DbUseProvider { db };
let num_instructions_executed_before = vm.num_instructions_executed;
vm.run(&use_provider, num_instructions);
let num_instruction_executed =
vm.num_instructions_executed - num_instructions_executed_before;
(
Status::StillFuzzing { vm, arguments },
num_instruction_executed,
)
Status::StillFuzzing { mut vm, arguments } => match vm.status() {
vm::Status::CanRun => {
vm.run(use_provider, execution_controller);
Status::StillFuzzing { vm, arguments }
}
vm::Status::WaitingForOperations => panic!("Fuzzing should not have to wait on channel operations because arguments were not channels."),
// The VM finished running without panicking.
vm::Status::Done => (
Status::new_fuzzing_attempt(db, &self.closure_heap, self.closure),
0,
),
vm::Status::Done => Status::new_fuzzing_attempt(&self.closure_heap, self.closure),
vm::Status::Panicked { reason } => {
// If a `needs` directly inside the tested closure was not
// satisfied, then the panic is not closure's fault, but our
// fault.
let TearDownResult { heap, tracer, .. } = vm.tear_down();
let is_our_fault =
did_need_in_closure_cause_panic(db, &self.closure_id, &vm.tracer);
let status = if is_our_fault {
Status::new_fuzzing_attempt(db, &self.closure_heap, self.closure)
did_need_in_closure_cause_panic(db, &self.closure_id, &tracer);
if is_our_fault {
Status::new_fuzzing_attempt(&self.closure_heap, self.closure)
} else {
Status::PanickedForArguments {
heap: vm.heap,
heap,
arguments,
reason: reason.clone(),
tracer: vm.tracer.clone(),
reason,
tracer,
}
};
(status, 0)
}
}
},
// We already found some arguments that caused the closure to panic,
Expand All @@ -132,15 +128,12 @@ impl Fuzzer {
arguments,
reason,
tracer,
} => (
Status::PanickedForArguments {
heap,
arguments,
reason,
tracer,
},
0,
),
} => Status::PanickedForArguments {
heap,
arguments,
reason,
tracer,
},
}
}
}
25 changes: 16 additions & 9 deletions compiler/src/fuzzer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,20 @@ pub use self::fuzzer::{Fuzzer, Status};
use crate::{
database::Database,
module::Module,
vm::{use_provider::DbUseProvider, Closure, Vm},
vm::{
context::{DbUseProvider, RunForever, RunLimitedNumberOfInstructions},
Closure, Vm,
},
};
use itertools::Itertools;
use tracing::info;

pub async fn fuzz(db: &Database, module: Module) {
let (fuzzables_heap, fuzzables) = {
let result = Vm::new_for_running_module_closure(
&DbUseProvider { db },
Closure::of_module(db, module.clone()).unwrap(),
)
.run_synchronously_until_completion(db);
let mut vm = Vm::new();
vm.set_up_for_running_module_closure(Closure::of_module(db, module.clone()).unwrap());
vm.run(&mut DbUseProvider { db }, &mut RunForever);
let result = vm.tear_down();
(result.heap, result.fuzzable_closures)
};

Expand All @@ -27,8 +29,13 @@ pub async fn fuzz(db: &Database, module: Module) {
);

for (id, closure) in fuzzables {
let mut fuzzer = Fuzzer::new(db, &fuzzables_heap, closure, id.clone());
fuzzer.run(db, 1000);
info!("Fuzzing {id}.");
let mut fuzzer = Fuzzer::new(&fuzzables_heap, closure, id.clone());
fuzzer.run(
db,
&mut DbUseProvider { db },
&mut RunLimitedNumberOfInstructions::new(1000),
);
match fuzzer.status() {
Status::StillFuzzing { .. } => {}
Status::PanickedForArguments {
Expand All @@ -48,7 +55,7 @@ pub async fn fuzz(db: &Database, module: Module) {
info!("This was the stack trace:");
tracer.dump_stack_trace(db, heap);

module.dump_associated_debug_file("trace", &tracer.format_call_tree(heap));
module.dump_associated_debug_file("trace", &tracer.full_trace().format(heap));
}
}
}
Expand Down
6 changes: 3 additions & 3 deletions compiler/src/fuzzer/utils.rs
Original file line number Diff line number Diff line change
@@ -1,23 +1,23 @@
use crate::{
compiler::hir::{self, Expression, HirDb, Lambda},
database::Database,
vm::tracer::{TraceEntry, Tracer},
vm::tracer::{EventData, Tracer},
};

pub fn did_need_in_closure_cause_panic(
db: &Database,
closure_id: &hir::Id,
tracer: &Tracer,
) -> bool {
let entry = if let Some(entry) = tracer.log().last() {
let entry = if let Some(entry) = tracer.events.last() {
entry
} else {
// The only way there's no trace log before the panic is when there's an
// error from an earlier compilation stage that got lowered into the
// LIR. That's also definitely the fault of the function.
return false;
};
if let TraceEntry::NeedsStarted { id, .. } = entry {
if let EventData::NeedsStarted { id, .. } = &entry.data {
let mut id = id.parent().unwrap();
loop {
if &id == closure_id {
Expand Down
Loading